summaryrefslogtreecommitdiffstats
path: root/vendor/xorm.io
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/xorm.io')
-rw-r--r--vendor/xorm.io/xorm/.drone.yml771
-rw-r--r--vendor/xorm.io/xorm/.gitignore33
-rw-r--r--vendor/xorm.io/xorm/CONTRIBUTING.md46
-rw-r--r--vendor/xorm.io/xorm/LICENSE27
-rw-r--r--vendor/xorm.io/xorm/README.md503
-rw-r--r--vendor/xorm.io/xorm/README_CN.md498
-rw-r--r--vendor/xorm.io/xorm/cache_lru.go284
-rw-r--r--vendor/xorm.io/xorm/cache_memory_store.go51
-rw-r--r--vendor/xorm.io/xorm/context_cache.go30
-rw-r--r--vendor/xorm.io/xorm/convert.go348
-rw-r--r--vendor/xorm.io/xorm/dialect_mssql.go567
-rw-r--r--vendor/xorm.io/xorm/dialect_mysql.go654
-rw-r--r--vendor/xorm.io/xorm/dialect_oracle.go902
-rw-r--r--vendor/xorm.io/xorm/dialect_postgres.go1253
-rw-r--r--vendor/xorm.io/xorm/dialect_sqlite3.go492
-rw-r--r--vendor/xorm.io/xorm/doc.go184
-rw-r--r--vendor/xorm.io/xorm/engine.go1659
-rw-r--r--vendor/xorm.io/xorm/engine_cond.go232
-rw-r--r--vendor/xorm.io/xorm/engine_context.go28
-rw-r--r--vendor/xorm.io/xorm/engine_group.go219
-rw-r--r--vendor/xorm.io/xorm/engine_group_policy.go116
-rw-r--r--vendor/xorm.io/xorm/engine_table.go113
-rw-r--r--vendor/xorm.io/xorm/error.go51
-rw-r--r--vendor/xorm.io/xorm/gen_reserved.sh6
-rw-r--r--vendor/xorm.io/xorm/go.mod15
-rw-r--r--vendor/xorm.io/xorm/go.sum149
-rw-r--r--vendor/xorm.io/xorm/helpers.go332
-rw-r--r--vendor/xorm.io/xorm/helpler_time.go21
-rw-r--r--vendor/xorm.io/xorm/interface.go118
-rw-r--r--vendor/xorm.io/xorm/json.go31
-rw-r--r--vendor/xorm.io/xorm/logger.go187
-rw-r--r--vendor/xorm.io/xorm/pg_reserved.txt746
-rw-r--r--vendor/xorm.io/xorm/processors.go78
-rw-r--r--vendor/xorm.io/xorm/rows.go121
-rw-r--r--vendor/xorm.io/xorm/session.go866
-rw-r--r--vendor/xorm.io/xorm/session_cols.go156
-rw-r--r--vendor/xorm.io/xorm/session_cond.go70
-rw-r--r--vendor/xorm.io/xorm/session_context.go23
-rw-r--r--vendor/xorm.io/xorm/session_convert.go671
-rw-r--r--vendor/xorm.io/xorm/session_delete.go244
-rw-r--r--vendor/xorm.io/xorm/session_exist.go96
-rw-r--r--vendor/xorm.io/xorm/session_find.go505
-rw-r--r--vendor/xorm.io/xorm/session_get.go356
-rw-r--r--vendor/xorm.io/xorm/session_insert.go878
-rw-r--r--vendor/xorm.io/xorm/session_iterate.go100
-rw-r--r--vendor/xorm.io/xorm/session_query.go320
-rw-r--r--vendor/xorm.io/xorm/session_raw.go227
-rw-r--r--vendor/xorm.io/xorm/session_schema.go430
-rw-r--r--vendor/xorm.io/xorm/session_stats.go98
-rw-r--r--vendor/xorm.io/xorm/session_tx.go83
-rw-r--r--vendor/xorm.io/xorm/session_update.go525
-rw-r--r--vendor/xorm.io/xorm/statement.go1256
-rw-r--r--vendor/xorm.io/xorm/statement_args.go170
-rw-r--r--vendor/xorm.io/xorm/statement_columnmap.go35
-rw-r--r--vendor/xorm.io/xorm/statement_exprparam.go117
-rw-r--r--vendor/xorm.io/xorm/statement_quote.go19
-rw-r--r--vendor/xorm.io/xorm/syslogger.go89
-rw-r--r--vendor/xorm.io/xorm/tag.go311
-rw-r--r--vendor/xorm.io/xorm/test_mssql.sh1
-rw-r--r--vendor/xorm.io/xorm/test_mssql_cache.sh1
-rw-r--r--vendor/xorm.io/xorm/test_mymysql.sh1
-rw-r--r--vendor/xorm.io/xorm/test_mymysql_cache.sh1
-rw-r--r--vendor/xorm.io/xorm/test_mysql.sh1
-rw-r--r--vendor/xorm.io/xorm/test_mysql_cache.sh1
-rw-r--r--vendor/xorm.io/xorm/test_postgres.sh1
-rw-r--r--vendor/xorm.io/xorm/test_postgres_cache.sh1
-rw-r--r--vendor/xorm.io/xorm/test_sqlite.sh1
-rw-r--r--vendor/xorm.io/xorm/test_sqlite_cache.sh1
-rw-r--r--vendor/xorm.io/xorm/test_tidb.sh1
-rw-r--r--vendor/xorm.io/xorm/transaction.go26
-rw-r--r--vendor/xorm.io/xorm/types.go16
-rw-r--r--vendor/xorm.io/xorm/xorm.go126
72 files changed, 18689 insertions, 0 deletions
diff --git a/vendor/xorm.io/xorm/.drone.yml b/vendor/xorm.io/xorm/.drone.yml
new file mode 100644
index 0000000000..c373975df6
--- /dev/null
+++ b/vendor/xorm.io/xorm/.drone.yml
@@ -0,0 +1,771 @@
+---
+kind: pipeline
+name: matrix-1
+
+platform:
+ os: linux
+ arch: amd64
+
+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 get -u xorm.io/core
+ - go get -u xorm.io/builder
+ - go build -v
+ when:
+ event:
+ - push
+ - pull_request
+
+- name: test-sqlite
+ pull: default
+ image: golang:1.10
+ commands:
+ - go get -u github.com/wadey/gocovmerge
+ - "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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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"
+ - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
+ 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: tidb
+ pull: default
+ image: pingcap/tidb:v3.0.3
+ 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
+
+---
+kind: pipeline
+name: matrix-2
+
+platform:
+ os: linux
+ arch: amd64
+
+workspace:
+ base: /go
+ path: src/gitea.com/xorm/xorm
+
+steps:
+- name: build
+ pull: default
+ image: golang:1.11
+ environment:
+ GO111MODULE: "off"
+ commands:
+ - go get -t -d -v ./...
+ - go get -u xorm.io/core
+ - go get -u xorm.io/builder
+ - go build -v
+ when:
+ event:
+ - push
+ - pull_request
+
+- name: build-gomod
+ pull: default
+ image: golang:1.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ commands:
+ - go build -v
+ when:
+ event:
+ - push
+ - pull_request
+
+- name: test-sqlite
+ pull: default
+ image: golang:1.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.11
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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"
+ - go get github.com/wadey/gocovmerge
+ - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
+ 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: tidb
+ pull: default
+ image: pingcap/tidb:v3.0.3
+ 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
+
+---
+kind: pipeline
+name: matrix-3
+
+platform:
+ os: linux
+ arch: amd64
+
+workspace:
+ base: /go
+ path: src/gitea.com/xorm/xorm
+
+steps:
+
+- name: build
+ pull: default
+ image: golang:1.12
+ environment:
+ GO111MODULE: "off"
+ commands:
+ - go get -t -d -v ./...
+ - go get -u xorm.io/core
+ - go get -u xorm.io/builder
+ - go build -v
+ when:
+ event:
+ - push
+ - pull_request
+
+- name: build-gomod
+ pull: default
+ image: golang:1.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ commands:
+ - go build -v
+ when:
+ event:
+ - push
+ - pull_request
+
+- name: test-sqlite
+ pull: default
+ image: golang:1.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.12
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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"
+ - go get github.com/wadey/gocovmerge
+ - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
+ 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: tidb
+ pull: default
+ image: pingcap/tidb:v3.0.3
+ 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
+
+---
+kind: pipeline
+name: go1.13
+
+platform:
+ os: linux
+ arch: amd64
+
+workspace:
+ base: /go
+ path: src/gitea.com/xorm/xorm
+
+steps:
+
+- name: build
+ pull: default
+ image: golang:1.13
+ environment:
+ GO111MODULE: "off"
+ commands:
+ - go get -t -d -v ./...
+ - go get -u xorm.io/core
+ - go get -u xorm.io/builder
+ - go build -v
+ when:
+ event:
+ - push
+ - pull_request
+
+- name: build-gomod
+ pull: default
+ image: golang:1.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ commands:
+ - go build -v
+ when:
+ event:
+ - push
+ - pull_request
+
+- name: test-sqlite
+ pull: default
+ image: golang:1.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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.13
+ environment:
+ GO111MODULE: "on"
+ GOPROXY: "https://goproxy.cn"
+ 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"
+ - go get github.com/wadey/gocovmerge
+ - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt
+ 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: tidb
+ pull: default
+ image: pingcap/tidb:v3.0.3
+ 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 \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/.gitignore b/vendor/xorm.io/xorm/.gitignore
new file mode 100644
index 0000000000..f1757b9830
--- /dev/null
+++ b/vendor/xorm.io/xorm/.gitignore
@@ -0,0 +1,33 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+*.db
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+*.log
+.vendor
+temp_test.go
+.vscode
+xorm.test
+*.sqlite3
+test.db.sql
+
+.idea/
diff --git a/vendor/xorm.io/xorm/CONTRIBUTING.md b/vendor/xorm.io/xorm/CONTRIBUTING.md
new file mode 100644
index 0000000000..442aa4d311
--- /dev/null
+++ b/vendor/xorm.io/xorm/CONTRIBUTING.md
@@ -0,0 +1,46 @@
+## Contributing to xorm
+
+`xorm` has a backlog of [pull requests](https://help.github.com/articles/using-pull-requests), but contributions are still very
+much welcome. You can help with patch review, submitting bug reports,
+or adding new functionality. There is no formal style guide, but
+please conform to the style of existing code and general Go formatting
+conventions when submitting patches.
+
+* [fork a repo](https://help.github.com/articles/fork-a-repo)
+* [creating a pull request ](https://help.github.com/articles/creating-a-pull-request)
+
+### Language
+
+Since `xorm` is a world-wide open source project, please describe your issues or code changes in English as soon as possible.
+
+### Sign your codes with comments
+```
+// !<you github id>! your comments
+
+e.g.,
+
+// !lunny! this is comments made by lunny
+```
+
+### Patch review
+
+Help review existing open [pull requests](https://help.github.com/articles/using-pull-requests) by commenting on the code or
+proposed functionality.
+
+### Bug reports
+
+We appreciate any bug reports, but especially ones with self-contained
+(doesn't depend on code outside of xorm), minimal (can't be simplified
+further) test cases. It's especially helpful if you can submit a pull
+request with just the failing test case(you can find some example test file like [session_get_test.go](https://gitea.com/xorm/xorm/src/branch/master/session_get_test.go)).
+
+If you implements a new database interface, you maybe need to add a test_<databasename>.sh file.
+For example, [mysql_test.go](https://gitea.com/xorm/xorm/src/branch/master/test_mysql.sh)
+
+### New functionality
+
+There are a number of pending patches for new functionality, so
+additional feature patches will take a while to merge. Still, patches
+are generally reviewed based on usefulness and complexity in addition
+to time-in-queue, so if you have a knockout idea, take a shot. Feel
+free to open an issue discussion your proposed patch beforehand.
diff --git a/vendor/xorm.io/xorm/LICENSE b/vendor/xorm.io/xorm/LICENSE
new file mode 100644
index 0000000000..84d2ae5386
--- /dev/null
+++ b/vendor/xorm.io/xorm/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013 - 2015 The Xorm Authors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the {organization} nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/xorm.io/xorm/README.md b/vendor/xorm.io/xorm/README.md
new file mode 100644
index 0000000000..17a6ed37ff
--- /dev/null
+++ b/vendor/xorm.io/xorm/README.md
@@ -0,0 +1,503 @@
+# xorm
+
+[中文](https://gitea.com/xorm/xorm/src/branch/master/README_CN.md)
+
+Xorm is a simple and powerful ORM for Go.
+
+[![Build Status](https://drone.gitea.com/api/badges/xorm/xorm/status.svg)](https://drone.gitea.com/xorm/xorm) [![](http://gocover.io/_badge/xorm.io/xorm)](https://gocover.io/xorm.io/xorm)
+[![](https://goreportcard.com/badge/xorm.io/xorm)](https://goreportcard.com/report/xorm.io/xorm)
+[![Join the chat at https://img.shields.io/discord/323460943201959939.svg](https://img.shields.io/discord/323460943201959939.svg)](https://discord.gg/HuR2CF3)
+
+## Features
+
+* Struct <-> Table Mapping Support
+
+* Chainable APIs
+
+* Transaction Support
+
+* Both ORM and raw SQL operation Support
+
+* Sync database schema Support
+
+* Query Cache speed up
+
+* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/cmd/blob/master/README.md)
+
+* Simple cascade loading support
+
+* Optimistic Locking support
+
+* SQL Builder support via [xorm.io/builder](https://xorm.io/builder)
+
+* Automatical Read/Write seperatelly
+
+* Postgres schema support
+
+* Context Cache support
+
+## Drivers Support
+
+Drivers for Go's sql package which currently support database/sql includes:
+
+* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
+
+* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/tree/master/godrv)
+
+* Postgres: [github.com/lib/pq](https://github.com/lib/pq)
+
+* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
+
+* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
+
+* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
+
+* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
+
+## Installation
+
+ go get xorm.io/xorm
+
+## Documents
+
+* [Manual](http://xorm.io/docs)
+
+* [GoDoc](http://godoc.org/xorm.io/xorm)
+
+## Quick Start
+
+* Create Engine
+
+```Go
+engine, err := xorm.NewEngine(driverName, dataSourceName)
+```
+
+* Define a struct and Sync2 table struct to database
+
+```Go
+type User struct {
+ Id int64
+ Name string
+ Salt string
+ Age int
+ Passwd string `xorm:"varchar(200)"`
+ Created time.Time `xorm:"created"`
+ Updated time.Time `xorm:"updated"`
+}
+
+err := engine.Sync2(new(User))
+```
+
+* Create Engine Group
+
+```Go
+dataSourceNameSlice := []string{masterDataSourceName, slave1DataSourceName, slave2DataSourceName}
+engineGroup, err := xorm.NewEngineGroup(driverName, dataSourceNameSlice)
+```
+
+```Go
+masterEngine, err := xorm.NewEngine(driverName, masterDataSourceName)
+slave1Engine, err := xorm.NewEngine(driverName, slave1DataSourceName)
+slave2Engine, err := xorm.NewEngine(driverName, slave2DataSourceName)
+engineGroup, err := xorm.NewEngineGroup(masterEngine, []*Engine{slave1Engine, slave2Engine})
+```
+
+Then all place where `engine` you can just use `engineGroup`.
+
+* `Query` runs a SQL string, the returned results is `[]map[string][]byte`, `QueryString` returns `[]map[string]string`, `QueryInterface` returns `[]map[string]interface{}`.
+
+```Go
+results, err := engine.Query("select * from user")
+results, err := engine.Where("a = 1").Query()
+
+results, err := engine.QueryString("select * from user")
+results, err := engine.Where("a = 1").QueryString()
+
+results, err := engine.QueryInterface("select * from user")
+results, err := engine.Where("a = 1").QueryInterface()
+```
+
+* `Exec` runs a SQL string, it returns `affected` and `error`
+
+```Go
+affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
+```
+
+* `Insert` one or multiple records to database
+
+```Go
+affected, err := engine.Insert(&user)
+// INSERT INTO struct () values ()
+
+affected, err := engine.Insert(&user1, &user2)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values ()
+
+affected, err := engine.Insert(&users)
+// INSERT INTO struct () values (),(),()
+
+affected, err := engine.Insert(&user1, &users)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values (),(),()
+```
+
+* `Get` query one record from database
+
+```Go
+has, err := engine.Get(&user)
+// SELECT * FROM user LIMIT 1
+
+has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
+// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
+
+var name string
+has, err := engine.Table(&user).Where("id = ?", id).Cols("name").Get(&name)
+// SELECT name FROM user WHERE id = ?
+
+var id int64
+has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id)
+has, err := engine.SQL("select id from user").Get(&id)
+// SELECT id FROM user WHERE name = ?
+
+var valuesMap = make(map[string]string)
+has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap)
+// SELECT * FROM user WHERE id = ?
+
+var valuesSlice = make([]interface{}, len(cols))
+has, err := engine.Table(&user).Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
+// SELECT col1, col2, col3 FROM user WHERE id = ?
+```
+
+* `Exist` check if one record exist on table
+
+```Go
+has, err := testEngine.Exist(new(RecordExist))
+// SELECT * FROM record_exist LIMIT 1
+
+has, err = testEngine.Exist(&RecordExist{
+ Name: "test1",
+ })
+// SELECT * FROM record_exist WHERE name = ? LIMIT 1
+
+has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{})
+// SELECT * FROM record_exist WHERE name = ? LIMIT 1
+
+has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist()
+// select * from record_exist where name = ?
+
+has, err = testEngine.Table("record_exist").Exist()
+// SELECT * FROM record_exist LIMIT 1
+
+has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist()
+// SELECT * FROM record_exist WHERE name = ? LIMIT 1
+```
+
+* `Find` query multiple records from database, also you can use join and extends
+
+```Go
+var users []User
+err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
+// SELECT * FROM user WHERE name = ? AND age > 10 limit 10 offset 0
+
+type Detail struct {
+ Id int64
+ UserId int64 `xorm:"index"`
+}
+
+type UserDetail struct {
+ User `xorm:"extends"`
+ Detail `xorm:"extends"`
+}
+
+var users []UserDetail
+err := engine.Table("user").Select("user.*, detail.*").
+ Join("INNER", "detail", "detail.user_id = user.id").
+ Where("user.name = ?", name).Limit(10, 0).
+ Find(&users)
+// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 10 offset 0
+```
+
+* `Iterate` and `Rows` query multiple records and record by record handle, there are two methods Iterate and Rows
+
+```Go
+err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
+ user := bean.(*User)
+ return nil
+})
+// SELECT * FROM user
+
+err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
+ user := bean.(*User)
+ return nil
+})
+// SELECT * FROM user Limit 0, 100
+// SELECT * FROM user Limit 101, 100
+
+rows, err := engine.Rows(&User{Name:name})
+// SELECT * FROM user
+defer rows.Close()
+bean := new(Struct)
+for rows.Next() {
+ err = rows.Scan(bean)
+}
+```
+
+* `Update` update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on.
+
+```Go
+affected, err := engine.ID(1).Update(&user)
+// UPDATE user SET ... Where id = ?
+
+affected, err := engine.Update(&user, &User{Name:name})
+// UPDATE user SET ... Where name = ?
+
+var ids = []int64{1, 2, 3}
+affected, err := engine.In("id", ids).Update(&user)
+// UPDATE user SET ... Where id IN (?, ?, ?)
+
+// force update indicated columns by Cols
+affected, err := engine.ID(1).Cols("age").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+// force NOT update indicated columns by Omit
+affected, err := engine.ID(1).Omit("name").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+affected, err := engine.ID(1).AllCols().Update(&user)
+// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
+```
+
+* `Delete` delete one or more records, Delete MUST have condition
+
+```Go
+affected, err := engine.Where(...).Delete(&user)
+// DELETE FROM user Where ...
+
+affected, err := engine.ID(2).Delete(&user)
+// DELETE FROM user Where id = ?
+```
+
+* `Count` count records
+
+```Go
+counts, err := engine.Count(&user)
+// SELECT count(*) AS total FROM user
+```
+
+* `FindAndCount` combines function `Find` with `Count` which is usually used in query by page
+
+```Go
+var users []User
+counts, err := engine.FindAndCount(&users)
+```
+
+* `Sum` sum functions
+
+```Go
+agesFloat64, err := engine.Sum(&user, "age")
+// SELECT sum(age) AS total FROM user
+
+agesInt64, err := engine.SumInt(&user, "age")
+// SELECT sum(age) AS total FROM user
+
+sumFloat64Slice, err := engine.Sums(&user, "age", "score")
+// SELECT sum(age), sum(score) FROM user
+
+sumInt64Slice, err := engine.SumsInt(&user, "age", "score")
+// SELECT sum(age), sum(score) FROM user
+```
+
+* Query conditions builder
+
+```Go
+err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))).Find(&users)
+// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
+```
+
+* Multiple operations in one go routine, no transation here but resue session memory
+
+```Go
+session := engine.NewSession()
+defer session.Close()
+
+user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
+if _, err := session.Insert(&user1); err != nil {
+ return err
+}
+
+user2 := Userinfo{Username: "yyy"}
+if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
+ return err
+}
+
+if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
+ return err
+}
+
+return nil
+```
+
+* Transation should be on one go routine. There is transaction and resue session memory
+
+```Go
+session := engine.NewSession()
+defer session.Close()
+
+// add Begin() before any action
+if err := session.Begin(); err != nil {
+ // if returned then will rollback automatically
+ return err
+}
+
+user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
+if _, err := session.Insert(&user1); err != nil {
+ return err
+}
+
+user2 := Userinfo{Username: "yyy"}
+if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
+ return err
+}
+
+if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
+ return err
+}
+
+// add Commit() after all actions
+return session.Commit()
+```
+
+* Or you can use `Transaction` to replace above codes.
+
+```Go
+res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) {
+ user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
+ if _, err := session.Insert(&user1); err != nil {
+ return nil, err
+ }
+
+ user2 := Userinfo{Username: "yyy"}
+ if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
+ return nil, err
+ }
+
+ if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
+ return nil, err
+ }
+ return nil, nil
+})
+```
+
+* Context Cache, if enabled, current query result will be cached on session and be used by next same statement on the same session.
+
+```Go
+ sess := engine.NewSession()
+ defer sess.Close()
+
+ var context = xorm.NewMemoryContextCache()
+
+ var c2 ContextGetStruct
+ has, err := sess.ID(1).ContextCache(context).Get(&c2)
+ assert.NoError(t, err)
+ assert.True(t, has)
+ assert.EqualValues(t, 1, c2.Id)
+ assert.EqualValues(t, "1", c2.Name)
+ sql, args := sess.LastSQL()
+ assert.True(t, len(sql) > 0)
+ assert.True(t, len(args) > 0)
+
+ var c3 ContextGetStruct
+ has, err = sess.ID(1).ContextCache(context).Get(&c3)
+ assert.NoError(t, err)
+ assert.True(t, has)
+ assert.EqualValues(t, 1, c3.Id)
+ assert.EqualValues(t, "1", c3.Name)
+ sql, args = sess.LastSQL()
+ assert.True(t, len(sql) == 0)
+ assert.True(t, len(args) == 0)
+```
+
+## Contributing
+
+If you want to pull request, please see [CONTRIBUTING](https://gitea.com/xorm/xorm/src/branch/master/CONTRIBUTING.md). And we also provide [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) to discuss.
+
+## Credits
+
+### Contributors
+
+This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
+<a href="graphs/contributors"><img src="https://opencollective.com/xorm/contributors.svg?width=890&button=false" /></a>
+
+### Backers
+
+Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/xorm#backer)]
+
+<a href="https://opencollective.com/xorm#backers" target="_blank"><img src="https://opencollective.com/xorm/backers.svg?width=890"></a>
+
+### Sponsors
+
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/xorm#sponsor)]
+
+## Changelog
+
+* **v0.7.0**
+ * Some bugs fixed
+
+* **v0.6.6**
+ * Some bugs fixed
+
+* **v0.6.5**
+ * Postgres schema support
+ * vgo support
+ * Add FindAndCount
+ * Database special params support via NewEngineWithParams
+ * Some bugs fixed
+
+* **v0.6.4**
+ * Automatical Read/Write seperatelly
+ * Query/QueryString/QueryInterface and action with Where/And
+ * Get support non-struct variables
+ * BufferSize on Iterate
+ * fix some other bugs.
+
+[More changes ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16)
+
+## Cases
+
+* [studygolang](http://studygolang.com/) - [github.com/studygolang/studygolang](https://github.com/studygolang/studygolang)
+
+* [Gitea](http://gitea.io) - [github.com/go-gitea/gitea](http://github.com/go-gitea/gitea)
+
+* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
+
+* [grafana](https://grafana.com/) - [github.com/grafana/grafana](http://github.com/grafana/grafana)
+
+* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
+
+* [Wego](http://github.com/go-tango/wego)
+
+* [Docker.cn](https://docker.cn/)
+
+* [Xorm Adapter](https://github.com/casbin/xorm-adapter) for [Casbin](https://github.com/casbin/casbin) - [github.com/casbin/xorm-adapter](https://github.com/casbin/xorm-adapter)
+
+* [Gorevel](http://gorevel.cn/) - [github.com/goofcc/gorevel](http://github.com/goofcc/gorevel)
+
+* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
+
+* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
+
+* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
+
+* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
+
+* [YouGam](http://www.yougam.com/)
+
+* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
+
+* [GoBBS - gobbs.domolo.com](http://gobbs.domolo.com/)
+
+* [go-blog](http://wangcheng.me) - [github.com/easykoo/go-blog](https://github.com/easykoo/go-blog)
+
+## LICENSE
+
+BSD License [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)
diff --git a/vendor/xorm.io/xorm/README_CN.md b/vendor/xorm.io/xorm/README_CN.md
new file mode 100644
index 0000000000..644bdc0b63
--- /dev/null
+++ b/vendor/xorm.io/xorm/README_CN.md
@@ -0,0 +1,498 @@
+# xorm
+
+[English](https://gitea.com/xorm/xorm/src/branch/master/README.md)
+
+xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
+
+[![Build Status](https://drone.gitea.com/api/badges/xorm/builder/status.svg)](https://drone.gitea.com/xorm/builder) [![](http://gocover.io/_badge/xorm.io/xorm)](https://gocover.io/xorm.io/xorm)
+[![](https://goreportcard.com/badge/xorm.io/xorm)](https://goreportcard.com/report/xorm.io/xorm)
+[![Join the chat at https://img.shields.io/discord/323460943201959939.svg](https://img.shields.io/discord/323460943201959939.svg)](https://discord.gg/HuR2CF3)
+
+## 特性
+
+* 支持Struct和数据库表之间的灵活映射,并支持自动同步
+
+* 事务支持
+
+* 同时支持原始SQL语句和ORM操作的混合执行
+
+* 使用连写来简化调用
+
+* 支持使用Id, In, Where, Limit, Join, Having, Table, Sql, Cols等函数和结构体等方式作为条件
+
+* 支持级联加载Struct
+
+* Schema支持(仅Postgres)
+
+* 支持缓存
+
+* 支持根据数据库自动生成xorm的结构体
+
+* 支持记录版本(即乐观锁)
+
+* 内置SQL Builder支持
+
+* 上下文缓存支持
+
+## 驱动支持
+
+目前支持的Go数据库驱动和对应的数据库如下:
+
+* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
+
+* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
+
+* Postgres: [github.com/lib/pq](https://github.com/lib/pq)
+
+* Tidb: [github.com/pingcap/tidb](https://github.com/pingcap/tidb)
+
+* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
+
+* MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
+
+* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
+
+* Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持)
+
+## 安装
+
+ go get xorm.io/xorm
+
+## 文档
+
+* [操作指南](http://xorm.io/docs)
+
+* [Godoc代码文档](http://godoc.org/xorm.io/xorm)
+
+# 快速开始
+
+* 第一步创建引擎,driverName, dataSourceName和database/sql接口相同
+
+```Go
+engine, err := xorm.NewEngine(driverName, dataSourceName)
+```
+
+* 定义一个和表同步的结构体,并且自动同步结构体到数据库
+
+```Go
+type User struct {
+ Id int64
+ Name string
+ Salt string
+ Age int
+ Passwd string `xorm:"varchar(200)"`
+ Created time.Time `xorm:"created"`
+ Updated time.Time `xorm:"updated"`
+}
+
+err := engine.Sync2(new(User))
+```
+
+* 创建Engine组
+
+```Go
+dataSourceNameSlice := []string{masterDataSourceName, slave1DataSourceName, slave2DataSourceName}
+engineGroup, err := xorm.NewEngineGroup(driverName, dataSourceNameSlice)
+```
+
+```Go
+masterEngine, err := xorm.NewEngine(driverName, masterDataSourceName)
+slave1Engine, err := xorm.NewEngine(driverName, slave1DataSourceName)
+slave2Engine, err := xorm.NewEngine(driverName, slave2DataSourceName)
+engineGroup, err := xorm.NewEngineGroup(masterEngine, []*Engine{slave1Engine, slave2Engine})
+```
+
+所有使用 `engine` 都可以简单的用 `engineGroup` 来替换。
+
+* `Query` 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte。`QueryString` 返回 []map[string]string, `QueryInterface` 返回 `[]map[string]interface{}`.
+
+```Go
+results, err := engine.Query("select * from user")
+results, err := engine.Where("a = 1").Query()
+
+results, err := engine.QueryString("select * from user")
+results, err := engine.Where("a = 1").QueryString()
+
+results, err := engine.QueryInterface("select * from user")
+results, err := engine.Where("a = 1").QueryInterface()
+```
+
+* `Exec` 执行一个SQL语句
+
+```Go
+affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
+```
+
+* `Insert` 插入一条或者多条记录
+
+```Go
+affected, err := engine.Insert(&user)
+// INSERT INTO struct () values ()
+
+affected, err := engine.Insert(&user1, &user2)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values ()
+
+affected, err := engine.Insert(&users)
+// INSERT INTO struct () values (),(),()
+
+affected, err := engine.Insert(&user1, &users)
+// INSERT INTO struct1 () values ()
+// INSERT INTO struct2 () values (),(),()
+```
+
+* `Get` 查询单条记录
+
+```Go
+has, err := engine.Get(&user)
+// SELECT * FROM user LIMIT 1
+
+has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
+// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
+
+var name string
+has, err := engine.Table(&user).Where("id = ?", id).Cols("name").Get(&name)
+// SELECT name FROM user WHERE id = ?
+
+var id int64
+has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id)
+has, err := engine.SQL("select id from user").Get(&id)
+// SELECT id FROM user WHERE name = ?
+
+var valuesMap = make(map[string]string)
+has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap)
+// SELECT * FROM user WHERE id = ?
+
+var valuesSlice = make([]interface{}, len(cols))
+has, err := engine.Table(&user).Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
+// SELECT col1, col2, col3 FROM user WHERE id = ?
+```
+
+* `Exist` 检测记录是否存在
+
+```Go
+has, err := testEngine.Exist(new(RecordExist))
+// SELECT * FROM record_exist LIMIT 1
+
+has, err = testEngine.Exist(&RecordExist{
+ Name: "test1",
+ })
+// SELECT * FROM record_exist WHERE name = ? LIMIT 1
+
+has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{})
+// SELECT * FROM record_exist WHERE name = ? LIMIT 1
+
+has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist()
+// select * from record_exist where name = ?
+
+has, err = testEngine.Table("record_exist").Exist()
+// SELECT * FROM record_exist LIMIT 1
+
+has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist()
+// SELECT * FROM record_exist WHERE name = ? LIMIT 1
+```
+
+* `Find` 查询多条记录,当然可以使用Join和extends来组合使用
+
+```Go
+var users []User
+err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
+// SELECT * FROM user WHERE name = ? AND age > 10 limit 10 offset 0
+
+type Detail struct {
+ Id int64
+ UserId int64 `xorm:"index"`
+}
+
+type UserDetail struct {
+ User `xorm:"extends"`
+ Detail `xorm:"extends"`
+}
+
+var users []UserDetail
+err := engine.Table("user").Select("user.*, detail.*")
+ Join("INNER", "detail", "detail.user_id = user.id").
+ Where("user.name = ?", name).Limit(10, 0).
+ Find(&users)
+// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 10 offset 0
+```
+
+* `Iterate` 和 `Rows` 根据条件遍历数据库,可以有两种方式: Iterate and Rows
+
+```Go
+err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
+ user := bean.(*User)
+ return nil
+})
+// SELECT * FROM user
+
+err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
+ user := bean.(*User)
+ return nil
+})
+// SELECT * FROM user Limit 0, 100
+// SELECT * FROM user Limit 101, 100
+
+rows, err := engine.Rows(&User{Name:name})
+// SELECT * FROM user
+defer rows.Close()
+bean := new(Struct)
+for rows.Next() {
+ err = rows.Scan(bean)
+}
+```
+
+* `Update` 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段
+
+```Go
+affected, err := engine.ID(1).Update(&user)
+// UPDATE user SET ... Where id = ?
+
+affected, err := engine.Update(&user, &User{Name:name})
+// UPDATE user SET ... Where name = ?
+
+var ids = []int64{1, 2, 3}
+affected, err := engine.In(ids).Update(&user)
+// UPDATE user SET ... Where id IN (?, ?, ?)
+
+// force update indicated columns by Cols
+affected, err := engine.ID(1).Cols("age").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+// force NOT update indicated columns by Omit
+affected, err := engine.ID(1).Omit("name").Update(&User{Name:name, Age: 12})
+// UPDATE user SET age = ?, updated=? Where id = ?
+
+affected, err := engine.ID(1).AllCols().Update(&user)
+// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
+```
+
+* `Delete` 删除记录,需要注意,删除必须至少有一个条件,否则会报错。要清空数据库可以用EmptyTable
+
+```Go
+affected, err := engine.Where(...).Delete(&user)
+// DELETE FROM user Where ...
+
+affected, err := engine.ID(2).Delete(&user)
+// DELETE FROM user Where id = ?
+```
+
+* `Count` 获取记录条数
+
+```Go
+counts, err := engine.Count(&user)
+// SELECT count(*) AS total FROM user
+```
+
+* `Sum` 求和函数
+
+```Go
+agesFloat64, err := engine.Sum(&user, "age")
+// SELECT sum(age) AS total FROM user
+
+agesInt64, err := engine.SumInt(&user, "age")
+// SELECT sum(age) AS total FROM user
+
+sumFloat64Slice, err := engine.Sums(&user, "age", "score")
+// SELECT sum(age), sum(score) FROM user
+
+sumInt64Slice, err := engine.SumsInt(&user, "age", "score")
+// SELECT sum(age), sum(score) FROM user
+```
+
+* 条件编辑器
+
+```Go
+err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))).Find(&users)
+// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
+```
+
+* 在一个Go程中多次操作数据库,但没有事务
+
+```Go
+session := engine.NewSession()
+defer session.Close()
+
+user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
+if _, err := session.Insert(&user1); err != nil {
+ return err
+}
+
+user2 := Userinfo{Username: "yyy"}
+if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
+ return err
+}
+
+if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
+ return err
+}
+
+return nil
+```
+
+* 在一个Go程中有事务
+
+```Go
+session := engine.NewSession()
+defer session.Close()
+
+// add Begin() before any action
+if err := session.Begin(); err != nil {
+ // if returned then will rollback automatically
+ return err
+}
+
+user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
+if _, err := session.Insert(&user1); err != nil {
+ return err
+}
+
+user2 := Userinfo{Username: "yyy"}
+if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
+ return err
+}
+
+if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
+ return err
+}
+
+// add Commit() after all actions
+return session.Commit()
+```
+
+* 事务的简写方法
+
+```Go
+res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) {
+ user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
+ if _, err := session.Insert(&user1); err != nil {
+ return nil, err
+ }
+
+ user2 := Userinfo{Username: "yyy"}
+ if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
+ return nil, err
+ }
+
+ if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
+ return nil, err
+ }
+ return nil, nil
+})
+```
+
+* 上下文缓存,如果启用,那么针对单个对象的查询将会被缓存到系统中,可以被下一个查询使用。
+
+```Go
+ sess := engine.NewSession()
+ defer sess.Close()
+
+ var context = xorm.NewMemoryContextCache()
+
+ var c2 ContextGetStruct
+ has, err := sess.ID(1).ContextCache(context).Get(&c2)
+ assert.NoError(t, err)
+ assert.True(t, has)
+ assert.EqualValues(t, 1, c2.Id)
+ assert.EqualValues(t, "1", c2.Name)
+ sql, args := sess.LastSQL()
+ assert.True(t, len(sql) > 0)
+ assert.True(t, len(args) > 0)
+
+ var c3 ContextGetStruct
+ has, err = sess.ID(1).ContextCache(context).Get(&c3)
+ assert.NoError(t, err)
+ assert.True(t, has)
+ assert.EqualValues(t, 1, c3.Id)
+ assert.EqualValues(t, "1", c3.Name)
+ sql, args = sess.LastSQL()
+ assert.True(t, len(sql) == 0)
+ assert.True(t, len(args) == 0)
+```
+
+## 贡献
+
+如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://gitea.com/xorm/xorm/src/branch/master/CONTRIBUTING.md)。您也可以加入QQ群 技术帮助和讨论。
+群一:280360085 (已满)
+群二:795010183
+
+## Credits
+
+### Contributors
+
+感谢所有的贡献者. [[Contribute](CONTRIBUTING.md)].
+<a href="graphs/contributors"><img src="https://opencollective.com/xorm/contributors.svg?width=890&button=false" /></a>
+
+### Backers
+
+感谢我们所有的 backers! 🙏 [[成为 backer](https://opencollective.com/xorm#backer)]
+
+<a href="https://opencollective.com/xorm#backers" target="_blank"><img src="https://opencollective.com/xorm/backers.svg?width=890"></a>
+
+### Sponsors
+
+成为 sponsor 来支持 xorm。您的 logo 将会被显示并被链接到您的网站。 [[成为 sponsor](https://opencollective.com/xorm#sponsor)]
+
+# 案例
+
+* [Go语言中文网](http://studygolang.com/) - [github.com/studygolang/studygolang](https://github.com/studygolang/studygolang)
+
+* [Gitea](http://gitea.io) - [github.com/go-gitea/gitea](http://github.com/go-gitea/gitea)
+
+* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs)
+
+* [grafana](https://grafana.com/) - [github.com/grafana/grafana](http://github.com/grafana/grafana)
+
+* [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader)
+
+* [Wego](http://github.com/go-tango/wego)
+
+* [Docker.cn](https://docker.cn/)
+
+* [Xorm Adapter](https://github.com/casbin/xorm-adapter) for [Casbin](https://github.com/casbin/casbin) - [github.com/casbin/xorm-adapter](https://github.com/casbin/xorm-adapter)
+
+* [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker)
+
+* [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild)
+
+* [Sudo China](http://sudochina.com) - [github.com/insionng/toropress](http://github.com/insionng/toropress)
+
+* [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
+
+* [YouGam](http://www.yougam.com/)
+
+* [GoCMS - github.com/zzboy/GoCMS](https://github.com/zzdboy/GoCMS)
+
+* [GoBBS - gobbs.domolo.com](http://gobbs.domolo.com/)
+
+* [go-blog](http://wangcheng.me) - [github.com/easykoo/go-blog](https://github.com/easykoo/go-blog)
+
+
+## 更新日志
+
+* **v0.7.0**
+ * 修正部分Bug
+
+* **v0.6.6**
+ * 修正部分Bug
+
+* **v0.6.5**
+ * 通过 engine.SetSchema 来支持 schema,当前仅支持Postgres
+ * vgo 支持
+ * 新增 `FindAndCount` 函数
+ * 通过 `NewEngineWithParams` 支持数据库特别参数
+ * 修正部分Bug
+
+* **v0.6.4**
+ * 自动读写分离支持
+ * Query/QueryString/QueryInterface 支持与 Where/And 合用
+ * `Get` 支持获取非结构体变量
+ * `Iterate` 支持 `BufferSize`
+ * 修正部分Bug
+
+[更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16)
+
+## LICENSE
+
+BSD License
+[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)
diff --git a/vendor/xorm.io/xorm/cache_lru.go b/vendor/xorm.io/xorm/cache_lru.go
new file mode 100644
index 0000000000..ab948bd28e
--- /dev/null
+++ b/vendor/xorm.io/xorm/cache_lru.go
@@ -0,0 +1,284 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "container/list"
+ "fmt"
+ "sync"
+ "time"
+
+ "xorm.io/core"
+)
+
+// LRUCacher implments cache object facilities
+type LRUCacher struct {
+ idList *list.List
+ sqlList *list.List
+ idIndex map[string]map[string]*list.Element
+ sqlIndex map[string]map[string]*list.Element
+ store core.CacheStore
+ mutex sync.Mutex
+ MaxElementSize int
+ Expired time.Duration
+ GcInterval time.Duration
+}
+
+// NewLRUCacher creates a cacher
+func NewLRUCacher(store core.CacheStore, maxElementSize int) *LRUCacher {
+ return NewLRUCacher2(store, 3600*time.Second, maxElementSize)
+}
+
+// NewLRUCacher2 creates a cache include different params
+func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize int) *LRUCacher {
+ cacher := &LRUCacher{store: store, idList: list.New(),
+ sqlList: list.New(), Expired: expired,
+ GcInterval: core.CacheGcInterval, MaxElementSize: maxElementSize,
+ sqlIndex: make(map[string]map[string]*list.Element),
+ idIndex: make(map[string]map[string]*list.Element),
+ }
+ cacher.RunGC()
+ return cacher
+}
+
+// RunGC run once every m.GcInterval
+func (m *LRUCacher) RunGC() {
+ time.AfterFunc(m.GcInterval, func() {
+ m.RunGC()
+ m.GC()
+ })
+}
+
+// GC check ids lit and sql list to remove all element expired
+func (m *LRUCacher) GC() {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ var removedNum int
+ for e := m.idList.Front(); e != nil; {
+ if removedNum <= core.CacheGcMaxRemoved &&
+ time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired {
+ removedNum++
+ next := e.Next()
+ node := e.Value.(*idNode)
+ m.delBean(node.tbName, node.id)
+ e = next
+ } else {
+ break
+ }
+ }
+
+ removedNum = 0
+ for e := m.sqlList.Front(); e != nil; {
+ if removedNum <= core.CacheGcMaxRemoved &&
+ time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired {
+ removedNum++
+ next := e.Next()
+ node := e.Value.(*sqlNode)
+ m.delIds(node.tbName, node.sql)
+ e = next
+ } else {
+ break
+ }
+ }
+}
+
+// GetIds returns all bean's ids according to sql and parameter from cache
+func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ if _, ok := m.sqlIndex[tableName]; !ok {
+ m.sqlIndex[tableName] = make(map[string]*list.Element)
+ }
+ if v, err := m.store.Get(sql); err == nil {
+ if el, ok := m.sqlIndex[tableName][sql]; !ok {
+ el = m.sqlList.PushBack(newSQLNode(tableName, sql))
+ m.sqlIndex[tableName][sql] = el
+ } else {
+ lastTime := el.Value.(*sqlNode).lastVisit
+ // if expired, remove the node and return nil
+ if time.Now().Sub(lastTime) > m.Expired {
+ m.delIds(tableName, sql)
+ return nil
+ }
+ m.sqlList.MoveToBack(el)
+ el.Value.(*sqlNode).lastVisit = time.Now()
+ }
+ return v
+ }
+
+ m.delIds(tableName, sql)
+ return nil
+}
+
+// GetBean returns bean according tableName and id from cache
+func (m *LRUCacher) GetBean(tableName string, id string) interface{} {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+ if _, ok := m.idIndex[tableName]; !ok {
+ m.idIndex[tableName] = make(map[string]*list.Element)
+ }
+ tid := genID(tableName, id)
+ if v, err := m.store.Get(tid); err == nil {
+ if el, ok := m.idIndex[tableName][id]; ok {
+ lastTime := el.Value.(*idNode).lastVisit
+ // if expired, remove the node and return nil
+ if time.Now().Sub(lastTime) > m.Expired {
+ m.delBean(tableName, id)
+ return nil
+ }
+ m.idList.MoveToBack(el)
+ el.Value.(*idNode).lastVisit = time.Now()
+ } else {
+ el = m.idList.PushBack(newIDNode(tableName, id))
+ m.idIndex[tableName][id] = el
+ }
+ return v
+ }
+
+ // store bean is not exist, then remove memory's index
+ m.delBean(tableName, id)
+ return nil
+}
+
+// clearIds clears all sql-ids mapping on table tableName from cache
+func (m *LRUCacher) clearIds(tableName string) {
+ if tis, ok := m.sqlIndex[tableName]; ok {
+ for sql, v := range tis {
+ m.sqlList.Remove(v)
+ m.store.Del(sql)
+ }
+ }
+ m.sqlIndex[tableName] = make(map[string]*list.Element)
+}
+
+// ClearIds clears all sql-ids mapping on table tableName from cache
+func (m *LRUCacher) ClearIds(tableName string) {
+ m.mutex.Lock()
+ m.clearIds(tableName)
+ m.mutex.Unlock()
+}
+
+func (m *LRUCacher) clearBeans(tableName string) {
+ if tis, ok := m.idIndex[tableName]; ok {
+ for id, v := range tis {
+ m.idList.Remove(v)
+ tid := genID(tableName, id)
+ m.store.Del(tid)
+ }
+ }
+ m.idIndex[tableName] = make(map[string]*list.Element)
+}
+
+// ClearBeans clears all beans in some table
+func (m *LRUCacher) ClearBeans(tableName string) {
+ m.mutex.Lock()
+ m.clearBeans(tableName)
+ m.mutex.Unlock()
+}
+
+// PutIds pus ids into table
+func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
+ m.mutex.Lock()
+ if _, ok := m.sqlIndex[tableName]; !ok {
+ m.sqlIndex[tableName] = make(map[string]*list.Element)
+ }
+ if el, ok := m.sqlIndex[tableName][sql]; !ok {
+ el = m.sqlList.PushBack(newSQLNode(tableName, sql))
+ m.sqlIndex[tableName][sql] = el
+ } else {
+ el.Value.(*sqlNode).lastVisit = time.Now()
+ }
+ m.store.Put(sql, ids)
+ if m.sqlList.Len() > m.MaxElementSize {
+ e := m.sqlList.Front()
+ node := e.Value.(*sqlNode)
+ m.delIds(node.tbName, node.sql)
+ }
+ m.mutex.Unlock()
+}
+
+// PutBean puts beans into table
+func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
+ m.mutex.Lock()
+ var el *list.Element
+ var ok bool
+
+ if el, ok = m.idIndex[tableName][id]; !ok {
+ el = m.idList.PushBack(newIDNode(tableName, id))
+ m.idIndex[tableName][id] = el
+ } else {
+ el.Value.(*idNode).lastVisit = time.Now()
+ }
+
+ m.store.Put(genID(tableName, id), obj)
+ if m.idList.Len() > m.MaxElementSize {
+ e := m.idList.Front()
+ node := e.Value.(*idNode)
+ m.delBean(node.tbName, node.id)
+ }
+ m.mutex.Unlock()
+}
+
+func (m *LRUCacher) delIds(tableName, sql string) {
+ if _, ok := m.sqlIndex[tableName]; ok {
+ if el, ok := m.sqlIndex[tableName][sql]; ok {
+ delete(m.sqlIndex[tableName], sql)
+ m.sqlList.Remove(el)
+ }
+ }
+ m.store.Del(sql)
+}
+
+// DelIds deletes ids
+func (m *LRUCacher) DelIds(tableName, sql string) {
+ m.mutex.Lock()
+ m.delIds(tableName, sql)
+ m.mutex.Unlock()
+}
+
+func (m *LRUCacher) delBean(tableName string, id string) {
+ tid := genID(tableName, id)
+ if el, ok := m.idIndex[tableName][id]; ok {
+ delete(m.idIndex[tableName], id)
+ m.idList.Remove(el)
+ m.clearIds(tableName)
+ }
+ m.store.Del(tid)
+}
+
+// DelBean deletes beans in some table
+func (m *LRUCacher) DelBean(tableName string, id string) {
+ m.mutex.Lock()
+ m.delBean(tableName, id)
+ m.mutex.Unlock()
+}
+
+type idNode struct {
+ tbName string
+ id string
+ lastVisit time.Time
+}
+
+type sqlNode struct {
+ tbName string
+ sql string
+ lastVisit time.Time
+}
+
+func genSQLKey(sql string, args interface{}) string {
+ return fmt.Sprintf("%v-%v", sql, args)
+}
+
+func genID(prefix string, id string) string {
+ return fmt.Sprintf("%v-%v", prefix, id)
+}
+
+func newIDNode(tbName string, id string) *idNode {
+ return &idNode{tbName, id, time.Now()}
+}
+
+func newSQLNode(tbName, sql string) *sqlNode {
+ return &sqlNode{tbName, sql, time.Now()}
+}
diff --git a/vendor/xorm.io/xorm/cache_memory_store.go b/vendor/xorm.io/xorm/cache_memory_store.go
new file mode 100644
index 0000000000..0c483f4583
--- /dev/null
+++ b/vendor/xorm.io/xorm/cache_memory_store.go
@@ -0,0 +1,51 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "sync"
+
+ "xorm.io/core"
+)
+
+var _ core.CacheStore = NewMemoryStore()
+
+// MemoryStore represents in-memory store
+type MemoryStore struct {
+ store map[interface{}]interface{}
+ mutex sync.RWMutex
+}
+
+// NewMemoryStore creates a new store in memory
+func NewMemoryStore() *MemoryStore {
+ return &MemoryStore{store: make(map[interface{}]interface{})}
+}
+
+// Put puts object into store
+func (s *MemoryStore) Put(key string, value interface{}) error {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ s.store[key] = value
+ return nil
+}
+
+// Get gets object from store
+func (s *MemoryStore) Get(key string) (interface{}, error) {
+ s.mutex.RLock()
+ defer s.mutex.RUnlock()
+ if v, ok := s.store[key]; ok {
+ return v, nil
+ }
+
+ return nil, ErrNotExist
+}
+
+// Del deletes object
+func (s *MemoryStore) Del(key string) error {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ delete(s.store, key)
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/context_cache.go b/vendor/xorm.io/xorm/context_cache.go
new file mode 100644
index 0000000000..1bc2288496
--- /dev/null
+++ b/vendor/xorm.io/xorm/context_cache.go
@@ -0,0 +1,30 @@
+// Copyright 2018 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+// ContextCache is the interface that operates the cache data.
+type ContextCache interface {
+ // Put puts value into cache with key.
+ Put(key string, val interface{})
+ // Get gets cached value by given key.
+ Get(key string) interface{}
+}
+
+type memoryContextCache map[string]interface{}
+
+// NewMemoryContextCache return memoryContextCache
+func NewMemoryContextCache() memoryContextCache {
+ return make(map[string]interface{})
+}
+
+// Put puts value into cache with key.
+func (m memoryContextCache) Put(key string, val interface{}) {
+ m[key] = val
+}
+
+// Get gets cached value by given key.
+func (m memoryContextCache) Get(key string) interface{} {
+ return m[key]
+}
diff --git a/vendor/xorm.io/xorm/convert.go b/vendor/xorm.io/xorm/convert.go
new file mode 100644
index 0000000000..2316ca0b4d
--- /dev/null
+++ b/vendor/xorm.io/xorm/convert.go
@@ -0,0 +1,348 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql/driver"
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "time"
+)
+
+var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
+
+func strconvErr(err error) error {
+ if ne, ok := err.(*strconv.NumError); ok {
+ return ne.Err
+ }
+ return err
+}
+
+func cloneBytes(b []byte) []byte {
+ if b == nil {
+ return nil
+ } else {
+ c := make([]byte, len(b))
+ copy(c, b)
+ return c
+ }
+}
+
+func asString(src interface{}) string {
+ switch v := src.(type) {
+ case string:
+ return v
+ case []byte:
+ return string(v)
+ }
+ rv := reflect.ValueOf(src)
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return strconv.FormatInt(rv.Int(), 10)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return strconv.FormatUint(rv.Uint(), 10)
+ case reflect.Float64:
+ return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
+ case reflect.Float32:
+ return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
+ case reflect.Bool:
+ return strconv.FormatBool(rv.Bool())
+ }
+ return fmt.Sprintf("%v", src)
+}
+
+func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return strconv.AppendInt(buf, rv.Int(), 10), true
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return strconv.AppendUint(buf, rv.Uint(), 10), true
+ case reflect.Float32:
+ return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true
+ case reflect.Float64:
+ return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true
+ case reflect.Bool:
+ return strconv.AppendBool(buf, rv.Bool()), true
+ case reflect.String:
+ s := rv.String()
+ return append(buf, s...), true
+ }
+ return
+}
+
+// convertAssign copies to dest the value in src, converting it if possible.
+// An error is returned if the copy would result in loss of information.
+// dest should be a pointer type.
+func convertAssign(dest, src interface{}) error {
+ // Common cases, without reflect.
+ switch s := src.(type) {
+ case string:
+ switch d := dest.(type) {
+ case *string:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = s
+ return nil
+ case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = []byte(s)
+ return nil
+ }
+ case []byte:
+ switch d := dest.(type) {
+ case *string:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = string(s)
+ return nil
+ case *interface{}:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = cloneBytes(s)
+ return nil
+ case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = cloneBytes(s)
+ return nil
+ }
+
+ case time.Time:
+ switch d := dest.(type) {
+ case *string:
+ *d = s.Format(time.RFC3339Nano)
+ return nil
+ case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = []byte(s.Format(time.RFC3339Nano))
+ return nil
+ }
+ case nil:
+ switch d := dest.(type) {
+ case *interface{}:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = nil
+ return nil
+ case *[]byte:
+ if d == nil {
+ return errNilPtr
+ }
+ *d = nil
+ return nil
+ }
+ }
+
+ var sv reflect.Value
+
+ switch d := dest.(type) {
+ case *string:
+ sv = reflect.ValueOf(src)
+ switch sv.Kind() {
+ case reflect.Bool,
+ reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+ reflect.Float32, reflect.Float64:
+ *d = asString(src)
+ return nil
+ }
+ case *[]byte:
+ sv = reflect.ValueOf(src)
+ if b, ok := asBytes(nil, sv); ok {
+ *d = b
+ return nil
+ }
+ case *bool:
+ bv, err := driver.Bool.ConvertValue(src)
+ if err == nil {
+ *d = bv.(bool)
+ }
+ return err
+ case *interface{}:
+ *d = src
+ return nil
+ }
+
+ dpv := reflect.ValueOf(dest)
+ if dpv.Kind() != reflect.Ptr {
+ return errors.New("destination not a pointer")
+ }
+ if dpv.IsNil() {
+ return errNilPtr
+ }
+
+ if !sv.IsValid() {
+ sv = reflect.ValueOf(src)
+ }
+
+ dv := reflect.Indirect(dpv)
+ if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) {
+ switch b := src.(type) {
+ case []byte:
+ dv.Set(reflect.ValueOf(cloneBytes(b)))
+ default:
+ dv.Set(sv)
+ }
+ return nil
+ }
+
+ if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) {
+ dv.Set(sv.Convert(dv.Type()))
+ return nil
+ }
+
+ switch dv.Kind() {
+ case reflect.Ptr:
+ if src == nil {
+ dv.Set(reflect.Zero(dv.Type()))
+ return nil
+ }
+
+ dv.Set(reflect.New(dv.Type().Elem()))
+ return convertAssign(dv.Interface(), src)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ s := asString(src)
+ i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
+ if err != nil {
+ err = strconvErr(err)
+ return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
+ }
+ dv.SetInt(i64)
+ return nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ s := asString(src)
+ u64, err := strconv.ParseUint(s, 10, dv.Type().Bits())
+ if err != nil {
+ err = strconvErr(err)
+ return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
+ }
+ dv.SetUint(u64)
+ return nil
+ case reflect.Float32, reflect.Float64:
+ s := asString(src)
+ f64, err := strconv.ParseFloat(s, dv.Type().Bits())
+ if err != nil {
+ err = strconvErr(err)
+ return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
+ }
+ dv.SetFloat(f64)
+ return nil
+ case reflect.String:
+ dv.SetString(asString(src))
+ return nil
+ }
+
+ return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
+}
+
+func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) {
+ switch tp.Kind() {
+ case reflect.Int64:
+ return vv.Int(), nil
+ case reflect.Int:
+ return int(vv.Int()), nil
+ case reflect.Int32:
+ return int32(vv.Int()), nil
+ case reflect.Int16:
+ return int16(vv.Int()), nil
+ case reflect.Int8:
+ return int8(vv.Int()), nil
+ case reflect.Uint64:
+ return vv.Uint(), nil
+ case reflect.Uint:
+ return uint(vv.Uint()), nil
+ case reflect.Uint32:
+ return uint32(vv.Uint()), nil
+ case reflect.Uint16:
+ return uint16(vv.Uint()), nil
+ case reflect.Uint8:
+ return uint8(vv.Uint()), nil
+ case reflect.String:
+ return vv.String(), nil
+ case reflect.Slice:
+ if tp.Elem().Kind() == reflect.Uint8 {
+ v, err := strconv.ParseInt(string(vv.Interface().([]byte)), 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ return v, nil
+ }
+
+ }
+ return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv)
+}
+
+func convertFloat(v interface{}) (float64, error) {
+ switch v.(type) {
+ case float32:
+ return float64(v.(float32)), nil
+ case float64:
+ return v.(float64), nil
+ case string:
+ i, err := strconv.ParseFloat(v.(string), 64)
+ if err != nil {
+ return 0, err
+ }
+ return i, nil
+ case []byte:
+ i, err := strconv.ParseFloat(string(v.([]byte)), 64)
+ if err != nil {
+ return 0, err
+ }
+ return i, nil
+ }
+ return 0, fmt.Errorf("unsupported type: %v", v)
+}
+
+func convertInt(v interface{}) (int64, error) {
+ switch v.(type) {
+ case int:
+ return int64(v.(int)), nil
+ case int8:
+ return int64(v.(int8)), nil
+ case int16:
+ return int64(v.(int16)), nil
+ case int32:
+ return int64(v.(int32)), nil
+ case int64:
+ return v.(int64), nil
+ case []byte:
+ i, err := strconv.ParseInt(string(v.([]byte)), 10, 64)
+ if err != nil {
+ return 0, err
+ }
+ return i, nil
+ case string:
+ i, err := strconv.ParseInt(v.(string), 10, 64)
+ if err != nil {
+ return 0, err
+ }
+ return i, nil
+ }
+ return 0, fmt.Errorf("unsupported type: %v", v)
+}
+
+func asBool(bs []byte) (bool, error) {
+ if len(bs) == 0 {
+ return false, nil
+ }
+ if bs[0] == 0x00 {
+ return false, nil
+ } else if bs[0] == 0x01 {
+ return true, nil
+ }
+ return strconv.ParseBool(string(bs))
+}
diff --git a/vendor/xorm.io/xorm/dialect_mssql.go b/vendor/xorm.io/xorm/dialect_mssql.go
new file mode 100644
index 0000000000..29070da2fb
--- /dev/null
+++ b/vendor/xorm.io/xorm/dialect_mssql.go
@@ -0,0 +1,567 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+
+ "xorm.io/core"
+)
+
+var (
+ mssqlReservedWords = map[string]bool{
+ "ADD": true,
+ "EXTERNAL": true,
+ "PROCEDURE": true,
+ "ALL": true,
+ "FETCH": true,
+ "PUBLIC": true,
+ "ALTER": true,
+ "FILE": true,
+ "RAISERROR": true,
+ "AND": true,
+ "FILLFACTOR": true,
+ "READ": true,
+ "ANY": true,
+ "FOR": true,
+ "READTEXT": true,
+ "AS": true,
+ "FOREIGN": true,
+ "RECONFIGURE": true,
+ "ASC": true,
+ "FREETEXT": true,
+ "REFERENCES": true,
+ "AUTHORIZATION": true,
+ "FREETEXTTABLE": true,
+ "REPLICATION": true,
+ "BACKUP": true,
+ "FROM": true,
+ "RESTORE": true,
+ "BEGIN": true,
+ "FULL": true,
+ "RESTRICT": true,
+ "BETWEEN": true,
+ "FUNCTION": true,
+ "RETURN": true,
+ "BREAK": true,
+ "GOTO": true,
+ "REVERT": true,
+ "BROWSE": true,
+ "GRANT": true,
+ "REVOKE": true,
+ "BULK": true,
+ "GROUP": true,
+ "RIGHT": true,
+ "BY": true,
+ "HAVING": true,
+ "ROLLBACK": true,
+ "CASCADE": true,
+ "HOLDLOCK": true,
+ "ROWCOUNT": true,
+ "CASE": true,
+ "IDENTITY": true,
+ "ROWGUIDCOL": true,
+ "CHECK": true,
+ "IDENTITY_INSERT": true,
+ "RULE": true,
+ "CHECKPOINT": true,
+ "IDENTITYCOL": true,
+ "SAVE": true,
+ "CLOSE": true,
+ "IF": true,
+ "SCHEMA": true,
+ "CLUSTERED": true,
+ "IN": true,
+ "SECURITYAUDIT": true,
+ "COALESCE": true,
+ "INDEX": true,
+ "SELECT": true,
+ "COLLATE": true,
+ "INNER": true,
+ "SEMANTICKEYPHRASETABLE": true,
+ "COLUMN": true,
+ "INSERT": true,
+ "SEMANTICSIMILARITYDETAILSTABLE": true,
+ "COMMIT": true,
+ "INTERSECT": true,
+ "SEMANTICSIMILARITYTABLE": true,
+ "COMPUTE": true,
+ "INTO": true,
+ "SESSION_USER": true,
+ "CONSTRAINT": true,
+ "IS": true,
+ "SET": true,
+ "CONTAINS": true,
+ "JOIN": true,
+ "SETUSER": true,
+ "CONTAINSTABLE": true,
+ "KEY": true,
+ "SHUTDOWN": true,
+ "CONTINUE": true,
+ "KILL": true,
+ "SOME": true,
+ "CONVERT": true,
+ "LEFT": true,
+ "STATISTICS": true,
+ "CREATE": true,
+ "LIKE": true,
+ "SYSTEM_USER": true,
+ "CROSS": true,
+ "LINENO": true,
+ "TABLE": true,
+ "CURRENT": true,
+ "LOAD": true,
+ "TABLESAMPLE": true,
+ "CURRENT_DATE": true,
+ "MERGE": true,
+ "TEXTSIZE": true,
+ "CURRENT_TIME": true,
+ "NATIONAL": true,
+ "THEN": true,
+ "CURRENT_TIMESTAMP": true,
+ "NOCHECK": true,
+ "TO": true,
+ "CURRENT_USER": true,
+ "NONCLUSTERED": true,
+ "TOP": true,
+ "CURSOR": true,
+ "NOT": true,
+ "TRAN": true,
+ "DATABASE": true,
+ "NULL": true,
+ "TRANSACTION": true,
+ "DBCC": true,
+ "NULLIF": true,
+ "TRIGGER": true,
+ "DEALLOCATE": true,
+ "OF": true,
+ "TRUNCATE": true,
+ "DECLARE": true,
+ "OFF": true,
+ "TRY_CONVERT": true,
+ "DEFAULT": true,
+ "OFFSETS": true,
+ "TSEQUAL": true,
+ "DELETE": true,
+ "ON": true,
+ "UNION": true,
+ "DENY": true,
+ "OPEN": true,
+ "UNIQUE": true,
+ "DESC": true,
+ "OPENDATASOURCE": true,
+ "UNPIVOT": true,
+ "DISK": true,
+ "OPENQUERY": true,
+ "UPDATE": true,
+ "DISTINCT": true,
+ "OPENROWSET": true,
+ "UPDATETEXT": true,
+ "DISTRIBUTED": true,
+ "OPENXML": true,
+ "USE": true,
+ "DOUBLE": true,
+ "OPTION": true,
+ "USER": true,
+ "DROP": true,
+ "OR": true,
+ "VALUES": true,
+ "DUMP": true,
+ "ORDER": true,
+ "VARYING": true,
+ "ELSE": true,
+ "OUTER": true,
+ "VIEW": true,
+ "END": true,
+ "OVER": true,
+ "WAITFOR": true,
+ "ERRLVL": true,
+ "PERCENT": true,
+ "WHEN": true,
+ "ESCAPE": true,
+ "PIVOT": true,
+ "WHERE": true,
+ "EXCEPT": true,
+ "PLAN": true,
+ "WHILE": true,
+ "EXEC": true,
+ "PRECISION": true,
+ "WITH": true,
+ "EXECUTE": true,
+ "PRIMARY": true,
+ "WITHIN": true,
+ "EXISTS": true,
+ "PRINT": true,
+ "WRITETEXT": true,
+ "EXIT": true,
+ "PROC": true,
+ }
+)
+
+type mssql struct {
+ core.Base
+}
+
+func (db *mssql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *mssql) SqlType(c *core.Column) string {
+ var res string
+ switch t := c.SQLType.Name; t {
+ case core.Bool:
+ res = core.Bit
+ if strings.EqualFold(c.Default, "true") {
+ c.Default = "1"
+ } else if strings.EqualFold(c.Default, "false") {
+ c.Default = "0"
+ }
+ case core.Serial:
+ c.IsAutoIncrement = true
+ c.IsPrimaryKey = true
+ c.Nullable = false
+ res = core.Int
+ case core.BigSerial:
+ c.IsAutoIncrement = true
+ c.IsPrimaryKey = true
+ c.Nullable = false
+ res = core.BigInt
+ case core.Bytea, core.Blob, core.Binary, core.TinyBlob, core.MediumBlob, core.LongBlob:
+ res = core.VarBinary
+ if c.Length == 0 {
+ c.Length = 50
+ }
+ case core.TimeStamp:
+ res = core.DateTime
+ case core.TimeStampz:
+ res = "DATETIMEOFFSET"
+ c.Length = 7
+ case core.MediumInt:
+ res = core.Int
+ case core.Text, core.MediumText, core.TinyText, core.LongText, core.Json:
+ res = core.Varchar + "(MAX)"
+ case core.Double:
+ res = core.Real
+ case core.Uuid:
+ res = core.Varchar
+ c.Length = 40
+ case core.TinyInt:
+ res = core.TinyInt
+ c.Length = 0
+ case core.BigInt:
+ res = core.BigInt
+ c.Length = 0
+ default:
+ res = t
+ }
+
+ if res == core.Int {
+ return core.Int
+ }
+
+ hasLen1 := (c.Length > 0)
+ hasLen2 := (c.Length2 > 0)
+
+ if hasLen2 {
+ res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
+ } else if hasLen1 {
+ res += "(" + strconv.Itoa(c.Length) + ")"
+ }
+ return res
+}
+
+func (db *mssql) SupportInsertMany() bool {
+ return true
+}
+
+func (db *mssql) IsReserved(name string) bool {
+ _, ok := mssqlReservedWords[name]
+ return ok
+}
+
+func (db *mssql) Quote(name string) string {
+ return "\"" + name + "\""
+}
+
+func (db *mssql) SupportEngine() bool {
+ return false
+}
+
+func (db *mssql) AutoIncrStr() string {
+ return "IDENTITY"
+}
+
+func (db *mssql) DropTableSql(tableName string) string {
+ return fmt.Sprintf("IF EXISTS (SELECT * FROM sysobjects WHERE id = "+
+ "object_id(N'%s') and OBJECTPROPERTY(id, N'IsUserTable') = 1) "+
+ "DROP TABLE \"%s\"", tableName, tableName)
+}
+
+func (db *mssql) SupportCharset() bool {
+ return false
+}
+
+func (db *mssql) IndexOnTable() bool {
+ return true
+}
+
+func (db *mssql) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{idxName}
+ sql := "select name from sysindexes where id=object_id('" + tableName + "') and name=?"
+ return sql, args
+}
+
+/*func (db *mssql) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{tableName, colName}
+ sql := `SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = ? AND "COLUMN_NAME" = ?`
+ return sql, args
+}*/
+
+func (db *mssql) IsColumnExist(tableName, colName string) (bool, error) {
+ query := `SELECT "COLUMN_NAME" FROM "INFORMATION_SCHEMA"."COLUMNS" WHERE "TABLE_NAME" = ? AND "COLUMN_NAME" = ?`
+
+ return db.HasRecords(query, tableName, colName)
+}
+
+func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{}
+ sql := "select * from sysobjects where id = object_id(N'" + tableName + "') and OBJECTPROPERTY(id, N'IsUserTable') = 1"
+ return sql, args
+}
+
+func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
+ args := []interface{}{}
+ s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable,
+ "default_is_null" = (CASE WHEN c.text is null THEN 1 ELSE 0 END),
+ replace(replace(isnull(c.text,''),'(',''),')','') as vdefault,
+ ISNULL(i.is_primary_key, 0), a.is_identity as is_identity
+ from sys.columns a
+ left join sys.types b on a.user_type_id=b.user_type_id
+ left join sys.syscomments c on a.default_object_id=c.id
+ LEFT OUTER JOIN
+ sys.index_columns ic ON ic.object_id = a.object_id AND ic.column_id = a.column_id
+ LEFT OUTER JOIN
+ sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
+ where a.object_id=object_id('` + tableName + `')`
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+ for rows.Next() {
+ var name, ctype, vdefault string
+ var maxLen, precision, scale int
+ var nullable, isPK, defaultIsNull, isIncrement bool
+ err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &defaultIsNull, &vdefault, &isPK, &isIncrement)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+ col.Name = strings.Trim(name, "` ")
+ col.Nullable = nullable
+ col.DefaultIsEmpty = defaultIsNull
+ if !defaultIsNull {
+ col.Default = vdefault
+ }
+ col.IsPrimaryKey = isPK
+ col.IsAutoIncrement = isIncrement
+ ct := strings.ToUpper(ctype)
+ if ct == "DECIMAL" {
+ col.Length = precision
+ col.Length2 = scale
+ } else {
+ col.Length = maxLen
+ }
+ switch ct {
+ case "DATETIMEOFFSET":
+ col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0}
+ case "NVARCHAR":
+ col.SQLType = core.SQLType{Name: core.NVarchar, DefaultLength: 0, DefaultLength2: 0}
+ case "IMAGE":
+ col.SQLType = core.SQLType{Name: core.VarBinary, DefaultLength: 0, DefaultLength2: 0}
+ default:
+ if _, ok := core.SqlTypes[ct]; ok {
+ col.SQLType = core.SQLType{Name: ct, DefaultLength: 0, DefaultLength2: 0}
+ } else {
+ return nil, nil, fmt.Errorf("Unknown colType %v for %v - %v", ct, tableName, col.Name)
+ }
+ }
+
+ cols[col.Name] = col
+ colSeq = append(colSeq, col.Name)
+ }
+ return colSeq, cols, nil
+}
+
+func (db *mssql) GetTables() ([]*core.Table, error) {
+ args := []interface{}{}
+ s := `select name from sysobjects where xtype ='U'`
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ var name string
+ err = rows.Scan(&name)
+ if err != nil {
+ return nil, err
+ }
+ table.Name = strings.Trim(name, "` ")
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *mssql) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{tableName}
+ s := `SELECT
+IXS.NAME AS [INDEX_NAME],
+C.NAME AS [COLUMN_NAME],
+IXS.is_unique AS [IS_UNIQUE]
+FROM SYS.INDEXES IXS
+INNER JOIN SYS.INDEX_COLUMNS IXCS
+ON IXS.OBJECT_ID=IXCS.OBJECT_ID AND IXS.INDEX_ID = IXCS.INDEX_ID
+INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID
+AND IXCS.COLUMN_ID=C.COLUMN_ID
+WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
+`
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var indexType int
+ var indexName, colName, isUnique string
+
+ err = rows.Scan(&indexName, &colName, &isUnique)
+ if err != nil {
+ return nil, err
+ }
+
+ i, err := strconv.ParseBool(isUnique)
+ if err != nil {
+ return nil, err
+ }
+
+ if i {
+ indexType = core.UniqueType
+ } else {
+ indexType = core.IndexType
+ }
+
+ colName = strings.Trim(colName, "` ")
+ var isRegular bool
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ indexName = indexName[5+len(tableName):]
+ isRegular = true
+ }
+
+ var index *core.Index
+ var ok bool
+ if index, ok = indexes[indexName]; !ok {
+ index = new(core.Index)
+ index.Type = indexType
+ index.Name = indexName
+ index.IsRegular = isRegular
+ indexes[indexName] = index
+ }
+ index.AddColumn(colName)
+ }
+ return indexes, nil
+}
+
+func (db *mssql) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
+ var sql string
+ if tableName == "" {
+ tableName = table.Name
+ }
+
+ sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE "
+
+ sql += db.Quote(tableName) + " ("
+
+ pkList := table.PrimaryKeys
+
+ for _, colName := range table.ColumnsSeq() {
+ col := table.GetColumn(colName)
+ if col.IsPrimaryKey && len(pkList) == 1 {
+ sql += col.String(db)
+ } else {
+ sql += col.StringNoPk(db)
+ }
+ sql = strings.TrimSpace(sql)
+ sql += ", "
+ }
+
+ if len(pkList) > 1 {
+ sql += "PRIMARY KEY ( "
+ sql += strings.Join(pkList, ",")
+ sql += " ), "
+ }
+
+ sql = sql[:len(sql)-2] + ")"
+ sql += ";"
+ return sql
+}
+
+func (db *mssql) ForUpdateSql(query string) string {
+ return query
+}
+
+func (db *mssql) Filters() []core.Filter {
+ return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
+}
+
+type odbcDriver struct {
+}
+
+func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ var dbName string
+
+ if strings.HasPrefix(dataSourceName, "sqlserver://") {
+ u, err := url.Parse(dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+ dbName = u.Query().Get("database")
+ } else {
+ kv := strings.Split(dataSourceName, ";")
+ for _, c := range kv {
+ vv := strings.Split(strings.TrimSpace(c), "=")
+ if len(vv) == 2 {
+ switch strings.ToLower(vv[0]) {
+ case "database":
+ dbName = vv[1]
+ }
+ }
+ }
+ }
+ if dbName == "" {
+ return nil, errors.New("no db name provided")
+ }
+ return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil
+}
diff --git a/vendor/xorm.io/xorm/dialect_mysql.go b/vendor/xorm.io/xorm/dialect_mysql.go
new file mode 100644
index 0000000000..cf1dbb6f21
--- /dev/null
+++ b/vendor/xorm.io/xorm/dialect_mysql.go
@@ -0,0 +1,654 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "xorm.io/core"
+)
+
+var (
+ mysqlReservedWords = map[string]bool{
+ "ADD": true,
+ "ALL": true,
+ "ALTER": true,
+ "ANALYZE": true,
+ "AND": true,
+ "AS": true,
+ "ASC": true,
+ "ASENSITIVE": true,
+ "BEFORE": true,
+ "BETWEEN": true,
+ "BIGINT": true,
+ "BINARY": true,
+ "BLOB": true,
+ "BOTH": true,
+ "BY": true,
+ "CALL": true,
+ "CASCADE": true,
+ "CASE": true,
+ "CHANGE": true,
+ "CHAR": true,
+ "CHARACTER": true,
+ "CHECK": true,
+ "COLLATE": true,
+ "COLUMN": true,
+ "CONDITION": true,
+ "CONNECTION": true,
+ "CONSTRAINT": true,
+ "CONTINUE": true,
+ "CONVERT": true,
+ "CREATE": true,
+ "CROSS": true,
+ "CURRENT_DATE": true,
+ "CURRENT_TIME": true,
+ "CURRENT_TIMESTAMP": true,
+ "CURRENT_USER": true,
+ "CURSOR": true,
+ "DATABASE": true,
+ "DATABASES": true,
+ "DAY_HOUR": true,
+ "DAY_MICROSECOND": true,
+ "DAY_MINUTE": true,
+ "DAY_SECOND": true,
+ "DEC": true,
+ "DECIMAL": true,
+ "DECLARE": true,
+ "DEFAULT": true,
+ "DELAYED": true,
+ "DELETE": true,
+ "DESC": true,
+ "DESCRIBE": true,
+ "DETERMINISTIC": true,
+ "DISTINCT": true,
+ "DISTINCTROW": true,
+ "DIV": true,
+ "DOUBLE": true,
+ "DROP": true,
+ "DUAL": true,
+ "EACH": true,
+ "ELSE": true,
+ "ELSEIF": true,
+ "ENCLOSED": true,
+ "ESCAPED": true,
+ "EXISTS": true,
+ "EXIT": true,
+ "EXPLAIN": true,
+ "FALSE": true,
+ "FETCH": true,
+ "FLOAT": true,
+ "FLOAT4": true,
+ "FLOAT8": true,
+ "FOR": true,
+ "FORCE": true,
+ "FOREIGN": true,
+ "FROM": true,
+ "FULLTEXT": true,
+ "GOTO": true,
+ "GRANT": true,
+ "GROUP": true,
+ "HAVING": true,
+ "HIGH_PRIORITY": true,
+ "HOUR_MICROSECOND": true,
+ "HOUR_MINUTE": true,
+ "HOUR_SECOND": true,
+ "IF": true,
+ "IGNORE": true,
+ "IN": true, "INDEX": true,
+ "INFILE": true, "INNER": true, "INOUT": true,
+ "INSENSITIVE": true, "INSERT": true, "INT": true,
+ "INT1": true, "INT2": true, "INT3": true,
+ "INT4": true, "INT8": true, "INTEGER": true,
+ "INTERVAL": true, "INTO": true, "IS": true,
+ "ITERATE": true, "JOIN": true, "KEY": true,
+ "KEYS": true, "KILL": true, "LABEL": true,
+ "LEADING": true, "LEAVE": true, "LEFT": true,
+ "LIKE": true, "LIMIT": true, "LINEAR": true,
+ "LINES": true, "LOAD": true, "LOCALTIME": true,
+ "LOCALTIMESTAMP": true, "LOCK": true, "LONG": true,
+ "LONGBLOB": true, "LONGTEXT": true, "LOOP": true,
+ "LOW_PRIORITY": true, "MATCH": true, "MEDIUMBLOB": true,
+ "MEDIUMINT": true, "MEDIUMTEXT": true, "MIDDLEINT": true,
+ "MINUTE_MICROSECOND": true, "MINUTE_SECOND": true, "MOD": true,
+ "MODIFIES": true, "NATURAL": true, "NOT": true,
+ "NO_WRITE_TO_BINLOG": true, "NULL": true, "NUMERIC": true,
+ "ON OPTIMIZE": true, "OPTION": true,
+ "OPTIONALLY": true, "OR": true, "ORDER": true,
+ "OUT": true, "OUTER": true, "OUTFILE": true,
+ "PRECISION": true, "PRIMARY": true, "PROCEDURE": true,
+ "PURGE": true, "RAID0": true, "RANGE": true,
+ "READ": true, "READS": true, "REAL": true,
+ "REFERENCES": true, "REGEXP": true, "RELEASE": true,
+ "RENAME": true, "REPEAT": true, "REPLACE": true,
+ "REQUIRE": true, "RESTRICT": true, "RETURN": true,
+ "REVOKE": true, "RIGHT": true, "RLIKE": true,
+ "SCHEMA": true, "SCHEMAS": true, "SECOND_MICROSECOND": true,
+ "SELECT": true, "SENSITIVE": true, "SEPARATOR": true,
+ "SET": true, "SHOW": true, "SMALLINT": true,
+ "SPATIAL": true, "SPECIFIC": true, "SQL": true,
+ "SQLEXCEPTION": true, "SQLSTATE": true, "SQLWARNING": true,
+ "SQL_BIG_RESULT": true, "SQL_CALC_FOUND_ROWS": true, "SQL_SMALL_RESULT": true,
+ "SSL": true, "STARTING": true, "STRAIGHT_JOIN": true,
+ "TABLE": true, "TERMINATED": true, "THEN": true,
+ "TINYBLOB": true, "TINYINT": true, "TINYTEXT": true,
+ "TO": true, "TRAILING": true, "TRIGGER": true,
+ "TRUE": true, "UNDO": true, "UNION": true,
+ "UNIQUE": true, "UNLOCK": true, "UNSIGNED": true,
+ "UPDATE": true, "USAGE": true, "USE": true,
+ "USING": true, "UTC_DATE": true, "UTC_TIME": true,
+ "UTC_TIMESTAMP": true, "VALUES": true, "VARBINARY": true,
+ "VARCHAR": true,
+ "VARCHARACTER": true,
+ "VARYING": true,
+ "WHEN": true,
+ "WHERE": true,
+ "WHILE": true,
+ "WITH": true,
+ "WRITE": true,
+ "X509": true,
+ "XOR": true,
+ "YEAR_MONTH": true,
+ "ZEROFILL": true,
+ }
+)
+
+type mysql struct {
+ core.Base
+ net string
+ addr string
+ params map[string]string
+ loc *time.Location
+ timeout time.Duration
+ tls *tls.Config
+ allowAllFiles bool
+ allowOldPasswords bool
+ clientFoundRows bool
+ rowFormat string
+}
+
+func (db *mysql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *mysql) SetParams(params map[string]string) {
+ rowFormat, ok := params["rowFormat"]
+ if ok {
+ var t = strings.ToUpper(rowFormat)
+ switch t {
+ case "COMPACT":
+ fallthrough
+ case "REDUNDANT":
+ fallthrough
+ case "DYNAMIC":
+ fallthrough
+ case "COMPRESSED":
+ db.rowFormat = t
+ break
+ default:
+ break
+ }
+ }
+}
+
+func (db *mysql) SqlType(c *core.Column) string {
+ var res string
+ switch t := c.SQLType.Name; t {
+ case core.Bool:
+ res = core.TinyInt
+ c.Length = 1
+ case core.Serial:
+ c.IsAutoIncrement = true
+ c.IsPrimaryKey = true
+ c.Nullable = false
+ res = core.Int
+ case core.BigSerial:
+ c.IsAutoIncrement = true
+ c.IsPrimaryKey = true
+ c.Nullable = false
+ res = core.BigInt
+ case core.Bytea:
+ res = core.Blob
+ case core.TimeStampz:
+ res = core.Char
+ c.Length = 64
+ case core.Enum: // mysql enum
+ res = core.Enum
+ res += "("
+ opts := ""
+ for v := range c.EnumOptions {
+ opts += fmt.Sprintf(",'%v'", v)
+ }
+ res += strings.TrimLeft(opts, ",")
+ res += ")"
+ case core.Set: // mysql set
+ res = core.Set
+ res += "("
+ opts := ""
+ for v := range c.SetOptions {
+ opts += fmt.Sprintf(",'%v'", v)
+ }
+ res += strings.TrimLeft(opts, ",")
+ res += ")"
+ case core.NVarchar:
+ res = core.Varchar
+ case core.Uuid:
+ res = core.Varchar
+ c.Length = 40
+ case core.Json:
+ res = core.Text
+ default:
+ res = t
+ }
+
+ hasLen1 := (c.Length > 0)
+ hasLen2 := (c.Length2 > 0)
+
+ if res == core.BigInt && !hasLen1 && !hasLen2 {
+ c.Length = 20
+ hasLen1 = true
+ }
+
+ if hasLen2 {
+ res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
+ } else if hasLen1 {
+ res += "(" + strconv.Itoa(c.Length) + ")"
+ }
+ return res
+}
+
+func (db *mysql) SupportInsertMany() bool {
+ return true
+}
+
+func (db *mysql) IsReserved(name string) bool {
+ _, ok := mysqlReservedWords[name]
+ return ok
+}
+
+func (db *mysql) Quote(name string) string {
+ return "`" + name + "`"
+}
+
+func (db *mysql) SupportEngine() bool {
+ return true
+}
+
+func (db *mysql) AutoIncrStr() string {
+ return "AUTO_INCREMENT"
+}
+
+func (db *mysql) SupportCharset() bool {
+ return true
+}
+
+func (db *mysql) IndexOnTable() bool {
+ return true
+}
+
+func (db *mysql) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{db.DbName, tableName, idxName}
+ sql := "SELECT `INDEX_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS`"
+ sql += " WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `INDEX_NAME`=?"
+ return sql, args
+}
+
+/*func (db *mysql) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{db.DbName, tableName, colName}
+ sql := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
+ return sql, args
+}*/
+
+func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{db.DbName, tableName}
+ sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?"
+ return sql, args
+}
+
+func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
+ args := []interface{}{db.DbName, tableName}
+ s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," +
+ " `COLUMN_KEY`, `EXTRA`,`COLUMN_COMMENT` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+ for rows.Next() {
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+
+ var columnName, isNullable, colType, colKey, extra, comment string
+ var colDefault *string
+ err = rows.Scan(&columnName, &isNullable, &colDefault, &colType, &colKey, &extra, &comment)
+ if err != nil {
+ return nil, nil, err
+ }
+ col.Name = strings.Trim(columnName, "` ")
+ col.Comment = comment
+ if "YES" == isNullable {
+ col.Nullable = true
+ }
+
+ if colDefault != nil {
+ col.Default = *colDefault
+ col.DefaultIsEmpty = false
+ } else {
+ col.DefaultIsEmpty = true
+ }
+
+ cts := strings.Split(colType, "(")
+ colName := cts[0]
+ colType = strings.ToUpper(colName)
+ var len1, len2 int
+ if len(cts) == 2 {
+ idx := strings.Index(cts[1], ")")
+ if colType == core.Enum && cts[1][0] == '\'' { // enum
+ options := strings.Split(cts[1][0:idx], ",")
+ col.EnumOptions = make(map[string]int)
+ for k, v := range options {
+ v = strings.TrimSpace(v)
+ v = strings.Trim(v, "'")
+ col.EnumOptions[v] = k
+ }
+ } else if colType == core.Set && cts[1][0] == '\'' {
+ options := strings.Split(cts[1][0:idx], ",")
+ col.SetOptions = make(map[string]int)
+ for k, v := range options {
+ v = strings.TrimSpace(v)
+ v = strings.Trim(v, "'")
+ col.SetOptions[v] = k
+ }
+ } else {
+ lens := strings.Split(cts[1][0:idx], ",")
+ len1, err = strconv.Atoi(strings.TrimSpace(lens[0]))
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(lens) == 2 {
+ len2, err = strconv.Atoi(lens[1])
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+ }
+ }
+ if colType == "FLOAT UNSIGNED" {
+ colType = "FLOAT"
+ }
+ if colType == "DOUBLE UNSIGNED" {
+ colType = "DOUBLE"
+ }
+ col.Length = len1
+ col.Length2 = len2
+ if _, ok := core.SqlTypes[colType]; ok {
+ col.SQLType = core.SQLType{Name: colType, DefaultLength: len1, DefaultLength2: len2}
+ } else {
+ return nil, nil, fmt.Errorf("Unknown colType %v", colType)
+ }
+
+ if colKey == "PRI" {
+ col.IsPrimaryKey = true
+ }
+ if colKey == "UNI" {
+ // col.is
+ }
+
+ if extra == "auto_increment" {
+ col.IsAutoIncrement = true
+ }
+
+ if !col.DefaultIsEmpty {
+ if col.SQLType.IsText() {
+ col.Default = "'" + col.Default + "'"
+ } else if col.SQLType.IsTime() && col.Default != "CURRENT_TIMESTAMP" {
+ col.Default = "'" + col.Default + "'"
+ }
+ }
+ cols[col.Name] = col
+ colSeq = append(colSeq, col.Name)
+ }
+ return colSeq, cols, nil
+}
+
+func (db *mysql) GetTables() ([]*core.Table, error) {
+ args := []interface{}{db.DbName}
+ s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT`, `TABLE_COMMENT` from " +
+ "`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ var name, engine, tableRows, comment string
+ var autoIncr *string
+ err = rows.Scan(&name, &engine, &tableRows, &autoIncr, &comment)
+ if err != nil {
+ return nil, err
+ }
+
+ table.Name = name
+ table.Comment = comment
+ table.StoreEngine = engine
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{db.DbName, tableName}
+ s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var indexType int
+ var indexName, colName, nonUnique string
+ err = rows.Scan(&indexName, &nonUnique, &colName)
+ if err != nil {
+ return nil, err
+ }
+
+ if indexName == "PRIMARY" {
+ continue
+ }
+
+ if "YES" == nonUnique || nonUnique == "1" {
+ indexType = core.IndexType
+ } else {
+ indexType = core.UniqueType
+ }
+
+ colName = strings.Trim(colName, "` ")
+ var isRegular bool
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ indexName = indexName[5+len(tableName):]
+ isRegular = true
+ }
+
+ var index *core.Index
+ var ok bool
+ if index, ok = indexes[indexName]; !ok {
+ index = new(core.Index)
+ index.IsRegular = isRegular
+ index.Type = indexType
+ index.Name = indexName
+ indexes[indexName] = index
+ }
+ index.AddColumn(colName)
+ }
+ return indexes, nil
+}
+
+func (db *mysql) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
+ var sql string
+ sql = "CREATE TABLE IF NOT EXISTS "
+ if tableName == "" {
+ tableName = table.Name
+ }
+
+ sql += db.Quote(tableName)
+ sql += " ("
+
+ if len(table.ColumnsSeq()) > 0 {
+ pkList := table.PrimaryKeys
+
+ for _, colName := range table.ColumnsSeq() {
+ col := table.GetColumn(colName)
+ if col.IsPrimaryKey && len(pkList) == 1 {
+ sql += col.String(db)
+ } else {
+ sql += col.StringNoPk(db)
+ }
+ sql = strings.TrimSpace(sql)
+ if len(col.Comment) > 0 {
+ sql += " COMMENT '" + col.Comment + "'"
+ }
+ sql += ", "
+ }
+
+ if len(pkList) > 1 {
+ sql += "PRIMARY KEY ( "
+ sql += db.Quote(strings.Join(pkList, db.Quote(",")))
+ sql += " ), "
+ }
+
+ sql = sql[:len(sql)-2]
+ }
+ sql += ")"
+
+ if storeEngine != "" {
+ sql += " ENGINE=" + storeEngine
+ }
+
+ if len(charset) == 0 {
+ charset = db.URI().Charset
+ }
+ if len(charset) != 0 {
+ sql += " DEFAULT CHARSET " + charset
+ }
+
+ if db.rowFormat != "" {
+ sql += " ROW_FORMAT=" + db.rowFormat
+ }
+ return sql
+}
+
+func (db *mysql) Filters() []core.Filter {
+ return []core.Filter{&core.IdFilter{}}
+}
+
+type mymysqlDriver struct {
+}
+
+func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ db := &core.Uri{DbType: core.MYSQL}
+
+ pd := strings.SplitN(dataSourceName, "*", 2)
+ if len(pd) == 2 {
+ // Parse protocol part of URI
+ p := strings.SplitN(pd[0], ":", 2)
+ if len(p) != 2 {
+ return nil, errors.New("Wrong protocol part of URI")
+ }
+ db.Proto = p[0]
+ options := strings.Split(p[1], ",")
+ db.Raddr = options[0]
+ for _, o := range options[1:] {
+ kv := strings.SplitN(o, "=", 2)
+ var k, v string
+ if len(kv) == 2 {
+ k, v = kv[0], kv[1]
+ } else {
+ k, v = o, "true"
+ }
+ switch k {
+ case "laddr":
+ db.Laddr = v
+ case "timeout":
+ to, err := time.ParseDuration(v)
+ if err != nil {
+ return nil, err
+ }
+ db.Timeout = to
+ default:
+ return nil, errors.New("Unknown option: " + k)
+ }
+ }
+ // Remove protocol part
+ pd = pd[1:]
+ }
+ // Parse database part of URI
+ dup := strings.SplitN(pd[0], "/", 3)
+ if len(dup) != 3 {
+ return nil, errors.New("Wrong database part of URI")
+ }
+ db.DbName = dup[0]
+ db.User = dup[1]
+ db.Passwd = dup[2]
+
+ return db, nil
+}
+
+type mysqlDriver struct {
+}
+
+func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ dsnPattern := regexp.MustCompile(
+ `^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
+ `(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
+ `\/(?P<dbname>.*?)` + // /dbname
+ `(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
+ matches := dsnPattern.FindStringSubmatch(dataSourceName)
+ // tlsConfigRegister := make(map[string]*tls.Config)
+ names := dsnPattern.SubexpNames()
+
+ uri := &core.Uri{DbType: core.MYSQL}
+
+ for i, match := range matches {
+ switch names[i] {
+ case "dbname":
+ uri.DbName = match
+ case "params":
+ if len(match) > 0 {
+ kvs := strings.Split(match, "&")
+ for _, kv := range kvs {
+ splits := strings.Split(kv, "=")
+ if len(splits) == 2 {
+ switch splits[0] {
+ case "charset":
+ uri.Charset = splits[1]
+ }
+ }
+ }
+ }
+
+ }
+ }
+ return uri, nil
+}
diff --git a/vendor/xorm.io/xorm/dialect_oracle.go b/vendor/xorm.io/xorm/dialect_oracle.go
new file mode 100644
index 0000000000..15010ca5a3
--- /dev/null
+++ b/vendor/xorm.io/xorm/dialect_oracle.go
@@ -0,0 +1,902 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "xorm.io/core"
+)
+
+var (
+ oracleReservedWords = map[string]bool{
+ "ACCESS": true,
+ "ACCOUNT": true,
+ "ACTIVATE": true,
+ "ADD": true,
+ "ADMIN": true,
+ "ADVISE": true,
+ "AFTER": true,
+ "ALL": true,
+ "ALL_ROWS": true,
+ "ALLOCATE": true,
+ "ALTER": true,
+ "ANALYZE": true,
+ "AND": true,
+ "ANY": true,
+ "ARCHIVE": true,
+ "ARCHIVELOG": true,
+ "ARRAY": true,
+ "AS": true,
+ "ASC": true,
+ "AT": true,
+ "AUDIT": true,
+ "AUTHENTICATED": true,
+ "AUTHORIZATION": true,
+ "AUTOEXTEND": true,
+ "AUTOMATIC": true,
+ "BACKUP": true,
+ "BECOME": true,
+ "BEFORE": true,
+ "BEGIN": true,
+ "BETWEEN": true,
+ "BFILE": true,
+ "BITMAP": true,
+ "BLOB": true,
+ "BLOCK": true,
+ "BODY": true,
+ "BY": true,
+ "CACHE": true,
+ "CACHE_INSTANCES": true,
+ "CANCEL": true,
+ "CASCADE": true,
+ "CAST": true,
+ "CFILE": true,
+ "CHAINED": true,
+ "CHANGE": true,
+ "CHAR": true,
+ "CHAR_CS": true,
+ "CHARACTER": true,
+ "CHECK": true,
+ "CHECKPOINT": true,
+ "CHOOSE": true,
+ "CHUNK": true,
+ "CLEAR": true,
+ "CLOB": true,
+ "CLONE": true,
+ "CLOSE": true,
+ "CLOSE_CACHED_OPEN_CURSORS": true,
+ "CLUSTER": true,
+ "COALESCE": true,
+ "COLUMN": true,
+ "COLUMNS": true,
+ "COMMENT": true,
+ "COMMIT": true,
+ "COMMITTED": true,
+ "COMPATIBILITY": true,
+ "COMPILE": true,
+ "COMPLETE": true,
+ "COMPOSITE_LIMIT": true,
+ "COMPRESS": true,
+ "COMPUTE": true,
+ "CONNECT": true,
+ "CONNECT_TIME": true,
+ "CONSTRAINT": true,
+ "CONSTRAINTS": true,
+ "CONTENTS": true,
+ "CONTINUE": true,
+ "CONTROLFILE": true,
+ "CONVERT": true,
+ "COST": true,
+ "CPU_PER_CALL": true,
+ "CPU_PER_SESSION": true,
+ "CREATE": true,
+ "CURRENT": true,
+ "CURRENT_SCHEMA": true,
+ "CURREN_USER": true,
+ "CURSOR": true,
+ "CYCLE": true,
+ "DANGLING": true,
+ "DATABASE": true,
+ "DATAFILE": true,
+ "DATAFILES": true,
+ "DATAOBJNO": true,
+ "DATE": true,
+ "DBA": true,
+ "DBHIGH": true,
+ "DBLOW": true,
+ "DBMAC": true,
+ "DEALLOCATE": true,
+ "DEBUG": true,
+ "DEC": true,
+ "DECIMAL": true,
+ "DECLARE": true,
+ "DEFAULT": true,
+ "DEFERRABLE": true,
+ "DEFERRED": true,
+ "DEGREE": true,
+ "DELETE": true,
+ "DEREF": true,
+ "DESC": true,
+ "DIRECTORY": true,
+ "DISABLE": true,
+ "DISCONNECT": true,
+ "DISMOUNT": true,
+ "DISTINCT": true,
+ "DISTRIBUTED": true,
+ "DML": true,
+ "DOUBLE": true,
+ "DROP": true,
+ "DUMP": true,
+ "EACH": true,
+ "ELSE": true,
+ "ENABLE": true,
+ "END": true,
+ "ENFORCE": true,
+ "ENTRY": true,
+ "ESCAPE": true,
+ "EXCEPT": true,
+ "EXCEPTIONS": true,
+ "EXCHANGE": true,
+ "EXCLUDING": true,
+ "EXCLUSIVE": true,
+ "EXECUTE": true,
+ "EXISTS": true,
+ "EXPIRE": true,
+ "EXPLAIN": true,
+ "EXTENT": true,
+ "EXTENTS": true,
+ "EXTERNALLY": true,
+ "FAILED_LOGIN_ATTEMPTS": true,
+ "FALSE": true,
+ "FAST": true,
+ "FILE": true,
+ "FIRST_ROWS": true,
+ "FLAGGER": true,
+ "FLOAT": true,
+ "FLOB": true,
+ "FLUSH": true,
+ "FOR": true,
+ "FORCE": true,
+ "FOREIGN": true,
+ "FREELIST": true,
+ "FREELISTS": true,
+ "FROM": true,
+ "FULL": true,
+ "FUNCTION": true,
+ "GLOBAL": true,
+ "GLOBALLY": true,
+ "GLOBAL_NAME": true,
+ "GRANT": true,
+ "GROUP": true,
+ "GROUPS": true,
+ "HASH": true,
+ "HASHKEYS": true,
+ "HAVING": true,
+ "HEADER": true,
+ "HEAP": true,
+ "IDENTIFIED": true,
+ "IDGENERATORS": true,
+ "IDLE_TIME": true,
+ "IF": true,
+ "IMMEDIATE": true,
+ "IN": true,
+ "INCLUDING": true,
+ "INCREMENT": true,
+ "INDEX": true,
+ "INDEXED": true,
+ "INDEXES": true,
+ "INDICATOR": true,
+ "IND_PARTITION": true,
+ "INITIAL": true,
+ "INITIALLY": true,
+ "INITRANS": true,
+ "INSERT": true,
+ "INSTANCE": true,
+ "INSTANCES": true,
+ "INSTEAD": true,
+ "INT": true,
+ "INTEGER": true,
+ "INTERMEDIATE": true,
+ "INTERSECT": true,
+ "INTO": true,
+ "IS": true,
+ "ISOLATION": true,
+ "ISOLATION_LEVEL": true,
+ "KEEP": true,
+ "KEY": true,
+ "KILL": true,
+ "LABEL": true,
+ "LAYER": true,
+ "LESS": true,
+ "LEVEL": true,
+ "LIBRARY": true,
+ "LIKE": true,
+ "LIMIT": true,
+ "LINK": true,
+ "LIST": true,
+ "LOB": true,
+ "LOCAL": true,
+ "LOCK": true,
+ "LOCKED": true,
+ "LOG": true,
+ "LOGFILE": true,
+ "LOGGING": true,
+ "LOGICAL_READS_PER_CALL": true,
+ "LOGICAL_READS_PER_SESSION": true,
+ "LONG": true,
+ "MANAGE": true,
+ "MASTER": true,
+ "MAX": true,
+ "MAXARCHLOGS": true,
+ "MAXDATAFILES": true,
+ "MAXEXTENTS": true,
+ "MAXINSTANCES": true,
+ "MAXLOGFILES": true,
+ "MAXLOGHISTORY": true,
+ "MAXLOGMEMBERS": true,
+ "MAXSIZE": true,
+ "MAXTRANS": true,
+ "MAXVALUE": true,
+ "MIN": true,
+ "MEMBER": true,
+ "MINIMUM": true,
+ "MINEXTENTS": true,
+ "MINUS": true,
+ "MINVALUE": true,
+ "MLSLABEL": true,
+ "MLS_LABEL_FORMAT": true,
+ "MODE": true,
+ "MODIFY": true,
+ "MOUNT": true,
+ "MOVE": true,
+ "MTS_DISPATCHERS": true,
+ "MULTISET": true,
+ "NATIONAL": true,
+ "NCHAR": true,
+ "NCHAR_CS": true,
+ "NCLOB": true,
+ "NEEDED": true,
+ "NESTED": true,
+ "NETWORK": true,
+ "NEW": true,
+ "NEXT": true,
+ "NOARCHIVELOG": true,
+ "NOAUDIT": true,
+ "NOCACHE": true,
+ "NOCOMPRESS": true,
+ "NOCYCLE": true,
+ "NOFORCE": true,
+ "NOLOGGING": true,
+ "NOMAXVALUE": true,
+ "NOMINVALUE": true,
+ "NONE": true,
+ "NOORDER": true,
+ "NOOVERRIDE": true,
+ "NOPARALLEL": true,
+ "NOREVERSE": true,
+ "NORMAL": true,
+ "NOSORT": true,
+ "NOT": true,
+ "NOTHING": true,
+ "NOWAIT": true,
+ "NULL": true,
+ "NUMBER": true,
+ "NUMERIC": true,
+ "NVARCHAR2": true,
+ "OBJECT": true,
+ "OBJNO": true,
+ "OBJNO_REUSE": true,
+ "OF": true,
+ "OFF": true,
+ "OFFLINE": true,
+ "OID": true,
+ "OIDINDEX": true,
+ "OLD": true,
+ "ON": true,
+ "ONLINE": true,
+ "ONLY": true,
+ "OPCODE": true,
+ "OPEN": true,
+ "OPTIMAL": true,
+ "OPTIMIZER_GOAL": true,
+ "OPTION": true,
+ "OR": true,
+ "ORDER": true,
+ "ORGANIZATION": true,
+ "OSLABEL": true,
+ "OVERFLOW": true,
+ "OWN": true,
+ "PACKAGE": true,
+ "PARALLEL": true,
+ "PARTITION": true,
+ "PASSWORD": true,
+ "PASSWORD_GRACE_TIME": true,
+ "PASSWORD_LIFE_TIME": true,
+ "PASSWORD_LOCK_TIME": true,
+ "PASSWORD_REUSE_MAX": true,
+ "PASSWORD_REUSE_TIME": true,
+ "PASSWORD_VERIFY_FUNCTION": true,
+ "PCTFREE": true,
+ "PCTINCREASE": true,
+ "PCTTHRESHOLD": true,
+ "PCTUSED": true,
+ "PCTVERSION": true,
+ "PERCENT": true,
+ "PERMANENT": true,
+ "PLAN": true,
+ "PLSQL_DEBUG": true,
+ "POST_TRANSACTION": true,
+ "PRECISION": true,
+ "PRESERVE": true,
+ "PRIMARY": true,
+ "PRIOR": true,
+ "PRIVATE": true,
+ "PRIVATE_SGA": true,
+ "PRIVILEGE": true,
+ "PRIVILEGES": true,
+ "PROCEDURE": true,
+ "PROFILE": true,
+ "PUBLIC": true,
+ "PURGE": true,
+ "QUEUE": true,
+ "QUOTA": true,
+ "RANGE": true,
+ "RAW": true,
+ "RBA": true,
+ "READ": true,
+ "READUP": true,
+ "REAL": true,
+ "REBUILD": true,
+ "RECOVER": true,
+ "RECOVERABLE": true,
+ "RECOVERY": true,
+ "REF": true,
+ "REFERENCES": true,
+ "REFERENCING": true,
+ "REFRESH": true,
+ "RENAME": true,
+ "REPLACE": true,
+ "RESET": true,
+ "RESETLOGS": true,
+ "RESIZE": true,
+ "RESOURCE": true,
+ "RESTRICTED": true,
+ "RETURN": true,
+ "RETURNING": true,
+ "REUSE": true,
+ "REVERSE": true,
+ "REVOKE": true,
+ "ROLE": true,
+ "ROLES": true,
+ "ROLLBACK": true,
+ "ROW": true,
+ "ROWID": true,
+ "ROWNUM": true,
+ "ROWS": true,
+ "RULE": true,
+ "SAMPLE": true,
+ "SAVEPOINT": true,
+ "SB4": true,
+ "SCAN_INSTANCES": true,
+ "SCHEMA": true,
+ "SCN": true,
+ "SCOPE": true,
+ "SD_ALL": true,
+ "SD_INHIBIT": true,
+ "SD_SHOW": true,
+ "SEGMENT": true,
+ "SEG_BLOCK": true,
+ "SEG_FILE": true,
+ "SELECT": true,
+ "SEQUENCE": true,
+ "SERIALIZABLE": true,
+ "SESSION": true,
+ "SESSION_CACHED_CURSORS": true,
+ "SESSIONS_PER_USER": true,
+ "SET": true,
+ "SHARE": true,
+ "SHARED": true,
+ "SHARED_POOL": true,
+ "SHRINK": true,
+ "SIZE": true,
+ "SKIP": true,
+ "SKIP_UNUSABLE_INDEXES": true,
+ "SMALLINT": true,
+ "SNAPSHOT": true,
+ "SOME": true,
+ "SORT": true,
+ "SPECIFICATION": true,
+ "SPLIT": true,
+ "SQL_TRACE": true,
+ "STANDBY": true,
+ "START": true,
+ "STATEMENT_ID": true,
+ "STATISTICS": true,
+ "STOP": true,
+ "STORAGE": true,
+ "STORE": true,
+ "STRUCTURE": true,
+ "SUCCESSFUL": true,
+ "SWITCH": true,
+ "SYS_OP_ENFORCE_NOT_NULL$": true,
+ "SYS_OP_NTCIMG$": true,
+ "SYNONYM": true,
+ "SYSDATE": true,
+ "SYSDBA": true,
+ "SYSOPER": true,
+ "SYSTEM": true,
+ "TABLE": true,
+ "TABLES": true,
+ "TABLESPACE": true,
+ "TABLESPACE_NO": true,
+ "TABNO": true,
+ "TEMPORARY": true,
+ "THAN": true,
+ "THE": true,
+ "THEN": true,
+ "THREAD": true,
+ "TIMESTAMP": true,
+ "TIME": true,
+ "TO": true,
+ "TOPLEVEL": true,
+ "TRACE": true,
+ "TRACING": true,
+ "TRANSACTION": true,
+ "TRANSITIONAL": true,
+ "TRIGGER": true,
+ "TRIGGERS": true,
+ "TRUE": true,
+ "TRUNCATE": true,
+ "TX": true,
+ "TYPE": true,
+ "UB2": true,
+ "UBA": true,
+ "UID": true,
+ "UNARCHIVED": true,
+ "UNDO": true,
+ "UNION": true,
+ "UNIQUE": true,
+ "UNLIMITED": true,
+ "UNLOCK": true,
+ "UNRECOVERABLE": true,
+ "UNTIL": true,
+ "UNUSABLE": true,
+ "UNUSED": true,
+ "UPDATABLE": true,
+ "UPDATE": true,
+ "USAGE": true,
+ "USE": true,
+ "USER": true,
+ "USING": true,
+ "VALIDATE": true,
+ "VALIDATION": true,
+ "VALUE": true,
+ "VALUES": true,
+ "VARCHAR": true,
+ "VARCHAR2": true,
+ "VARYING": true,
+ "VIEW": true,
+ "WHEN": true,
+ "WHENEVER": true,
+ "WHERE": true,
+ "WITH": true,
+ "WITHOUT": true,
+ "WORK": true,
+ "WRITE": true,
+ "WRITEDOWN": true,
+ "WRITEUP": true,
+ "XID": true,
+ "YEAR": true,
+ "ZONE": true,
+ }
+)
+
+type oracle struct {
+ core.Base
+}
+
+func (db *oracle) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *oracle) SqlType(c *core.Column) string {
+ var res string
+ switch t := c.SQLType.Name; t {
+ case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool, core.Serial, core.BigSerial:
+ res = "NUMBER"
+ case core.Binary, core.VarBinary, core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob, core.Bytea:
+ return core.Blob
+ case core.Time, core.DateTime, core.TimeStamp:
+ res = core.TimeStamp
+ case core.TimeStampz:
+ res = "TIMESTAMP WITH TIME ZONE"
+ case core.Float, core.Double, core.Numeric, core.Decimal:
+ res = "NUMBER"
+ case core.Text, core.MediumText, core.LongText, core.Json:
+ res = "CLOB"
+ case core.Char, core.Varchar, core.TinyText:
+ res = "VARCHAR2"
+ default:
+ res = t
+ }
+
+ hasLen1 := (c.Length > 0)
+ hasLen2 := (c.Length2 > 0)
+
+ if hasLen2 {
+ res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
+ } else if hasLen1 {
+ res += "(" + strconv.Itoa(c.Length) + ")"
+ }
+ return res
+}
+
+func (db *oracle) AutoIncrStr() string {
+ return "AUTO_INCREMENT"
+}
+
+func (db *oracle) SupportInsertMany() bool {
+ return true
+}
+
+func (db *oracle) IsReserved(name string) bool {
+ _, ok := oracleReservedWords[name]
+ return ok
+}
+
+func (db *oracle) Quote(name string) string {
+ return "[" + name + "]"
+}
+
+func (db *oracle) SupportEngine() bool {
+ return false
+}
+
+func (db *oracle) SupportCharset() bool {
+ return false
+}
+
+func (db *oracle) SupportDropIfExists() bool {
+ return false
+}
+
+func (db *oracle) IndexOnTable() bool {
+ return false
+}
+
+func (db *oracle) DropTableSql(tableName string) string {
+ return fmt.Sprintf("DROP TABLE `%s`", tableName)
+}
+
+func (db *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string {
+ var sql string
+ sql = "CREATE TABLE "
+ if tableName == "" {
+ tableName = table.Name
+ }
+
+ sql += db.Quote(tableName) + " ("
+
+ pkList := table.PrimaryKeys
+
+ for _, colName := range table.ColumnsSeq() {
+ col := table.GetColumn(colName)
+ /*if col.IsPrimaryKey && len(pkList) == 1 {
+ sql += col.String(b.dialect)
+ } else {*/
+ sql += col.StringNoPk(db)
+ // }
+ sql = strings.TrimSpace(sql)
+ sql += ", "
+ }
+
+ if len(pkList) > 0 {
+ sql += "PRIMARY KEY ( "
+ sql += db.Quote(strings.Join(pkList, db.Quote(",")))
+ sql += " ), "
+ }
+
+ sql = sql[:len(sql)-2] + ")"
+ if db.SupportEngine() && storeEngine != "" {
+ sql += " ENGINE=" + storeEngine
+ }
+ if db.SupportCharset() {
+ if len(charset) == 0 {
+ charset = db.URI().Charset
+ }
+ if len(charset) > 0 {
+ sql += " DEFAULT CHARSET " + charset
+ }
+ }
+ return sql
+}
+
+func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{tableName, idxName}
+ return `SELECT INDEX_NAME FROM USER_INDEXES ` +
+ `WHERE TABLE_NAME = :1 AND INDEX_NAME = :2`, args
+}
+
+func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{tableName}
+ return `SELECT table_name FROM user_tables WHERE table_name = :1`, args
+}
+
+func (db *oracle) MustDropTable(tableName string) error {
+ sql, args := db.TableCheckSql(tableName)
+ db.LogSQL(sql, args)
+
+ rows, err := db.DB().Query(sql, args...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ return nil
+ }
+
+ sql = "Drop Table \"" + tableName + "\""
+ db.LogSQL(sql, args)
+
+ _, err = db.DB().Exec(sql)
+ return err
+}
+
+/*func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)}
+ return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" +
+ " AND column_name = ?", args
+}*/
+
+func (db *oracle) IsColumnExist(tableName, colName string) (bool, error) {
+ args := []interface{}{tableName, colName}
+ query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = :1" +
+ " AND column_name = :2"
+ db.LogSQL(query, args)
+
+ rows, err := db.DB().Query(query, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ if rows.Next() {
+ return true, nil
+ }
+ return false, nil
+}
+
+func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
+ args := []interface{}{tableName}
+ s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," +
+ "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+ for rows.Next() {
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+
+ var colName, colDefault, nullable, dataType, dataPrecision, dataScale *string
+ var dataLen int
+
+ err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision,
+ &dataScale, &nullable)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ col.Name = strings.Trim(*colName, `" `)
+ if colDefault != nil {
+ col.Default = *colDefault
+ col.DefaultIsEmpty = false
+ }
+
+ if *nullable == "Y" {
+ col.Nullable = true
+ } else {
+ col.Nullable = false
+ }
+
+ var ignore bool
+
+ var dt string
+ var len1, len2 int
+ dts := strings.Split(*dataType, "(")
+ dt = dts[0]
+ if len(dts) > 1 {
+ lens := strings.Split(dts[1][:len(dts[1])-1], ",")
+ if len(lens) > 1 {
+ len1, _ = strconv.Atoi(lens[0])
+ len2, _ = strconv.Atoi(lens[1])
+ } else {
+ len1, _ = strconv.Atoi(lens[0])
+ }
+ }
+
+ switch dt {
+ case "VARCHAR2":
+ col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: len1, DefaultLength2: len2}
+ case "NVARCHAR2":
+ col.SQLType = core.SQLType{Name: core.NVarchar, DefaultLength: len1, DefaultLength2: len2}
+ case "TIMESTAMP WITH TIME ZONE":
+ col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0}
+ case "NUMBER":
+ col.SQLType = core.SQLType{Name: core.Double, DefaultLength: len1, DefaultLength2: len2}
+ case "LONG", "LONG RAW":
+ col.SQLType = core.SQLType{Name: core.Text, DefaultLength: 0, DefaultLength2: 0}
+ case "RAW":
+ col.SQLType = core.SQLType{Name: core.Binary, DefaultLength: 0, DefaultLength2: 0}
+ case "ROWID":
+ col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: 18, DefaultLength2: 0}
+ case "AQ$_SUBSCRIBERS":
+ ignore = true
+ default:
+ col.SQLType = core.SQLType{Name: strings.ToUpper(dt), DefaultLength: len1, DefaultLength2: len2}
+ }
+
+ if ignore {
+ continue
+ }
+
+ if _, ok := core.SqlTypes[col.SQLType.Name]; !ok {
+ return nil, nil, fmt.Errorf("Unknown colType %v %v", *dataType, col.SQLType)
+ }
+
+ col.Length = dataLen
+
+ if col.SQLType.IsText() || col.SQLType.IsTime() {
+ if !col.DefaultIsEmpty {
+ col.Default = "'" + col.Default + "'"
+ }
+ }
+ cols[col.Name] = col
+ colSeq = append(colSeq, col.Name)
+ }
+
+ return colSeq, cols, nil
+}
+
+func (db *oracle) GetTables() ([]*core.Table, error) {
+ args := []interface{}{}
+ s := "SELECT table_name FROM user_tables"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ err = rows.Scan(&table.Name)
+ if err != nil {
+ return nil, err
+ }
+
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{tableName}
+ s := "SELECT t.column_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " +
+ "WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =:1"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var indexType int
+ var indexName, colName, uniqueness string
+
+ err = rows.Scan(&colName, &uniqueness, &indexName)
+ if err != nil {
+ return nil, err
+ }
+
+ indexName = strings.Trim(indexName, `" `)
+
+ var isRegular bool
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ indexName = indexName[5+len(tableName):]
+ isRegular = true
+ }
+
+ if uniqueness == "UNIQUE" {
+ indexType = core.UniqueType
+ } else {
+ indexType = core.IndexType
+ }
+
+ var index *core.Index
+ var ok bool
+ if index, ok = indexes[indexName]; !ok {
+ index = new(core.Index)
+ index.Type = indexType
+ index.Name = indexName
+ index.IsRegular = isRegular
+ indexes[indexName] = index
+ }
+ index.AddColumn(colName)
+ }
+ return indexes, nil
+}
+
+func (db *oracle) Filters() []core.Filter {
+ return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: ":", Start: 1}, &core.IdFilter{}}
+}
+
+type goracleDriver struct {
+}
+
+func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ db := &core.Uri{DbType: core.ORACLE}
+ dsnPattern := regexp.MustCompile(
+ `^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
+ `(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
+ `\/(?P<dbname>.*?)` + // /dbname
+ `(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
+ matches := dsnPattern.FindStringSubmatch(dataSourceName)
+ // tlsConfigRegister := make(map[string]*tls.Config)
+ names := dsnPattern.SubexpNames()
+
+ for i, match := range matches {
+ switch names[i] {
+ case "dbname":
+ db.DbName = match
+ }
+ }
+ if db.DbName == "" {
+ return nil, errors.New("dbname is empty")
+ }
+ return db, nil
+}
+
+type oci8Driver struct {
+}
+
+// dataSourceName=user/password@ipv4:port/dbname
+// dataSourceName=user/password@[ipv6]:port/dbname
+func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ db := &core.Uri{DbType: core.ORACLE}
+ dsnPattern := regexp.MustCompile(
+ `^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
+ `(?P<net>.*)` + // ip:port
+ `\/(?P<dbname>.*)`) // dbname
+ matches := dsnPattern.FindStringSubmatch(dataSourceName)
+ names := dsnPattern.SubexpNames()
+ for i, match := range matches {
+ switch names[i] {
+ case "dbname":
+ db.DbName = match
+ }
+ }
+ if db.DbName == "" {
+ return nil, errors.New("dbname is empty")
+ }
+ return db, nil
+}
diff --git a/vendor/xorm.io/xorm/dialect_postgres.go b/vendor/xorm.io/xorm/dialect_postgres.go
new file mode 100644
index 0000000000..ccef3086b2
--- /dev/null
+++ b/vendor/xorm.io/xorm/dialect_postgres.go
@@ -0,0 +1,1253 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+
+ "xorm.io/core"
+)
+
+// from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html
+var (
+ postgresReservedWords = map[string]bool{
+ "A": true,
+ "ABORT": true,
+ "ABS": true,
+ "ABSENT": true,
+ "ABSOLUTE": true,
+ "ACCESS": true,
+ "ACCORDING": true,
+ "ACTION": true,
+ "ADA": true,
+ "ADD": true,
+ "ADMIN": true,
+ "AFTER": true,
+ "AGGREGATE": true,
+ "ALL": true,
+ "ALLOCATE": true,
+ "ALSO": true,
+ "ALTER": true,
+ "ALWAYS": true,
+ "ANALYSE": true,
+ "ANALYZE": true,
+ "AND": true,
+ "ANY": true,
+ "ARE": true,
+ "ARRAY": true,
+ "ARRAY_AGG": true,
+ "ARRAY_MAX_CARDINALITY": true,
+ "AS": true,
+ "ASC": true,
+ "ASENSITIVE": true,
+ "ASSERTION": true,
+ "ASSIGNMENT": true,
+ "ASYMMETRIC": true,
+ "AT": true,
+ "ATOMIC": true,
+ "ATTRIBUTE": true,
+ "ATTRIBUTES": true,
+ "AUTHORIZATION": true,
+ "AVG": true,
+ "BACKWARD": true,
+ "BASE64": true,
+ "BEFORE": true,
+ "BEGIN": true,
+ "BEGIN_FRAME": true,
+ "BEGIN_PARTITION": true,
+ "BERNOULLI": true,
+ "BETWEEN": true,
+ "BIGINT": true,
+ "BINARY": true,
+ "BIT": true,
+ "BIT_LENGTH": true,
+ "BLOB": true,
+ "BLOCKED": true,
+ "BOM": true,
+ "BOOLEAN": true,
+ "BOTH": true,
+ "BREADTH": true,
+ "BY": true,
+ "C": true,
+ "CACHE": true,
+ "CALL": true,
+ "CALLED": true,
+ "CARDINALITY": true,
+ "CASCADE": true,
+ "CASCADED": true,
+ "CASE": true,
+ "CAST": true,
+ "CATALOG": true,
+ "CATALOG_NAME": true,
+ "CEIL": true,
+ "CEILING": true,
+ "CHAIN": true,
+ "CHAR": true,
+ "CHARACTER": true,
+ "CHARACTERISTICS": true,
+ "CHARACTERS": true,
+ "CHARACTER_LENGTH": true,
+ "CHARACTER_SET_CATALOG": true,
+ "CHARACTER_SET_NAME": true,
+ "CHARACTER_SET_SCHEMA": true,
+ "CHAR_LENGTH": true,
+ "CHECK": true,
+ "CHECKPOINT": true,
+ "CLASS": true,
+ "CLASS_ORIGIN": true,
+ "CLOB": true,
+ "CLOSE": true,
+ "CLUSTER": true,
+ "COALESCE": true,
+ "COBOL": true,
+ "COLLATE": true,
+ "COLLATION": true,
+ "COLLATION_CATALOG": true,
+ "COLLATION_NAME": true,
+ "COLLATION_SCHEMA": true,
+ "COLLECT": true,
+ "COLUMN": true,
+ "COLUMNS": true,
+ "COLUMN_NAME": true,
+ "COMMAND_FUNCTION": true,
+ "COMMAND_FUNCTION_CODE": true,
+ "COMMENT": true,
+ "COMMENTS": true,
+ "COMMIT": true,
+ "COMMITTED": true,
+ "CONCURRENTLY": true,
+ "CONDITION": true,
+ "CONDITION_NUMBER": true,
+ "CONFIGURATION": true,
+ "CONNECT": true,
+ "CONNECTION": true,
+ "CONNECTION_NAME": true,
+ "CONSTRAINT": true,
+ "CONSTRAINTS": true,
+ "CONSTRAINT_CATALOG": true,
+ "CONSTRAINT_NAME": true,
+ "CONSTRAINT_SCHEMA": true,
+ "CONSTRUCTOR": true,
+ "CONTAINS": true,
+ "CONTENT": true,
+ "CONTINUE": true,
+ "CONTROL": true,
+ "CONVERSION": true,
+ "CONVERT": true,
+ "COPY": true,
+ "CORR": true,
+ "CORRESPONDING": true,
+ "COST": true,
+ "COUNT": true,
+ "COVAR_POP": true,
+ "COVAR_SAMP": true,
+ "CREATE": true,
+ "CROSS": true,
+ "CSV": true,
+ "CUBE": true,
+ "CUME_DIST": true,
+ "CURRENT": true,
+ "CURRENT_CATALOG": true,
+ "CURRENT_DATE": true,
+ "CURRENT_DEFAULT_TRANSFORM_GROUP": true,
+ "CURRENT_PATH": true,
+ "CURRENT_ROLE": true,
+ "CURRENT_ROW": true,
+ "CURRENT_SCHEMA": true,
+ "CURRENT_TIME": true,
+ "CURRENT_TIMESTAMP": true,
+ "CURRENT_TRANSFORM_GROUP_FOR_TYPE": true,
+ "CURRENT_USER": true,
+ "CURSOR": true,
+ "CURSOR_NAME": true,
+ "CYCLE": true,
+ "DATA": true,
+ "DATABASE": true,
+ "DATALINK": true,
+ "DATE": true,
+ "DATETIME_INTERVAL_CODE": true,
+ "DATETIME_INTERVAL_PRECISION": true,
+ "DAY": true,
+ "DB": true,
+ "DEALLOCATE": true,
+ "DEC": true,
+ "DECIMAL": true,
+ "DECLARE": true,
+ "DEFAULT": true,
+ "DEFAULTS": true,
+ "DEFERRABLE": true,
+ "DEFERRED": true,
+ "DEFINED": true,
+ "DEFINER": true,
+ "DEGREE": true,
+ "DELETE": true,
+ "DELIMITER": true,
+ "DELIMITERS": true,
+ "DENSE_RANK": true,
+ "DEPTH": true,
+ "DEREF": true,
+ "DERIVED": true,
+ "DESC": true,
+ "DESCRIBE": true,
+ "DESCRIPTOR": true,
+ "DETERMINISTIC": true,
+ "DIAGNOSTICS": true,
+ "DICTIONARY": true,
+ "DISABLE": true,
+ "DISCARD": true,
+ "DISCONNECT": true,
+ "DISPATCH": true,
+ "DISTINCT": true,
+ "DLNEWCOPY": true,
+ "DLPREVIOUSCOPY": true,
+ "DLURLCOMPLETE": true,
+ "DLURLCOMPLETEONLY": true,
+ "DLURLCOMPLETEWRITE": true,
+ "DLURLPATH": true,
+ "DLURLPATHONLY": true,
+ "DLURLPATHWRITE": true,
+ "DLURLSCHEME": true,
+ "DLURLSERVER": true,
+ "DLVALUE": true,
+ "DO": true,
+ "DOCUMENT": true,
+ "DOMAIN": true,
+ "DOUBLE": true,
+ "DROP": true,
+ "DYNAMIC": true,
+ "DYNAMIC_FUNCTION": true,
+ "DYNAMIC_FUNCTION_CODE": true,
+ "EACH": true,
+ "ELEMENT": true,
+ "ELSE": true,
+ "EMPTY": true,
+ "ENABLE": true,
+ "ENCODING": true,
+ "ENCRYPTED": true,
+ "END": true,
+ "END-EXEC": true,
+ "END_FRAME": true,
+ "END_PARTITION": true,
+ "ENFORCED": true,
+ "ENUM": true,
+ "EQUALS": true,
+ "ESCAPE": true,
+ "EVENT": true,
+ "EVERY": true,
+ "EXCEPT": true,
+ "EXCEPTION": true,
+ "EXCLUDE": true,
+ "EXCLUDING": true,
+ "EXCLUSIVE": true,
+ "EXEC": true,
+ "EXECUTE": true,
+ "EXISTS": true,
+ "EXP": true,
+ "EXPLAIN": true,
+ "EXPRESSION": true,
+ "EXTENSION": true,
+ "EXTERNAL": true,
+ "EXTRACT": true,
+ "FALSE": true,
+ "FAMILY": true,
+ "FETCH": true,
+ "FILE": true,
+ "FILTER": true,
+ "FINAL": true,
+ "FIRST": true,
+ "FIRST_VALUE": true,
+ "FLAG": true,
+ "FLOAT": true,
+ "FLOOR": true,
+ "FOLLOWING": true,
+ "FOR": true,
+ "FORCE": true,
+ "FOREIGN": true,
+ "FORTRAN": true,
+ "FORWARD": true,
+ "FOUND": true,
+ "FRAME_ROW": true,
+ "FREE": true,
+ "FREEZE": true,
+ "FROM": true,
+ "FS": true,
+ "FULL": true,
+ "FUNCTION": true,
+ "FUNCTIONS": true,
+ "FUSION": true,
+ "G": true,
+ "GENERAL": true,
+ "GENERATED": true,
+ "GET": true,
+ "GLOBAL": true,
+ "GO": true,
+ "GOTO": true,
+ "GRANT": true,
+ "GRANTED": true,
+ "GREATEST": true,
+ "GROUP": true,
+ "GROUPING": true,
+ "GROUPS": true,
+ "HANDLER": true,
+ "HAVING": true,
+ "HEADER": true,
+ "HEX": true,
+ "HIERARCHY": true,
+ "HOLD": true,
+ "HOUR": true,
+ "ID": true,
+ "IDENTITY": true,
+ "IF": true,
+ "IGNORE": true,
+ "ILIKE": true,
+ "IMMEDIATE": true,
+ "IMMEDIATELY": true,
+ "IMMUTABLE": true,
+ "IMPLEMENTATION": true,
+ "IMPLICIT": true,
+ "IMPORT": true,
+ "IN": true,
+ "INCLUDING": true,
+ "INCREMENT": true,
+ "INDENT": true,
+ "INDEX": true,
+ "INDEXES": true,
+ "INDICATOR": true,
+ "INHERIT": true,
+ "INHERITS": true,
+ "INITIALLY": true,
+ "INLINE": true,
+ "INNER": true,
+ "INOUT": true,
+ "INPUT": true,
+ "INSENSITIVE": true,
+ "INSERT": true,
+ "INSTANCE": true,
+ "INSTANTIABLE": true,
+ "INSTEAD": true,
+ "INT": true,
+ "INTEGER": true,
+ "INTEGRITY": true,
+ "INTERSECT": true,
+ "INTERSECTION": true,
+ "INTERVAL": true,
+ "INTO": true,
+ "INVOKER": true,
+ "IS": true,
+ "ISNULL": true,
+ "ISOLATION": true,
+ "JOIN": true,
+ "K": true,
+ "KEY": true,
+ "KEY_MEMBER": true,
+ "KEY_TYPE": true,
+ "LABEL": true,
+ "LAG": true,
+ "LANGUAGE": true,
+ "LARGE": true,
+ "LAST": true,
+ "LAST_VALUE": true,
+ "LATERAL": true,
+ "LC_COLLATE": true,
+ "LC_CTYPE": true,
+ "LEAD": true,
+ "LEADING": true,
+ "LEAKPROOF": true,
+ "LEAST": true,
+ "LEFT": true,
+ "LENGTH": true,
+ "LEVEL": true,
+ "LIBRARY": true,
+ "LIKE": true,
+ "LIKE_REGEX": true,
+ "LIMIT": true,
+ "LINK": true,
+ "LISTEN": true,
+ "LN": true,
+ "LOAD": true,
+ "LOCAL": true,
+ "LOCALTIME": true,
+ "LOCALTIMESTAMP": true,
+ "LOCATION": true,
+ "LOCATOR": true,
+ "LOCK": true,
+ "LOWER": true,
+ "M": true,
+ "MAP": true,
+ "MAPPING": true,
+ "MATCH": true,
+ "MATCHED": true,
+ "MATERIALIZED": true,
+ "MAX": true,
+ "MAXVALUE": true,
+ "MAX_CARDINALITY": true,
+ "MEMBER": true,
+ "MERGE": true,
+ "MESSAGE_LENGTH": true,
+ "MESSAGE_OCTET_LENGTH": true,
+ "MESSAGE_TEXT": true,
+ "METHOD": true,
+ "MIN": true,
+ "MINUTE": true,
+ "MINVALUE": true,
+ "MOD": true,
+ "MODE": true,
+ "MODIFIES": true,
+ "MODULE": true,
+ "MONTH": true,
+ "MORE": true,
+ "MOVE": true,
+ "MULTISET": true,
+ "MUMPS": true,
+ "NAME": true,
+ "NAMES": true,
+ "NAMESPACE": true,
+ "NATIONAL": true,
+ "NATURAL": true,
+ "NCHAR": true,
+ "NCLOB": true,
+ "NESTING": true,
+ "NEW": true,
+ "NEXT": true,
+ "NFC": true,
+ "NFD": true,
+ "NFKC": true,
+ "NFKD": true,
+ "NIL": true,
+ "NO": true,
+ "NONE": true,
+ "NORMALIZE": true,
+ "NORMALIZED": true,
+ "NOT": true,
+ "NOTHING": true,
+ "NOTIFY": true,
+ "NOTNULL": true,
+ "NOWAIT": true,
+ "NTH_VALUE": true,
+ "NTILE": true,
+ "NULL": true,
+ "NULLABLE": true,
+ "NULLIF": true,
+ "NULLS": true,
+ "NUMBER": true,
+ "NUMERIC": true,
+ "OBJECT": true,
+ "OCCURRENCES_REGEX": true,
+ "OCTETS": true,
+ "OCTET_LENGTH": true,
+ "OF": true,
+ "OFF": true,
+ "OFFSET": true,
+ "OIDS": true,
+ "OLD": true,
+ "ON": true,
+ "ONLY": true,
+ "OPEN": true,
+ "OPERATOR": true,
+ "OPTION": true,
+ "OPTIONS": true,
+ "OR": true,
+ "ORDER": true,
+ "ORDERING": true,
+ "ORDINALITY": true,
+ "OTHERS": true,
+ "OUT": true,
+ "OUTER": true,
+ "OUTPUT": true,
+ "OVER": true,
+ "OVERLAPS": true,
+ "OVERLAY": true,
+ "OVERRIDING": true,
+ "OWNED": true,
+ "OWNER": true,
+ "P": true,
+ "PAD": true,
+ "PARAMETER": true,
+ "PARAMETER_MODE": true,
+ "PARAMETER_NAME": true,
+ "PARAMETER_ORDINAL_POSITION": true,
+ "PARAMETER_SPECIFIC_CATALOG": true,
+ "PARAMETER_SPECIFIC_NAME": true,
+ "PARAMETER_SPECIFIC_SCHEMA": true,
+ "PARSER": true,
+ "PARTIAL": true,
+ "PARTITION": true,
+ "PASCAL": true,
+ "PASSING": true,
+ "PASSTHROUGH": true,
+ "PASSWORD": true,
+ "PATH": true,
+ "PERCENT": true,
+ "PERCENTILE_CONT": true,
+ "PERCENTILE_DISC": true,
+ "PERCENT_RANK": true,
+ "PERIOD": true,
+ "PERMISSION": true,
+ "PLACING": true,
+ "PLANS": true,
+ "PLI": true,
+ "PORTION": true,
+ "POSITION": true,
+ "POSITION_REGEX": true,
+ "POWER": true,
+ "PRECEDES": true,
+ "PRECEDING": true,
+ "PRECISION": true,
+ "PREPARE": true,
+ "PREPARED": true,
+ "PRESERVE": true,
+ "PRIMARY": true,
+ "PRIOR": true,
+ "PRIVILEGES": true,
+ "PROCEDURAL": true,
+ "PROCEDURE": true,
+ "PROGRAM": true,
+ "PUBLIC": true,
+ "QUOTE": true,
+ "RANGE": true,
+ "RANK": true,
+ "READ": true,
+ "READS": true,
+ "REAL": true,
+ "REASSIGN": true,
+ "RECHECK": true,
+ "RECOVERY": true,
+ "RECURSIVE": true,
+ "REF": true,
+ "REFERENCES": true,
+ "REFERENCING": true,
+ "REFRESH": true,
+ "REGR_AVGX": true,
+ "REGR_AVGY": true,
+ "REGR_COUNT": true,
+ "REGR_INTERCEPT": true,
+ "REGR_R2": true,
+ "REGR_SLOPE": true,
+ "REGR_SXX": true,
+ "REGR_SXY": true,
+ "REGR_SYY": true,
+ "REINDEX": true,
+ "RELATIVE": true,
+ "RELEASE": true,
+ "RENAME": true,
+ "REPEATABLE": true,
+ "REPLACE": true,
+ "REPLICA": true,
+ "REQUIRING": true,
+ "RESET": true,
+ "RESPECT": true,
+ "RESTART": true,
+ "RESTORE": true,
+ "RESTRICT": true,
+ "RESULT": true,
+ "RETURN": true,
+ "RETURNED_CARDINALITY": true,
+ "RETURNED_LENGTH": true,
+ "RETURNED_OCTET_LENGTH": true,
+ "RETURNED_SQLSTATE": true,
+ "RETURNING": true,
+ "RETURNS": true,
+ "REVOKE": true,
+ "RIGHT": true,
+ "ROLE": true,
+ "ROLLBACK": true,
+ "ROLLUP": true,
+ "ROUTINE": true,
+ "ROUTINE_CATALOG": true,
+ "ROUTINE_NAME": true,
+ "ROUTINE_SCHEMA": true,
+ "ROW": true,
+ "ROWS": true,
+ "ROW_COUNT": true,
+ "ROW_NUMBER": true,
+ "RULE": true,
+ "SAVEPOINT": true,
+ "SCALE": true,
+ "SCHEMA": true,
+ "SCHEMA_NAME": true,
+ "SCOPE": true,
+ "SCOPE_CATALOG": true,
+ "SCOPE_NAME": true,
+ "SCOPE_SCHEMA": true,
+ "SCROLL": true,
+ "SEARCH": true,
+ "SECOND": true,
+ "SECTION": true,
+ "SECURITY": true,
+ "SELECT": true,
+ "SELECTIVE": true,
+ "SELF": true,
+ "SENSITIVE": true,
+ "SEQUENCE": true,
+ "SEQUENCES": true,
+ "SERIALIZABLE": true,
+ "SERVER": true,
+ "SERVER_NAME": true,
+ "SESSION": true,
+ "SESSION_USER": true,
+ "SET": true,
+ "SETOF": true,
+ "SETS": true,
+ "SHARE": true,
+ "SHOW": true,
+ "SIMILAR": true,
+ "SIMPLE": true,
+ "SIZE": true,
+ "SMALLINT": true,
+ "SNAPSHOT": true,
+ "SOME": true,
+ "SOURCE": true,
+ "SPACE": true,
+ "SPECIFIC": true,
+ "SPECIFICTYPE": true,
+ "SPECIFIC_NAME": true,
+ "SQL": true,
+ "SQLCODE": true,
+ "SQLERROR": true,
+ "SQLEXCEPTION": true,
+ "SQLSTATE": true,
+ "SQLWARNING": true,
+ "SQRT": true,
+ "STABLE": true,
+ "STANDALONE": true,
+ "START": true,
+ "STATE": true,
+ "STATEMENT": true,
+ "STATIC": true,
+ "STATISTICS": true,
+ "STDDEV_POP": true,
+ "STDDEV_SAMP": true,
+ "STDIN": true,
+ "STDOUT": true,
+ "STORAGE": true,
+ "STRICT": true,
+ "STRIP": true,
+ "STRUCTURE": true,
+ "STYLE": true,
+ "SUBCLASS_ORIGIN": true,
+ "SUBMULTISET": true,
+ "SUBSTRING": true,
+ "SUBSTRING_REGEX": true,
+ "SUCCEEDS": true,
+ "SUM": true,
+ "SYMMETRIC": true,
+ "SYSID": true,
+ "SYSTEM": true,
+ "SYSTEM_TIME": true,
+ "SYSTEM_USER": true,
+ "T": true,
+ "TABLE": true,
+ "TABLES": true,
+ "TABLESAMPLE": true,
+ "TABLESPACE": true,
+ "TABLE_NAME": true,
+ "TEMP": true,
+ "TEMPLATE": true,
+ "TEMPORARY": true,
+ "TEXT": true,
+ "THEN": true,
+ "TIES": true,
+ "TIME": true,
+ "TIMESTAMP": true,
+ "TIMEZONE_HOUR": true,
+ "TIMEZONE_MINUTE": true,
+ "TO": true,
+ "TOKEN": true,
+ "TOP_LEVEL_COUNT": true,
+ "TRAILING": true,
+ "TRANSACTION": true,
+ "TRANSACTIONS_COMMITTED": true,
+ "TRANSACTIONS_ROLLED_BACK": true,
+ "TRANSACTION_ACTIVE": true,
+ "TRANSFORM": true,
+ "TRANSFORMS": true,
+ "TRANSLATE": true,
+ "TRANSLATE_REGEX": true,
+ "TRANSLATION": true,
+ "TREAT": true,
+ "TRIGGER": true,
+ "TRIGGER_CATALOG": true,
+ "TRIGGER_NAME": true,
+ "TRIGGER_SCHEMA": true,
+ "TRIM": true,
+ "TRIM_ARRAY": true,
+ "TRUE": true,
+ "TRUNCATE": true,
+ "TRUSTED": true,
+ "TYPE": true,
+ "TYPES": true,
+ "UESCAPE": true,
+ "UNBOUNDED": true,
+ "UNCOMMITTED": true,
+ "UNDER": true,
+ "UNENCRYPTED": true,
+ "UNION": true,
+ "UNIQUE": true,
+ "UNKNOWN": true,
+ "UNLINK": true,
+ "UNLISTEN": true,
+ "UNLOGGED": true,
+ "UNNAMED": true,
+ "UNNEST": true,
+ "UNTIL": true,
+ "UNTYPED": true,
+ "UPDATE": true,
+ "UPPER": true,
+ "URI": true,
+ "USAGE": true,
+ "USER": true,
+ "USER_DEFINED_TYPE_CATALOG": true,
+ "USER_DEFINED_TYPE_CODE": true,
+ "USER_DEFINED_TYPE_NAME": true,
+ "USER_DEFINED_TYPE_SCHEMA": true,
+ "USING": true,
+ "VACUUM": true,
+ "VALID": true,
+ "VALIDATE": true,
+ "VALIDATOR": true,
+ "VALUE": true,
+ "VALUES": true,
+ "VALUE_OF": true,
+ "VARBINARY": true,
+ "VARCHAR": true,
+ "VARIADIC": true,
+ "VARYING": true,
+ "VAR_POP": true,
+ "VAR_SAMP": true,
+ "VERBOSE": true,
+ "VERSION": true,
+ "VERSIONING": true,
+ "VIEW": true,
+ "VOLATILE": true,
+ "WHEN": true,
+ "WHENEVER": true,
+ "WHERE": true,
+ "WHITESPACE": true,
+ "WIDTH_BUCKET": true,
+ "WINDOW": true,
+ "WITH": true,
+ "WITHIN": true,
+ "WITHOUT": true,
+ "WORK": true,
+ "WRAPPER": true,
+ "WRITE": true,
+ "XML": true,
+ "XMLAGG": true,
+ "XMLATTRIBUTES": true,
+ "XMLBINARY": true,
+ "XMLCAST": true,
+ "XMLCOMMENT": true,
+ "XMLCONCAT": true,
+ "XMLDECLARATION": true,
+ "XMLDOCUMENT": true,
+ "XMLELEMENT": true,
+ "XMLEXISTS": true,
+ "XMLFOREST": true,
+ "XMLITERATE": true,
+ "XMLNAMESPACES": true,
+ "XMLPARSE": true,
+ "XMLPI": true,
+ "XMLQUERY": true,
+ "XMLROOT": true,
+ "XMLSCHEMA": true,
+ "XMLSERIALIZE": true,
+ "XMLTABLE": true,
+ "XMLTEXT": true,
+ "XMLVALIDATE": true,
+ "YEAR": true,
+ "YES": true,
+ "ZONE": true,
+ }
+
+ // DefaultPostgresSchema default postgres schema
+ DefaultPostgresSchema = "public"
+)
+
+const postgresPublicSchema = "public"
+
+type postgres struct {
+ core.Base
+}
+
+func (db *postgres) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ err := db.Base.Init(d, db, uri, drivername, dataSourceName)
+ if err != nil {
+ return err
+ }
+ if db.Schema == "" {
+ db.Schema = DefaultPostgresSchema
+ }
+ return nil
+}
+
+func (db *postgres) SqlType(c *core.Column) string {
+ var res string
+ switch t := c.SQLType.Name; t {
+ case core.TinyInt:
+ res = core.SmallInt
+ return res
+ case core.Bit:
+ res = core.Boolean
+ return res
+ case core.MediumInt, core.Int, core.Integer:
+ if c.IsAutoIncrement {
+ return core.Serial
+ }
+ return core.Integer
+ case core.BigInt:
+ if c.IsAutoIncrement {
+ return core.BigSerial
+ }
+ return core.BigInt
+ case core.Serial, core.BigSerial:
+ c.IsAutoIncrement = true
+ c.Nullable = false
+ res = t
+ case core.Binary, core.VarBinary:
+ return core.Bytea
+ case core.DateTime:
+ res = core.TimeStamp
+ case core.TimeStampz:
+ return "timestamp with time zone"
+ case core.Float:
+ res = core.Real
+ case core.TinyText, core.MediumText, core.LongText:
+ res = core.Text
+ case core.NVarchar:
+ res = core.Varchar
+ case core.Uuid:
+ return core.Uuid
+ case core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob:
+ return core.Bytea
+ case core.Double:
+ return "DOUBLE PRECISION"
+ default:
+ if c.IsAutoIncrement {
+ return core.Serial
+ }
+ res = t
+ }
+
+ if strings.EqualFold(res, "bool") {
+ // for bool, we don't need length information
+ return res
+ }
+ hasLen1 := (c.Length > 0)
+ hasLen2 := (c.Length2 > 0)
+
+ if hasLen2 {
+ res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
+ } else if hasLen1 {
+ res += "(" + strconv.Itoa(c.Length) + ")"
+ }
+ return res
+}
+
+func (db *postgres) SupportInsertMany() bool {
+ return true
+}
+
+func (db *postgres) IsReserved(name string) bool {
+ _, ok := postgresReservedWords[name]
+ return ok
+}
+
+func (db *postgres) Quote(name string) string {
+ name = strings.Replace(name, ".", `"."`, -1)
+ return "\"" + name + "\""
+}
+
+func (db *postgres) AutoIncrStr() string {
+ return ""
+}
+
+func (db *postgres) SupportEngine() bool {
+ return false
+}
+
+func (db *postgres) SupportCharset() bool {
+ return false
+}
+
+func (db *postgres) IndexOnTable() bool {
+ return false
+}
+
+func (db *postgres) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ if len(db.Schema) == 0 {
+ args := []interface{}{tableName, idxName}
+ return `SELECT indexname FROM pg_indexes WHERE tablename = ? AND indexname = ?`, args
+ }
+
+ args := []interface{}{db.Schema, tableName, idxName}
+ return `SELECT indexname FROM pg_indexes ` +
+ `WHERE schemaname = ? AND tablename = ? AND indexname = ?`, args
+}
+
+func (db *postgres) TableCheckSql(tableName string) (string, []interface{}) {
+ if len(db.Schema) == 0 {
+ args := []interface{}{tableName}
+ return `SELECT tablename FROM pg_tables WHERE tablename = ?`, args
+ }
+
+ args := []interface{}{db.Schema, tableName}
+ return `SELECT tablename FROM pg_tables WHERE schemaname = ? AND tablename = ?`, args
+}
+
+func (db *postgres) ModifyColumnSql(tableName string, col *core.Column) string {
+ if len(db.Schema) == 0 {
+ return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s",
+ tableName, col.Name, db.SqlType(col))
+ }
+ return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s",
+ db.Schema, tableName, col.Name, db.SqlType(col))
+}
+
+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)
+
+ if !strings.HasPrefix(idxName, "UQE_") &&
+ !strings.HasPrefix(idxName, "IDX_") {
+ if index.Type == core.UniqueType {
+ idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
+ } else {
+ idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
+ }
+ }
+ if db.Uri.Schema != "" {
+ idxName = db.Uri.Schema + "." + idxName
+ }
+ return fmt.Sprintf("DROP INDEX %v", quote(idxName))
+}
+
+func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) {
+ args := []interface{}{db.Schema, tableName, colName}
+ query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = $1 AND table_name = $2" +
+ " AND column_name = $3"
+ if len(db.Schema) == 0 {
+ args = []interface{}{tableName, colName}
+ query = "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" +
+ " AND column_name = $2"
+ }
+ db.LogSQL(query, args)
+
+ rows, err := db.DB().Query(query, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ return rows.Next(), nil
+}
+
+func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
+ args := []interface{}{tableName}
+ s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length,
+ CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey,
+ CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey
+FROM pg_attribute f
+ JOIN pg_class c ON c.oid = f.attrelid JOIN pg_type t ON t.oid = f.atttypid
+ LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = f.attnum
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)
+ LEFT JOIN pg_class AS g ON p.confrelid = g.oid
+ LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name
+WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.attnum;`
+
+ var f string
+ if len(db.Schema) != 0 {
+ args = append(args, db.Schema)
+ f = " AND s.table_schema = $2"
+ }
+ s = fmt.Sprintf(s, f)
+
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+
+ for rows.Next() {
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+
+ var colName, isNullable, dataType string
+ var maxLenStr, colDefault *string
+ var isPK, isUnique bool
+ err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &isPK, &isUnique)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // fmt.Println(args, colName, isNullable, dataType, maxLenStr, colDefault, isPK, isUnique)
+ var maxLen int
+ if maxLenStr != nil {
+ maxLen, err = strconv.Atoi(*maxLenStr)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ col.Name = strings.Trim(colName, `" `)
+
+ if colDefault != nil {
+ col.Default = *colDefault
+ col.DefaultIsEmpty = false
+ if strings.HasPrefix(col.Default, "nextval(") {
+ col.IsAutoIncrement = true
+ }
+ } else {
+ col.DefaultIsEmpty = true
+ }
+
+ if isPK {
+ col.IsPrimaryKey = true
+ }
+
+ col.Nullable = (isNullable == "YES")
+
+ switch dataType {
+ case "character varying", "character":
+ col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: 0, DefaultLength2: 0}
+ case "timestamp without time zone":
+ col.SQLType = core.SQLType{Name: core.DateTime, DefaultLength: 0, DefaultLength2: 0}
+ case "timestamp with time zone":
+ col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0}
+ case "double precision":
+ col.SQLType = core.SQLType{Name: core.Double, DefaultLength: 0, DefaultLength2: 0}
+ case "boolean":
+ col.SQLType = core.SQLType{Name: core.Bool, DefaultLength: 0, DefaultLength2: 0}
+ case "time without time zone":
+ col.SQLType = core.SQLType{Name: core.Time, DefaultLength: 0, DefaultLength2: 0}
+ case "oid":
+ col.SQLType = core.SQLType{Name: core.BigInt, DefaultLength: 0, DefaultLength2: 0}
+ default:
+ col.SQLType = core.SQLType{Name: strings.ToUpper(dataType), DefaultLength: 0, DefaultLength2: 0}
+ }
+ if _, ok := core.SqlTypes[col.SQLType.Name]; !ok {
+ return nil, nil, fmt.Errorf("Unknown colType: %v", dataType)
+ }
+
+ col.Length = maxLen
+
+ if !col.DefaultIsEmpty {
+ if col.SQLType.IsText() {
+ if strings.HasSuffix(col.Default, "::character varying") {
+ col.Default = strings.TrimRight(col.Default, "::character varying")
+ } else if !strings.HasPrefix(col.Default, "'") {
+ col.Default = "'" + col.Default + "'"
+ }
+ } else if col.SQLType.IsTime() {
+ if strings.HasSuffix(col.Default, "::timestamp without time zone") {
+ col.Default = strings.TrimRight(col.Default, "::timestamp without time zone")
+ }
+ }
+ }
+ cols[col.Name] = col
+ colSeq = append(colSeq, col.Name)
+ }
+
+ return colSeq, cols, nil
+}
+
+func (db *postgres) GetTables() ([]*core.Table, error) {
+ args := []interface{}{}
+ s := "SELECT tablename FROM pg_tables"
+ if len(db.Schema) != 0 {
+ args = append(args, db.Schema)
+ s = s + " WHERE schemaname = $1"
+ }
+
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ var name string
+ err = rows.Scan(&name)
+ if err != nil {
+ return nil, err
+ }
+ table.Name = name
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func getIndexColName(indexdef string) []string {
+ var colNames []string
+
+ cs := strings.Split(indexdef, "(")
+ for _, v := range strings.Split(strings.Split(cs[1], ")")[0], ",") {
+ colNames = append(colNames, strings.Split(strings.TrimLeft(v, " "), " ")[0])
+ }
+
+ return colNames
+}
+
+func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{tableName}
+ s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1")
+ if len(db.Schema) != 0 {
+ args = append(args, db.Schema)
+ s = s + " AND schemaname=$2"
+ }
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var indexType int
+ var indexName, indexdef string
+ var colNames []string
+ err = rows.Scan(&indexName, &indexdef)
+ if err != nil {
+ return nil, err
+ }
+ indexName = strings.Trim(indexName, `" `)
+ if strings.HasSuffix(indexName, "_pkey") {
+ continue
+ }
+ if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") {
+ indexType = core.UniqueType
+ } else {
+ indexType = core.IndexType
+ }
+ colNames = getIndexColName(indexdef)
+ var isRegular bool
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ newIdxName := indexName[5+len(tableName):]
+ isRegular = true
+ if newIdxName != "" {
+ indexName = newIdxName
+ }
+ }
+
+ index := &core.Index{Name: indexName, Type: indexType, Cols: make([]string, 0)}
+ for _, colName := range colNames {
+ index.Cols = append(index.Cols, strings.Trim(colName, `" `))
+ }
+ index.IsRegular = isRegular
+ indexes[index.Name] = index
+ }
+ return indexes, nil
+}
+
+func (db *postgres) Filters() []core.Filter {
+ return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{Prefix: "$", Start: 1}}
+}
+
+type pqDriver struct {
+}
+
+type values map[string]string
+
+func (vs values) Set(k, v string) {
+ vs[k] = v
+}
+
+func (vs values) Get(k string) (v string) {
+ return vs[k]
+}
+
+func parseURL(connstr string) (string, error) {
+ u, err := url.Parse(connstr)
+ if err != nil {
+ return "", err
+ }
+
+ if u.Scheme != "postgresql" && u.Scheme != "postgres" {
+ return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
+ }
+
+ escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
+
+ if u.Path != "" {
+ return escaper.Replace(u.Path[1:]), nil
+ }
+
+ return "", nil
+}
+
+func parseOpts(name string, o values) error {
+ if len(name) == 0 {
+ return fmt.Errorf("invalid options: %s", name)
+ }
+
+ name = strings.TrimSpace(name)
+
+ ps := strings.Split(name, " ")
+ for _, p := range ps {
+ kv := strings.Split(p, "=")
+ if len(kv) < 2 {
+ return fmt.Errorf("invalid option: %q", p)
+ }
+ o.Set(kv[0], kv[1])
+ }
+
+ return nil
+}
+
+func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ db := &core.Uri{DbType: core.POSTGRES}
+ var err error
+
+ if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
+ db.DbName, err = parseURL(dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ o := make(values)
+ err = parseOpts(dataSourceName, o)
+ if err != nil {
+ return nil, err
+ }
+
+ db.DbName = o.Get("dbname")
+ }
+
+ if db.DbName == "" {
+ return nil, errors.New("dbname is empty")
+ }
+
+ return db, nil
+}
+
+type pqDriverPgx struct {
+ pqDriver
+}
+
+func (pgx *pqDriverPgx) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ // Remove the leading characters for driver to work
+ if len(dataSourceName) >= 9 && dataSourceName[0] == 0 {
+ dataSourceName = dataSourceName[9:]
+ }
+ return pgx.pqDriver.Parse(driverName, dataSourceName)
+}
diff --git a/vendor/xorm.io/xorm/dialect_sqlite3.go b/vendor/xorm.io/xorm/dialect_sqlite3.go
new file mode 100644
index 0000000000..0a290f3c48
--- /dev/null
+++ b/vendor/xorm.io/xorm/dialect_sqlite3.go
@@ -0,0 +1,492 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql"
+ "errors"
+ "fmt"
+ "regexp"
+ "strings"
+
+ "xorm.io/core"
+)
+
+var (
+ sqlite3ReservedWords = map[string]bool{
+ "ABORT": true,
+ "ACTION": true,
+ "ADD": true,
+ "AFTER": true,
+ "ALL": true,
+ "ALTER": true,
+ "ANALYZE": true,
+ "AND": true,
+ "AS": true,
+ "ASC": true,
+ "ATTACH": true,
+ "AUTOINCREMENT": true,
+ "BEFORE": true,
+ "BEGIN": true,
+ "BETWEEN": true,
+ "BY": true,
+ "CASCADE": true,
+ "CASE": true,
+ "CAST": true,
+ "CHECK": true,
+ "COLLATE": true,
+ "COLUMN": true,
+ "COMMIT": true,
+ "CONFLICT": true,
+ "CONSTRAINT": true,
+ "CREATE": true,
+ "CROSS": true,
+ "CURRENT_DATE": true,
+ "CURRENT_TIME": true,
+ "CURRENT_TIMESTAMP": true,
+ "DATABASE": true,
+ "DEFAULT": true,
+ "DEFERRABLE": true,
+ "DEFERRED": true,
+ "DELETE": true,
+ "DESC": true,
+ "DETACH": true,
+ "DISTINCT": true,
+ "DROP": true,
+ "EACH": true,
+ "ELSE": true,
+ "END": true,
+ "ESCAPE": true,
+ "EXCEPT": true,
+ "EXCLUSIVE": true,
+ "EXISTS": true,
+ "EXPLAIN": true,
+ "FAIL": true,
+ "FOR": true,
+ "FOREIGN": true,
+ "FROM": true,
+ "FULL": true,
+ "GLOB": true,
+ "GROUP": true,
+ "HAVING": true,
+ "IF": true,
+ "IGNORE": true,
+ "IMMEDIATE": true,
+ "IN": true,
+ "INDEX": true,
+ "INDEXED": true,
+ "INITIALLY": true,
+ "INNER": true,
+ "INSERT": true,
+ "INSTEAD": true,
+ "INTERSECT": true,
+ "INTO": true,
+ "IS": true,
+ "ISNULL": true,
+ "JOIN": true,
+ "KEY": true,
+ "LEFT": true,
+ "LIKE": true,
+ "LIMIT": true,
+ "MATCH": true,
+ "NATURAL": true,
+ "NO": true,
+ "NOT": true,
+ "NOTNULL": true,
+ "NULL": true,
+ "OF": true,
+ "OFFSET": true,
+ "ON": true,
+ "OR": true,
+ "ORDER": true,
+ "OUTER": true,
+ "PLAN": true,
+ "PRAGMA": true,
+ "PRIMARY": true,
+ "QUERY": true,
+ "RAISE": true,
+ "RECURSIVE": true,
+ "REFERENCES": true,
+ "REGEXP": true,
+ "REINDEX": true,
+ "RELEASE": true,
+ "RENAME": true,
+ "REPLACE": true,
+ "RESTRICT": true,
+ "RIGHT": true,
+ "ROLLBACK": true,
+ "ROW": true,
+ "SAVEPOINT": true,
+ "SELECT": true,
+ "SET": true,
+ "TABLE": true,
+ "TEMP": true,
+ "TEMPORARY": true,
+ "THEN": true,
+ "TO": true,
+ "TRANSACTI": true,
+ "TRIGGER": true,
+ "UNION": true,
+ "UNIQUE": true,
+ "UPDATE": true,
+ "USING": true,
+ "VACUUM": true,
+ "VALUES": true,
+ "VIEW": true,
+ "VIRTUAL": true,
+ "WHEN": true,
+ "WHERE": true,
+ "WITH": true,
+ "WITHOUT": true,
+ }
+)
+
+type sqlite3 struct {
+ core.Base
+}
+
+func (db *sqlite3) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
+ return db.Base.Init(d, db, uri, drivername, dataSourceName)
+}
+
+func (db *sqlite3) SqlType(c *core.Column) string {
+ switch t := c.SQLType.Name; t {
+ case core.Bool:
+ if c.Default == "true" {
+ c.Default = "1"
+ } else if c.Default == "false" {
+ c.Default = "0"
+ }
+ return core.Integer
+ case core.Date, core.DateTime, core.TimeStamp, core.Time:
+ return core.DateTime
+ case core.TimeStampz:
+ return core.Text
+ case core.Char, core.Varchar, core.NVarchar, core.TinyText,
+ core.Text, core.MediumText, core.LongText, core.Json:
+ return core.Text
+ case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt:
+ return core.Integer
+ case core.Float, core.Double, core.Real:
+ return core.Real
+ case core.Decimal, core.Numeric:
+ return core.Numeric
+ case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea, core.Binary, core.VarBinary:
+ return core.Blob
+ case core.Serial, core.BigSerial:
+ c.IsPrimaryKey = true
+ c.IsAutoIncrement = true
+ c.Nullable = false
+ return core.Integer
+ default:
+ return t
+ }
+}
+
+func (db *sqlite3) FormatBytes(bs []byte) string {
+ return fmt.Sprintf("X'%x'", bs)
+}
+
+func (db *sqlite3) SupportInsertMany() bool {
+ return true
+}
+
+func (db *sqlite3) IsReserved(name string) bool {
+ _, ok := sqlite3ReservedWords[name]
+ return ok
+}
+
+func (db *sqlite3) Quote(name string) string {
+ return "`" + name + "`"
+}
+
+func (db *sqlite3) AutoIncrStr() string {
+ return "AUTOINCREMENT"
+}
+
+func (db *sqlite3) SupportEngine() bool {
+ return false
+}
+
+func (db *sqlite3) SupportCharset() bool {
+ return false
+}
+
+func (db *sqlite3) IndexOnTable() bool {
+ return false
+}
+
+func (db *sqlite3) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
+ args := []interface{}{idxName}
+ return "SELECT name FROM sqlite_master WHERE type='index' and name = ?", args
+}
+
+func (db *sqlite3) TableCheckSql(tableName string) (string, []interface{}) {
+ args := []interface{}{tableName}
+ return "SELECT name FROM sqlite_master WHERE type='table' and name = ?", args
+}
+
+func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string {
+ // var unique string
+ quote := db.Quote
+ idxName := index.Name
+
+ if !strings.HasPrefix(idxName, "UQE_") &&
+ !strings.HasPrefix(idxName, "IDX_") {
+ if index.Type == core.UniqueType {
+ idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
+ } else {
+ idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name)
+ }
+ }
+ return fmt.Sprintf("DROP INDEX %v", quote(idxName))
+}
+
+func (db *sqlite3) ForUpdateSql(query string) string {
+ return query
+}
+
+/*func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
+ args := []interface{}{tableName}
+ sql := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
+ return sql, args
+}*/
+
+func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) {
+ args := []interface{}{tableName}
+ query := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))"
+ db.LogSQL(query, args)
+ rows, err := db.DB().Query(query, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ if rows.Next() {
+ return true, nil
+ }
+ return false, nil
+}
+
+// splitColStr splits a sqlite col strings as fields
+func splitColStr(colStr string) []string {
+ colStr = strings.TrimSpace(colStr)
+ var results = make([]string, 0, 10)
+ var lastIdx int
+ var hasC, hasQuote bool
+ for i, c := range colStr {
+ if c == ' ' && !hasQuote {
+ if hasC {
+ results = append(results, colStr[lastIdx:i])
+ hasC = false
+ }
+ } else {
+ if c == '\'' {
+ hasQuote = !hasQuote
+ }
+ if !hasC {
+ lastIdx = i
+ }
+ hasC = true
+ if i == len(colStr)-1 {
+ results = append(results, colStr[lastIdx:i+1])
+ }
+ }
+ }
+ return results
+}
+
+func parseString(colStr string) (*core.Column, error) {
+ fields := splitColStr(colStr)
+ col := new(core.Column)
+ col.Indexes = make(map[string]int)
+ col.Nullable = true
+ col.DefaultIsEmpty = true
+
+ for idx, field := range fields {
+ if idx == 0 {
+ col.Name = strings.Trim(strings.Trim(field, "`[] "), `"`)
+ continue
+ } else if idx == 1 {
+ col.SQLType = core.SQLType{Name: field, DefaultLength: 0, DefaultLength2: 0}
+ continue
+ }
+ switch field {
+ case "PRIMARY":
+ col.IsPrimaryKey = true
+ case "AUTOINCREMENT":
+ col.IsAutoIncrement = true
+ case "NULL":
+ if fields[idx-1] == "NOT" {
+ col.Nullable = false
+ } else {
+ col.Nullable = true
+ }
+ case "DEFAULT":
+ col.Default = fields[idx+1]
+ col.DefaultIsEmpty = false
+ }
+ }
+ return col, nil
+}
+
+func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
+ args := []interface{}{tableName}
+ s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?"
+ db.LogSQL(s, args)
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+
+ var name string
+ for rows.Next() {
+ err = rows.Scan(&name)
+ if err != nil {
+ return nil, nil, err
+ }
+ break
+ }
+
+ if name == "" {
+ return nil, nil, errors.New("no table named " + tableName)
+ }
+
+ nStart := strings.Index(name, "(")
+ nEnd := strings.LastIndex(name, ")")
+ reg := regexp.MustCompile(`[^\(,\)]*(\([^\(]*\))?`)
+ colCreates := reg.FindAllString(name[nStart+1:nEnd], -1)
+ cols := make(map[string]*core.Column)
+ colSeq := make([]string, 0)
+
+ for _, colStr := range colCreates {
+ reg = regexp.MustCompile(`,\s`)
+ colStr = reg.ReplaceAllString(colStr, ",")
+ if strings.HasPrefix(strings.TrimSpace(colStr), "PRIMARY KEY") {
+ parts := strings.Split(strings.TrimSpace(colStr), "(")
+ if len(parts) == 2 {
+ pkCols := strings.Split(strings.TrimRight(strings.TrimSpace(parts[1]), ")"), ",")
+ for _, pk := range pkCols {
+ if col, ok := cols[strings.Trim(strings.TrimSpace(pk), "`")]; ok {
+ col.IsPrimaryKey = true
+ }
+ }
+ }
+ continue
+ }
+
+ col, err := parseString(colStr)
+ if err != nil {
+ return colSeq, cols, err
+ }
+
+ cols[col.Name] = col
+ colSeq = append(colSeq, col.Name)
+ }
+ return colSeq, cols, nil
+}
+
+func (db *sqlite3) GetTables() ([]*core.Table, error) {
+ args := []interface{}{}
+ s := "SELECT name FROM sqlite_master WHERE type='table'"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ tables := make([]*core.Table, 0)
+ for rows.Next() {
+ table := core.NewEmptyTable()
+ err = rows.Scan(&table.Name)
+ if err != nil {
+ return nil, err
+ }
+ if table.Name == "sqlite_sequence" {
+ continue
+ }
+ tables = append(tables, table)
+ }
+ return tables, nil
+}
+
+func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) {
+ args := []interface{}{tableName}
+ s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?"
+ db.LogSQL(s, args)
+
+ rows, err := db.DB().Query(s, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ indexes := make(map[string]*core.Index, 0)
+ for rows.Next() {
+ var tmpSQL sql.NullString
+ err = rows.Scan(&tmpSQL)
+ if err != nil {
+ return nil, err
+ }
+
+ if !tmpSQL.Valid {
+ continue
+ }
+ sql := tmpSQL.String
+
+ index := new(core.Index)
+ nNStart := strings.Index(sql, "INDEX")
+ nNEnd := strings.Index(sql, "ON")
+ if nNStart == -1 || nNEnd == -1 {
+ continue
+ }
+
+ indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
+ var isRegular bool
+ if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+ index.Name = indexName[5+len(tableName):]
+ isRegular = true
+ } else {
+ index.Name = indexName
+ }
+
+ if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") {
+ index.Type = core.UniqueType
+ } else {
+ index.Type = core.IndexType
+ }
+
+ nStart := strings.Index(sql, "(")
+ nEnd := strings.Index(sql, ")")
+ colIndexes := strings.Split(sql[nStart+1:nEnd], ",")
+
+ index.Cols = make([]string, 0)
+ for _, col := range colIndexes {
+ index.Cols = append(index.Cols, strings.Trim(col, "` []"))
+ }
+ index.IsRegular = isRegular
+ indexes[index.Name] = index
+ }
+
+ return indexes, nil
+}
+
+func (db *sqlite3) Filters() []core.Filter {
+ return []core.Filter{&core.IdFilter{}}
+}
+
+type sqlite3Driver struct {
+}
+
+func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+ if strings.Contains(dataSourceName, "?") {
+ dataSourceName = dataSourceName[:strings.Index(dataSourceName, "?")]
+ }
+
+ return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil
+}
diff --git a/vendor/xorm.io/xorm/doc.go b/vendor/xorm.io/xorm/doc.go
new file mode 100644
index 0000000000..9620bca19f
--- /dev/null
+++ b/vendor/xorm.io/xorm/doc.go
@@ -0,0 +1,184 @@
+// Copyright 2013 - 2016 The XORM Authors. All rights reserved.
+// Use of this source code is governed by a BSD
+// license that can be found in the LICENSE file.
+
+/*
+
+Package xorm is a simple and powerful ORM for Go.
+
+Installation
+
+Make sure you have installed Go 1.6+ and then:
+
+ go get xorm.io/xorm
+
+Create Engine
+
+Firstly, we should new an engine for a database
+
+ engine, err := xorm.NewEngine(driverName, dataSourceName)
+
+Method NewEngine's parameters is the same as sql.Open. It depends
+drivers' implementation.
+Generally, one engine for an application is enough. You can set it as package variable.
+
+Raw Methods
+
+XORM also support raw SQL execution:
+
+1. query a SQL string, the returned results is []map[string][]byte
+
+ results, err := engine.Query("select * from user")
+
+2. execute a SQL string, the returned results
+
+ affected, err := engine.Exec("update user set .... where ...")
+
+ORM Methods
+
+There are 8 major ORM methods and many helpful methods to use to operate database.
+
+1. Insert one or multiple records to database
+
+ affected, err := engine.Insert(&struct)
+ // INSERT INTO struct () values ()
+ affected, err := engine.Insert(&struct1, &struct2)
+ // INSERT INTO struct1 () values ()
+ // INSERT INTO struct2 () values ()
+ affected, err := engine.Insert(&sliceOfStruct)
+ // INSERT INTO struct () values (),(),()
+ affected, err := engine.Insert(&struct1, &sliceOfStruct2)
+ // INSERT INTO struct1 () values ()
+ // INSERT INTO struct2 () values (),(),()
+
+2. Query one record or one variable from database
+
+ has, err := engine.Get(&user)
+ // SELECT * FROM user LIMIT 1
+
+ var id int64
+ has, err := engine.Table("user").Where("name = ?", name).Get(&id)
+ // SELECT id FROM user WHERE name = ? LIMIT 1
+
+3. Query multiple records from database
+
+ var sliceOfStructs []Struct
+ err := engine.Find(&sliceOfStructs)
+ // SELECT * FROM user
+
+ var mapOfStructs = make(map[int64]Struct)
+ err := engine.Find(&mapOfStructs)
+ // SELECT * FROM user
+
+ var int64s []int64
+ err := engine.Table("user").Cols("id").Find(&int64s)
+ // SELECT id FROM user
+
+4. Query multiple records and record by record handle, there two methods, one is Iterate,
+another is Rows
+
+ err := engine.Iterate(...)
+ // SELECT * FROM user
+
+ rows, err := engine.Rows(...)
+ // SELECT * FROM user
+ defer rows.Close()
+ bean := new(Struct)
+ for rows.Next() {
+ err = rows.Scan(bean)
+ }
+
+5. Update one or more records
+
+ affected, err := engine.ID(...).Update(&user)
+ // UPDATE user SET ...
+
+6. Delete one or more records, Delete MUST has condition
+
+ affected, err := engine.Where(...).Delete(&user)
+ // DELETE FROM user Where ...
+
+7. Count records
+
+ counts, err := engine.Count(&user)
+ // SELECT count(*) AS total FROM user
+
+ counts, err := engine.SQL("select count(*) FROM user").Count()
+ // select count(*) FROM user
+
+8. Sum records
+
+ sumFloat64, err := engine.Sum(&user, "id")
+ // SELECT sum(id) from user
+
+ sumFloat64s, err := engine.Sums(&user, "id1", "id2")
+ // SELECT sum(id1), sum(id2) from user
+
+ sumInt64s, err := engine.SumsInt(&user, "id1", "id2")
+ // SELECT sum(id1), sum(id2) from user
+
+Conditions
+
+The above 8 methods could use with condition methods chainable.
+Attention: the above 8 methods should be the last chainable method.
+
+1. ID, In
+
+ engine.ID(1).Get(&user) // for single primary key
+ // SELECT * FROM user WHERE id = 1
+ engine.ID(core.PK{1, 2}).Get(&user) // for composite primary keys
+ // SELECT * FROM user WHERE id1 = 1 AND id2 = 2
+ engine.In("id", 1, 2, 3).Find(&users)
+ // SELECT * FROM user WHERE id IN (1, 2, 3)
+ engine.In("id", []int{1, 2, 3}).Find(&users)
+ // SELECT * FROM user WHERE id IN (1, 2, 3)
+
+2. Where, And, Or
+
+ engine.Where().And().Or().Find()
+ // SELECT * FROM user WHERE (.. AND ..) OR ...
+
+3. OrderBy, Asc, Desc
+
+ engine.Asc().Desc().Find()
+ // SELECT * FROM user ORDER BY .. ASC, .. DESC
+ engine.OrderBy().Find()
+ // SELECT * FROM user ORDER BY ..
+
+4. Limit, Top
+
+ engine.Limit().Find()
+ // SELECT * FROM user LIMIT .. OFFSET ..
+ engine.Top(5).Find()
+ // SELECT TOP 5 * FROM user // for mssql
+ // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases
+
+5. SQL, let you custom SQL
+
+ var users []User
+ engine.SQL("select * from user").Find(&users)
+
+6. Cols, Omit, Distinct
+
+ var users []*User
+ engine.Cols("col1, col2").Find(&users)
+ // SELECT col1, col2 FROM user
+ engine.Cols("col1", "col2").Where().Update(user)
+ // UPDATE user set col1 = ?, col2 = ? Where ...
+ engine.Omit("col1").Find(&users)
+ // SELECT col2, col3 FROM user
+ engine.Omit("col1").Insert(&user)
+ // INSERT INTO table (non-col1) VALUES ()
+ engine.Distinct("col1").Find(&users)
+ // SELECT DISTINCT col1 FROM user
+
+7. Join, GroupBy, Having
+
+ engine.GroupBy("name").Having("name='xlw'").Find(&users)
+ //SELECT * FROM user GROUP BY name HAVING name='xlw'
+ engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users)
+ //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id
+
+More usage, please visit http://xorm.io/docs
+*/
+package xorm
diff --git a/vendor/xorm.io/xorm/engine.go b/vendor/xorm.io/xorm/engine.go
new file mode 100644
index 0000000000..4ed0f77a92
--- /dev/null
+++ b/vendor/xorm.io/xorm/engine.go
@@ -0,0 +1,1659 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "database/sql"
+ "encoding/gob"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+// Engine is the major struct of xorm, it means a database manager.
+// Commonly, an application only need one engine
+type Engine struct {
+ db *core.DB
+ dialect core.Dialect
+
+ ColumnMapper core.IMapper
+ TableMapper core.IMapper
+ TagIdentifier string
+ Tables map[reflect.Type]*core.Table
+
+ mutex *sync.RWMutex
+ Cacher core.Cacher
+
+ showSQL bool
+ showExecTime bool
+
+ logger core.ILogger
+ TZLocation *time.Location // The timezone of the application
+ DatabaseTZ *time.Location // The timezone of the database
+
+ disableGlobalCache bool
+
+ tagHandlers map[string]tagHandler
+
+ engineGroup *EngineGroup
+
+ cachers map[string]core.Cacher
+ cacherLock sync.RWMutex
+
+ defaultContext context.Context
+}
+
+func (engine *Engine) setCacher(tableName string, cacher core.Cacher) {
+ engine.cacherLock.Lock()
+ engine.cachers[tableName] = cacher
+ engine.cacherLock.Unlock()
+}
+
+func (engine *Engine) SetCacher(tableName string, cacher core.Cacher) {
+ engine.setCacher(tableName, cacher)
+}
+
+func (engine *Engine) getCacher(tableName string) core.Cacher {
+ var cacher core.Cacher
+ var ok bool
+ engine.cacherLock.RLock()
+ cacher, ok = engine.cachers[tableName]
+ engine.cacherLock.RUnlock()
+ if !ok && !engine.disableGlobalCache {
+ cacher = engine.Cacher
+ }
+ return cacher
+}
+
+func (engine *Engine) GetCacher(tableName string) core.Cacher {
+ return engine.getCacher(tableName)
+}
+
+// BufferSize sets buffer size for iterate
+func (engine *Engine) BufferSize(size int) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.BufferSize(size)
+}
+
+// CondDeleted returns the conditions whether a record is soft deleted.
+func (engine *Engine) CondDeleted(colName string) builder.Cond {
+ if engine.dialect.DBType() == core.MSSQL {
+ return builder.IsNull{colName}
+ }
+ return builder.IsNull{colName}.Or(builder.Eq{colName: zeroTime1})
+}
+
+// ShowSQL show SQL statement or not on logger if log level is great than INFO
+func (engine *Engine) ShowSQL(show ...bool) {
+ engine.logger.ShowSQL(show...)
+ if len(show) == 0 {
+ engine.showSQL = true
+ } else {
+ engine.showSQL = show[0]
+ }
+}
+
+// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO
+func (engine *Engine) ShowExecTime(show ...bool) {
+ if len(show) == 0 {
+ engine.showExecTime = true
+ } else {
+ engine.showExecTime = show[0]
+ }
+}
+
+// Logger return the logger interface
+func (engine *Engine) Logger() core.ILogger {
+ return engine.logger
+}
+
+// SetLogger set the new logger
+func (engine *Engine) SetLogger(logger core.ILogger) {
+ engine.logger = logger
+ engine.showSQL = logger.IsShowSQL()
+ engine.dialect.SetLogger(logger)
+}
+
+// SetLogLevel sets the logger level
+func (engine *Engine) SetLogLevel(level core.LogLevel) {
+ engine.logger.SetLevel(level)
+}
+
+// SetDisableGlobalCache disable global cache or not
+func (engine *Engine) SetDisableGlobalCache(disable bool) {
+ if engine.disableGlobalCache != disable {
+ engine.disableGlobalCache = disable
+ }
+}
+
+// DriverName return the current sql driver's name
+func (engine *Engine) DriverName() string {
+ return engine.dialect.DriverName()
+}
+
+// DataSourceName return the current connection string
+func (engine *Engine) DataSourceName() string {
+ return engine.dialect.DataSourceName()
+}
+
+// SetMapper set the name mapping rules
+func (engine *Engine) SetMapper(mapper core.IMapper) {
+ engine.SetTableMapper(mapper)
+ engine.SetColumnMapper(mapper)
+}
+
+// SetTableMapper set the table name mapping rule
+func (engine *Engine) SetTableMapper(mapper core.IMapper) {
+ engine.TableMapper = mapper
+}
+
+// SetColumnMapper set the column name mapping rule
+func (engine *Engine) SetColumnMapper(mapper core.IMapper) {
+ engine.ColumnMapper = mapper
+}
+
+// SupportInsertMany If engine's database support batch insert records like
+// "insert into user values (name, age), (name, age)".
+// When the return is ture, then engine.Insert(&users) will
+// generate batch sql and exeute.
+func (engine *Engine) SupportInsertMany() bool {
+ return engine.dialect.SupportInsertMany()
+}
+
+func (engine *Engine) quoteColumns(columnStr string) string {
+ columns := strings.Split(columnStr, ",")
+ for i := 0; i < len(columns); i++ {
+ columns[i] = engine.Quote(strings.TrimSpace(columns[i]))
+ }
+ return strings.Join(columns, ",")
+}
+
+// Quote Use QuoteStr quote the string sql
+func (engine *Engine) Quote(value string) string {
+ value = strings.TrimSpace(value)
+ if len(value) == 0 {
+ return value
+ }
+
+ buf := strings.Builder{}
+ engine.QuoteTo(&buf, value)
+
+ return buf.String()
+}
+
+// QuoteTo quotes string and writes into the buffer
+func (engine *Engine) QuoteTo(buf *strings.Builder, value string) {
+ if buf == nil {
+ return
+ }
+
+ value = strings.TrimSpace(value)
+ if value == "" {
+ return
+ }
+
+ quotePair := engine.dialect.Quote("")
+
+ if value[0] == '`' || len(quotePair) < 2 || value[0] == quotePair[0] { // no quote
+ _, _ = buf.WriteString(value)
+ return
+ } else {
+ prefix, suffix := quotePair[0], quotePair[1]
+
+ _ = buf.WriteByte(prefix)
+ for i := 0; i < len(value); i++ {
+ if value[i] == '.' {
+ _ = buf.WriteByte(suffix)
+ _ = buf.WriteByte('.')
+ _ = buf.WriteByte(prefix)
+ } else {
+ _ = buf.WriteByte(value[i])
+ }
+ }
+ _ = buf.WriteByte(suffix)
+ }
+}
+
+func (engine *Engine) quote(sql string) string {
+ return engine.dialect.Quote(sql)
+}
+
+// SqlType will be deprecated, please use SQLType instead
+//
+// Deprecated: use SQLType instead
+func (engine *Engine) SqlType(c *core.Column) string {
+ return engine.SQLType(c)
+}
+
+// SQLType A simple wrapper to dialect's core.SqlType method
+func (engine *Engine) SQLType(c *core.Column) string {
+ return engine.dialect.SqlType(c)
+}
+
+// AutoIncrStr Database's autoincrement statement
+func (engine *Engine) AutoIncrStr() string {
+ return engine.dialect.AutoIncrStr()
+}
+
+// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
+func (engine *Engine) SetConnMaxLifetime(d time.Duration) {
+ engine.db.SetConnMaxLifetime(d)
+}
+
+// SetMaxOpenConns is only available for go 1.2+
+func (engine *Engine) SetMaxOpenConns(conns int) {
+ engine.db.SetMaxOpenConns(conns)
+}
+
+// SetMaxIdleConns set the max idle connections on pool, default is 2
+func (engine *Engine) SetMaxIdleConns(conns int) {
+ engine.db.SetMaxIdleConns(conns)
+}
+
+// SetDefaultCacher set the default cacher. Xorm's default not enable cacher.
+func (engine *Engine) SetDefaultCacher(cacher core.Cacher) {
+ engine.Cacher = cacher
+}
+
+// GetDefaultCacher returns the default cacher
+func (engine *Engine) GetDefaultCacher() core.Cacher {
+ return engine.Cacher
+}
+
+// NoCache If you has set default cacher, and you want temporilly stop use cache,
+// you can use NoCache()
+func (engine *Engine) NoCache() *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.NoCache()
+}
+
+// NoCascade If you do not want to auto cascade load object
+func (engine *Engine) NoCascade() *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.NoCascade()
+}
+
+// MapCacher Set a table use a special cacher
+func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) error {
+ engine.setCacher(engine.TableName(bean, true), cacher)
+ return nil
+}
+
+// NewDB provides an interface to operate database directly
+func (engine *Engine) NewDB() (*core.DB, error) {
+ return core.OpenDialect(engine.dialect)
+}
+
+// DB return the wrapper of sql.DB
+func (engine *Engine) DB() *core.DB {
+ return engine.db
+}
+
+// Dialect return database dialect
+func (engine *Engine) Dialect() core.Dialect {
+ return engine.dialect
+}
+
+// NewSession New a session
+func (engine *Engine) NewSession() *Session {
+ session := &Session{engine: engine}
+ session.Init()
+ return session
+}
+
+// Close the engine
+func (engine *Engine) Close() error {
+ return engine.db.Close()
+}
+
+// Ping tests if database is alive
+func (engine *Engine) Ping() error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Ping()
+}
+
+// logging sql
+func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
+ if engine.showSQL && !engine.showExecTime {
+ if len(sqlArgs) > 0 {
+ engine.logger.Infof("[SQL] %v %#v", sqlStr, sqlArgs)
+ } else {
+ engine.logger.Infof("[SQL] %v", sqlStr)
+ }
+ }
+}
+
+// Sql provides raw sql input parameter. When you have a complex SQL statement
+// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
+//
+// Deprecated: use SQL instead.
+func (engine *Engine) Sql(querystring string, args ...interface{}) *Session {
+ return engine.SQL(querystring, args...)
+}
+
+// SQL method let's you manually write raw SQL and operate
+// For example:
+//
+// engine.SQL("select * from user").Find(&users)
+//
+// This code will execute "select * from user" and set the records to users
+func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.SQL(query, args...)
+}
+
+// NoAutoTime Default if your struct has "created" or "updated" filed tag, the fields
+// will automatically be filled with current time when Insert or Update
+// invoked. Call NoAutoTime if you dont' want to fill automatically.
+func (engine *Engine) NoAutoTime() *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.NoAutoTime()
+}
+
+// NoAutoCondition disable auto generate Where condition from bean or not
+func (engine *Engine) NoAutoCondition(no ...bool) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.NoAutoCondition(no...)
+}
+
+func (engine *Engine) loadTableInfo(table *core.Table) error {
+ colSeq, cols, err := engine.dialect.GetColumns(table.Name)
+ if err != nil {
+ return err
+ }
+ for _, name := range colSeq {
+ table.AddColumn(cols[name])
+ }
+ indexes, err := engine.dialect.GetIndexes(table.Name)
+ if err != nil {
+ return err
+ }
+ table.Indexes = indexes
+
+ for _, index := range indexes {
+ for _, name := range index.Cols {
+ if col := table.GetColumn(name); col != nil {
+ col.Indexes[index.Name] = index.Type
+ } else {
+ return fmt.Errorf("Unknown col %s in index %v of table %v, columns %v", name, index.Name, table.Name, table.ColumnsSeq())
+ }
+ }
+ }
+ return nil
+}
+
+// DBMetas Retrieve all tables, columns, indexes' informations from database.
+func (engine *Engine) DBMetas() ([]*core.Table, error) {
+ tables, err := engine.dialect.GetTables()
+ if err != nil {
+ return nil, err
+ }
+
+ for _, table := range tables {
+ if err = engine.loadTableInfo(table); err != nil {
+ return nil, err
+ }
+ }
+ return tables, nil
+}
+
+// DumpAllToFile dump database all table structs and data to a file
+func (engine *Engine) DumpAllToFile(fp string, tp ...core.DbType) error {
+ f, err := os.Create(fp)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return engine.DumpAll(f, tp...)
+}
+
+// DumpAll dump database all table structs and data to w
+func (engine *Engine) DumpAll(w io.Writer, tp ...core.DbType) error {
+ tables, err := engine.DBMetas()
+ if err != nil {
+ return err
+ }
+ return engine.DumpTables(tables, w, tp...)
+}
+
+// DumpTablesToFile dump specified tables to SQL file.
+func (engine *Engine) DumpTablesToFile(tables []*core.Table, fp string, tp ...core.DbType) error {
+ f, err := os.Create(fp)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return engine.DumpTables(tables, f, tp...)
+}
+
+// DumpTables dump specify tables to io.Writer
+func (engine *Engine) DumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error {
+ return engine.dumpTables(tables, w, tp...)
+}
+
+// dumpTables dump database all table structs and data to w with specify db type
+func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error {
+ var dialect core.Dialect
+ var distDBName string
+ if len(tp) == 0 {
+ dialect = engine.dialect
+ distDBName = string(engine.dialect.DBType())
+ } else {
+ dialect = core.QueryDialect(tp[0])
+ if dialect == nil {
+ return errors.New("Unsupported database type")
+ }
+ dialect.Init(nil, engine.dialect.URI(), "", "")
+ distDBName = string(tp[0])
+ }
+
+ _, err := io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s, from %s to %s*/\n\n",
+ Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05"), engine.dialect.DBType(), strings.ToUpper(distDBName)))
+ if err != nil {
+ return err
+ }
+
+ for i, table := range tables {
+ if i > 0 {
+ _, err = io.WriteString(w, "\n")
+ if err != nil {
+ return err
+ }
+ }
+ _, err = io.WriteString(w, dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n")
+ if err != nil {
+ return err
+ }
+ for _, index := range table.Indexes {
+ _, err = io.WriteString(w, dialect.CreateIndexSql(table.Name, index)+";\n")
+ if err != nil {
+ return err
+ }
+ }
+
+ cols := table.ColumnsSeq()
+ colNames := engine.dialect.Quote(strings.Join(cols, engine.dialect.Quote(", ")))
+ destColNames := dialect.Quote(strings.Join(cols, dialect.Quote(", ")))
+
+ rows, err := engine.DB().Query("SELECT " + colNames + " FROM " + engine.Quote(table.Name))
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ dest := make([]interface{}, len(cols))
+ err = rows.ScanSlice(&dest)
+ if err != nil {
+ return err
+ }
+
+ _, err = io.WriteString(w, "INSERT INTO "+dialect.Quote(table.Name)+" ("+destColNames+") VALUES (")
+ if err != nil {
+ return err
+ }
+
+ var temp string
+ for i, d := range dest {
+ col := table.GetColumn(cols[i])
+ if col == nil {
+ return errors.New("unknow column error")
+ }
+
+ if d == nil {
+ temp += ", NULL"
+ } else if col.SQLType.IsText() || col.SQLType.IsTime() {
+ var v = fmt.Sprintf("%s", d)
+ if strings.HasSuffix(v, " +0000 UTC") {
+ temp += fmt.Sprintf(", '%s'", v[0:len(v)-len(" +0000 UTC")])
+ } else {
+ temp += ", '" + strings.Replace(v, "'", "''", -1) + "'"
+ }
+ } else if col.SQLType.IsBlob() {
+ if reflect.TypeOf(d).Kind() == reflect.Slice {
+ temp += fmt.Sprintf(", %s", dialect.FormatBytes(d.([]byte)))
+ } else if reflect.TypeOf(d).Kind() == reflect.String {
+ temp += fmt.Sprintf(", '%s'", d.(string))
+ }
+ } else if col.SQLType.IsNumeric() {
+ switch reflect.TypeOf(d).Kind() {
+ case reflect.Slice:
+ if col.SQLType.Name == core.Bool {
+ temp += fmt.Sprintf(", %v", strconv.FormatBool(d.([]byte)[0] != byte('0')))
+ } else {
+ temp += fmt.Sprintf(", %s", string(d.([]byte)))
+ }
+ case reflect.Int16, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int:
+ if col.SQLType.Name == core.Bool {
+ temp += fmt.Sprintf(", %v", strconv.FormatBool(reflect.ValueOf(d).Int() > 0))
+ } else {
+ temp += fmt.Sprintf(", %v", d)
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ if col.SQLType.Name == core.Bool {
+ temp += fmt.Sprintf(", %v", strconv.FormatBool(reflect.ValueOf(d).Uint() > 0))
+ } else {
+ temp += fmt.Sprintf(", %v", d)
+ }
+ default:
+ temp += fmt.Sprintf(", %v", d)
+ }
+ } else {
+ s := fmt.Sprintf("%v", d)
+ if strings.Contains(s, ":") || strings.Contains(s, "-") {
+ if strings.HasSuffix(s, " +0000 UTC") {
+ temp += fmt.Sprintf(", '%s'", s[0:len(s)-len(" +0000 UTC")])
+ } else {
+ temp += fmt.Sprintf(", '%s'", s)
+ }
+ } else {
+ temp += fmt.Sprintf(", %s", s)
+ }
+ }
+ }
+ _, err = io.WriteString(w, temp[2:]+");\n")
+ if err != nil {
+ return err
+ }
+ }
+
+ // FIXME: Hack for postgres
+ if string(dialect.DBType()) == core.POSTGRES && table.AutoIncrColumn() != nil {
+ _, err = io.WriteString(w, "SELECT setval('"+table.Name+"_id_seq', COALESCE((SELECT MAX("+table.AutoIncrColumn().Name+") + 1 FROM "+dialect.Quote(table.Name)+"), 1), false);\n")
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// Cascade use cascade or not
+func (engine *Engine) Cascade(trueOrFalse ...bool) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Cascade(trueOrFalse...)
+}
+
+// Where method provide a condition query
+func (engine *Engine) Where(query interface{}, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Where(query, args...)
+}
+
+// Id will be deprecated, please use ID instead
+func (engine *Engine) Id(id interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Id(id)
+}
+
+// ID method provoide a condition as (id) = ?
+func (engine *Engine) ID(id interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.ID(id)
+}
+
+// Before apply before Processor, affected bean is passed to closure arg
+func (engine *Engine) Before(closures func(interface{})) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Before(closures)
+}
+
+// After apply after insert Processor, affected bean is passed to closure arg
+func (engine *Engine) After(closures func(interface{})) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.After(closures)
+}
+
+// Charset set charset when create table, only support mysql now
+func (engine *Engine) Charset(charset string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Charset(charset)
+}
+
+// StoreEngine set store engine when create table, only support mysql now
+func (engine *Engine) StoreEngine(storeEngine string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.StoreEngine(storeEngine)
+}
+
+// Distinct use for distinct columns. Caution: when you are using cache,
+// distinct will not be cached because cache system need id,
+// but distinct will not provide id
+func (engine *Engine) Distinct(columns ...string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Distinct(columns...)
+}
+
+// Select customerize your select columns or contents
+func (engine *Engine) Select(str string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Select(str)
+}
+
+// Cols only use the parameters as select or update columns
+func (engine *Engine) Cols(columns ...string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Cols(columns...)
+}
+
+// AllCols indicates that all columns should be use
+func (engine *Engine) AllCols() *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.AllCols()
+}
+
+// MustCols specify some columns must use even if they are empty
+func (engine *Engine) MustCols(columns ...string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.MustCols(columns...)
+}
+
+// UseBool xorm automatically retrieve condition according struct, but
+// if struct has bool field, it will ignore them. So use UseBool
+// to tell system to do not ignore them.
+// If no parameters, it will use all the bool field of struct, or
+// it will use parameters's columns
+func (engine *Engine) UseBool(columns ...string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.UseBool(columns...)
+}
+
+// Omit only not use the parameters as select or update columns
+func (engine *Engine) Omit(columns ...string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Omit(columns...)
+}
+
+// Nullable set null when column is zero-value and nullable for update
+func (engine *Engine) Nullable(columns ...string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Nullable(columns...)
+}
+
+// In will generate "column IN (?, ?)"
+func (engine *Engine) In(column string, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.In(column, args...)
+}
+
+// NotIn will generate "column NOT IN (?, ?)"
+func (engine *Engine) NotIn(column string, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.NotIn(column, args...)
+}
+
+// Incr provides a update string like "column = column + ?"
+func (engine *Engine) Incr(column string, arg ...interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Incr(column, arg...)
+}
+
+// Decr provides a update string like "column = column - ?"
+func (engine *Engine) Decr(column string, arg ...interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Decr(column, arg...)
+}
+
+// SetExpr provides a update string like "column = {expression}"
+func (engine *Engine) SetExpr(column string, expression interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.SetExpr(column, expression)
+}
+
+// Table temporarily change the Get, Find, Update's table
+func (engine *Engine) Table(tableNameOrBean interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Table(tableNameOrBean)
+}
+
+// Alias set the table alias
+func (engine *Engine) Alias(alias string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Alias(alias)
+}
+
+// Limit will generate "LIMIT start, limit"
+func (engine *Engine) Limit(limit int, start ...int) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Limit(limit, start...)
+}
+
+// Desc will generate "ORDER BY column1 DESC, column2 DESC"
+func (engine *Engine) Desc(colNames ...string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Desc(colNames...)
+}
+
+// Asc will generate "ORDER BY column1,column2 Asc"
+// This method can chainable use.
+//
+// engine.Desc("name").Asc("age").Find(&users)
+// // SELECT * FROM user ORDER BY name DESC, age ASC
+//
+func (engine *Engine) Asc(colNames ...string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Asc(colNames...)
+}
+
+// OrderBy will generate "ORDER BY order"
+func (engine *Engine) OrderBy(order string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.OrderBy(order)
+}
+
+// Prepare enables prepare statement
+func (engine *Engine) Prepare() *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Prepare()
+}
+
+// Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
+func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Join(joinOperator, tablename, condition, args...)
+}
+
+// GroupBy generate group by statement
+func (engine *Engine) GroupBy(keys string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.GroupBy(keys)
+}
+
+// Having generate having statement
+func (engine *Engine) Having(conditions string) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Having(conditions)
+}
+
+// UnMapType removes the datbase mapper of a type
+func (engine *Engine) UnMapType(t reflect.Type) {
+ engine.mutex.Lock()
+ defer engine.mutex.Unlock()
+ delete(engine.Tables, t)
+}
+
+func (engine *Engine) autoMapType(v reflect.Value) (*core.Table, error) {
+ t := v.Type()
+ engine.mutex.Lock()
+ defer engine.mutex.Unlock()
+ table, ok := engine.Tables[t]
+ if !ok {
+ var err error
+ table, err = engine.mapType(v)
+ if err != nil {
+ return nil, err
+ }
+
+ engine.Tables[t] = table
+ if engine.Cacher != nil {
+ if v.CanAddr() {
+ engine.GobRegister(v.Addr().Interface())
+ } else {
+ engine.GobRegister(v.Interface())
+ }
+ }
+ }
+ return table, nil
+}
+
+// GobRegister register one struct to gob for cache use
+func (engine *Engine) GobRegister(v interface{}) *Engine {
+ gob.Register(v)
+ return engine
+}
+
+// Table table struct
+type Table struct {
+ *core.Table
+ Name string
+}
+
+// IsValid if table is valid
+func (t *Table) IsValid() bool {
+ return t.Table != nil && len(t.Name) > 0
+}
+
+// TableInfo get table info according to bean's content
+func (engine *Engine) TableInfo(bean interface{}) *Table {
+ v := rValue(bean)
+ tb, err := engine.autoMapType(v)
+ if err != nil {
+ engine.logger.Error(err)
+ }
+ return &Table{tb, engine.TableName(bean)}
+}
+
+func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
+ if index, ok := table.Indexes[indexName]; ok {
+ index.AddColumn(col.Name)
+ col.Indexes[index.Name] = indexType
+ } else {
+ index := core.NewIndex(indexName, indexType)
+ index.AddColumn(col.Name)
+ table.AddIndex(index)
+ col.Indexes[index.Name] = indexType
+ }
+}
+
+// TableName table name interface to define customerize table name
+type TableName interface {
+ TableName() string
+}
+
+var (
+ tpTableName = reflect.TypeOf((*TableName)(nil)).Elem()
+)
+
+func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
+ t := v.Type()
+ table := core.NewEmptyTable()
+ table.Type = t
+ table.Name = engine.tbNameForMap(v)
+
+ var idFieldColName string
+ var hasCacheTag, hasNoCacheTag bool
+
+ for i := 0; i < t.NumField(); i++ {
+ tag := t.Field(i).Tag
+
+ ormTagStr := tag.Get(engine.TagIdentifier)
+ var col *core.Column
+ fieldValue := v.Field(i)
+ fieldType := fieldValue.Type()
+
+ if ormTagStr != "" {
+ col = &core.Column{
+ FieldName: t.Field(i).Name,
+ Nullable: true,
+ IsPrimaryKey: false,
+ IsAutoIncrement: false,
+ MapType: core.TWOSIDES,
+ Indexes: make(map[string]int),
+ DefaultIsEmpty: true,
+ }
+ tags := splitTag(ormTagStr)
+
+ if len(tags) > 0 {
+ if tags[0] == "-" {
+ continue
+ }
+
+ var ctx = tagContext{
+ table: table,
+ col: col,
+ fieldValue: fieldValue,
+ indexNames: make(map[string]int),
+ engine: engine,
+ }
+
+ if strings.HasPrefix(strings.ToUpper(tags[0]), "EXTENDS") {
+ pStart := strings.Index(tags[0], "(")
+ if pStart > -1 && strings.HasSuffix(tags[0], ")") {
+ var tagPrefix = strings.TrimFunc(tags[0][pStart+1:len(tags[0])-1], func(r rune) bool {
+ return r == '\'' || r == '"'
+ })
+
+ ctx.params = []string{tagPrefix}
+ }
+
+ if err := ExtendsTagHandler(&ctx); err != nil {
+ return nil, err
+ }
+ continue
+ }
+
+ for j, key := range tags {
+ if ctx.ignoreNext {
+ ctx.ignoreNext = false
+ continue
+ }
+
+ k := strings.ToUpper(key)
+ ctx.tagName = k
+ ctx.params = []string{}
+
+ pStart := strings.Index(k, "(")
+ if pStart == 0 {
+ return nil, errors.New("( could not be the first charactor")
+ }
+ if pStart > -1 {
+ if !strings.HasSuffix(k, ")") {
+ return nil, fmt.Errorf("field %s tag %s cannot match ) charactor", col.FieldName, key)
+ }
+
+ ctx.tagName = k[:pStart]
+ ctx.params = strings.Split(key[pStart+1:len(k)-1], ",")
+ }
+
+ if j > 0 {
+ ctx.preTag = strings.ToUpper(tags[j-1])
+ }
+ if j < len(tags)-1 {
+ ctx.nextTag = tags[j+1]
+ } else {
+ ctx.nextTag = ""
+ }
+
+ if h, ok := engine.tagHandlers[ctx.tagName]; ok {
+ if err := h(&ctx); err != nil {
+ return nil, err
+ }
+ } else {
+ if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") {
+ col.Name = key[1 : len(key)-1]
+ } else {
+ col.Name = key
+ }
+ }
+
+ if ctx.hasCacheTag {
+ hasCacheTag = true
+ }
+ if ctx.hasNoCacheTag {
+ hasNoCacheTag = true
+ }
+ }
+
+ if col.SQLType.Name == "" {
+ col.SQLType = core.Type2SQLType(fieldType)
+ }
+ engine.dialect.SqlType(col)
+ if col.Length == 0 {
+ col.Length = col.SQLType.DefaultLength
+ }
+ if col.Length2 == 0 {
+ col.Length2 = col.SQLType.DefaultLength2
+ }
+ if col.Name == "" {
+ col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name)
+ }
+
+ if ctx.isUnique {
+ ctx.indexNames[col.Name] = core.UniqueType
+ } else if ctx.isIndex {
+ ctx.indexNames[col.Name] = core.IndexType
+ }
+
+ for indexName, indexType := range ctx.indexNames {
+ addIndex(indexName, table, col, indexType)
+ }
+ }
+ } else {
+ var sqlType core.SQLType
+ if fieldValue.CanAddr() {
+ if _, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ sqlType = core.SQLType{Name: core.Text}
+ }
+ }
+ if _, ok := fieldValue.Interface().(core.Conversion); ok {
+ sqlType = core.SQLType{Name: core.Text}
+ } else {
+ sqlType = core.Type2SQLType(fieldType)
+ }
+ col = core.NewColumn(engine.ColumnMapper.Obj2Table(t.Field(i).Name),
+ t.Field(i).Name, sqlType, sqlType.DefaultLength,
+ sqlType.DefaultLength2, true)
+
+ if fieldType.Kind() == reflect.Int64 && (strings.ToUpper(col.FieldName) == "ID" || strings.HasSuffix(strings.ToUpper(col.FieldName), ".ID")) {
+ idFieldColName = col.Name
+ }
+ }
+ if col.IsAutoIncrement {
+ col.Nullable = false
+ }
+
+ table.AddColumn(col)
+
+ } // end for
+
+ if idFieldColName != "" && len(table.PrimaryKeys) == 0 {
+ col := table.GetColumn(idFieldColName)
+ col.IsPrimaryKey = true
+ col.IsAutoIncrement = true
+ col.Nullable = false
+ table.PrimaryKeys = append(table.PrimaryKeys, col.Name)
+ table.AutoIncrement = col.Name
+ }
+
+ if hasCacheTag {
+ if engine.Cacher != nil { // !nash! use engine's cacher if provided
+ engine.logger.Info("enable cache on table:", table.Name)
+ engine.setCacher(table.Name, engine.Cacher)
+ } else {
+ engine.logger.Info("enable LRU cache on table:", table.Name)
+ engine.setCacher(table.Name, NewLRUCacher2(NewMemoryStore(), time.Hour, 10000))
+ }
+ }
+ if hasNoCacheTag {
+ engine.logger.Info("disable cache on table:", table.Name)
+ engine.setCacher(table.Name, nil)
+ }
+
+ return table, nil
+}
+
+// IsTableEmpty if a table has any reocrd
+func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.IsTableEmpty(bean)
+}
+
+// IsTableExist if a table is exist
+func (engine *Engine) IsTableExist(beanOrTableName interface{}) (bool, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.IsTableExist(beanOrTableName)
+}
+
+// IdOf get id from one struct
+//
+// Deprecated: use IDOf instead.
+func (engine *Engine) IdOf(bean interface{}) core.PK {
+ return engine.IDOf(bean)
+}
+
+// IDOf get id from one struct
+func (engine *Engine) IDOf(bean interface{}) core.PK {
+ return engine.IdOfV(reflect.ValueOf(bean))
+}
+
+// IdOfV get id from one value of struct
+//
+// Deprecated: use IDOfV instead.
+func (engine *Engine) IdOfV(rv reflect.Value) core.PK {
+ return engine.IDOfV(rv)
+}
+
+// IDOfV get id from one value of struct
+func (engine *Engine) IDOfV(rv reflect.Value) core.PK {
+ pk, err := engine.idOfV(rv)
+ if err != nil {
+ engine.logger.Error(err)
+ return nil
+ }
+ return pk
+}
+
+func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) {
+ v := reflect.Indirect(rv)
+ table, err := engine.autoMapType(v)
+ if err != nil {
+ return nil, err
+ }
+
+ pk := make([]interface{}, len(table.PrimaryKeys))
+ for i, col := range table.PKColumns() {
+ var err error
+
+ fieldName := col.FieldName
+ for {
+ parts := strings.SplitN(fieldName, ".", 2)
+ if len(parts) == 1 {
+ break
+ }
+
+ v = v.FieldByName(parts[0])
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+ if v.Kind() != reflect.Struct {
+ return nil, ErrUnSupportedType
+ }
+ fieldName = parts[1]
+ }
+
+ pkField := v.FieldByName(fieldName)
+ switch pkField.Kind() {
+ case reflect.String:
+ pk[i], err = engine.idTypeAssertion(col, pkField.String())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ pk[i], err = engine.idTypeAssertion(col, strconv.FormatInt(pkField.Int(), 10))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ // id of uint will be converted to int64
+ pk[i], err = engine.idTypeAssertion(col, strconv.FormatUint(pkField.Uint(), 10))
+ }
+
+ if err != nil {
+ return nil, err
+ }
+ }
+ return core.PK(pk), nil
+}
+
+func (engine *Engine) idTypeAssertion(col *core.Column, sid string) (interface{}, error) {
+ if col.SQLType.IsNumeric() {
+ n, err := strconv.ParseInt(sid, 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ return n, nil
+ } else if col.SQLType.IsText() {
+ return sid, nil
+ } else {
+ return nil, errors.New("not supported")
+ }
+}
+
+// CreateIndexes create indexes
+func (engine *Engine) CreateIndexes(bean interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.CreateIndexes(bean)
+}
+
+// CreateUniques create uniques
+func (engine *Engine) CreateUniques(bean interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.CreateUniques(bean)
+}
+
+// ClearCacheBean if enabled cache, clear the cache bean
+func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
+ tableName := engine.TableName(bean)
+ cacher := engine.getCacher(tableName)
+ if cacher != nil {
+ cacher.ClearIds(tableName)
+ cacher.DelBean(tableName, id)
+ }
+ return nil
+}
+
+// ClearCache if enabled cache, clear some tables' cache
+func (engine *Engine) ClearCache(beans ...interface{}) error {
+ for _, bean := range beans {
+ tableName := engine.TableName(bean)
+ cacher := engine.getCacher(tableName)
+ if cacher != nil {
+ cacher.ClearIds(tableName)
+ cacher.ClearBeans(tableName)
+ }
+ }
+ return nil
+}
+
+// Sync the new struct changes to database, this method will automatically add
+// table, column, index, unique. but will not delete or change anything.
+// If you change some field, you should change the database manually.
+func (engine *Engine) Sync(beans ...interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+
+ for _, bean := range beans {
+ v := rValue(bean)
+ tableNameNoSchema := engine.TableName(bean)
+ table, err := engine.autoMapType(v)
+ if err != nil {
+ return err
+ }
+
+ isExist, err := session.Table(bean).isTableExist(tableNameNoSchema)
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ err = session.createTable(bean)
+ if err != nil {
+ return err
+ }
+ }
+ /*isEmpty, err := engine.IsEmptyTable(bean)
+ if err != nil {
+ return err
+ }*/
+ var isEmpty bool
+ if isEmpty {
+ err = session.dropTable(bean)
+ if err != nil {
+ return err
+ }
+ err = session.createTable(bean)
+ if err != nil {
+ return err
+ }
+ } else {
+ for _, col := range table.Columns() {
+ isExist, err := engine.dialect.IsColumnExist(tableNameNoSchema, col.Name)
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+ err = session.addColumn(col.Name)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ for name, index := range table.Indexes {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+ if index.Type == core.UniqueType {
+ isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, true)
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+
+ err = session.addUnique(tableNameNoSchema, name)
+ if err != nil {
+ return err
+ }
+ }
+ } else if index.Type == core.IndexType {
+ isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, false)
+ if err != nil {
+ return err
+ }
+ if !isExist {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+
+ err = session.addIndex(tableNameNoSchema, name)
+ if err != nil {
+ return err
+ }
+ }
+ } else {
+ return errors.New("unknow index type")
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// Sync2 synchronize structs to database tables
+func (engine *Engine) Sync2(beans ...interface{}) error {
+ s := engine.NewSession()
+ defer s.Close()
+ return s.Sync2(beans...)
+}
+
+// CreateTables create tabls according bean
+func (engine *Engine) CreateTables(beans ...interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+
+ err := session.Begin()
+ if err != nil {
+ return err
+ }
+
+ for _, bean := range beans {
+ err = session.createTable(bean)
+ if err != nil {
+ session.Rollback()
+ return err
+ }
+ }
+ return session.Commit()
+}
+
+// DropTables drop specify tables
+func (engine *Engine) DropTables(beans ...interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+
+ err := session.Begin()
+ if err != nil {
+ return err
+ }
+
+ for _, bean := range beans {
+ err = session.dropTable(bean)
+ if err != nil {
+ session.Rollback()
+ return err
+ }
+ }
+ return session.Commit()
+}
+
+// DropIndexes drop indexes of a table
+func (engine *Engine) DropIndexes(bean interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.DropIndexes(bean)
+}
+
+// Exec raw sql
+func (engine *Engine) Exec(sqlOrArgs ...interface{}) (sql.Result, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Exec(sqlOrArgs...)
+}
+
+// Query a raw sql and return records as []map[string][]byte
+func (engine *Engine) Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Query(sqlOrArgs...)
+}
+
+// QueryString runs a raw sql and return records as []map[string]string
+func (engine *Engine) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.QueryString(sqlOrArgs...)
+}
+
+// QueryInterface runs a raw sql and return records as []map[string]interface{}
+func (engine *Engine) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.QueryInterface(sqlOrArgs...)
+}
+
+// Insert one or more records
+func (engine *Engine) Insert(beans ...interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Insert(beans...)
+}
+
+// InsertOne insert only one record
+func (engine *Engine) InsertOne(bean interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.InsertOne(bean)
+}
+
+// Update records, bean's non-empty fields are updated contents,
+// condiBean' non-empty filds are conditions
+// CAUTION:
+// 1.bool will defaultly be updated content nor conditions
+// You should call UseBool if you have bool to use.
+// 2.float32 & float64 may be not inexact as conditions
+func (engine *Engine) Update(bean interface{}, condiBeans ...interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Update(bean, condiBeans...)
+}
+
+// Delete records, bean's non-empty fields are conditions
+func (engine *Engine) Delete(bean interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Delete(bean)
+}
+
+// Get retrieve one record from table, bean's non-empty fields
+// are conditions
+func (engine *Engine) Get(bean interface{}) (bool, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Get(bean)
+}
+
+// Exist returns true if the record exist otherwise return false
+func (engine *Engine) Exist(bean ...interface{}) (bool, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Exist(bean...)
+}
+
+// Find retrieve records from table, condiBeans's non-empty fields
+// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
+// map[int64]*Struct
+func (engine *Engine) Find(beans interface{}, condiBeans ...interface{}) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Find(beans, condiBeans...)
+}
+
+// FindAndCount find the results and also return the counts
+func (engine *Engine) FindAndCount(rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.FindAndCount(rowsSlicePtr, condiBean...)
+}
+
+// Iterate record by record handle records from table, bean's non-empty fields
+// are conditions.
+func (engine *Engine) Iterate(bean interface{}, fun IterFunc) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Iterate(bean, fun)
+}
+
+// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields
+// are conditions.
+func (engine *Engine) Rows(bean interface{}) (*Rows, error) {
+ session := engine.NewSession()
+ return session.Rows(bean)
+}
+
+// Count counts the records. bean's non-empty fields are conditions.
+func (engine *Engine) Count(bean ...interface{}) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Count(bean...)
+}
+
+// Sum sum the records by some column. bean's non-empty fields are conditions.
+func (engine *Engine) Sum(bean interface{}, colName string) (float64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Sum(bean, colName)
+}
+
+// SumInt sum the records by some column. bean's non-empty fields are conditions.
+func (engine *Engine) SumInt(bean interface{}, colName string) (int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.SumInt(bean, colName)
+}
+
+// Sums sum the records by some columns. bean's non-empty fields are conditions.
+func (engine *Engine) Sums(bean interface{}, colNames ...string) ([]float64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.Sums(bean, colNames...)
+}
+
+// SumsInt like Sums but return slice of int64 instead of float64.
+func (engine *Engine) SumsInt(bean interface{}, colNames ...string) ([]int64, error) {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.SumsInt(bean, colNames...)
+}
+
+// ImportFile SQL DDL file
+func (engine *Engine) ImportFile(ddlPath string) ([]sql.Result, error) {
+ file, err := os.Open(ddlPath)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ return engine.Import(file)
+}
+
+// Import SQL DDL from io.Reader
+func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
+ var results []sql.Result
+ var lastError error
+ scanner := bufio.NewScanner(r)
+
+ semiColSpliter := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+ if i := bytes.IndexByte(data, ';'); i >= 0 {
+ return i + 1, data[0:i], nil
+ }
+ // If we're at EOF, we have a final, non-terminated line. Return it.
+ if atEOF {
+ return len(data), data, nil
+ }
+ // Request more data.
+ return 0, nil, nil
+ }
+
+ scanner.Split(semiColSpliter)
+
+ for scanner.Scan() {
+ query := strings.Trim(scanner.Text(), " \t\n\r")
+ if len(query) > 0 {
+ engine.logSQL(query)
+ result, err := engine.DB().Exec(query)
+ results = append(results, result)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return results, lastError
+}
+
+// nowTime return current time
+func (engine *Engine) nowTime(col *core.Column) (interface{}, time.Time) {
+ t := time.Now()
+ var tz = engine.DatabaseTZ
+ if !col.DisableTimeZone && col.TimeZone != nil {
+ tz = col.TimeZone
+ }
+ return engine.formatTime(col.SQLType.Name, t.In(tz)), t.In(engine.TZLocation)
+}
+
+func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{}) {
+ if t.IsZero() {
+ if col.Nullable {
+ return nil
+ }
+ return ""
+ }
+
+ if col.TimeZone != nil {
+ return engine.formatTime(col.SQLType.Name, t.In(col.TimeZone))
+ }
+ return engine.formatTime(col.SQLType.Name, t.In(engine.DatabaseTZ))
+}
+
+// formatTime format time as column type
+func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{}) {
+ switch sqlTypeName {
+ case core.Time:
+ s := t.Format("2006-01-02 15:04:05") // time.RFC3339
+ v = s[11:19]
+ case core.Date:
+ v = t.Format("2006-01-02")
+ case core.DateTime, core.TimeStamp:
+ v = t.Format("2006-01-02 15:04:05")
+ case core.TimeStampz:
+ if engine.dialect.DBType() == core.MSSQL {
+ v = t.Format("2006-01-02T15:04:05.9999999Z07:00")
+ } else {
+ v = t.Format(time.RFC3339Nano)
+ }
+ case core.BigInt, core.Int:
+ v = t.Unix()
+ default:
+ v = t
+ }
+ return
+}
+
+// GetColumnMapper returns the column name mapper
+func (engine *Engine) GetColumnMapper() core.IMapper {
+ return engine.ColumnMapper
+}
+
+// GetTableMapper returns the table name mapper
+func (engine *Engine) GetTableMapper() core.IMapper {
+ return engine.TableMapper
+}
+
+// GetTZLocation returns time zone of the application
+func (engine *Engine) GetTZLocation() *time.Location {
+ return engine.TZLocation
+}
+
+// SetTZLocation sets time zone of the application
+func (engine *Engine) SetTZLocation(tz *time.Location) {
+ engine.TZLocation = tz
+}
+
+// GetTZDatabase returns time zone of the database
+func (engine *Engine) GetTZDatabase() *time.Location {
+ return engine.DatabaseTZ
+}
+
+// SetTZDatabase sets time zone of the database
+func (engine *Engine) SetTZDatabase(tz *time.Location) {
+ engine.DatabaseTZ = tz
+}
+
+// SetSchema sets the schema of database
+func (engine *Engine) SetSchema(schema string) {
+ engine.dialect.URI().Schema = schema
+}
+
+// Unscoped always disable struct tag "deleted"
+func (engine *Engine) Unscoped() *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Unscoped()
+}
diff --git a/vendor/xorm.io/xorm/engine_cond.go b/vendor/xorm.io/xorm/engine_cond.go
new file mode 100644
index 0000000000..702ac80434
--- /dev/null
+++ b/vendor/xorm.io/xorm/engine_cond.go
@@ -0,0 +1,232 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql/driver"
+ "fmt"
+ "reflect"
+ "strings"
+ "time"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+func (engine *Engine) buildConds(table *core.Table, bean interface{},
+ includeVersion bool, includeUpdated bool, includeNil bool,
+ includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool,
+ mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) {
+ var conds []builder.Cond
+ for _, col := range table.Columns() {
+ if !includeVersion && col.IsVersion {
+ continue
+ }
+ if !includeUpdated && col.IsUpdated {
+ continue
+ }
+ if !includeAutoIncr && col.IsAutoIncrement {
+ continue
+ }
+
+ if engine.dialect.DBType() == core.MSSQL && (col.SQLType.Name == core.Text || col.SQLType.IsBlob() || col.SQLType.Name == core.TimeStampz) {
+ continue
+ }
+ if col.SQLType.IsJson() {
+ continue
+ }
+
+ var colName string
+ if addedTableName {
+ var nm = tableName
+ if len(aliasName) > 0 {
+ nm = aliasName
+ }
+ colName = engine.Quote(nm) + "." + engine.Quote(col.Name)
+ } else {
+ colName = engine.Quote(col.Name)
+ }
+
+ fieldValuePtr, err := col.ValueOf(bean)
+ if err != nil {
+ if !strings.Contains(err.Error(), "is not valid") {
+ engine.logger.Warn(err)
+ }
+ continue
+ }
+
+ if col.IsDeleted && !unscoped { // tag "deleted" is enabled
+ conds = append(conds, engine.CondDeleted(colName))
+ }
+
+ fieldValue := *fieldValuePtr
+ if fieldValue.Interface() == nil {
+ continue
+ }
+
+ fieldType := reflect.TypeOf(fieldValue.Interface())
+ requiredField := useAllCols
+
+ if b, ok := getFlagForColumn(mustColumnMap, col); ok {
+ if b {
+ requiredField = true
+ } else {
+ continue
+ }
+ }
+
+ if fieldType.Kind() == reflect.Ptr {
+ if fieldValue.IsNil() {
+ if includeNil {
+ conds = append(conds, builder.Eq{colName: nil})
+ }
+ continue
+ } else if !fieldValue.IsValid() {
+ continue
+ } else {
+ // dereference ptr type to instance type
+ fieldValue = fieldValue.Elem()
+ fieldType = reflect.TypeOf(fieldValue.Interface())
+ requiredField = true
+ }
+ }
+
+ var val interface{}
+ switch fieldType.Kind() {
+ case reflect.Bool:
+ if allUseBool || requiredField {
+ val = fieldValue.Interface()
+ } else {
+ // if a bool in a struct, it will not be as a condition because it default is false,
+ // please use Where() instead
+ continue
+ }
+ case reflect.String:
+ if !requiredField && fieldValue.String() == "" {
+ continue
+ }
+ // for MyString, should convert to string or panic
+ if fieldType.String() != reflect.String.String() {
+ val = fieldValue.String()
+ } else {
+ val = fieldValue.Interface()
+ }
+ case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
+ if !requiredField && fieldValue.Int() == 0 {
+ continue
+ }
+ val = fieldValue.Interface()
+ case reflect.Float32, reflect.Float64:
+ if !requiredField && fieldValue.Float() == 0.0 {
+ continue
+ }
+ val = fieldValue.Interface()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
+ if !requiredField && fieldValue.Uint() == 0 {
+ continue
+ }
+ t := int64(fieldValue.Uint())
+ val = reflect.ValueOf(&t).Interface()
+ case reflect.Struct:
+ if fieldType.ConvertibleTo(core.TimeType) {
+ t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+ if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
+ continue
+ }
+ val = engine.formatColTime(col, t)
+ } else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok {
+ continue
+ } else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
+ val, _ = valNul.Value()
+ if val == nil {
+ continue
+ }
+ } else {
+ if col.SQLType.IsJson() {
+ if col.SQLType.IsText() {
+ bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = string(bytes)
+ } else if col.SQLType.IsBlob() {
+ var bytes []byte
+ var err error
+ bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = bytes
+ }
+ } else {
+ engine.autoMapType(fieldValue)
+ if table, ok := engine.Tables[fieldValue.Type()]; ok {
+ if len(table.PrimaryKeys) == 1 {
+ pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
+ // fix non-int pk issues
+ //if pkField.Int() != 0 {
+ if pkField.IsValid() && !isZero(pkField.Interface()) {
+ val = pkField.Interface()
+ } else {
+ continue
+ }
+ } else {
+ //TODO: how to handler?
+ return nil, fmt.Errorf("not supported %v as %v", fieldValue.Interface(), table.PrimaryKeys)
+ }
+ } else {
+ val = fieldValue.Interface()
+ }
+ }
+ }
+ case reflect.Array:
+ continue
+ case reflect.Slice, reflect.Map:
+ if fieldValue == reflect.Zero(fieldType) {
+ continue
+ }
+ if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
+ continue
+ }
+
+ if col.SQLType.IsText() {
+ bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = string(bytes)
+ } else if col.SQLType.IsBlob() {
+ var bytes []byte
+ var err error
+ if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
+ fieldType.Elem().Kind() == reflect.Uint8 {
+ if fieldValue.Len() > 0 {
+ val = fieldValue.Bytes()
+ } else {
+ continue
+ }
+ } else {
+ bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = bytes
+ }
+ } else {
+ continue
+ }
+ default:
+ val = fieldValue.Interface()
+ }
+
+ conds = append(conds, builder.Eq{colName: val})
+ }
+
+ return builder.And(conds...), nil
+}
diff --git a/vendor/xorm.io/xorm/engine_context.go b/vendor/xorm.io/xorm/engine_context.go
new file mode 100644
index 0000000000..c6cbb76c1d
--- /dev/null
+++ b/vendor/xorm.io/xorm/engine_context.go
@@ -0,0 +1,28 @@
+// Copyright 2019 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.8
+
+package xorm
+
+import "context"
+
+// Context creates a session with the context
+func (engine *Engine) Context(ctx context.Context) *Session {
+ session := engine.NewSession()
+ session.isAutoClose = true
+ return session.Context(ctx)
+}
+
+// SetDefaultContext set the default context
+func (engine *Engine) SetDefaultContext(ctx context.Context) {
+ engine.defaultContext = ctx
+}
+
+// PingContext tests if database is alive
+func (engine *Engine) PingContext(ctx context.Context) error {
+ session := engine.NewSession()
+ defer session.Close()
+ return session.PingContext(ctx)
+}
diff --git a/vendor/xorm.io/xorm/engine_group.go b/vendor/xorm.io/xorm/engine_group.go
new file mode 100644
index 0000000000..42d49eca93
--- /dev/null
+++ b/vendor/xorm.io/xorm/engine_group.go
@@ -0,0 +1,219 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "context"
+ "time"
+
+ "xorm.io/core"
+)
+
+// EngineGroup defines an engine group
+type EngineGroup struct {
+ *Engine
+ slaves []*Engine
+ policy GroupPolicy
+}
+
+// NewEngineGroup creates a new engine group
+func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) {
+ var eg EngineGroup
+ if len(policies) > 0 {
+ eg.policy = policies[0]
+ } else {
+ eg.policy = RoundRobinPolicy()
+ }
+
+ driverName, ok1 := args1.(string)
+ conns, ok2 := args2.([]string)
+ if ok1 && ok2 {
+ engines := make([]*Engine, len(conns))
+ for i, conn := range conns {
+ engine, err := NewEngine(driverName, conn)
+ if err != nil {
+ return nil, err
+ }
+ engine.engineGroup = &eg
+ engines[i] = engine
+ }
+
+ eg.Engine = engines[0]
+ eg.slaves = engines[1:]
+ return &eg, nil
+ }
+
+ master, ok3 := args1.(*Engine)
+ slaves, ok4 := args2.([]*Engine)
+ if ok3 && ok4 {
+ master.engineGroup = &eg
+ for i := 0; i < len(slaves); i++ {
+ slaves[i].engineGroup = &eg
+ }
+ eg.Engine = master
+ eg.slaves = slaves
+ return &eg, nil
+ }
+ return nil, ErrParamsType
+}
+
+// Close the engine
+func (eg *EngineGroup) Close() error {
+ err := eg.Engine.Close()
+ if err != nil {
+ return err
+ }
+
+ for i := 0; i < len(eg.slaves); i++ {
+ err := eg.slaves[i].Close()
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Context returned a group session
+func (eg *EngineGroup) Context(ctx context.Context) *Session {
+ sess := eg.NewSession()
+ sess.isAutoClose = true
+ return sess.Context(ctx)
+}
+
+// NewSession returned a group session
+func (eg *EngineGroup) NewSession() *Session {
+ sess := eg.Engine.NewSession()
+ sess.sessionType = groupSession
+ return sess
+}
+
+// Master returns the master engine
+func (eg *EngineGroup) Master() *Engine {
+ return eg.Engine
+}
+
+// Ping tests if database is alive
+func (eg *EngineGroup) Ping() error {
+ if err := eg.Engine.Ping(); err != nil {
+ return err
+ }
+
+ for _, slave := range eg.slaves {
+ if err := slave.Ping(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// SetColumnMapper set the column name mapping rule
+func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) {
+ eg.Engine.ColumnMapper = mapper
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].ColumnMapper = mapper
+ }
+}
+
+// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
+func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) {
+ eg.Engine.SetConnMaxLifetime(d)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].SetConnMaxLifetime(d)
+ }
+}
+
+// SetDefaultCacher set the default cacher
+func (eg *EngineGroup) SetDefaultCacher(cacher core.Cacher) {
+ eg.Engine.SetDefaultCacher(cacher)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].SetDefaultCacher(cacher)
+ }
+}
+
+// SetLogger set the new logger
+func (eg *EngineGroup) SetLogger(logger core.ILogger) {
+ eg.Engine.SetLogger(logger)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].SetLogger(logger)
+ }
+}
+
+// SetLogLevel sets the logger level
+func (eg *EngineGroup) SetLogLevel(level core.LogLevel) {
+ eg.Engine.SetLogLevel(level)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].SetLogLevel(level)
+ }
+}
+
+// SetMapper set the name mapping rules
+func (eg *EngineGroup) SetMapper(mapper core.IMapper) {
+ eg.Engine.SetMapper(mapper)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].SetMapper(mapper)
+ }
+}
+
+// SetMaxIdleConns set the max idle connections on pool, default is 2
+func (eg *EngineGroup) SetMaxIdleConns(conns int) {
+ eg.Engine.db.SetMaxIdleConns(conns)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].db.SetMaxIdleConns(conns)
+ }
+}
+
+// SetMaxOpenConns is only available for go 1.2+
+func (eg *EngineGroup) SetMaxOpenConns(conns int) {
+ eg.Engine.db.SetMaxOpenConns(conns)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].db.SetMaxOpenConns(conns)
+ }
+}
+
+// SetPolicy set the group policy
+func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup {
+ eg.policy = policy
+ return eg
+}
+
+// SetTableMapper set the table name mapping rule
+func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) {
+ eg.Engine.TableMapper = mapper
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].TableMapper = mapper
+ }
+}
+
+// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO
+func (eg *EngineGroup) ShowExecTime(show ...bool) {
+ eg.Engine.ShowExecTime(show...)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].ShowExecTime(show...)
+ }
+}
+
+// ShowSQL show SQL statement or not on logger if log level is great than INFO
+func (eg *EngineGroup) ShowSQL(show ...bool) {
+ eg.Engine.ShowSQL(show...)
+ for i := 0; i < len(eg.slaves); i++ {
+ eg.slaves[i].ShowSQL(show...)
+ }
+}
+
+// Slave returns one of the physical databases which is a slave according the policy
+func (eg *EngineGroup) Slave() *Engine {
+ switch len(eg.slaves) {
+ case 0:
+ return eg.Engine
+ case 1:
+ return eg.slaves[0]
+ }
+ return eg.policy.Slave(eg)
+}
+
+// Slaves returns all the slaves
+func (eg *EngineGroup) Slaves() []*Engine {
+ return eg.slaves
+}
diff --git a/vendor/xorm.io/xorm/engine_group_policy.go b/vendor/xorm.io/xorm/engine_group_policy.go
new file mode 100644
index 0000000000..5b56e8995f
--- /dev/null
+++ b/vendor/xorm.io/xorm/engine_group_policy.go
@@ -0,0 +1,116 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "math/rand"
+ "sync"
+ "time"
+)
+
+// GroupPolicy is be used by chosing the current slave from slaves
+type GroupPolicy interface {
+ Slave(*EngineGroup) *Engine
+}
+
+// GroupPolicyHandler should be used when a function is a GroupPolicy
+type GroupPolicyHandler func(*EngineGroup) *Engine
+
+// Slave implements the chosen of slaves
+func (h GroupPolicyHandler) Slave(eg *EngineGroup) *Engine {
+ return h(eg)
+}
+
+// RandomPolicy implmentes randomly chose the slave of slaves
+func RandomPolicy() GroupPolicyHandler {
+ var r = rand.New(rand.NewSource(time.Now().UnixNano()))
+ return func(g *EngineGroup) *Engine {
+ return g.Slaves()[r.Intn(len(g.Slaves()))]
+ }
+}
+
+// WeightRandomPolicy implmentes randomly chose the slave of slaves
+func WeightRandomPolicy(weights []int) GroupPolicyHandler {
+ var rands = make([]int, 0, len(weights))
+ for i := 0; i < len(weights); i++ {
+ for n := 0; n < weights[i]; n++ {
+ rands = append(rands, i)
+ }
+ }
+ var r = rand.New(rand.NewSource(time.Now().UnixNano()))
+
+ return func(g *EngineGroup) *Engine {
+ var slaves = g.Slaves()
+ idx := rands[r.Intn(len(rands))]
+ if idx >= len(slaves) {
+ idx = len(slaves) - 1
+ }
+ return slaves[idx]
+ }
+}
+
+func RoundRobinPolicy() GroupPolicyHandler {
+ var pos = -1
+ var lock sync.Mutex
+ return func(g *EngineGroup) *Engine {
+ var slaves = g.Slaves()
+
+ lock.Lock()
+ defer lock.Unlock()
+ pos++
+ if pos >= len(slaves) {
+ pos = 0
+ }
+
+ return slaves[pos]
+ }
+}
+
+func WeightRoundRobinPolicy(weights []int) GroupPolicyHandler {
+ var rands = make([]int, 0, len(weights))
+ for i := 0; i < len(weights); i++ {
+ for n := 0; n < weights[i]; n++ {
+ rands = append(rands, i)
+ }
+ }
+ var pos = -1
+ var lock sync.Mutex
+
+ return func(g *EngineGroup) *Engine {
+ var slaves = g.Slaves()
+ lock.Lock()
+ defer lock.Unlock()
+ pos++
+ if pos >= len(rands) {
+ pos = 0
+ }
+
+ idx := rands[pos]
+ if idx >= len(slaves) {
+ idx = len(slaves) - 1
+ }
+ return slaves[idx]
+ }
+}
+
+// LeastConnPolicy implements GroupPolicy, every time will get the least connections slave
+func LeastConnPolicy() GroupPolicyHandler {
+ return func(g *EngineGroup) *Engine {
+ var slaves = g.Slaves()
+ connections := 0
+ idx := 0
+ for i := 0; i < len(slaves); i++ {
+ openConnections := slaves[i].DB().Stats().OpenConnections
+ if i == 0 {
+ connections = openConnections
+ idx = i
+ } else if openConnections <= connections {
+ connections = openConnections
+ idx = i
+ }
+ }
+ return slaves[idx]
+ }
+}
diff --git a/vendor/xorm.io/xorm/engine_table.go b/vendor/xorm.io/xorm/engine_table.go
new file mode 100644
index 0000000000..eb5aa850af
--- /dev/null
+++ b/vendor/xorm.io/xorm/engine_table.go
@@ -0,0 +1,113 @@
+// Copyright 2018 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ "xorm.io/core"
+)
+
+// tbNameWithSchema will automatically add schema prefix on table name
+func (engine *Engine) tbNameWithSchema(v string) string {
+ // Add schema name as prefix of table name.
+ // Only for postgres database.
+ if engine.dialect.DBType() == core.POSTGRES &&
+ engine.dialect.URI().Schema != "" &&
+ engine.dialect.URI().Schema != postgresPublicSchema &&
+ strings.Index(v, ".") == -1 {
+ return engine.dialect.URI().Schema + "." + v
+ }
+ return v
+}
+
+// TableName returns table name with schema prefix if has
+func (engine *Engine) TableName(bean interface{}, includeSchema ...bool) string {
+ tbName := engine.tbNameNoSchema(bean)
+ if len(includeSchema) > 0 && includeSchema[0] {
+ tbName = engine.tbNameWithSchema(tbName)
+ }
+
+ return tbName
+}
+
+// tbName get some table's table name
+func (session *Session) tbNameNoSchema(table *core.Table) string {
+ if len(session.statement.AltTableName) > 0 {
+ return session.statement.AltTableName
+ }
+
+ return table.Name
+}
+
+func (engine *Engine) tbNameForMap(v reflect.Value) string {
+ if v.Type().Implements(tpTableName) {
+ return v.Interface().(TableName).TableName()
+ }
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ if v.Type().Implements(tpTableName) {
+ return v.Interface().(TableName).TableName()
+ }
+ }
+
+ return engine.TableMapper.Obj2Table(v.Type().Name())
+}
+
+func (engine *Engine) tbNameNoSchema(tablename interface{}) string {
+ switch tablename.(type) {
+ case []string:
+ t := tablename.([]string)
+ if len(t) > 1 {
+ return fmt.Sprintf("%v AS %v", engine.Quote(t[0]), engine.Quote(t[1]))
+ } else if len(t) == 1 {
+ return engine.Quote(t[0])
+ }
+ case []interface{}:
+ t := tablename.([]interface{})
+ l := len(t)
+ var table string
+ if l > 0 {
+ f := t[0]
+ switch f.(type) {
+ case string:
+ table = f.(string)
+ case TableName:
+ table = f.(TableName).TableName()
+ default:
+ v := rValue(f)
+ t := v.Type()
+ if t.Kind() == reflect.Struct {
+ table = engine.tbNameForMap(v)
+ } else {
+ table = engine.Quote(fmt.Sprintf("%v", f))
+ }
+ }
+ }
+ if l > 1 {
+ return fmt.Sprintf("%v AS %v", engine.Quote(table),
+ engine.Quote(fmt.Sprintf("%v", t[1])))
+ } else if l == 1 {
+ return engine.Quote(table)
+ }
+ case TableName:
+ return tablename.(TableName).TableName()
+ case string:
+ return tablename.(string)
+ case reflect.Value:
+ v := tablename.(reflect.Value)
+ return engine.tbNameForMap(v)
+ default:
+ v := rValue(tablename)
+ t := v.Type()
+ if t.Kind() == reflect.Struct {
+ return engine.tbNameForMap(v)
+ }
+ return engine.Quote(fmt.Sprintf("%v", tablename))
+ }
+ return ""
+}
diff --git a/vendor/xorm.io/xorm/error.go b/vendor/xorm.io/xorm/error.go
new file mode 100644
index 0000000000..a67527acda
--- /dev/null
+++ b/vendor/xorm.io/xorm/error.go
@@ -0,0 +1,51 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+)
+
+var (
+ // ErrParamsType params error
+ ErrParamsType = errors.New("Params type error")
+ // ErrTableNotFound table not found error
+ ErrTableNotFound = errors.New("Table not found")
+ // ErrUnSupportedType unsupported error
+ ErrUnSupportedType = errors.New("Unsupported type error")
+ // ErrNotExist record does not exist error
+ ErrNotExist = errors.New("Record does not exist")
+ // ErrCacheFailed cache failed error
+ ErrCacheFailed = errors.New("Cache failed")
+ // ErrNeedDeletedCond delete needs less one condition error
+ ErrNeedDeletedCond = errors.New("Delete action needs at least one condition")
+ // ErrNotImplemented not implemented
+ ErrNotImplemented = errors.New("Not implemented")
+ // ErrConditionType condition type unsupported
+ ErrConditionType = errors.New("Unsupported condition type")
+ // ErrUnSupportedSQLType parameter of SQL is not supported
+ ErrUnSupportedSQLType = errors.New("unsupported sql type")
+)
+
+// ErrFieldIsNotExist columns does not exist
+type ErrFieldIsNotExist struct {
+ FieldName string
+ TableName string
+}
+
+func (e ErrFieldIsNotExist) Error() string {
+ return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName)
+}
+
+// ErrFieldIsNotValid is not valid
+type ErrFieldIsNotValid struct {
+ FieldName string
+ TableName string
+}
+
+func (e ErrFieldIsNotValid) Error() string {
+ return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName)
+}
diff --git a/vendor/xorm.io/xorm/gen_reserved.sh b/vendor/xorm.io/xorm/gen_reserved.sh
new file mode 100644
index 0000000000..434a1bfcb0
--- /dev/null
+++ b/vendor/xorm.io/xorm/gen_reserved.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+if [ -f $1 ];then
+ cat $1| awk '{printf("\""$1"\":true,\n")}'
+else
+ echo "argument $1 if not a file!"
+fi
diff --git a/vendor/xorm.io/xorm/go.mod b/vendor/xorm.io/xorm/go.mod
new file mode 100644
index 0000000000..6d8b58f41a
--- /dev/null
+++ b/vendor/xorm.io/xorm/go.mod
@@ -0,0 +1,15 @@
+module xorm.io/xorm
+
+go 1.11
+
+require (
+ github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4
+ github.com/go-sql-driver/mysql v1.4.1
+ github.com/kr/pretty v0.1.0 // indirect
+ github.com/lib/pq v1.0.0
+ github.com/mattn/go-sqlite3 v1.10.0
+ github.com/stretchr/testify v1.4.0
+ github.com/ziutek/mymysql v1.5.4
+ xorm.io/builder v0.3.6
+ xorm.io/core v0.7.2
+)
diff --git a/vendor/xorm.io/xorm/go.sum b/vendor/xorm.io/xorm/go.sum
new file mode 100644
index 0000000000..2102cc5b7a
--- /dev/null
+++ b/vendor/xorm.io/xorm/go.sum
@@ -0,0 +1,149 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
+cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
+github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
+github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
+github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
+github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
+google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+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=
diff --git a/vendor/xorm.io/xorm/helpers.go b/vendor/xorm.io/xorm/helpers.go
new file mode 100644
index 0000000000..a31e922c0b
--- /dev/null
+++ b/vendor/xorm.io/xorm/helpers.go
@@ -0,0 +1,332 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+
+ "xorm.io/core"
+)
+
+// str2PK convert string value to primary key value according to tp
+func str2PKValue(s string, tp reflect.Type) (reflect.Value, error) {
+ var err error
+ var result interface{}
+ var defReturn = reflect.Zero(tp)
+
+ switch tp.Kind() {
+ case reflect.Int:
+ result, err = strconv.Atoi(s)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as int: %s", s, err.Error())
+ }
+ case reflect.Int8:
+ x, err := strconv.Atoi(s)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as int8: %s", s, err.Error())
+ }
+ result = int8(x)
+ case reflect.Int16:
+ x, err := strconv.Atoi(s)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as int16: %s", s, err.Error())
+ }
+ result = int16(x)
+ case reflect.Int32:
+ x, err := strconv.Atoi(s)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as int32: %s", s, err.Error())
+ }
+ result = int32(x)
+ case reflect.Int64:
+ result, err = strconv.ParseInt(s, 10, 64)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as int64: %s", s, err.Error())
+ }
+ case reflect.Uint:
+ x, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as uint: %s", s, err.Error())
+ }
+ result = uint(x)
+ case reflect.Uint8:
+ x, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as uint8: %s", s, err.Error())
+ }
+ result = uint8(x)
+ case reflect.Uint16:
+ x, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as uint16: %s", s, err.Error())
+ }
+ result = uint16(x)
+ case reflect.Uint32:
+ x, err := strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as uint32: %s", s, err.Error())
+ }
+ result = uint32(x)
+ case reflect.Uint64:
+ result, err = strconv.ParseUint(s, 10, 64)
+ if err != nil {
+ return defReturn, fmt.Errorf("convert %s as uint64: %s", s, err.Error())
+ }
+ case reflect.String:
+ result = s
+ default:
+ return defReturn, errors.New("unsupported convert type")
+ }
+ return reflect.ValueOf(result).Convert(tp), nil
+}
+
+func str2PK(s string, tp reflect.Type) (interface{}, error) {
+ v, err := str2PKValue(s, tp)
+ if err != nil {
+ return nil, err
+ }
+ return v.Interface(), nil
+}
+
+func splitTag(tag string) (tags []string) {
+ tag = strings.TrimSpace(tag)
+ var hasQuote = false
+ var lastIdx = 0
+ for i, t := range tag {
+ if t == '\'' {
+ hasQuote = !hasQuote
+ } else if t == ' ' {
+ if lastIdx < i && !hasQuote {
+ tags = append(tags, strings.TrimSpace(tag[lastIdx:i]))
+ lastIdx = i + 1
+ }
+ }
+ }
+ if lastIdx < len(tag) {
+ tags = append(tags, strings.TrimSpace(tag[lastIdx:]))
+ }
+ return
+}
+
+type zeroable interface {
+ IsZero() bool
+}
+
+func isZero(k interface{}) bool {
+ switch k.(type) {
+ case int:
+ return k.(int) == 0
+ case int8:
+ return k.(int8) == 0
+ case int16:
+ return k.(int16) == 0
+ case int32:
+ return k.(int32) == 0
+ case int64:
+ return k.(int64) == 0
+ case uint:
+ return k.(uint) == 0
+ case uint8:
+ return k.(uint8) == 0
+ case uint16:
+ return k.(uint16) == 0
+ case uint32:
+ return k.(uint32) == 0
+ case uint64:
+ return k.(uint64) == 0
+ case float32:
+ return k.(float32) == 0
+ case float64:
+ return k.(float64) == 0
+ case bool:
+ return k.(bool) == false
+ case string:
+ return k.(string) == ""
+ case zeroable:
+ return k.(zeroable).IsZero()
+ }
+ return false
+}
+
+func isStructZero(v reflect.Value) bool {
+ if !v.IsValid() {
+ return true
+ }
+
+ for i := 0; i < v.NumField(); i++ {
+ field := v.Field(i)
+ switch field.Kind() {
+ case reflect.Ptr:
+ field = field.Elem()
+ fallthrough
+ case reflect.Struct:
+ if !isStructZero(field) {
+ return false
+ }
+ default:
+ if field.CanInterface() && !isZero(field.Interface()) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+func isArrayValueZero(v reflect.Value) bool {
+ if !v.IsValid() || v.Len() == 0 {
+ return true
+ }
+
+ for i := 0; i < v.Len(); i++ {
+ if !isZero(v.Index(i).Interface()) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func int64ToIntValue(id int64, tp reflect.Type) reflect.Value {
+ var v interface{}
+ kind := tp.Kind()
+
+ if kind == reflect.Ptr {
+ kind = tp.Elem().Kind()
+ }
+
+ switch kind {
+ case reflect.Int16:
+ temp := int16(id)
+ v = &temp
+ case reflect.Int32:
+ temp := int32(id)
+ v = &temp
+ case reflect.Int:
+ temp := int(id)
+ v = &temp
+ case reflect.Int64:
+ temp := id
+ v = &temp
+ case reflect.Uint16:
+ temp := uint16(id)
+ v = &temp
+ case reflect.Uint32:
+ temp := uint32(id)
+ v = &temp
+ case reflect.Uint64:
+ temp := uint64(id)
+ v = &temp
+ case reflect.Uint:
+ temp := uint(id)
+ v = &temp
+ }
+
+ if tp.Kind() == reflect.Ptr {
+ return reflect.ValueOf(v).Convert(tp)
+ }
+ return reflect.ValueOf(v).Elem().Convert(tp)
+}
+
+func int64ToInt(id int64, tp reflect.Type) interface{} {
+ return int64ToIntValue(id, tp).Interface()
+}
+
+func isPKZero(pk core.PK) bool {
+ for _, k := range pk {
+ if isZero(k) {
+ return true
+ }
+ }
+ return false
+}
+
+func indexNoCase(s, sep string) int {
+ return strings.Index(strings.ToLower(s), strings.ToLower(sep))
+}
+
+func splitNoCase(s, sep string) []string {
+ idx := indexNoCase(s, sep)
+ if idx < 0 {
+ return []string{s}
+ }
+ return strings.Split(s, s[idx:idx+len(sep)])
+}
+
+func splitNNoCase(s, sep string, n int) []string {
+ idx := indexNoCase(s, sep)
+ if idx < 0 {
+ return []string{s}
+ }
+ return strings.SplitN(s, s[idx:idx+len(sep)], n)
+}
+
+func makeArray(elem string, count int) []string {
+ res := make([]string, count)
+ for i := 0; i < count; i++ {
+ res[i] = elem
+ }
+ return res
+}
+
+func rValue(bean interface{}) reflect.Value {
+ return reflect.Indirect(reflect.ValueOf(bean))
+}
+
+func rType(bean interface{}) reflect.Type {
+ sliceValue := reflect.Indirect(reflect.ValueOf(bean))
+ // return reflect.TypeOf(sliceValue.Interface())
+ return sliceValue.Type()
+}
+
+func structName(v reflect.Type) string {
+ for v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+ return v.Name()
+}
+
+func sliceEq(left, right []string) bool {
+ if len(left) != len(right) {
+ return false
+ }
+ sort.Sort(sort.StringSlice(left))
+ sort.Sort(sort.StringSlice(right))
+ for i := 0; i < len(left); i++ {
+ if left[i] != right[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func indexName(tableName, idxName string) string {
+ return fmt.Sprintf("IDX_%v_%v", tableName, idxName)
+}
+
+func eraseAny(value string, strToErase ...string) string {
+ if len(strToErase) == 0 {
+ return value
+ }
+ var replaceSeq []string
+ for _, s := range strToErase {
+ replaceSeq = append(replaceSeq, s, "")
+ }
+
+ replacer := strings.NewReplacer(replaceSeq...)
+
+ return replacer.Replace(value)
+}
+
+func quoteColumns(cols []string, quoteFunc func(string) string, sep string) string {
+ for i := range cols {
+ cols[i] = quoteFunc(cols[i])
+ }
+ return strings.Join(cols, sep+" ")
+}
diff --git a/vendor/xorm.io/xorm/helpler_time.go b/vendor/xorm.io/xorm/helpler_time.go
new file mode 100644
index 0000000000..f4013e27e1
--- /dev/null
+++ b/vendor/xorm.io/xorm/helpler_time.go
@@ -0,0 +1,21 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import "time"
+
+const (
+ zeroTime0 = "0000-00-00 00:00:00"
+ zeroTime1 = "0001-01-01 00:00:00"
+)
+
+func formatTime(t time.Time) string {
+ return t.Format("2006-01-02 15:04:05")
+}
+
+func isTimeZero(t time.Time) bool {
+ return t.IsZero() || formatTime(t) == zeroTime0 ||
+ formatTime(t) == zeroTime1
+}
diff --git a/vendor/xorm.io/xorm/interface.go b/vendor/xorm.io/xorm/interface.go
new file mode 100644
index 0000000000..a564db1260
--- /dev/null
+++ b/vendor/xorm.io/xorm/interface.go
@@ -0,0 +1,118 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "context"
+ "database/sql"
+ "reflect"
+ "time"
+
+ "xorm.io/core"
+)
+
+// Interface defines the interface which Engine, EngineGroup and Session will implementate.
+type Interface interface {
+ AllCols() *Session
+ Alias(alias string) *Session
+ Asc(colNames ...string) *Session
+ BufferSize(size int) *Session
+ Cols(columns ...string) *Session
+ Count(...interface{}) (int64, error)
+ CreateIndexes(bean interface{}) error
+ CreateUniques(bean interface{}) error
+ Decr(column string, arg ...interface{}) *Session
+ Desc(...string) *Session
+ Delete(interface{}) (int64, error)
+ Distinct(columns ...string) *Session
+ DropIndexes(bean interface{}) error
+ Exec(sqlOrArgs ...interface{}) (sql.Result, error)
+ Exist(bean ...interface{}) (bool, error)
+ Find(interface{}, ...interface{}) error
+ FindAndCount(interface{}, ...interface{}) (int64, error)
+ Get(interface{}) (bool, error)
+ GroupBy(keys string) *Session
+ ID(interface{}) *Session
+ In(string, ...interface{}) *Session
+ Incr(column string, arg ...interface{}) *Session
+ Insert(...interface{}) (int64, error)
+ InsertOne(interface{}) (int64, error)
+ IsTableEmpty(bean interface{}) (bool, error)
+ IsTableExist(beanOrTableName interface{}) (bool, error)
+ Iterate(interface{}, IterFunc) error
+ Limit(int, ...int) *Session
+ MustCols(columns ...string) *Session
+ NoAutoCondition(...bool) *Session
+ NotIn(string, ...interface{}) *Session
+ Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session
+ Omit(columns ...string) *Session
+ OrderBy(order string) *Session
+ Ping() error
+ Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error)
+ QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error)
+ QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error)
+ Rows(bean interface{}) (*Rows, error)
+ SetExpr(string, interface{}) *Session
+ SQL(interface{}, ...interface{}) *Session
+ Sum(bean interface{}, colName string) (float64, error)
+ SumInt(bean interface{}, colName string) (int64, error)
+ Sums(bean interface{}, colNames ...string) ([]float64, error)
+ SumsInt(bean interface{}, colNames ...string) ([]int64, error)
+ Table(tableNameOrBean interface{}) *Session
+ Unscoped() *Session
+ Update(bean interface{}, condiBeans ...interface{}) (int64, error)
+ UseBool(...string) *Session
+ Where(interface{}, ...interface{}) *Session
+}
+
+// EngineInterface defines the interface which Engine, EngineGroup will implementate.
+type EngineInterface interface {
+ Interface
+
+ Before(func(interface{})) *Session
+ Charset(charset string) *Session
+ ClearCache(...interface{}) error
+ Context(context.Context) *Session
+ CreateTables(...interface{}) error
+ DBMetas() ([]*core.Table, error)
+ Dialect() core.Dialect
+ DropTables(...interface{}) error
+ DumpAllToFile(fp string, tp ...core.DbType) error
+ GetCacher(string) core.Cacher
+ GetColumnMapper() core.IMapper
+ GetDefaultCacher() core.Cacher
+ GetTableMapper() core.IMapper
+ GetTZDatabase() *time.Location
+ GetTZLocation() *time.Location
+ MapCacher(interface{}, core.Cacher) error
+ NewSession() *Session
+ NoAutoTime() *Session
+ Quote(string) string
+ SetCacher(string, core.Cacher)
+ SetConnMaxLifetime(time.Duration)
+ SetDefaultCacher(core.Cacher)
+ SetLogger(logger core.ILogger)
+ SetLogLevel(core.LogLevel)
+ SetMapper(core.IMapper)
+ SetMaxOpenConns(int)
+ SetMaxIdleConns(int)
+ SetSchema(string)
+ SetTZDatabase(tz *time.Location)
+ SetTZLocation(tz *time.Location)
+ ShowExecTime(...bool)
+ ShowSQL(show ...bool)
+ Sync(...interface{}) error
+ Sync2(...interface{}) error
+ StoreEngine(storeEngine string) *Session
+ TableInfo(bean interface{}) *Table
+ TableName(interface{}, ...bool) string
+ UnMapType(reflect.Type)
+}
+
+var (
+ _ Interface = &Session{}
+ _ EngineInterface = &Engine{}
+ _ EngineInterface = &EngineGroup{}
+)
diff --git a/vendor/xorm.io/xorm/json.go b/vendor/xorm.io/xorm/json.go
new file mode 100644
index 0000000000..fdb6ce5654
--- /dev/null
+++ b/vendor/xorm.io/xorm/json.go
@@ -0,0 +1,31 @@
+// Copyright 2019 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import "encoding/json"
+
+// JSONInterface represents an interface to handle json data
+type JSONInterface interface {
+ Marshal(v interface{}) ([]byte, error)
+ Unmarshal(data []byte, v interface{}) error
+}
+
+var (
+ // DefaultJSONHandler default json handler
+ DefaultJSONHandler JSONInterface = StdJSON{}
+)
+
+// StdJSON implements JSONInterface via encoding/json
+type StdJSON struct{}
+
+// Marshal implements JSONInterface
+func (StdJSON) Marshal(v interface{}) ([]byte, error) {
+ return json.Marshal(v)
+}
+
+// Unmarshal implements JSONInterface
+func (StdJSON) Unmarshal(data []byte, v interface{}) error {
+ return json.Unmarshal(data, v)
+}
diff --git a/vendor/xorm.io/xorm/logger.go b/vendor/xorm.io/xorm/logger.go
new file mode 100644
index 0000000000..7b26e77f3b
--- /dev/null
+++ b/vendor/xorm.io/xorm/logger.go
@@ -0,0 +1,187 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "fmt"
+ "io"
+ "log"
+
+ "xorm.io/core"
+)
+
+// default log options
+const (
+ DEFAULT_LOG_PREFIX = "[xorm]"
+ DEFAULT_LOG_FLAG = log.Ldate | log.Lmicroseconds
+ DEFAULT_LOG_LEVEL = core.LOG_DEBUG
+)
+
+var _ core.ILogger = DiscardLogger{}
+
+// DiscardLogger don't log implementation for core.ILogger
+type DiscardLogger struct{}
+
+// Debug empty implementation
+func (DiscardLogger) Debug(v ...interface{}) {}
+
+// Debugf empty implementation
+func (DiscardLogger) Debugf(format string, v ...interface{}) {}
+
+// Error empty implementation
+func (DiscardLogger) Error(v ...interface{}) {}
+
+// Errorf empty implementation
+func (DiscardLogger) Errorf(format string, v ...interface{}) {}
+
+// Info empty implementation
+func (DiscardLogger) Info(v ...interface{}) {}
+
+// Infof empty implementation
+func (DiscardLogger) Infof(format string, v ...interface{}) {}
+
+// Warn empty implementation
+func (DiscardLogger) Warn(v ...interface{}) {}
+
+// Warnf empty implementation
+func (DiscardLogger) Warnf(format string, v ...interface{}) {}
+
+// Level empty implementation
+func (DiscardLogger) Level() core.LogLevel {
+ return core.LOG_UNKNOWN
+}
+
+// SetLevel empty implementation
+func (DiscardLogger) SetLevel(l core.LogLevel) {}
+
+// ShowSQL empty implementation
+func (DiscardLogger) ShowSQL(show ...bool) {}
+
+// IsShowSQL empty implementation
+func (DiscardLogger) IsShowSQL() bool {
+ return false
+}
+
+// SimpleLogger is the default implment of core.ILogger
+type SimpleLogger struct {
+ DEBUG *log.Logger
+ ERR *log.Logger
+ INFO *log.Logger
+ WARN *log.Logger
+ level core.LogLevel
+ showSQL bool
+}
+
+var _ core.ILogger = &SimpleLogger{}
+
+// NewSimpleLogger use a special io.Writer as logger output
+func NewSimpleLogger(out io.Writer) *SimpleLogger {
+ return NewSimpleLogger2(out, DEFAULT_LOG_PREFIX, DEFAULT_LOG_FLAG)
+}
+
+// NewSimpleLogger2 let you customrize your logger prefix and flag
+func NewSimpleLogger2(out io.Writer, prefix string, flag int) *SimpleLogger {
+ return NewSimpleLogger3(out, prefix, flag, DEFAULT_LOG_LEVEL)
+}
+
+// NewSimpleLogger3 let you customrize your logger prefix and flag and logLevel
+func NewSimpleLogger3(out io.Writer, prefix string, flag int, l core.LogLevel) *SimpleLogger {
+ return &SimpleLogger{
+ DEBUG: log.New(out, fmt.Sprintf("%s [debug] ", prefix), flag),
+ ERR: log.New(out, fmt.Sprintf("%s [error] ", prefix), flag),
+ INFO: log.New(out, fmt.Sprintf("%s [info] ", prefix), flag),
+ WARN: log.New(out, fmt.Sprintf("%s [warn] ", prefix), flag),
+ level: l,
+ }
+}
+
+// Error implement core.ILogger
+func (s *SimpleLogger) Error(v ...interface{}) {
+ if s.level <= core.LOG_ERR {
+ s.ERR.Output(2, fmt.Sprint(v...))
+ }
+ return
+}
+
+// Errorf implement core.ILogger
+func (s *SimpleLogger) Errorf(format string, v ...interface{}) {
+ if s.level <= core.LOG_ERR {
+ s.ERR.Output(2, fmt.Sprintf(format, v...))
+ }
+ return
+}
+
+// Debug implement core.ILogger
+func (s *SimpleLogger) Debug(v ...interface{}) {
+ if s.level <= core.LOG_DEBUG {
+ s.DEBUG.Output(2, fmt.Sprint(v...))
+ }
+ return
+}
+
+// Debugf implement core.ILogger
+func (s *SimpleLogger) Debugf(format string, v ...interface{}) {
+ if s.level <= core.LOG_DEBUG {
+ s.DEBUG.Output(2, fmt.Sprintf(format, v...))
+ }
+ return
+}
+
+// Info implement core.ILogger
+func (s *SimpleLogger) Info(v ...interface{}) {
+ if s.level <= core.LOG_INFO {
+ s.INFO.Output(2, fmt.Sprint(v...))
+ }
+ return
+}
+
+// Infof implement core.ILogger
+func (s *SimpleLogger) Infof(format string, v ...interface{}) {
+ if s.level <= core.LOG_INFO {
+ s.INFO.Output(2, fmt.Sprintf(format, v...))
+ }
+ return
+}
+
+// Warn implement core.ILogger
+func (s *SimpleLogger) Warn(v ...interface{}) {
+ if s.level <= core.LOG_WARNING {
+ s.WARN.Output(2, fmt.Sprint(v...))
+ }
+ return
+}
+
+// Warnf implement core.ILogger
+func (s *SimpleLogger) Warnf(format string, v ...interface{}) {
+ if s.level <= core.LOG_WARNING {
+ s.WARN.Output(2, fmt.Sprintf(format, v...))
+ }
+ return
+}
+
+// Level implement core.ILogger
+func (s *SimpleLogger) Level() core.LogLevel {
+ return s.level
+}
+
+// SetLevel implement core.ILogger
+func (s *SimpleLogger) SetLevel(l core.LogLevel) {
+ s.level = l
+ return
+}
+
+// ShowSQL implement core.ILogger
+func (s *SimpleLogger) ShowSQL(show ...bool) {
+ if len(show) == 0 {
+ s.showSQL = true
+ return
+ }
+ s.showSQL = show[0]
+}
+
+// IsShowSQL implement core.ILogger
+func (s *SimpleLogger) IsShowSQL() bool {
+ return s.showSQL
+}
diff --git a/vendor/xorm.io/xorm/pg_reserved.txt b/vendor/xorm.io/xorm/pg_reserved.txt
new file mode 100644
index 0000000000..720ed377ba
--- /dev/null
+++ b/vendor/xorm.io/xorm/pg_reserved.txt
@@ -0,0 +1,746 @@
+A non-reserved non-reserved
+ABORT non-reserved
+ABS reserved reserved
+ABSENT non-reserved non-reserved
+ABSOLUTE non-reserved non-reserved non-reserved reserved
+ACCESS non-reserved
+ACCORDING non-reserved non-reserved
+ACTION non-reserved non-reserved non-reserved reserved
+ADA non-reserved non-reserved non-reserved
+ADD non-reserved non-reserved non-reserved reserved
+ADMIN non-reserved non-reserved non-reserved
+AFTER non-reserved non-reserved non-reserved
+AGGREGATE non-reserved
+ALL reserved reserved reserved reserved
+ALLOCATE reserved reserved reserved
+ALSO non-reserved
+ALTER non-reserved reserved reserved reserved
+ALWAYS non-reserved non-reserved non-reserved
+ANALYSE reserved
+ANALYZE reserved
+AND reserved reserved reserved reserved
+ANY reserved reserved reserved reserved
+ARE reserved reserved reserved
+ARRAY reserved reserved reserved
+ARRAY_AGG reserved reserved
+ARRAY_MAX_CARDINALITY reserved
+AS reserved reserved reserved reserved
+ASC reserved non-reserved non-reserved reserved
+ASENSITIVE reserved reserved
+ASSERTION non-reserved non-reserved non-reserved reserved
+ASSIGNMENT non-reserved non-reserved non-reserved
+ASYMMETRIC reserved reserved reserved
+AT non-reserved reserved reserved reserved
+ATOMIC reserved reserved
+ATTRIBUTE non-reserved non-reserved non-reserved
+ATTRIBUTES non-reserved non-reserved
+AUTHORIZATION reserved (can be function or type) reserved reserved reserved
+AVG reserved reserved reserved
+BACKWARD non-reserved
+BASE64 non-reserved non-reserved
+BEFORE non-reserved non-reserved non-reserved
+BEGIN non-reserved reserved reserved reserved
+BEGIN_FRAME reserved
+BEGIN_PARTITION reserved
+BERNOULLI non-reserved non-reserved
+BETWEEN non-reserved (cannot be function or type) reserved reserved reserved
+BIGINT non-reserved (cannot be function or type) reserved reserved
+BINARY reserved (can be function or type) reserved reserved
+BIT non-reserved (cannot be function or type) reserved
+BIT_LENGTH reserved
+BLOB reserved reserved
+BLOCKED non-reserved non-reserved
+BOM non-reserved non-reserved
+BOOLEAN non-reserved (cannot be function or type) reserved reserved
+BOTH reserved reserved reserved reserved
+BREADTH non-reserved non-reserved
+BY non-reserved reserved reserved reserved
+C non-reserved non-reserved non-reserved
+CACHE non-reserved
+CALL reserved reserved
+CALLED non-reserved reserved reserved
+CARDINALITY reserved reserved
+CASCADE non-reserved non-reserved non-reserved reserved
+CASCADED non-reserved reserved reserved reserved
+CASE reserved reserved reserved reserved
+CAST reserved reserved reserved reserved
+CATALOG non-reserved non-reserved non-reserved reserved
+CATALOG_NAME non-reserved non-reserved non-reserved
+CEIL reserved reserved
+CEILING reserved reserved
+CHAIN non-reserved non-reserved non-reserved
+CHAR non-reserved (cannot be function or type) reserved reserved reserved
+CHARACTER non-reserved (cannot be function or type) reserved reserved reserved
+CHARACTERISTICS non-reserved non-reserved non-reserved
+CHARACTERS non-reserved non-reserved
+CHARACTER_LENGTH reserved reserved reserved
+CHARACTER_SET_CATALOG non-reserved non-reserved non-reserved
+CHARACTER_SET_NAME non-reserved non-reserved non-reserved
+CHARACTER_SET_SCHEMA non-reserved non-reserved non-reserved
+CHAR_LENGTH reserved reserved reserved
+CHECK reserved reserved reserved reserved
+CHECKPOINT non-reserved
+CLASS non-reserved
+CLASS_ORIGIN non-reserved non-reserved non-reserved
+CLOB reserved reserved
+CLOSE non-reserved reserved reserved reserved
+CLUSTER non-reserved
+COALESCE non-reserved (cannot be function or type) reserved reserved reserved
+COBOL non-reserved non-reserved non-reserved
+COLLATE reserved reserved reserved reserved
+COLLATION reserved (can be function or type) non-reserved non-reserved reserved
+COLLATION_CATALOG non-reserved non-reserved non-reserved
+COLLATION_NAME non-reserved non-reserved non-reserved
+COLLATION_SCHEMA non-reserved non-reserved non-reserved
+COLLECT reserved reserved
+COLUMN reserved reserved reserved reserved
+COLUMNS non-reserved non-reserved
+COLUMN_NAME non-reserved non-reserved non-reserved
+COMMAND_FUNCTION non-reserved non-reserved non-reserved
+COMMAND_FUNCTION_CODE non-reserved non-reserved
+COMMENT non-reserved
+COMMENTS non-reserved
+COMMIT non-reserved reserved reserved reserved
+COMMITTED non-reserved non-reserved non-reserved non-reserved
+CONCURRENTLY reserved (can be function or type)
+CONDITION reserved reserved
+CONDITION_NUMBER non-reserved non-reserved non-reserved
+CONFIGURATION non-reserved
+CONNECT reserved reserved reserved
+CONNECTION non-reserved non-reserved non-reserved reserved
+CONNECTION_NAME non-reserved non-reserved non-reserved
+CONSTRAINT reserved reserved reserved reserved
+CONSTRAINTS non-reserved non-reserved non-reserved reserved
+CONSTRAINT_CATALOG non-reserved non-reserved non-reserved
+CONSTRAINT_NAME non-reserved non-reserved non-reserved
+CONSTRAINT_SCHEMA non-reserved non-reserved non-reserved
+CONSTRUCTOR non-reserved non-reserved
+CONTAINS reserved non-reserved
+CONTENT non-reserved non-reserved non-reserved
+CONTINUE non-reserved non-reserved non-reserved reserved
+CONTROL non-reserved non-reserved
+CONVERSION non-reserved
+CONVERT reserved reserved reserved
+COPY non-reserved
+CORR reserved reserved
+CORRESPONDING reserved reserved reserved
+COST non-reserved
+COUNT reserved reserved reserved
+COVAR_POP reserved reserved
+COVAR_SAMP reserved reserved
+CREATE reserved reserved reserved reserved
+CROSS reserved (can be function or type) reserved reserved reserved
+CSV non-reserved
+CUBE reserved reserved
+CUME_DIST reserved reserved
+CURRENT non-reserved reserved reserved reserved
+CURRENT_CATALOG reserved reserved reserved
+CURRENT_DATE reserved reserved reserved reserved
+CURRENT_DEFAULT_TRANSFORM_GROUP reserved reserved
+CURRENT_PATH reserved reserved
+CURRENT_ROLE reserved reserved reserved
+CURRENT_ROW reserved
+CURRENT_SCHEMA reserved (can be function or type) reserved reserved
+CURRENT_TIME reserved reserved reserved reserved
+CURRENT_TIMESTAMP reserved reserved reserved reserved
+CURRENT_TRANSFORM_GROUP_FOR_TYPE reserved reserved
+CURRENT_USER reserved reserved reserved reserved
+CURSOR non-reserved reserved reserved reserved
+CURSOR_NAME non-reserved non-reserved non-reserved
+CYCLE non-reserved reserved reserved
+DATA non-reserved non-reserved non-reserved non-reserved
+DATABASE non-reserved
+DATALINK reserved reserved
+DATE reserved reserved reserved
+DATETIME_INTERVAL_CODE non-reserved non-reserved non-reserved
+DATETIME_INTERVAL_PRECISION non-reserved non-reserved non-reserved
+DAY non-reserved reserved reserved reserved
+DB non-reserved non-reserved
+DEALLOCATE non-reserved reserved reserved reserved
+DEC non-reserved (cannot be function or type) reserved reserved reserved
+DECIMAL non-reserved (cannot be function or type) reserved reserved reserved
+DECLARE non-reserved reserved reserved reserved
+DEFAULT reserved reserved reserved reserved
+DEFAULTS non-reserved non-reserved non-reserved
+DEFERRABLE reserved non-reserved non-reserved reserved
+DEFERRED non-reserved non-reserved non-reserved reserved
+DEFINED non-reserved non-reserved
+DEFINER non-reserved non-reserved non-reserved
+DEGREE non-reserved non-reserved
+DELETE non-reserved reserved reserved reserved
+DELIMITER non-reserved
+DELIMITERS non-reserved
+DENSE_RANK reserved reserved
+DEPTH non-reserved non-reserved
+DEREF reserved reserved
+DERIVED non-reserved non-reserved
+DESC reserved non-reserved non-reserved reserved
+DESCRIBE reserved reserved reserved
+DESCRIPTOR non-reserved non-reserved reserved
+DETERMINISTIC reserved reserved
+DIAGNOSTICS non-reserved non-reserved reserved
+DICTIONARY non-reserved
+DISABLE non-reserved
+DISCARD non-reserved
+DISCONNECT reserved reserved reserved
+DISPATCH non-reserved non-reserved
+DISTINCT reserved reserved reserved reserved
+DLNEWCOPY reserved reserved
+DLPREVIOUSCOPY reserved reserved
+DLURLCOMPLETE reserved reserved
+DLURLCOMPLETEONLY reserved reserved
+DLURLCOMPLETEWRITE reserved reserved
+DLURLPATH reserved reserved
+DLURLPATHONLY reserved reserved
+DLURLPATHWRITE reserved reserved
+DLURLSCHEME reserved reserved
+DLURLSERVER reserved reserved
+DLVALUE reserved reserved
+DO reserved
+DOCUMENT non-reserved non-reserved non-reserved
+DOMAIN non-reserved non-reserved non-reserved reserved
+DOUBLE non-reserved reserved reserved reserved
+DROP non-reserved reserved reserved reserved
+DYNAMIC reserved reserved
+DYNAMIC_FUNCTION non-reserved non-reserved non-reserved
+DYNAMIC_FUNCTION_CODE non-reserved non-reserved
+EACH non-reserved reserved reserved
+ELEMENT reserved reserved
+ELSE reserved reserved reserved reserved
+EMPTY non-reserved non-reserved
+ENABLE non-reserved
+ENCODING non-reserved non-reserved non-reserved
+ENCRYPTED non-reserved
+END reserved reserved reserved reserved
+END-EXEC reserved reserved reserved
+END_FRAME reserved
+END_PARTITION reserved
+ENFORCED non-reserved
+ENUM non-reserved
+EQUALS reserved non-reserved
+ESCAPE non-reserved reserved reserved reserved
+EVENT non-reserved
+EVERY reserved reserved
+EXCEPT reserved reserved reserved reserved
+EXCEPTION reserved
+EXCLUDE non-reserved non-reserved non-reserved
+EXCLUDING non-reserved non-reserved non-reserved
+EXCLUSIVE non-reserved
+EXEC reserved reserved reserved
+EXECUTE non-reserved reserved reserved reserved
+EXISTS non-reserved (cannot be function or type) reserved reserved reserved
+EXP reserved reserved
+EXPLAIN non-reserved
+EXPRESSION non-reserved
+EXTENSION non-reserved
+EXTERNAL non-reserved reserved reserved reserved
+EXTRACT non-reserved (cannot be function or type) reserved reserved reserved
+FALSE reserved reserved reserved reserved
+FAMILY non-reserved
+FETCH reserved reserved reserved reserved
+FILE non-reserved non-reserved
+FILTER reserved reserved
+FINAL non-reserved non-reserved
+FIRST non-reserved non-reserved non-reserved reserved
+FIRST_VALUE reserved reserved
+FLAG non-reserved non-reserved
+FLOAT non-reserved (cannot be function or type) reserved reserved reserved
+FLOOR reserved reserved
+FOLLOWING non-reserved non-reserved non-reserved
+FOR reserved reserved reserved reserved
+FORCE non-reserved
+FOREIGN reserved reserved reserved reserved
+FORTRAN non-reserved non-reserved non-reserved
+FORWARD non-reserved
+FOUND non-reserved non-reserved reserved
+FRAME_ROW reserved
+FREE reserved reserved
+FREEZE reserved (can be function or type)
+FROM reserved reserved reserved reserved
+FS non-reserved non-reserved
+FULL reserved (can be function or type) reserved reserved reserved
+FUNCTION non-reserved reserved reserved
+FUNCTIONS non-reserved
+FUSION reserved reserved
+G non-reserved non-reserved
+GENERAL non-reserved non-reserved
+GENERATED non-reserved non-reserved
+GET reserved reserved reserved
+GLOBAL non-reserved reserved reserved reserved
+GO non-reserved non-reserved reserved
+GOTO non-reserved non-reserved reserved
+GRANT reserved reserved reserved reserved
+GRANTED non-reserved non-reserved non-reserved
+GREATEST non-reserved (cannot be function or type)
+GROUP reserved reserved reserved reserved
+GROUPING reserved reserved
+GROUPS reserved
+HANDLER non-reserved
+HAVING reserved reserved reserved reserved
+HEADER non-reserved
+HEX non-reserved non-reserved
+HIERARCHY non-reserved non-reserved
+HOLD non-reserved reserved reserved
+HOUR non-reserved reserved reserved reserved
+ID non-reserved non-reserved
+IDENTITY non-reserved reserved reserved reserved
+IF non-reserved
+IGNORE non-reserved non-reserved
+ILIKE reserved (can be function or type)
+IMMEDIATE non-reserved non-reserved non-reserved reserved
+IMMEDIATELY non-reserved
+IMMUTABLE non-reserved
+IMPLEMENTATION non-reserved non-reserved
+IMPLICIT non-reserved
+IMPORT reserved reserved
+IN reserved reserved reserved reserved
+INCLUDING non-reserved non-reserved non-reserved
+INCREMENT non-reserved non-reserved non-reserved
+INDENT non-reserved non-reserved
+INDEX non-reserved
+INDEXES non-reserved
+INDICATOR reserved reserved reserved
+INHERIT non-reserved
+INHERITS non-reserved
+INITIALLY reserved non-reserved non-reserved reserved
+INLINE non-reserved
+INNER reserved (can be function or type) reserved reserved reserved
+INOUT non-reserved (cannot be function or type) reserved reserved
+INPUT non-reserved non-reserved non-reserved reserved
+INSENSITIVE non-reserved reserved reserved reserved
+INSERT non-reserved reserved reserved reserved
+INSTANCE non-reserved non-reserved
+INSTANTIABLE non-reserved non-reserved
+INSTEAD non-reserved non-reserved non-reserved
+INT non-reserved (cannot be function or type) reserved reserved reserved
+INTEGER non-reserved (cannot be function or type) reserved reserved reserved
+INTEGRITY non-reserved non-reserved
+INTERSECT reserved reserved reserved reserved
+INTERSECTION reserved reserved
+INTERVAL non-reserved (cannot be function or type) reserved reserved reserved
+INTO reserved reserved reserved reserved
+INVOKER non-reserved non-reserved non-reserved
+IS reserved (can be function or type) reserved reserved reserved
+ISNULL reserved (can be function or type)
+ISOLATION non-reserved non-reserved non-reserved reserved
+JOIN reserved (can be function or type) reserved reserved reserved
+K non-reserved non-reserved
+KEY non-reserved non-reserved non-reserved reserved
+KEY_MEMBER non-reserved non-reserved
+KEY_TYPE non-reserved non-reserved
+LABEL non-reserved
+LAG reserved reserved
+LANGUAGE non-reserved reserved reserved reserved
+LARGE non-reserved reserved reserved
+LAST non-reserved non-reserved non-reserved reserved
+LAST_VALUE reserved reserved
+LATERAL reserved reserved reserved
+LC_COLLATE non-reserved
+LC_CTYPE non-reserved
+LEAD reserved reserved
+LEADING reserved reserved reserved reserved
+LEAKPROOF non-reserved
+LEAST non-reserved (cannot be function or type)
+LEFT reserved (can be function or type) reserved reserved reserved
+LENGTH non-reserved non-reserved non-reserved
+LEVEL non-reserved non-reserved non-reserved reserved
+LIBRARY non-reserved non-reserved
+LIKE reserved (can be function or type) reserved reserved reserved
+LIKE_REGEX reserved reserved
+LIMIT reserved non-reserved non-reserved
+LINK non-reserved non-reserved
+LISTEN non-reserved
+LN reserved reserved
+LOAD non-reserved
+LOCAL non-reserved reserved reserved reserved
+LOCALTIME reserved reserved reserved
+LOCALTIMESTAMP reserved reserved reserved
+LOCATION non-reserved non-reserved non-reserved
+LOCATOR non-reserved non-reserved
+LOCK non-reserved
+LOWER reserved reserved reserved
+M non-reserved non-reserved
+MAP non-reserved non-reserved
+MAPPING non-reserved non-reserved non-reserved
+MATCH non-reserved reserved reserved reserved
+MATCHED non-reserved non-reserved
+MATERIALIZED non-reserved
+MAX reserved reserved reserved
+MAXVALUE non-reserved non-reserved non-reserved
+MAX_CARDINALITY reserved
+MEMBER reserved reserved
+MERGE reserved reserved
+MESSAGE_LENGTH non-reserved non-reserved non-reserved
+MESSAGE_OCTET_LENGTH non-reserved non-reserved non-reserved
+MESSAGE_TEXT non-reserved non-reserved non-reserved
+METHOD reserved reserved
+MIN reserved reserved reserved
+MINUTE non-reserved reserved reserved reserved
+MINVALUE non-reserved non-reserved non-reserved
+MOD reserved reserved
+MODE non-reserved
+MODIFIES reserved reserved
+MODULE reserved reserved reserved
+MONTH non-reserved reserved reserved reserved
+MORE non-reserved non-reserved non-reserved
+MOVE non-reserved
+MULTISET reserved reserved
+MUMPS non-reserved non-reserved non-reserved
+NAME non-reserved non-reserved non-reserved non-reserved
+NAMES non-reserved non-reserved non-reserved reserved
+NAMESPACE non-reserved non-reserved
+NATIONAL non-reserved (cannot be function or type) reserved reserved reserved
+NATURAL reserved (can be function or type) reserved reserved reserved
+NCHAR non-reserved (cannot be function or type) reserved reserved reserved
+NCLOB reserved reserved
+NESTING non-reserved non-reserved
+NEW reserved reserved
+NEXT non-reserved non-reserved non-reserved reserved
+NFC non-reserved non-reserved
+NFD non-reserved non-reserved
+NFKC non-reserved non-reserved
+NFKD non-reserved non-reserved
+NIL non-reserved non-reserved
+NO non-reserved reserved reserved reserved
+NONE non-reserved (cannot be function or type) reserved reserved
+NORMALIZE reserved reserved
+NORMALIZED non-reserved non-reserved
+NOT reserved reserved reserved reserved
+NOTHING non-reserved
+NOTIFY non-reserved
+NOTNULL reserved (can be function or type)
+NOWAIT non-reserved
+NTH_VALUE reserved reserved
+NTILE reserved reserved
+NULL reserved reserved reserved reserved
+NULLABLE non-reserved non-reserved non-reserved
+NULLIF non-reserved (cannot be function or type) reserved reserved reserved
+NULLS non-reserved non-reserved non-reserved
+NUMBER non-reserved non-reserved non-reserved
+NUMERIC non-reserved (cannot be function or type) reserved reserved reserved
+OBJECT non-reserved non-reserved non-reserved
+OCCURRENCES_REGEX reserved reserved
+OCTETS non-reserved non-reserved
+OCTET_LENGTH reserved reserved reserved
+OF non-reserved reserved reserved reserved
+OFF non-reserved non-reserved non-reserved
+OFFSET reserved reserved reserved
+OIDS non-reserved
+OLD reserved reserved
+ON reserved reserved reserved reserved
+ONLY reserved reserved reserved reserved
+OPEN reserved reserved reserved
+OPERATOR non-reserved
+OPTION non-reserved non-reserved non-reserved reserved
+OPTIONS non-reserved non-reserved non-reserved
+OR reserved reserved reserved reserved
+ORDER reserved reserved reserved reserved
+ORDERING non-reserved non-reserved
+ORDINALITY non-reserved non-reserved
+OTHERS non-reserved non-reserved
+OUT non-reserved (cannot be function or type) reserved reserved
+OUTER reserved (can be function or type) reserved reserved reserved
+OUTPUT non-reserved non-reserved reserved
+OVER reserved (can be function or type) reserved reserved
+OVERLAPS reserved (can be function or type) reserved reserved reserved
+OVERLAY non-reserved (cannot be function or type) reserved reserved
+OVERRIDING non-reserved non-reserved
+OWNED non-reserved
+OWNER non-reserved
+P non-reserved non-reserved
+PAD non-reserved non-reserved reserved
+PARAMETER reserved reserved
+PARAMETER_MODE non-reserved non-reserved
+PARAMETER_NAME non-reserved non-reserved
+PARAMETER_ORDINAL_POSITION non-reserved non-reserved
+PARAMETER_SPECIFIC_CATALOG non-reserved non-reserved
+PARAMETER_SPECIFIC_NAME non-reserved non-reserved
+PARAMETER_SPECIFIC_SCHEMA non-reserved non-reserved
+PARSER non-reserved
+PARTIAL non-reserved non-reserved non-reserved reserved
+PARTITION non-reserved reserved reserved
+PASCAL non-reserved non-reserved non-reserved
+PASSING non-reserved non-reserved non-reserved
+PASSTHROUGH non-reserved non-reserved
+PASSWORD non-reserved
+PATH non-reserved non-reserved
+PERCENT reserved
+PERCENTILE_CONT reserved reserved
+PERCENTILE_DISC reserved reserved
+PERCENT_RANK reserved reserved
+PERIOD reserved
+PERMISSION non-reserved non-reserved
+PLACING reserved non-reserved non-reserved
+PLANS non-reserved
+PLI non-reserved non-reserved non-reserved
+PORTION reserved
+POSITION non-reserved (cannot be function or type) reserved reserved reserved
+POSITION_REGEX reserved reserved
+POWER reserved reserved
+PRECEDES reserved
+PRECEDING non-reserved non-reserved non-reserved
+PRECISION non-reserved (cannot be function or type) reserved reserved reserved
+PREPARE non-reserved reserved reserved reserved
+PREPARED non-reserved
+PRESERVE non-reserved non-reserved non-reserved reserved
+PRIMARY reserved reserved reserved reserved
+PRIOR non-reserved non-reserved non-reserved reserved
+PRIVILEGES non-reserved non-reserved non-reserved reserved
+PROCEDURAL non-reserved
+PROCEDURE non-reserved reserved reserved reserved
+PROGRAM non-reserved
+PUBLIC non-reserved non-reserved reserved
+QUOTE non-reserved
+RANGE non-reserved reserved reserved
+RANK reserved reserved
+READ non-reserved non-reserved non-reserved reserved
+READS reserved reserved
+REAL non-reserved (cannot be function or type) reserved reserved reserved
+REASSIGN non-reserved
+RECHECK non-reserved
+RECOVERY non-reserved non-reserved
+RECURSIVE non-reserved reserved reserved
+REF non-reserved reserved reserved
+REFERENCES reserved reserved reserved reserved
+REFERENCING reserved reserved
+REFRESH non-reserved
+REGR_AVGX reserved reserved
+REGR_AVGY reserved reserved
+REGR_COUNT reserved reserved
+REGR_INTERCEPT reserved reserved
+REGR_R2 reserved reserved
+REGR_SLOPE reserved reserved
+REGR_SXX reserved reserved
+REGR_SXY reserved reserved
+REGR_SYY reserved reserved
+REINDEX non-reserved
+RELATIVE non-reserved non-reserved non-reserved reserved
+RELEASE non-reserved reserved reserved
+RENAME non-reserved
+REPEATABLE non-reserved non-reserved non-reserved non-reserved
+REPLACE non-reserved
+REPLICA non-reserved
+REQUIRING non-reserved non-reserved
+RESET non-reserved
+RESPECT non-reserved non-reserved
+RESTART non-reserved non-reserved non-reserved
+RESTORE non-reserved non-reserved
+RESTRICT non-reserved non-reserved non-reserved reserved
+RESULT reserved reserved
+RETURN reserved reserved
+RETURNED_CARDINALITY non-reserved non-reserved
+RETURNED_LENGTH non-reserved non-reserved non-reserved
+RETURNED_OCTET_LENGTH non-reserved non-reserved non-reserved
+RETURNED_SQLSTATE non-reserved non-reserved non-reserved
+RETURNING reserved non-reserved non-reserved
+RETURNS non-reserved reserved reserved
+REVOKE non-reserved reserved reserved reserved
+RIGHT reserved (can be function or type) reserved reserved reserved
+ROLE non-reserved non-reserved non-reserved
+ROLLBACK non-reserved reserved reserved reserved
+ROLLUP reserved reserved
+ROUTINE non-reserved non-reserved
+ROUTINE_CATALOG non-reserved non-reserved
+ROUTINE_NAME non-reserved non-reserved
+ROUTINE_SCHEMA non-reserved non-reserved
+ROW non-reserved (cannot be function or type) reserved reserved
+ROWS non-reserved reserved reserved reserved
+ROW_COUNT non-reserved non-reserved non-reserved
+ROW_NUMBER reserved reserved
+RULE non-reserved
+SAVEPOINT non-reserved reserved reserved
+SCALE non-reserved non-reserved non-reserved
+SCHEMA non-reserved non-reserved non-reserved reserved
+SCHEMA_NAME non-reserved non-reserved non-reserved
+SCOPE reserved reserved
+SCOPE_CATALOG non-reserved non-reserved
+SCOPE_NAME non-reserved non-reserved
+SCOPE_SCHEMA non-reserved non-reserved
+SCROLL non-reserved reserved reserved reserved
+SEARCH non-reserved reserved reserved
+SECOND non-reserved reserved reserved reserved
+SECTION non-reserved non-reserved reserved
+SECURITY non-reserved non-reserved non-reserved
+SELECT reserved reserved reserved reserved
+SELECTIVE non-reserved non-reserved
+SELF non-reserved non-reserved
+SENSITIVE reserved reserved
+SEQUENCE non-reserved non-reserved non-reserved
+SEQUENCES non-reserved
+SERIALIZABLE non-reserved non-reserved non-reserved non-reserved
+SERVER non-reserved non-reserved non-reserved
+SERVER_NAME non-reserved non-reserved non-reserved
+SESSION non-reserved non-reserved non-reserved reserved
+SESSION_USER reserved reserved reserved reserved
+SET non-reserved reserved reserved reserved
+SETOF non-reserved (cannot be function or type)
+SETS non-reserved non-reserved
+SHARE non-reserved
+SHOW non-reserved
+SIMILAR reserved (can be function or type) reserved reserved
+SIMPLE non-reserved non-reserved non-reserved
+SIZE non-reserved non-reserved reserved
+SMALLINT non-reserved (cannot be function or type) reserved reserved reserved
+SNAPSHOT non-reserved
+SOME reserved reserved reserved reserved
+SOURCE non-reserved non-reserved
+SPACE non-reserved non-reserved reserved
+SPECIFIC reserved reserved
+SPECIFICTYPE reserved reserved
+SPECIFIC_NAME non-reserved non-reserved
+SQL reserved reserved reserved
+SQLCODE reserved
+SQLERROR reserved
+SQLEXCEPTION reserved reserved
+SQLSTATE reserved reserved reserved
+SQLWARNING reserved reserved
+SQRT reserved reserved
+STABLE non-reserved
+STANDALONE non-reserved non-reserved non-reserved
+START non-reserved reserved reserved
+STATE non-reserved non-reserved
+STATEMENT non-reserved non-reserved non-reserved
+STATIC reserved reserved
+STATISTICS non-reserved
+STDDEV_POP reserved reserved
+STDDEV_SAMP reserved reserved
+STDIN non-reserved
+STDOUT non-reserved
+STORAGE non-reserved
+STRICT non-reserved
+STRIP non-reserved non-reserved non-reserved
+STRUCTURE non-reserved non-reserved
+STYLE non-reserved non-reserved
+SUBCLASS_ORIGIN non-reserved non-reserved non-reserved
+SUBMULTISET reserved reserved
+SUBSTRING non-reserved (cannot be function or type) reserved reserved reserved
+SUBSTRING_REGEX reserved reserved
+SUCCEEDS reserved
+SUM reserved reserved reserved
+SYMMETRIC reserved reserved reserved
+SYSID non-reserved
+SYSTEM non-reserved reserved reserved
+SYSTEM_TIME reserved
+SYSTEM_USER reserved reserved reserved
+T non-reserved non-reserved
+TABLE reserved reserved reserved reserved
+TABLES non-reserved
+TABLESAMPLE reserved reserved
+TABLESPACE non-reserved
+TABLE_NAME non-reserved non-reserved non-reserved
+TEMP non-reserved
+TEMPLATE non-reserved
+TEMPORARY non-reserved non-reserved non-reserved reserved
+TEXT non-reserved
+THEN reserved reserved reserved reserved
+TIES non-reserved non-reserved
+TIME non-reserved (cannot be function or type) reserved reserved reserved
+TIMESTAMP non-reserved (cannot be function or type) reserved reserved reserved
+TIMEZONE_HOUR reserved reserved reserved
+TIMEZONE_MINUTE reserved reserved reserved
+TO reserved reserved reserved reserved
+TOKEN non-reserved non-reserved
+TOP_LEVEL_COUNT non-reserved non-reserved
+TRAILING reserved reserved reserved reserved
+TRANSACTION non-reserved non-reserved non-reserved reserved
+TRANSACTIONS_COMMITTED non-reserved non-reserved
+TRANSACTIONS_ROLLED_BACK non-reserved non-reserved
+TRANSACTION_ACTIVE non-reserved non-reserved
+TRANSFORM non-reserved non-reserved
+TRANSFORMS non-reserved non-reserved
+TRANSLATE reserved reserved reserved
+TRANSLATE_REGEX reserved reserved
+TRANSLATION reserved reserved reserved
+TREAT non-reserved (cannot be function or type) reserved reserved
+TRIGGER non-reserved reserved reserved
+TRIGGER_CATALOG non-reserved non-reserved
+TRIGGER_NAME non-reserved non-reserved
+TRIGGER_SCHEMA non-reserved non-reserved
+TRIM non-reserved (cannot be function or type) reserved reserved reserved
+TRIM_ARRAY reserved reserved
+TRUE reserved reserved reserved reserved
+TRUNCATE non-reserved reserved reserved
+TRUSTED non-reserved
+TYPE non-reserved non-reserved non-reserved non-reserved
+TYPES non-reserved
+UESCAPE reserved reserved
+UNBOUNDED non-reserved non-reserved non-reserved
+UNCOMMITTED non-reserved non-reserved non-reserved non-reserved
+UNDER non-reserved non-reserved
+UNENCRYPTED non-reserved
+UNION reserved reserved reserved reserved
+UNIQUE reserved reserved reserved reserved
+UNKNOWN non-reserved reserved reserved reserved
+UNLINK non-reserved non-reserved
+UNLISTEN non-reserved
+UNLOGGED non-reserved
+UNNAMED non-reserved non-reserved non-reserved
+UNNEST reserved reserved
+UNTIL non-reserved
+UNTYPED non-reserved non-reserved
+UPDATE non-reserved reserved reserved reserved
+UPPER reserved reserved reserved
+URI non-reserved non-reserved
+USAGE non-reserved non-reserved reserved
+USER reserved reserved reserved reserved
+USER_DEFINED_TYPE_CATALOG non-reserved non-reserved
+USER_DEFINED_TYPE_CODE non-reserved non-reserved
+USER_DEFINED_TYPE_NAME non-reserved non-reserved
+USER_DEFINED_TYPE_SCHEMA non-reserved non-reserved
+USING reserved reserved reserved reserved
+VACUUM non-reserved
+VALID non-reserved non-reserved non-reserved
+VALIDATE non-reserved
+VALIDATOR non-reserved
+VALUE non-reserved reserved reserved reserved
+VALUES non-reserved (cannot be function or type) reserved reserved reserved
+VALUE_OF reserved
+VARBINARY reserved reserved
+VARCHAR non-reserved (cannot be function or type) reserved reserved reserved
+VARIADIC reserved
+VARYING non-reserved reserved reserved reserved
+VAR_POP reserved reserved
+VAR_SAMP reserved reserved
+VERBOSE reserved (can be function or type)
+VERSION non-reserved non-reserved non-reserved
+VERSIONING reserved
+VIEW non-reserved non-reserved non-reserved reserved
+VOLATILE non-reserved
+WHEN reserved reserved reserved reserved
+WHENEVER reserved reserved reserved
+WHERE reserved reserved reserved reserved
+WHITESPACE non-reserved non-reserved non-reserved
+WIDTH_BUCKET reserved reserved
+WINDOW reserved reserved reserved
+WITH reserved reserved reserved reserved
+WITHIN reserved reserved
+WITHOUT non-reserved reserved reserved
+WORK non-reserved non-reserved non-reserved reserved
+WRAPPER non-reserved non-reserved non-reserved
+WRITE non-reserved non-reserved non-reserved reserved
+XML non-reserved reserved reserved
+XMLAGG reserved reserved
+XMLATTRIBUTES non-reserved (cannot be function or type) reserved reserved
+XMLBINARY reserved reserved
+XMLCAST reserved reserved
+XMLCOMMENT reserved reserved
+XMLCONCAT non-reserved (cannot be function or type) reserved reserved
+XMLDECLARATION non-reserved non-reserved
+XMLDOCUMENT reserved reserved
+XMLELEMENT non-reserved (cannot be function or type) reserved reserved
+XMLEXISTS non-reserved (cannot be function or type) reserved reserved
+XMLFOREST non-reserved (cannot be function or type) reserved reserved
+XMLITERATE reserved reserved
+XMLNAMESPACES reserved reserved
+XMLPARSE non-reserved (cannot be function or type) reserved reserved
+XMLPI non-reserved (cannot be function or type) reserved reserved
+XMLQUERY reserved reserved
+XMLROOT non-reserved (cannot be function or type)
+XMLSCHEMA non-reserved non-reserved
+XMLSERIALIZE non-reserved (cannot be function or type) reserved reserved
+XMLTABLE reserved reserved
+XMLTEXT reserved reserved
+XMLVALIDATE reserved reserved
+YEAR non-reserved reserved reserved reserved
+YES non-reserved non-reserved non-reserved
+ZONE non-reserved non-reserved non-reserved reserved \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/processors.go b/vendor/xorm.io/xorm/processors.go
new file mode 100644
index 0000000000..dcd9c6ac0b
--- /dev/null
+++ b/vendor/xorm.io/xorm/processors.go
@@ -0,0 +1,78 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+// BeforeInsertProcessor executed before an object is initially persisted to the database
+type BeforeInsertProcessor interface {
+ BeforeInsert()
+}
+
+// BeforeUpdateProcessor executed before an object is updated
+type BeforeUpdateProcessor interface {
+ BeforeUpdate()
+}
+
+// BeforeDeleteProcessor executed before an object is deleted
+type BeforeDeleteProcessor interface {
+ BeforeDelete()
+}
+
+// BeforeSetProcessor executed before data set to the struct fields
+type BeforeSetProcessor interface {
+ BeforeSet(string, Cell)
+}
+
+// AfterSetProcessor executed after data set to the struct fields
+type AfterSetProcessor interface {
+ AfterSet(string, Cell)
+}
+
+// AfterInsertProcessor executed after an object is persisted to the database
+type AfterInsertProcessor interface {
+ AfterInsert()
+}
+
+// AfterUpdateProcessor executed after an object has been updated
+type AfterUpdateProcessor interface {
+ AfterUpdate()
+}
+
+// AfterDeleteProcessor executed after an object has been deleted
+type AfterDeleteProcessor interface {
+ AfterDelete()
+}
+
+// AfterLoadProcessor executed after an ojbect has been loaded from database
+type AfterLoadProcessor interface {
+ AfterLoad()
+}
+
+// AfterLoadSessionProcessor executed after an ojbect has been loaded from database with session parameter
+type AfterLoadSessionProcessor interface {
+ AfterLoad(*Session)
+}
+
+type executedProcessorFunc func(*Session, interface{}) error
+
+type executedProcessor struct {
+ fun executedProcessorFunc
+ session *Session
+ bean interface{}
+}
+
+func (executor *executedProcessor) execute() error {
+ return executor.fun(executor.session, executor.bean)
+}
+
+func (session *Session) executeProcessors() error {
+ processors := session.afterProcessors
+ session.afterProcessors = make([]executedProcessor, 0)
+ for _, processor := range processors {
+ if err := processor.execute(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/rows.go b/vendor/xorm.io/xorm/rows.go
new file mode 100644
index 0000000000..bdd44589f8
--- /dev/null
+++ b/vendor/xorm.io/xorm/rows.go
@@ -0,0 +1,121 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql"
+ "fmt"
+ "reflect"
+
+ "xorm.io/core"
+)
+
+// Rows rows wrapper a rows to
+type Rows struct {
+ session *Session
+ rows *core.Rows
+ beanType reflect.Type
+ lastError error
+}
+
+func newRows(session *Session, bean interface{}) (*Rows, error) {
+ rows := new(Rows)
+ rows.session = session
+ rows.beanType = reflect.Indirect(reflect.ValueOf(bean)).Type()
+
+ var sqlStr string
+ var args []interface{}
+ var err error
+
+ if err = rows.session.statement.setRefBean(bean); err != nil {
+ return nil, err
+ }
+
+ if len(session.statement.TableName()) <= 0 {
+ return nil, ErrTableNotFound
+ }
+
+ if rows.session.statement.RawSQL == "" {
+ sqlStr, args, err = rows.session.statement.genGetSQL(bean)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ sqlStr = rows.session.statement.RawSQL
+ args = rows.session.statement.RawParams
+ }
+
+ rows.rows, err = rows.session.queryRows(sqlStr, args...)
+ if err != nil {
+ rows.lastError = err
+ rows.Close()
+ return nil, err
+ }
+
+ return rows, nil
+}
+
+// Next move cursor to next record, return false if end has reached
+func (rows *Rows) Next() bool {
+ if rows.lastError == nil && rows.rows != nil {
+ hasNext := rows.rows.Next()
+ if !hasNext {
+ rows.lastError = sql.ErrNoRows
+ }
+ return hasNext
+ }
+ return false
+}
+
+// Err returns the error, if any, that was encountered during iteration. Err may be called after an explicit or implicit Close.
+func (rows *Rows) Err() error {
+ return rows.lastError
+}
+
+// Scan row record to bean properties
+func (rows *Rows) Scan(bean interface{}) error {
+ if rows.lastError != nil {
+ return rows.lastError
+ }
+
+ if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType {
+ return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType)
+ }
+
+ if err := rows.session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+
+ fields, err := rows.rows.Columns()
+ if err != nil {
+ return err
+ }
+
+ scanResults, err := rows.session.row2Slice(rows.rows, fields, bean)
+ if err != nil {
+ return err
+ }
+
+ dataStruct := rValue(bean)
+ _, err = rows.session.slice2Bean(scanResults, fields, bean, &dataStruct, rows.session.statement.RefTable)
+ if err != nil {
+ return err
+ }
+
+ return rows.session.executeProcessors()
+}
+
+// Close session if session.IsAutoClose is true, and claimed any opened resources
+func (rows *Rows) Close() error {
+ if rows.session.isAutoClose {
+ defer rows.session.Close()
+ }
+
+ if rows.rows != nil {
+ return rows.rows.Close()
+ }
+
+ return rows.lastError
+}
diff --git a/vendor/xorm.io/xorm/session.go b/vendor/xorm.io/xorm/session.go
new file mode 100644
index 0000000000..b33955fdce
--- /dev/null
+++ b/vendor/xorm.io/xorm/session.go
@@ -0,0 +1,866 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "context"
+ "database/sql"
+ "errors"
+ "fmt"
+ "hash/crc32"
+ "reflect"
+ "strings"
+ "time"
+
+ "xorm.io/core"
+)
+
+type sessionType int
+
+const (
+ engineSession sessionType = iota
+ groupSession
+)
+
+// Session keep a pointer to sql.DB and provides all execution of all
+// kind of database operations.
+type Session struct {
+ db *core.DB
+ engine *Engine
+ tx *core.Tx
+ statement Statement
+ isAutoCommit bool
+ isCommitedOrRollbacked bool
+ isAutoClose bool
+
+ // Automatically reset the statement after operations that execute a SQL
+ // query such as Count(), Find(), Get(), ...
+ autoResetStatement bool
+
+ // !nashtsai! storing these beans due to yet committed tx
+ afterInsertBeans map[interface{}]*[]func(interface{})
+ afterUpdateBeans map[interface{}]*[]func(interface{})
+ afterDeleteBeans map[interface{}]*[]func(interface{})
+ // --
+
+ beforeClosures []func(interface{})
+ afterClosures []func(interface{})
+
+ afterProcessors []executedProcessor
+
+ prepareStmt bool
+ stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr))
+
+ // !evalphobia! stored the last executed query on this session
+ //beforeSQLExec func(string, ...interface{})
+ lastSQL string
+ lastSQLArgs []interface{}
+
+ ctx context.Context
+ sessionType sessionType
+}
+
+// Clone copy all the session's content and return a new session
+func (session *Session) Clone() *Session {
+ var sess = *session
+ return &sess
+}
+
+// Init reset the session as the init status.
+func (session *Session) Init() {
+ session.statement.Init()
+ session.statement.Engine = session.engine
+ session.isAutoCommit = true
+ session.isCommitedOrRollbacked = false
+ session.isAutoClose = false
+ session.autoResetStatement = true
+ session.prepareStmt = false
+
+ // !nashtsai! is lazy init better?
+ session.afterInsertBeans = make(map[interface{}]*[]func(interface{}), 0)
+ session.afterUpdateBeans = make(map[interface{}]*[]func(interface{}), 0)
+ session.afterDeleteBeans = make(map[interface{}]*[]func(interface{}), 0)
+ session.beforeClosures = make([]func(interface{}), 0)
+ session.afterClosures = make([]func(interface{}), 0)
+ session.stmtCache = make(map[uint32]*core.Stmt)
+
+ session.afterProcessors = make([]executedProcessor, 0)
+
+ session.lastSQL = ""
+ session.lastSQLArgs = []interface{}{}
+
+ session.ctx = session.engine.defaultContext
+}
+
+// Close release the connection from pool
+func (session *Session) Close() {
+ for _, v := range session.stmtCache {
+ v.Close()
+ }
+
+ if session.db != nil {
+ // When Close be called, if session is a transaction and do not call
+ // Commit or Rollback, then call Rollback.
+ if session.tx != nil && !session.isCommitedOrRollbacked {
+ session.Rollback()
+ }
+ session.tx = nil
+ session.stmtCache = nil
+ session.db = nil
+ }
+}
+
+// ContextCache enable context cache or not
+func (session *Session) ContextCache(context ContextCache) *Session {
+ session.statement.context = context
+ return session
+}
+
+// IsClosed returns if session is closed
+func (session *Session) IsClosed() bool {
+ return session.db == nil
+}
+
+func (session *Session) resetStatement() {
+ if session.autoResetStatement {
+ session.statement.Init()
+ }
+}
+
+// Prepare set a flag to session that should be prepare statement before execute query
+func (session *Session) Prepare() *Session {
+ session.prepareStmt = true
+ return session
+}
+
+// Before Apply before Processor, affected bean is passed to closure arg
+func (session *Session) Before(closures func(interface{})) *Session {
+ if closures != nil {
+ session.beforeClosures = append(session.beforeClosures, closures)
+ }
+ return session
+}
+
+// After Apply after Processor, affected bean is passed to closure arg
+func (session *Session) After(closures func(interface{})) *Session {
+ if closures != nil {
+ session.afterClosures = append(session.afterClosures, closures)
+ }
+ return session
+}
+
+// Table can input a string or pointer to struct for special a table to operate.
+func (session *Session) Table(tableNameOrBean interface{}) *Session {
+ session.statement.Table(tableNameOrBean)
+ return session
+}
+
+// Alias set the table alias
+func (session *Session) Alias(alias string) *Session {
+ session.statement.Alias(alias)
+ return session
+}
+
+// NoCascade indicate that no cascade load child object
+func (session *Session) NoCascade() *Session {
+ session.statement.UseCascade = false
+ return session
+}
+
+// ForUpdate Set Read/Write locking for UPDATE
+func (session *Session) ForUpdate() *Session {
+ session.statement.IsForUpdate = true
+ return session
+}
+
+// NoAutoCondition disable generate SQL condition from beans
+func (session *Session) NoAutoCondition(no ...bool) *Session {
+ session.statement.NoAutoCondition(no...)
+ return session
+}
+
+// Limit provide limit and offset query condition
+func (session *Session) Limit(limit int, start ...int) *Session {
+ session.statement.Limit(limit, start...)
+ return session
+}
+
+// OrderBy provide order by query condition, the input parameter is the content
+// after order by on a sql statement.
+func (session *Session) OrderBy(order string) *Session {
+ session.statement.OrderBy(order)
+ return session
+}
+
+// Desc provide desc order by query condition, the input parameters are columns.
+func (session *Session) Desc(colNames ...string) *Session {
+ session.statement.Desc(colNames...)
+ return session
+}
+
+// Asc provide asc order by query condition, the input parameters are columns.
+func (session *Session) Asc(colNames ...string) *Session {
+ session.statement.Asc(colNames...)
+ return session
+}
+
+// StoreEngine is only avialble mysql dialect currently
+func (session *Session) StoreEngine(storeEngine string) *Session {
+ session.statement.StoreEngine = storeEngine
+ return session
+}
+
+// Charset is only avialble mysql dialect currently
+func (session *Session) Charset(charset string) *Session {
+ session.statement.Charset = charset
+ return session
+}
+
+// Cascade indicates if loading sub Struct
+func (session *Session) Cascade(trueOrFalse ...bool) *Session {
+ if len(trueOrFalse) >= 1 {
+ session.statement.UseCascade = trueOrFalse[0]
+ }
+ return session
+}
+
+// NoCache ask this session do not retrieve data from cache system and
+// get data from database directly.
+func (session *Session) NoCache() *Session {
+ session.statement.UseCache = false
+ return session
+}
+
+// Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
+func (session *Session) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
+ session.statement.Join(joinOperator, tablename, condition, args...)
+ return session
+}
+
+// GroupBy Generate Group By statement
+func (session *Session) GroupBy(keys string) *Session {
+ session.statement.GroupBy(keys)
+ return session
+}
+
+// Having Generate Having statement
+func (session *Session) Having(conditions string) *Session {
+ session.statement.Having(conditions)
+ return session
+}
+
+// DB db return the wrapper of sql.DB
+func (session *Session) DB() *core.DB {
+ if session.db == nil {
+ session.db = session.engine.db
+ session.stmtCache = make(map[uint32]*core.Stmt, 0)
+ }
+ return session.db
+}
+
+func cleanupProcessorsClosures(slices *[]func(interface{})) {
+ if len(*slices) > 0 {
+ *slices = make([]func(interface{}), 0)
+ }
+}
+
+func (session *Session) canCache() bool {
+ if session.statement.RefTable == nil ||
+ session.statement.JoinStr != "" ||
+ session.statement.RawSQL != "" ||
+ !session.statement.UseCache ||
+ session.statement.IsForUpdate ||
+ session.tx != nil ||
+ len(session.statement.selectStr) > 0 {
+ return false
+ }
+ return true
+}
+
+func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, err error) {
+ crc := crc32.ChecksumIEEE([]byte(sqlStr))
+ // TODO try hash(sqlStr+len(sqlStr))
+ var has bool
+ stmt, has = session.stmtCache[crc]
+ if !has {
+ stmt, err = db.PrepareContext(session.ctx, sqlStr)
+ if err != nil {
+ return nil, err
+ }
+ session.stmtCache[crc] = stmt
+ }
+ return
+}
+
+func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) (*reflect.Value, error) {
+ var col *core.Column
+ if col = table.GetColumnIdx(key, idx); col == nil {
+ return nil, ErrFieldIsNotExist{key, table.Name}
+ }
+
+ fieldValue, err := col.ValueOfV(dataStruct)
+ if err != nil {
+ return nil, err
+ }
+
+ if !fieldValue.IsValid() || !fieldValue.CanSet() {
+ return nil, ErrFieldIsNotValid{key, table.Name}
+ }
+
+ return fieldValue, nil
+}
+
+// Cell cell is a result of one column field
+type Cell *interface{}
+
+func (session *Session) rows2Beans(rows *core.Rows, fields []string,
+ table *core.Table, newElemFunc func([]string) reflect.Value,
+ sliceValueSetFunc func(*reflect.Value, core.PK) error) error {
+ for rows.Next() {
+ var newValue = newElemFunc(fields)
+ bean := newValue.Interface()
+ dataStruct := newValue.Elem()
+
+ // handle beforeClosures
+ scanResults, err := session.row2Slice(rows, fields, bean)
+ if err != nil {
+ return err
+ }
+ pk, err := session.slice2Bean(scanResults, fields, bean, &dataStruct, table)
+ if err != nil {
+ return err
+ }
+ session.afterProcessors = append(session.afterProcessors, executedProcessor{
+ fun: func(*Session, interface{}) error {
+ return sliceValueSetFunc(&newValue, pk)
+ },
+ session: session,
+ bean: bean,
+ })
+ }
+ return nil
+}
+
+func (session *Session) row2Slice(rows *core.Rows, fields []string, bean interface{}) ([]interface{}, error) {
+ for _, closure := range session.beforeClosures {
+ closure(bean)
+ }
+
+ scanResults := make([]interface{}, len(fields))
+ for i := 0; i < len(fields); i++ {
+ var cell interface{}
+ scanResults[i] = &cell
+ }
+ if err := rows.Scan(scanResults...); err != nil {
+ return nil, err
+ }
+
+ if b, hasBeforeSet := bean.(BeforeSetProcessor); hasBeforeSet {
+ for ii, key := range fields {
+ b.BeforeSet(key, Cell(scanResults[ii].(*interface{})))
+ }
+ }
+ return scanResults, nil
+}
+
+func (session *Session) slice2Bean(scanResults []interface{}, fields []string, bean interface{}, dataStruct *reflect.Value, table *core.Table) (core.PK, error) {
+ defer func() {
+ if b, hasAfterSet := bean.(AfterSetProcessor); hasAfterSet {
+ for ii, key := range fields {
+ b.AfterSet(key, Cell(scanResults[ii].(*interface{})))
+ }
+ }
+ }()
+
+ // handle afterClosures
+ for _, closure := range session.afterClosures {
+ session.afterProcessors = append(session.afterProcessors, executedProcessor{
+ fun: func(sess *Session, bean interface{}) error {
+ closure(bean)
+ return nil
+ },
+ session: session,
+ bean: bean,
+ })
+ }
+
+ if a, has := bean.(AfterLoadProcessor); has {
+ session.afterProcessors = append(session.afterProcessors, executedProcessor{
+ fun: func(sess *Session, bean interface{}) error {
+ a.AfterLoad()
+ return nil
+ },
+ session: session,
+ bean: bean,
+ })
+ }
+
+ if a, has := bean.(AfterLoadSessionProcessor); has {
+ session.afterProcessors = append(session.afterProcessors, executedProcessor{
+ fun: func(sess *Session, bean interface{}) error {
+ a.AfterLoad(sess)
+ return nil
+ },
+ session: session,
+ bean: bean,
+ })
+ }
+
+ var tempMap = make(map[string]int)
+ var pk core.PK
+ for ii, key := range fields {
+ var idx int
+ var ok bool
+ var lKey = strings.ToLower(key)
+ if idx, ok = tempMap[lKey]; !ok {
+ idx = 0
+ } else {
+ idx = idx + 1
+ }
+ tempMap[lKey] = idx
+
+ fieldValue, err := session.getField(dataStruct, key, table, idx)
+ if err != nil {
+ if !strings.Contains(err.Error(), "is not valid") {
+ session.engine.logger.Warn(err)
+ }
+ continue
+ }
+ if fieldValue == nil {
+ continue
+ }
+ rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii]))
+
+ // if row is null then ignore
+ if rawValue.Interface() == nil {
+ continue
+ }
+
+ if fieldValue.CanAddr() {
+ if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ if data, err := value2Bytes(&rawValue); err == nil {
+ if err := structConvert.FromDB(data); err != nil {
+ return nil, err
+ }
+ } else {
+ return nil, err
+ }
+ continue
+ }
+ }
+
+ if _, ok := fieldValue.Interface().(core.Conversion); ok {
+ if data, err := value2Bytes(&rawValue); err == nil {
+ if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
+ fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
+ }
+ fieldValue.Interface().(core.Conversion).FromDB(data)
+ } else {
+ return nil, err
+ }
+ continue
+ }
+
+ rawValueType := reflect.TypeOf(rawValue.Interface())
+ vv := reflect.ValueOf(rawValue.Interface())
+ col := table.GetColumnIdx(key, idx)
+ if col.IsPrimaryKey {
+ pk = append(pk, rawValue.Interface())
+ }
+ fieldType := fieldValue.Type()
+ hasAssigned := false
+
+ if col.SQLType.IsJson() {
+ var bs []byte
+ if rawValueType.Kind() == reflect.String {
+ bs = []byte(vv.String())
+ } else if rawValueType.ConvertibleTo(core.BytesType) {
+ bs = vv.Bytes()
+ } else {
+ return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind())
+ }
+
+ hasAssigned = true
+
+ if len(bs) > 0 {
+ if fieldType.Kind() == reflect.String {
+ fieldValue.SetString(string(bs))
+ continue
+ }
+ if fieldValue.CanAddr() {
+ err := DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface())
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ x := reflect.New(fieldType)
+ err := DefaultJSONHandler.Unmarshal(bs, x.Interface())
+ if err != nil {
+ return nil, err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ }
+
+ continue
+ }
+
+ switch fieldType.Kind() {
+ case reflect.Complex64, reflect.Complex128:
+ // TODO: reimplement this
+ var bs []byte
+ if rawValueType.Kind() == reflect.String {
+ bs = []byte(vv.String())
+ } else if rawValueType.ConvertibleTo(core.BytesType) {
+ bs = vv.Bytes()
+ }
+
+ hasAssigned = true
+ if len(bs) > 0 {
+ if fieldValue.CanAddr() {
+ err := DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface())
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ x := reflect.New(fieldType)
+ err := DefaultJSONHandler.Unmarshal(bs, x.Interface())
+ if err != nil {
+ return nil, err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ }
+ case reflect.Slice, reflect.Array:
+ switch rawValueType.Kind() {
+ case reflect.Slice, reflect.Array:
+ switch rawValueType.Elem().Kind() {
+ case reflect.Uint8:
+ if fieldType.Elem().Kind() == reflect.Uint8 {
+ hasAssigned = true
+ if col.SQLType.IsText() {
+ x := reflect.New(fieldType)
+ err := DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface())
+ if err != nil {
+ return nil, err
+ }
+ fieldValue.Set(x.Elem())
+ } else {
+ if fieldValue.Len() > 0 {
+ for i := 0; i < fieldValue.Len(); i++ {
+ if i < vv.Len() {
+ fieldValue.Index(i).Set(vv.Index(i))
+ }
+ }
+ } else {
+ for i := 0; i < vv.Len(); i++ {
+ fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i)))
+ }
+ }
+ }
+ }
+ }
+ }
+ case reflect.String:
+ if rawValueType.Kind() == reflect.String {
+ hasAssigned = true
+ fieldValue.SetString(vv.String())
+ }
+ case reflect.Bool:
+ if rawValueType.Kind() == reflect.Bool {
+ hasAssigned = true
+ fieldValue.SetBool(vv.Bool())
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ switch rawValueType.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ hasAssigned = true
+ fieldValue.SetInt(vv.Int())
+ }
+ case reflect.Float32, reflect.Float64:
+ switch rawValueType.Kind() {
+ case reflect.Float32, reflect.Float64:
+ hasAssigned = true
+ fieldValue.SetFloat(vv.Float())
+ }
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ switch rawValueType.Kind() {
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ hasAssigned = true
+ fieldValue.SetUint(vv.Uint())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ hasAssigned = true
+ fieldValue.SetUint(uint64(vv.Int()))
+ }
+ case reflect.Struct:
+ if fieldType.ConvertibleTo(core.TimeType) {
+ dbTZ := session.engine.DatabaseTZ
+ if col.TimeZone != nil {
+ dbTZ = col.TimeZone
+ }
+
+ if rawValueType == core.TimeType {
+ hasAssigned = true
+
+ t := vv.Convert(core.TimeType).Interface().(time.Time)
+
+ z, _ := t.Zone()
+ // set new location if database don't save timezone or give an incorrect timezone
+ if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location
+ session.engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
+ t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
+ t.Minute(), t.Second(), t.Nanosecond(), dbTZ)
+ }
+
+ t = t.In(session.engine.TZLocation)
+ fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
+ } else if rawValueType == core.IntType || rawValueType == core.Int64Type ||
+ rawValueType == core.Int32Type {
+ hasAssigned = true
+
+ t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation)
+ fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
+ } else {
+ if d, ok := vv.Interface().([]uint8); ok {
+ hasAssigned = true
+ t, err := session.byte2Time(col, d)
+ if err != nil {
+ session.engine.logger.Error("byte2Time error:", err.Error())
+ hasAssigned = false
+ } else {
+ fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
+ }
+ } else if d, ok := vv.Interface().(string); ok {
+ hasAssigned = true
+ t, err := session.str2Time(col, d)
+ if err != nil {
+ session.engine.logger.Error("byte2Time error:", err.Error())
+ hasAssigned = false
+ } else {
+ fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
+ }
+ } else {
+ return nil, fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface())
+ }
+ }
+ } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
+ // !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
+ hasAssigned = true
+ if err := nulVal.Scan(vv.Interface()); err != nil {
+ session.engine.logger.Error("sql.Sanner error:", err.Error())
+ hasAssigned = false
+ }
+ } else if col.SQLType.IsJson() {
+ if rawValueType.Kind() == reflect.String {
+ hasAssigned = true
+ x := reflect.New(fieldType)
+ if len([]byte(vv.String())) > 0 {
+ err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), x.Interface())
+ if err != nil {
+ return nil, err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ } else if rawValueType.Kind() == reflect.Slice {
+ hasAssigned = true
+ x := reflect.New(fieldType)
+ if len(vv.Bytes()) > 0 {
+ err := DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface())
+ if err != nil {
+ return nil, err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ }
+ } else if session.statement.UseCascade {
+ table, err := session.engine.autoMapType(*fieldValue)
+ if err != nil {
+ return nil, err
+ }
+
+ hasAssigned = true
+ if len(table.PrimaryKeys) != 1 {
+ return nil, errors.New("unsupported non or composited primary key cascade")
+ }
+ var pk = make(core.PK, len(table.PrimaryKeys))
+ pk[0], err = asKind(vv, rawValueType)
+ if err != nil {
+ return nil, err
+ }
+
+ if !isPKZero(pk) {
+ // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+ // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+ // property to be fetched lazily
+ structInter := reflect.New(fieldValue.Type())
+ has, err := session.ID(pk).NoCascade().get(structInter.Interface())
+ if err != nil {
+ return nil, err
+ }
+ if has {
+ fieldValue.Set(structInter.Elem())
+ } else {
+ return nil, errors.New("cascade obj is not exist")
+ }
+ }
+ }
+ case reflect.Ptr:
+ // !nashtsai! TODO merge duplicated codes above
+ switch fieldType {
+ // following types case matching ptr's native type, therefore assign ptr directly
+ case core.PtrStringType:
+ if rawValueType.Kind() == reflect.String {
+ x := vv.String()
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrBoolType:
+ if rawValueType.Kind() == reflect.Bool {
+ x := vv.Bool()
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrTimeType:
+ if rawValueType == core.PtrTimeType {
+ hasAssigned = true
+ var x = rawValue.Interface().(time.Time)
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrFloat64Type:
+ if rawValueType.Kind() == reflect.Float64 {
+ x := vv.Float()
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrUint64Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint64(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrInt64Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ x := vv.Int()
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrFloat32Type:
+ if rawValueType.Kind() == reflect.Float64 {
+ var x = float32(vv.Float())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrIntType:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = int(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrInt32Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = int32(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrInt8Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = int8(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrInt16Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = int16(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrUintType:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.PtrUint32Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint32(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.Uint8Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint8(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.Uint16Type:
+ if rawValueType.Kind() == reflect.Int64 {
+ var x = uint16(vv.Int())
+ hasAssigned = true
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ case core.Complex64Type:
+ var x complex64
+ if len([]byte(vv.String())) > 0 {
+ err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x)
+ if err != nil {
+ return nil, err
+ }
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ hasAssigned = true
+ case core.Complex128Type:
+ var x complex128
+ if len([]byte(vv.String())) > 0 {
+ err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x)
+ if err != nil {
+ return nil, err
+ }
+ fieldValue.Set(reflect.ValueOf(&x))
+ }
+ hasAssigned = true
+ } // switch fieldType
+ } // switch fieldType.Kind()
+
+ // !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value
+ if !hasAssigned {
+ data, err := value2Bytes(&rawValue)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = session.bytes2Value(col, fieldValue, data); err != nil {
+ return nil, err
+ }
+ }
+ }
+ return pk, nil
+}
+
+// saveLastSQL stores executed query information
+func (session *Session) saveLastSQL(sql string, args ...interface{}) {
+ session.lastSQL = sql
+ session.lastSQLArgs = args
+ session.engine.logSQL(sql, args...)
+}
+
+// LastSQL returns last query information
+func (session *Session) LastSQL() (string, []interface{}) {
+ return session.lastSQL, session.lastSQLArgs
+}
+
+// Unscoped always disable struct tag "deleted"
+func (session *Session) Unscoped() *Session {
+ session.statement.Unscoped()
+ return session
+}
+
+func (session *Session) incrVersionFieldValue(fieldValue *reflect.Value) {
+ switch fieldValue.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ fieldValue.SetInt(fieldValue.Int() + 1)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ fieldValue.SetUint(fieldValue.Uint() + 1)
+ }
+}
diff --git a/vendor/xorm.io/xorm/session_cols.go b/vendor/xorm.io/xorm/session_cols.go
new file mode 100644
index 0000000000..1558074f3d
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_cols.go
@@ -0,0 +1,156 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "reflect"
+ "strings"
+ "time"
+
+ "xorm.io/core"
+)
+
+func setColumnInt(bean interface{}, col *core.Column, t int64) {
+ v, err := col.ValueOf(bean)
+ if err != nil {
+ return
+ }
+ if v.CanSet() {
+ switch v.Type().Kind() {
+ case reflect.Int, reflect.Int64, reflect.Int32:
+ v.SetInt(t)
+ case reflect.Uint, reflect.Uint64, reflect.Uint32:
+ v.SetUint(uint64(t))
+ }
+ }
+}
+
+func setColumnTime(bean interface{}, col *core.Column, t time.Time) {
+ v, err := col.ValueOf(bean)
+ if err != nil {
+ return
+ }
+ if v.CanSet() {
+ switch v.Type().Kind() {
+ case reflect.Struct:
+ v.Set(reflect.ValueOf(t).Convert(v.Type()))
+ case reflect.Int, reflect.Int64, reflect.Int32:
+ v.SetInt(t.Unix())
+ case reflect.Uint, reflect.Uint64, reflect.Uint32:
+ v.SetUint(uint64(t.Unix()))
+ }
+ }
+}
+
+func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) {
+ if len(m) == 0 {
+ return false, false
+ }
+
+ n := len(col.Name)
+
+ for mk := range m {
+ if len(mk) != n {
+ continue
+ }
+ if strings.EqualFold(mk, col.Name) {
+ return m[mk], true
+ }
+ }
+
+ return false, false
+}
+
+func col2NewCols(columns ...string) []string {
+ newColumns := make([]string, 0, len(columns))
+ for _, col := range columns {
+ col = strings.Replace(col, "`", "", -1)
+ col = strings.Replace(col, `"`, "", -1)
+ ccols := strings.Split(col, ",")
+ for _, c := range ccols {
+ newColumns = append(newColumns, strings.TrimSpace(c))
+ }
+ }
+ return newColumns
+}
+
+// Incr provides a query string like "count = count + 1"
+func (session *Session) Incr(column string, arg ...interface{}) *Session {
+ session.statement.Incr(column, arg...)
+ return session
+}
+
+// Decr provides a query string like "count = count - 1"
+func (session *Session) Decr(column string, arg ...interface{}) *Session {
+ session.statement.Decr(column, arg...)
+ return session
+}
+
+// SetExpr provides a query string like "column = {expression}"
+func (session *Session) SetExpr(column string, expression interface{}) *Session {
+ session.statement.SetExpr(column, expression)
+ return session
+}
+
+// Select provides some columns to special
+func (session *Session) Select(str string) *Session {
+ session.statement.Select(str)
+ return session
+}
+
+// Cols provides some columns to special
+func (session *Session) Cols(columns ...string) *Session {
+ session.statement.Cols(columns...)
+ return session
+}
+
+// AllCols ask all columns
+func (session *Session) AllCols() *Session {
+ session.statement.AllCols()
+ return session
+}
+
+// MustCols specify some columns must use even if they are empty
+func (session *Session) MustCols(columns ...string) *Session {
+ session.statement.MustCols(columns...)
+ return session
+}
+
+// UseBool automatically retrieve condition according struct, but
+// if struct has bool field, it will ignore them. So use UseBool
+// to tell system to do not ignore them.
+// If no parameters, it will use all the bool field of struct, or
+// it will use parameters's columns
+func (session *Session) UseBool(columns ...string) *Session {
+ session.statement.UseBool(columns...)
+ return session
+}
+
+// Distinct use for distinct columns. Caution: when you are using cache,
+// distinct will not be cached because cache system need id,
+// but distinct will not provide id
+func (session *Session) Distinct(columns ...string) *Session {
+ session.statement.Distinct(columns...)
+ return session
+}
+
+// Omit Only not use the parameters as select or update columns
+func (session *Session) Omit(columns ...string) *Session {
+ session.statement.Omit(columns...)
+ return session
+}
+
+// Nullable Set null when column is zero-value and nullable for update
+func (session *Session) Nullable(columns ...string) *Session {
+ session.statement.Nullable(columns...)
+ return session
+}
+
+// NoAutoTime means do not automatically give created field and updated field
+// the current time on the current session temporarily
+func (session *Session) NoAutoTime() *Session {
+ session.statement.UseAutoTime = false
+ return session
+}
diff --git a/vendor/xorm.io/xorm/session_cond.go b/vendor/xorm.io/xorm/session_cond.go
new file mode 100644
index 0000000000..b16bdea8e0
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_cond.go
@@ -0,0 +1,70 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import "xorm.io/builder"
+
+// Sql provides raw sql input parameter. When you have a complex SQL statement
+// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
+//
+// Deprecated: use SQL instead.
+func (session *Session) Sql(query string, args ...interface{}) *Session {
+ return session.SQL(query, args...)
+}
+
+// SQL provides raw sql input parameter. When you have a complex SQL statement
+// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
+func (session *Session) SQL(query interface{}, args ...interface{}) *Session {
+ session.statement.SQL(query, args...)
+ return session
+}
+
+// Where provides custom query condition.
+func (session *Session) Where(query interface{}, args ...interface{}) *Session {
+ session.statement.Where(query, args...)
+ return session
+}
+
+// And provides custom query condition.
+func (session *Session) And(query interface{}, args ...interface{}) *Session {
+ session.statement.And(query, args...)
+ return session
+}
+
+// Or provides custom query condition.
+func (session *Session) Or(query interface{}, args ...interface{}) *Session {
+ session.statement.Or(query, args...)
+ return session
+}
+
+// Id provides converting id as a query condition
+//
+// Deprecated: use ID instead
+func (session *Session) Id(id interface{}) *Session {
+ return session.ID(id)
+}
+
+// ID provides converting id as a query condition
+func (session *Session) ID(id interface{}) *Session {
+ session.statement.ID(id)
+ return session
+}
+
+// In provides a query string like "id in (1, 2, 3)"
+func (session *Session) In(column string, args ...interface{}) *Session {
+ session.statement.In(column, args...)
+ return session
+}
+
+// NotIn provides a query string like "id in (1, 2, 3)"
+func (session *Session) NotIn(column string, args ...interface{}) *Session {
+ session.statement.NotIn(column, args...)
+ return session
+}
+
+// Conds returns session query conditions except auto bean conditions
+func (session *Session) Conds() builder.Cond {
+ return session.statement.cond
+}
diff --git a/vendor/xorm.io/xorm/session_context.go b/vendor/xorm.io/xorm/session_context.go
new file mode 100644
index 0000000000..915f056858
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_context.go
@@ -0,0 +1,23 @@
+// Copyright 2019 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import "context"
+
+// Context sets the context on this session
+func (session *Session) Context(ctx context.Context) *Session {
+ session.ctx = ctx
+ return session
+}
+
+// PingContext test if database is ok
+func (session *Session) PingContext(ctx context.Context) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName())
+ return session.DB().PingContext(ctx)
+}
diff --git a/vendor/xorm.io/xorm/session_convert.go b/vendor/xorm.io/xorm/session_convert.go
new file mode 100644
index 0000000000..7f11354d5e
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_convert.go
@@ -0,0 +1,671 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "xorm.io/core"
+)
+
+func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) {
+ sdata := strings.TrimSpace(data)
+ var x time.Time
+ var err error
+
+ var parseLoc = session.engine.DatabaseTZ
+ if col.TimeZone != nil {
+ parseLoc = col.TimeZone
+ }
+
+ if sdata == zeroTime0 || sdata == zeroTime1 {
+ } else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column
+ // time stamp
+ sd, err := strconv.ParseInt(sdata, 10, 64)
+ if err == nil {
+ x = time.Unix(sd, 0)
+ //session.engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ } else {
+ //session.engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ }
+ } else if len(sdata) > 19 && strings.Contains(sdata, "-") {
+ x, err = time.ParseInLocation(time.RFC3339Nano, sdata, parseLoc)
+ session.engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ if err != nil {
+ x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, parseLoc)
+ //session.engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ }
+ if err != nil {
+ x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, parseLoc)
+ //session.engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ }
+ } else if len(sdata) == 19 && strings.Contains(sdata, "-") {
+ x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, parseLoc)
+ //session.engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ } else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
+ x, err = time.ParseInLocation("2006-01-02", sdata, parseLoc)
+ //session.engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ } else if col.SQLType.Name == core.Time {
+ if strings.Contains(sdata, " ") {
+ ssd := strings.Split(sdata, " ")
+ sdata = ssd[1]
+ }
+
+ sdata = strings.TrimSpace(sdata)
+ if session.engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 {
+ sdata = sdata[len(sdata)-8:]
+ }
+
+ st := fmt.Sprintf("2006-01-02 %v", sdata)
+ x, err = time.ParseInLocation("2006-01-02 15:04:05", st, parseLoc)
+ //session.engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+ } else {
+ outErr = fmt.Errorf("unsupported time format %v", sdata)
+ return
+ }
+ if err != nil {
+ outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err)
+ return
+ }
+ outTime = x.In(session.engine.TZLocation)
+ return
+}
+
+func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) {
+ return session.str2Time(col, string(data))
+}
+
+var (
+ nullFloatType = reflect.TypeOf(sql.NullFloat64{})
+)
+
+// convert a db data([]byte) to a field value
+func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error {
+ if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ return structConvert.FromDB(data)
+ }
+
+ if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
+ return structConvert.FromDB(data)
+ }
+
+ var v interface{}
+ key := col.Name
+ fieldType := fieldValue.Type()
+
+ switch fieldType.Kind() {
+ case reflect.Complex64, reflect.Complex128:
+ x := reflect.New(fieldType)
+ if len(data) > 0 {
+ err := DefaultJSONHandler.Unmarshal(data, x.Interface())
+ if err != nil {
+ session.engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ case reflect.Slice, reflect.Array, reflect.Map:
+ v = data
+ t := fieldType.Elem()
+ k := t.Kind()
+ if col.SQLType.IsText() {
+ x := reflect.New(fieldType)
+ if len(data) > 0 {
+ err := DefaultJSONHandler.Unmarshal(data, x.Interface())
+ if err != nil {
+ session.engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ } else if col.SQLType.IsBlob() {
+ if k == reflect.Uint8 {
+ fieldValue.Set(reflect.ValueOf(v))
+ } else {
+ x := reflect.New(fieldType)
+ if len(data) > 0 {
+ err := DefaultJSONHandler.Unmarshal(data, x.Interface())
+ if err != nil {
+ session.engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(x.Elem())
+ }
+ }
+ } else {
+ return ErrUnSupportedType
+ }
+ case reflect.String:
+ fieldValue.SetString(string(data))
+ case reflect.Bool:
+ v, err := asBool(data)
+ if err != nil {
+ return fmt.Errorf("arg %v as bool: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(v))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ sdata := string(data)
+ var x int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ session.engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
+ if len(data) == 1 {
+ x = int64(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x, err = strconv.ParseInt(sdata, 16, 64)
+ } else if strings.HasPrefix(sdata, "0") {
+ x, err = strconv.ParseInt(sdata, 8, 64)
+ } else if strings.EqualFold(sdata, "true") {
+ x = 1
+ } else if strings.EqualFold(sdata, "false") {
+ x = 0
+ } else {
+ x, err = strconv.ParseInt(sdata, 10, 64)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.SetInt(x)
+ case reflect.Float32, reflect.Float64:
+ x, err := strconv.ParseFloat(string(data), 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as float64: %s", key, err.Error())
+ }
+ fieldValue.SetFloat(x)
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ x, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.SetUint(x)
+ //Currently only support Time type
+ case reflect.Struct:
+ // !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
+ if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
+ if err := nulVal.Scan(data); err != nil {
+ return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error())
+ }
+ } else {
+ if fieldType.ConvertibleTo(core.TimeType) {
+ x, err := session.byte2Time(col, data)
+ if err != nil {
+ return err
+ }
+ v = x
+ fieldValue.Set(reflect.ValueOf(v).Convert(fieldType))
+ } else if session.statement.UseCascade {
+ table, err := session.engine.autoMapType(*fieldValue)
+ if err != nil {
+ return err
+ }
+
+ // TODO: current only support 1 primary key
+ if len(table.PrimaryKeys) > 1 {
+ return errors.New("unsupported composited primary key cascade")
+ }
+
+ var pk = make(core.PK, len(table.PrimaryKeys))
+ rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
+ pk[0], err = str2PK(string(data), rawValueType)
+ if err != nil {
+ return err
+ }
+
+ if !isPKZero(pk) {
+ // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+ // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+ // property to be fetched lazily
+ structInter := reflect.New(fieldValue.Type())
+ has, err := session.ID(pk).NoCascade().get(structInter.Interface())
+ if err != nil {
+ return err
+ }
+ if has {
+ v = structInter.Elem().Interface()
+ fieldValue.Set(reflect.ValueOf(v))
+ } else {
+ return errors.New("cascade obj is not exist")
+ }
+ }
+ }
+ }
+ case reflect.Ptr:
+ // !nashtsai! TODO merge duplicated codes above
+ //typeStr := fieldType.String()
+ switch fieldType.Elem().Kind() {
+ // case "*string":
+ case core.StringType.Kind():
+ x := string(data)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*bool":
+ case core.BoolType.Kind():
+ d := string(data)
+ v, err := strconv.ParseBool(d)
+ if err != nil {
+ return fmt.Errorf("arg %v as bool: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&v).Convert(fieldType))
+ // case "*complex64":
+ case core.Complex64Type.Kind():
+ var x complex64
+ if len(data) > 0 {
+ err := DefaultJSONHandler.Unmarshal(data, &x)
+ if err != nil {
+ session.engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ }
+ // case "*complex128":
+ case core.Complex128Type.Kind():
+ var x complex128
+ if len(data) > 0 {
+ err := DefaultJSONHandler.Unmarshal(data, &x)
+ if err != nil {
+ session.engine.logger.Error(err)
+ return err
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ }
+ // case "*float64":
+ case core.Float64Type.Kind():
+ x, err := strconv.ParseFloat(string(data), 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as float64: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*float32":
+ case core.Float32Type.Kind():
+ var x float32
+ x1, err := strconv.ParseFloat(string(data), 32)
+ if err != nil {
+ return fmt.Errorf("arg %v as float32: %s", key, err.Error())
+ }
+ x = float32(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint64":
+ case core.Uint64Type.Kind():
+ var x uint64
+ x, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint":
+ case core.UintType.Kind():
+ var x uint
+ x1, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ x = uint(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint32":
+ case core.Uint32Type.Kind():
+ var x uint32
+ x1, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ x = uint32(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint8":
+ case core.Uint8Type.Kind():
+ var x uint8
+ x1, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ x = uint8(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*uint16":
+ case core.Uint16Type.Kind():
+ var x uint16
+ x1, err := strconv.ParseUint(string(data), 10, 64)
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ x = uint16(x1)
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int64":
+ case core.Int64Type.Kind():
+ sdata := string(data)
+ var x int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ strings.Contains(session.engine.DriverName(), "mysql") {
+ if len(data) == 1 {
+ x = int64(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x, err = strconv.ParseInt(sdata, 16, 64)
+ } else if strings.HasPrefix(sdata, "0") {
+ x, err = strconv.ParseInt(sdata, 8, 64)
+ } else {
+ x, err = strconv.ParseInt(sdata, 10, 64)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int":
+ case core.IntType.Kind():
+ sdata := string(data)
+ var x int
+ var x1 int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ strings.Contains(session.engine.DriverName(), "mysql") {
+ if len(data) == 1 {
+ x = int(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x1, err = strconv.ParseInt(sdata, 16, 64)
+ x = int(x1)
+ } else if strings.HasPrefix(sdata, "0") {
+ x1, err = strconv.ParseInt(sdata, 8, 64)
+ x = int(x1)
+ } else {
+ x1, err = strconv.ParseInt(sdata, 10, 64)
+ x = int(x1)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int32":
+ case core.Int32Type.Kind():
+ sdata := string(data)
+ var x int32
+ var x1 int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ session.engine.dialect.DBType() == core.MYSQL {
+ if len(data) == 1 {
+ x = int32(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x1, err = strconv.ParseInt(sdata, 16, 64)
+ x = int32(x1)
+ } else if strings.HasPrefix(sdata, "0") {
+ x1, err = strconv.ParseInt(sdata, 8, 64)
+ x = int32(x1)
+ } else {
+ x1, err = strconv.ParseInt(sdata, 10, 64)
+ x = int32(x1)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int8":
+ case core.Int8Type.Kind():
+ sdata := string(data)
+ var x int8
+ var x1 int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ strings.Contains(session.engine.DriverName(), "mysql") {
+ if len(data) == 1 {
+ x = int8(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x1, err = strconv.ParseInt(sdata, 16, 64)
+ x = int8(x1)
+ } else if strings.HasPrefix(sdata, "0") {
+ x1, err = strconv.ParseInt(sdata, 8, 64)
+ x = int8(x1)
+ } else {
+ x1, err = strconv.ParseInt(sdata, 10, 64)
+ x = int8(x1)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*int16":
+ case core.Int16Type.Kind():
+ sdata := string(data)
+ var x int16
+ var x1 int64
+ var err error
+ // for mysql, when use bit, it returned \x01
+ if col.SQLType.Name == core.Bit &&
+ strings.Contains(session.engine.DriverName(), "mysql") {
+ if len(data) == 1 {
+ x = int16(data[0])
+ } else {
+ x = 0
+ }
+ } else if strings.HasPrefix(sdata, "0x") {
+ x1, err = strconv.ParseInt(sdata, 16, 64)
+ x = int16(x1)
+ } else if strings.HasPrefix(sdata, "0") {
+ x1, err = strconv.ParseInt(sdata, 8, 64)
+ x = int16(x1)
+ } else {
+ x1, err = strconv.ParseInt(sdata, 10, 64)
+ x = int16(x1)
+ }
+ if err != nil {
+ return fmt.Errorf("arg %v as int: %s", key, err.Error())
+ }
+ fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+ // case "*SomeStruct":
+ case reflect.Struct:
+ switch fieldType {
+ // case "*.time.Time":
+ case core.PtrTimeType:
+ x, err := session.byte2Time(col, data)
+ if err != nil {
+ return err
+ }
+ v = x
+ fieldValue.Set(reflect.ValueOf(&x))
+ default:
+ if session.statement.UseCascade {
+ structInter := reflect.New(fieldType.Elem())
+ table, err := session.engine.autoMapType(structInter.Elem())
+ if err != nil {
+ return err
+ }
+
+ if len(table.PrimaryKeys) > 1 {
+ return errors.New("unsupported composited primary key cascade")
+ }
+
+ var pk = make(core.PK, len(table.PrimaryKeys))
+ rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
+ pk[0], err = str2PK(string(data), rawValueType)
+ if err != nil {
+ return err
+ }
+
+ if !isPKZero(pk) {
+ // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+ // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+ // property to be fetched lazily
+ has, err := session.ID(pk).NoCascade().get(structInter.Interface())
+ if err != nil {
+ return err
+ }
+ if has {
+ v = structInter.Interface()
+ fieldValue.Set(reflect.ValueOf(v))
+ } else {
+ return errors.New("cascade obj is not exist")
+ }
+ }
+ } else {
+ return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String())
+ }
+ }
+ default:
+ return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
+ }
+ default:
+ return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
+ }
+
+ return nil
+}
+
+// convert a field value of a struct to interface for put into db
+func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) {
+ if fieldValue.CanAddr() {
+ if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ data, err := fieldConvert.ToDB()
+ if err != nil {
+ return 0, err
+ }
+ if col.SQLType.IsBlob() {
+ return data, nil
+ }
+ return string(data), nil
+ }
+ }
+
+ if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok {
+ data, err := fieldConvert.ToDB()
+ if err != nil {
+ return 0, err
+ }
+ if col.SQLType.IsBlob() {
+ return data, nil
+ }
+ return string(data), nil
+ }
+
+ fieldType := fieldValue.Type()
+ k := fieldType.Kind()
+ if k == reflect.Ptr {
+ if fieldValue.IsNil() {
+ return nil, nil
+ } else if !fieldValue.IsValid() {
+ session.engine.logger.Warn("the field[", col.FieldName, "] is invalid")
+ return nil, nil
+ } else {
+ // !nashtsai! deference pointer type to instance type
+ fieldValue = fieldValue.Elem()
+ fieldType = fieldValue.Type()
+ k = fieldType.Kind()
+ }
+ }
+
+ switch k {
+ case reflect.Bool:
+ return fieldValue.Bool(), nil
+ case reflect.String:
+ return fieldValue.String(), nil
+ case reflect.Struct:
+ if fieldType.ConvertibleTo(core.TimeType) {
+ t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+ tf := session.engine.formatColTime(col, t)
+ return tf, nil
+ } else if fieldType.ConvertibleTo(nullFloatType) {
+ t := fieldValue.Convert(nullFloatType).Interface().(sql.NullFloat64)
+ if !t.Valid {
+ return nil, nil
+ }
+ return t.Float64, nil
+ }
+
+ if !col.SQLType.IsJson() {
+ // !<winxxp>! 增加支持driver.Valuer接口的结构,如sql.NullString
+ if v, ok := fieldValue.Interface().(driver.Valuer); ok {
+ return v.Value()
+ }
+
+ fieldTable, err := session.engine.autoMapType(fieldValue)
+ if err != nil {
+ return nil, err
+ }
+ if len(fieldTable.PrimaryKeys) == 1 {
+ pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName)
+ return pkField.Interface(), nil
+ }
+ return 0, fmt.Errorf("no primary key for col %v", col.Name)
+ }
+
+ if col.SQLType.IsText() {
+ bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.engine.logger.Error(err)
+ return 0, err
+ }
+ return string(bytes), nil
+ } else if col.SQLType.IsBlob() {
+ bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.engine.logger.Error(err)
+ return 0, err
+ }
+ return bytes, nil
+ }
+ return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type())
+ case reflect.Complex64, reflect.Complex128:
+ bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.engine.logger.Error(err)
+ return 0, err
+ }
+ return string(bytes), nil
+ case reflect.Array, reflect.Slice, reflect.Map:
+ if !fieldValue.IsValid() {
+ return fieldValue.Interface(), nil
+ }
+
+ if col.SQLType.IsText() {
+ bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.engine.logger.Error(err)
+ return 0, err
+ }
+ return string(bytes), nil
+ } else if col.SQLType.IsBlob() {
+ var bytes []byte
+ var err error
+ if (k == reflect.Slice) &&
+ (fieldValue.Type().Elem().Kind() == reflect.Uint8) {
+ bytes = fieldValue.Bytes()
+ } else {
+ bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ session.engine.logger.Error(err)
+ return 0, err
+ }
+ }
+ return bytes, nil
+ }
+ return nil, ErrUnSupportedType
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return int64(fieldValue.Uint()), nil
+ default:
+ return fieldValue.Interface(), nil
+ }
+}
diff --git a/vendor/xorm.io/xorm/session_delete.go b/vendor/xorm.io/xorm/session_delete.go
new file mode 100644
index 0000000000..675d4d8c7d
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_delete.go
@@ -0,0 +1,244 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+
+ "xorm.io/core"
+)
+
+func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, args ...interface{}) error {
+ if table == nil ||
+ session.tx != nil {
+ return ErrCacheFailed
+ }
+
+ for _, filter := range session.engine.dialect.Filters() {
+ sqlStr = filter.Do(sqlStr, session.engine.dialect, table)
+ }
+
+ newsql := session.statement.convertIDSQL(sqlStr)
+ if newsql == "" {
+ return ErrCacheFailed
+ }
+
+ cacher := session.engine.getCacher(tableName)
+ pkColumns := table.PKColumns()
+ ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
+ if err != nil {
+ resultsSlice, err := session.queryBytes(newsql, args...)
+ if err != nil {
+ return err
+ }
+ ids = make([]core.PK, 0)
+ if len(resultsSlice) > 0 {
+ for _, data := range resultsSlice {
+ var id int64
+ var pk core.PK = make([]interface{}, 0)
+ for _, col := range pkColumns {
+ if v, ok := data[col.Name]; !ok {
+ return errors.New("no id")
+ } else if col.SQLType.IsText() {
+ pk = append(pk, string(v))
+ } else if col.SQLType.IsNumeric() {
+ id, err = strconv.ParseInt(string(v), 10, 64)
+ if err != nil {
+ return err
+ }
+ pk = append(pk, id)
+ } else {
+ return errors.New("not supported primary key type")
+ }
+ }
+ ids = append(ids, pk)
+ }
+ }
+ }
+
+ for _, id := range ids {
+ session.engine.logger.Debug("[cacheDelete] delete cache obj:", tableName, id)
+ sid, err := id.ToString()
+ if err != nil {
+ return err
+ }
+ cacher.DelBean(tableName, sid)
+ }
+ session.engine.logger.Debug("[cacheDelete] clear cache table:", tableName)
+ cacher.ClearIds(tableName)
+ return nil
+}
+
+// Delete records, bean's non-empty fields are conditions
+func (session *Session) Delete(bean interface{}) (int64, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ if session.statement.lastError != nil {
+ return 0, session.statement.lastError
+ }
+
+ if err := session.statement.setRefBean(bean); err != nil {
+ return 0, err
+ }
+
+ // handle before delete processors
+ for _, closure := range session.beforeClosures {
+ closure(bean)
+ }
+ cleanupProcessorsClosures(&session.beforeClosures)
+
+ if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok {
+ processor.BeforeDelete()
+ }
+
+ condSQL, condArgs, err := session.statement.genConds(bean)
+ if err != nil {
+ return 0, err
+ }
+ if len(condSQL) == 0 && session.statement.LimitN == 0 {
+ return 0, ErrNeedDeletedCond
+ }
+
+ var tableNameNoQuote = session.statement.TableName()
+ var tableName = session.engine.Quote(tableNameNoQuote)
+ var table = session.statement.RefTable
+ var deleteSQL string
+ if len(condSQL) > 0 {
+ deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL)
+ } else {
+ deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName)
+ }
+
+ var orderSQL string
+ if len(session.statement.OrderStr) > 0 {
+ orderSQL += fmt.Sprintf(" ORDER BY %s", session.statement.OrderStr)
+ }
+ if session.statement.LimitN > 0 {
+ orderSQL += fmt.Sprintf(" LIMIT %d", session.statement.LimitN)
+ }
+
+ if len(orderSQL) > 0 {
+ switch session.engine.dialect.DBType() {
+ case core.POSTGRES:
+ inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
+ if len(condSQL) > 0 {
+ deleteSQL += " AND " + inSQL
+ } else {
+ deleteSQL += " WHERE " + inSQL
+ }
+ case core.SQLITE:
+ inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
+ if len(condSQL) > 0 {
+ deleteSQL += " AND " + inSQL
+ } else {
+ deleteSQL += " WHERE " + inSQL
+ }
+ // TODO: how to handle delete limit on mssql?
+ case core.MSSQL:
+ return 0, ErrNotImplemented
+ default:
+ deleteSQL += orderSQL
+ }
+ }
+
+ var realSQL string
+ argsForCache := make([]interface{}, 0, len(condArgs)*2)
+ if session.statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled
+ realSQL = deleteSQL
+ copy(argsForCache, condArgs)
+ argsForCache = append(condArgs, argsForCache...)
+ } else {
+ // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache.
+ copy(argsForCache, condArgs)
+ argsForCache = append(condArgs, argsForCache...)
+
+ deletedColumn := table.DeletedColumn()
+ realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v",
+ session.engine.Quote(session.statement.TableName()),
+ session.engine.Quote(deletedColumn.Name),
+ condSQL)
+
+ if len(orderSQL) > 0 {
+ switch session.engine.dialect.DBType() {
+ case core.POSTGRES:
+ inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
+ if len(condSQL) > 0 {
+ realSQL += " AND " + inSQL
+ } else {
+ realSQL += " WHERE " + inSQL
+ }
+ case core.SQLITE:
+ inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
+ if len(condSQL) > 0 {
+ realSQL += " AND " + inSQL
+ } else {
+ realSQL += " WHERE " + inSQL
+ }
+ // TODO: how to handle delete limit on mssql?
+ case core.MSSQL:
+ return 0, ErrNotImplemented
+ default:
+ realSQL += orderSQL
+ }
+ }
+
+ // !oinume! Insert nowTime to the head of session.statement.Params
+ condArgs = append(condArgs, "")
+ paramsLen := len(condArgs)
+ copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
+
+ val, t := session.engine.nowTime(deletedColumn)
+ condArgs[0] = val
+
+ var colName = deletedColumn.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ }
+
+ if cacher := session.engine.getCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache {
+ session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...)
+ }
+
+ session.statement.RefTable = table
+ res, err := session.exec(realSQL, condArgs...)
+ if err != nil {
+ return 0, err
+ }
+
+ // handle after delete processors
+ if session.isAutoCommit {
+ for _, closure := range session.afterClosures {
+ closure(bean)
+ }
+ if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok {
+ processor.AfterDelete()
+ }
+ } else {
+ lenAfterClosures := len(session.afterClosures)
+ if lenAfterClosures > 0 {
+ if value, has := session.afterDeleteBeans[bean]; has && value != nil {
+ *value = append(*value, session.afterClosures...)
+ } else {
+ afterClosures := make([]func(interface{}), lenAfterClosures)
+ copy(afterClosures, session.afterClosures)
+ session.afterDeleteBeans[bean] = &afterClosures
+ }
+ } else {
+ if _, ok := interface{}(bean).(AfterDeleteProcessor); ok {
+ session.afterDeleteBeans[bean] = nil
+ }
+ }
+ }
+ cleanupProcessorsClosures(&session.afterClosures)
+ // --
+
+ return res.RowsAffected()
+}
diff --git a/vendor/xorm.io/xorm/session_exist.go b/vendor/xorm.io/xorm/session_exist.go
new file mode 100644
index 0000000000..660cc47e42
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_exist.go
@@ -0,0 +1,96 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+// Exist returns true if the record exist otherwise return false
+func (session *Session) Exist(bean ...interface{}) (bool, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ if session.statement.lastError != nil {
+ return false, session.statement.lastError
+ }
+
+ var sqlStr string
+ var args []interface{}
+ var err error
+
+ if session.statement.RawSQL == "" {
+ if len(bean) == 0 {
+ tableName := session.statement.TableName()
+ if len(tableName) <= 0 {
+ return false, ErrTableNotFound
+ }
+
+ tableName = session.statement.Engine.Quote(tableName)
+
+ if session.statement.cond.IsValid() {
+ condSQL, condArgs, err := builder.ToSQL(session.statement.cond)
+ if err != nil {
+ return false, err
+ }
+
+ if session.engine.dialect.DBType() == core.MSSQL {
+ sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s WHERE %s", tableName, condSQL)
+ } else if session.engine.dialect.DBType() == core.ORACLE {
+ sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE (%s) AND ROWNUM=1", tableName, condSQL)
+ } else {
+ sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL)
+ }
+ args = condArgs
+ } else {
+ if session.engine.dialect.DBType() == core.MSSQL {
+ sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s", tableName)
+ } else if session.engine.dialect.DBType() == core.ORACLE {
+ sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE ROWNUM=1", tableName)
+ } else {
+ sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName)
+ }
+ args = []interface{}{}
+ }
+ } else {
+ beanValue := reflect.ValueOf(bean[0])
+ if beanValue.Kind() != reflect.Ptr {
+ return false, errors.New("needs a pointer")
+ }
+
+ if beanValue.Elem().Kind() == reflect.Struct {
+ if err := session.statement.setRefBean(bean[0]); err != nil {
+ return false, err
+ }
+ }
+
+ if len(session.statement.TableName()) <= 0 {
+ return false, ErrTableNotFound
+ }
+ session.statement.Limit(1)
+ sqlStr, args, err = session.statement.genGetSQL(bean[0])
+ if err != nil {
+ return false, err
+ }
+ }
+ } else {
+ sqlStr = session.statement.RawSQL
+ args = session.statement.RawParams
+ }
+
+ rows, err := session.queryRows(sqlStr, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ return rows.Next(), nil
+}
diff --git a/vendor/xorm.io/xorm/session_find.go b/vendor/xorm.io/xorm/session_find.go
new file mode 100644
index 0000000000..e16ae54c94
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_find.go
@@ -0,0 +1,505 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+const (
+ tpStruct = iota
+ tpNonStruct
+)
+
+// Find retrieve records from table, condiBeans's non-empty fields
+// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
+// map[int64]*Struct
+func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+ return session.find(rowsSlicePtr, condiBean...)
+}
+
+// FindAndCount find the results and also return the counts
+func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...interface{}) (int64, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ session.autoResetStatement = false
+ err := session.find(rowsSlicePtr, condiBean...)
+ if err != nil {
+ return 0, err
+ }
+
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+ if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map {
+ return 0, errors.New("needs a pointer to a slice or a map")
+ }
+
+ sliceElementType := sliceValue.Type().Elem()
+ if sliceElementType.Kind() == reflect.Ptr {
+ sliceElementType = sliceElementType.Elem()
+ }
+ session.autoResetStatement = true
+
+ if session.statement.selectStr != "" {
+ session.statement.selectStr = ""
+ }
+ if session.statement.OrderStr != "" {
+ session.statement.OrderStr = ""
+ }
+
+ return session.Count(reflect.New(sliceElementType).Interface())
+}
+
+func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error {
+ defer session.resetStatement()
+
+ if session.statement.lastError != nil {
+ return session.statement.lastError
+ }
+
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+ if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map {
+ return errors.New("needs a pointer to a slice or a map")
+ }
+
+ sliceElementType := sliceValue.Type().Elem()
+
+ var tp = tpStruct
+ if session.statement.RefTable == nil {
+ if sliceElementType.Kind() == reflect.Ptr {
+ if sliceElementType.Elem().Kind() == reflect.Struct {
+ pv := reflect.New(sliceElementType.Elem())
+ if err := session.statement.setRefValue(pv); err != nil {
+ return err
+ }
+ } else {
+ tp = tpNonStruct
+ }
+ } else if sliceElementType.Kind() == reflect.Struct {
+ pv := reflect.New(sliceElementType)
+ if err := session.statement.setRefValue(pv); err != nil {
+ return err
+ }
+ } else {
+ tp = tpNonStruct
+ }
+ }
+
+ var table = session.statement.RefTable
+
+ var addedTableName = (len(session.statement.JoinStr) > 0)
+ var autoCond builder.Cond
+ if tp == tpStruct {
+ if !session.statement.noAutoCondition && len(condiBean) > 0 {
+ var err error
+ autoCond, err = session.statement.buildConds(table, condiBean[0], true, true, false, true, addedTableName)
+ if err != nil {
+ return err
+ }
+ } else {
+ // !oinume! Add "<col> IS NULL" to WHERE whatever condiBean is given.
+ // See https://gitea.com/xorm/xorm/issues/179
+ if col := table.DeletedColumn(); col != nil && !session.statement.unscoped { // tag "deleted" is enabled
+ var colName = session.engine.Quote(col.Name)
+ if addedTableName {
+ var nm = session.statement.TableName()
+ if len(session.statement.TableAlias) > 0 {
+ nm = session.statement.TableAlias
+ }
+ colName = session.engine.Quote(nm) + "." + colName
+ }
+
+ autoCond = session.engine.CondDeleted(colName)
+ }
+ }
+ }
+
+ var sqlStr string
+ var args []interface{}
+ var err error
+ if session.statement.RawSQL == "" {
+ if len(session.statement.TableName()) <= 0 {
+ return ErrTableNotFound
+ }
+
+ var columnStr = session.statement.ColumnStr
+ if len(session.statement.selectStr) > 0 {
+ columnStr = session.statement.selectStr
+ } else {
+ if session.statement.JoinStr == "" {
+ if columnStr == "" {
+ if session.statement.GroupByStr != "" {
+ columnStr = session.engine.quoteColumns(session.statement.GroupByStr)
+ } else {
+ columnStr = session.statement.genColumnStr()
+ }
+ }
+ } else {
+ if columnStr == "" {
+ if session.statement.GroupByStr != "" {
+ columnStr = session.engine.quoteColumns(session.statement.GroupByStr)
+ } else {
+ columnStr = "*"
+ }
+ }
+ }
+ if columnStr == "" {
+ columnStr = "*"
+ }
+ }
+
+ session.statement.cond = session.statement.cond.And(autoCond)
+ condSQL, condArgs, err := builder.ToSQL(session.statement.cond)
+ if err != nil {
+ return err
+ }
+
+ args = append(session.statement.joinArgs, condArgs...)
+ sqlStr, err = session.statement.genSelectSQL(columnStr, condSQL, true, true)
+ if err != nil {
+ return err
+ }
+ // for mssql and use limit
+ qs := strings.Count(sqlStr, "?")
+ if len(args)*2 == qs {
+ args = append(args, args...)
+ }
+ } else {
+ sqlStr = session.statement.RawSQL
+ args = session.statement.RawParams
+ }
+
+ if session.canCache() {
+ if cacher := session.engine.getCacher(session.statement.TableName()); cacher != nil &&
+ !session.statement.IsDistinct &&
+ !session.statement.unscoped {
+ err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...)
+ if err != ErrCacheFailed {
+ return err
+ }
+ err = nil // !nashtsai! reset err to nil for ErrCacheFailed
+ session.engine.logger.Warn("Cache Find Failed")
+ }
+ }
+
+ return session.noCacheFind(table, sliceValue, sqlStr, args...)
+}
+
+func (session *Session) noCacheFind(table *core.Table, containerValue reflect.Value, sqlStr string, args ...interface{}) error {
+ rows, err := session.queryRows(sqlStr, args...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ fields, err := rows.Columns()
+ if err != nil {
+ return err
+ }
+
+ var newElemFunc func(fields []string) reflect.Value
+ elemType := containerValue.Type().Elem()
+ var isPointer bool
+ if elemType.Kind() == reflect.Ptr {
+ isPointer = true
+ elemType = elemType.Elem()
+ }
+ if elemType.Kind() == reflect.Ptr {
+ return errors.New("pointer to pointer is not supported")
+ }
+
+ newElemFunc = func(fields []string) reflect.Value {
+ switch elemType.Kind() {
+ case reflect.Slice:
+ slice := reflect.MakeSlice(elemType, len(fields), len(fields))
+ x := reflect.New(slice.Type())
+ x.Elem().Set(slice)
+ return x
+ case reflect.Map:
+ mp := reflect.MakeMap(elemType)
+ x := reflect.New(mp.Type())
+ x.Elem().Set(mp)
+ return x
+ }
+ return reflect.New(elemType)
+ }
+
+ var containerValueSetFunc func(*reflect.Value, core.PK) error
+
+ if containerValue.Kind() == reflect.Slice {
+ containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error {
+ if isPointer {
+ containerValue.Set(reflect.Append(containerValue, newValue.Elem().Addr()))
+ } else {
+ containerValue.Set(reflect.Append(containerValue, newValue.Elem()))
+ }
+ return nil
+ }
+ } else {
+ keyType := containerValue.Type().Key()
+ if len(table.PrimaryKeys) == 0 {
+ return errors.New("don't support multiple primary key's map has non-slice key type")
+ }
+ if len(table.PrimaryKeys) > 1 && keyType.Kind() != reflect.Slice {
+ return errors.New("don't support multiple primary key's map has non-slice key type")
+ }
+
+ containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error {
+ keyValue := reflect.New(keyType)
+ err := convertPKToValue(table, keyValue.Interface(), pk)
+ if err != nil {
+ return err
+ }
+ if isPointer {
+ containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem().Addr())
+ } else {
+ containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem())
+ }
+ return nil
+ }
+ }
+
+ if elemType.Kind() == reflect.Struct {
+ var newValue = newElemFunc(fields)
+ dataStruct := rValue(newValue.Interface())
+ tb, err := session.engine.autoMapType(dataStruct)
+ if err != nil {
+ return err
+ }
+ err = session.rows2Beans(rows, fields, tb, newElemFunc, containerValueSetFunc)
+ rows.Close()
+ if err != nil {
+ return err
+ }
+ return session.executeProcessors()
+ }
+
+ for rows.Next() {
+ var newValue = newElemFunc(fields)
+ bean := newValue.Interface()
+
+ switch elemType.Kind() {
+ case reflect.Slice:
+ err = rows.ScanSlice(bean)
+ case reflect.Map:
+ err = rows.ScanMap(bean)
+ default:
+ err = rows.Scan(bean)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ if err := containerValueSetFunc(&newValue, nil); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func convertPKToValue(table *core.Table, dst interface{}, pk core.PK) error {
+ cols := table.PKColumns()
+ if len(cols) == 1 {
+ return convertAssign(dst, pk[0])
+ }
+
+ dst = pk
+ return nil
+}
+
+func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) {
+ if !session.canCache() ||
+ indexNoCase(sqlStr, "having") != -1 ||
+ indexNoCase(sqlStr, "group by") != -1 {
+ return ErrCacheFailed
+ }
+
+ tableName := session.statement.TableName()
+ cacher := session.engine.getCacher(tableName)
+ if cacher == nil {
+ return nil
+ }
+
+ for _, filter := range session.engine.dialect.Filters() {
+ sqlStr = filter.Do(sqlStr, session.engine.dialect, session.statement.RefTable)
+ }
+
+ newsql := session.statement.convertIDSQL(sqlStr)
+ if newsql == "" {
+ return ErrCacheFailed
+ }
+
+ table := session.statement.RefTable
+ ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
+ if err != nil {
+ rows, err := session.queryRows(newsql, args...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ var i int
+ ids = make([]core.PK, 0)
+ for rows.Next() {
+ i++
+ if i > 500 {
+ session.engine.logger.Debug("[cacheFind] ids length > 500, no cache")
+ return ErrCacheFailed
+ }
+ var res = make([]string, len(table.PrimaryKeys))
+ err = rows.ScanSlice(&res)
+ if err != nil {
+ return err
+ }
+ var pk core.PK = make([]interface{}, len(table.PrimaryKeys))
+ for i, col := range table.PKColumns() {
+ pk[i], err = session.engine.idTypeAssertion(col, res[i])
+ if err != nil {
+ return err
+ }
+ }
+
+ ids = append(ids, pk)
+ }
+
+ session.engine.logger.Debug("[cacheFind] cache sql:", ids, tableName, sqlStr, newsql, args)
+ err = core.PutCacheSql(cacher, ids, tableName, newsql, args)
+ if err != nil {
+ return err
+ }
+ } else {
+ session.engine.logger.Debug("[cacheFind] cache hit sql:", tableName, sqlStr, newsql, args)
+ }
+
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+
+ ididxes := make(map[string]int)
+ var ides []core.PK
+ var temps = make([]interface{}, len(ids))
+
+ for idx, id := range ids {
+ sid, err := id.ToString()
+ if err != nil {
+ return err
+ }
+ bean := cacher.GetBean(tableName, sid)
+ if bean == nil || reflect.ValueOf(bean).Elem().Type() != t {
+ ides = append(ides, id)
+ ididxes[sid] = idx
+ } else {
+ session.engine.logger.Debug("[cacheFind] cache hit bean:", tableName, id, bean)
+
+ pk := session.engine.IdOf(bean)
+ xid, err := pk.ToString()
+ if err != nil {
+ return err
+ }
+
+ if sid != xid {
+ session.engine.logger.Error("[cacheFind] error cache", xid, sid, bean)
+ return ErrCacheFailed
+ }
+ temps[idx] = bean
+ }
+ }
+
+ if len(ides) > 0 {
+ slices := reflect.New(reflect.SliceOf(t))
+ beans := slices.Interface()
+
+ if len(table.PrimaryKeys) == 1 {
+ ff := make([]interface{}, 0, len(ides))
+ for _, ie := range ides {
+ ff = append(ff, ie[0])
+ }
+
+ session.In("`"+table.PrimaryKeys[0]+"`", ff...)
+ } else {
+ for _, ie := range ides {
+ cond := builder.NewCond()
+ for i, name := range table.PrimaryKeys {
+ cond = cond.And(builder.Eq{"`" + name + "`": ie[i]})
+ }
+ session.Or(cond)
+ }
+ }
+
+ err = session.NoCache().Table(tableName).find(beans)
+ if err != nil {
+ return err
+ }
+
+ vs := reflect.Indirect(reflect.ValueOf(beans))
+ for i := 0; i < vs.Len(); i++ {
+ rv := vs.Index(i)
+ if rv.Kind() != reflect.Ptr {
+ rv = rv.Addr()
+ }
+ id, err := session.engine.idOfV(rv)
+ if err != nil {
+ return err
+ }
+ sid, err := id.ToString()
+ if err != nil {
+ return err
+ }
+
+ bean := rv.Interface()
+ temps[ididxes[sid]] = bean
+ session.engine.logger.Debug("[cacheFind] cache bean:", tableName, id, bean, temps)
+ cacher.PutBean(tableName, sid, bean)
+ }
+ }
+
+ for j := 0; j < len(temps); j++ {
+ bean := temps[j]
+ if bean == nil {
+ session.engine.logger.Warn("[cacheFind] cache no hit:", tableName, ids[j], temps)
+ // return errors.New("cache error") // !nashtsai! no need to return error, but continue instead
+ continue
+ }
+ if sliceValue.Kind() == reflect.Slice {
+ if t.Kind() == reflect.Ptr {
+ sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(bean)))
+ } else {
+ sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(bean))))
+ }
+ } else if sliceValue.Kind() == reflect.Map {
+ var key = ids[j]
+ keyType := sliceValue.Type().Key()
+ var ikey interface{}
+ if len(key) == 1 {
+ ikey, err = str2PK(fmt.Sprintf("%v", key[0]), keyType)
+ if err != nil {
+ return err
+ }
+ } else {
+ if keyType.Kind() != reflect.Slice {
+ return errors.New("table have multiple primary keys, key is not core.PK or slice")
+ }
+ ikey = key
+ }
+
+ if t.Kind() == reflect.Ptr {
+ sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.ValueOf(bean))
+ } else {
+ sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.Indirect(reflect.ValueOf(bean)))
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/session_get.go b/vendor/xorm.io/xorm/session_get.go
new file mode 100644
index 0000000000..cc0a2019ea
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_get.go
@@ -0,0 +1,356 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql"
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+
+ "xorm.io/core"
+)
+
+// Get retrieve one record from database, bean's non-empty fields
+// will be as conditions
+func (session *Session) Get(bean interface{}) (bool, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+ return session.get(bean)
+}
+
+func (session *Session) get(bean interface{}) (bool, error) {
+ defer session.resetStatement()
+
+ if session.statement.lastError != nil {
+ return false, session.statement.lastError
+ }
+
+ beanValue := reflect.ValueOf(bean)
+ if beanValue.Kind() != reflect.Ptr {
+ return false, errors.New("needs a pointer to a value")
+ } else if beanValue.Elem().Kind() == reflect.Ptr {
+ return false, errors.New("a pointer to a pointer is not allowed")
+ }
+
+ if beanValue.Elem().Kind() == reflect.Struct {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return false, err
+ }
+ }
+
+ var sqlStr string
+ var args []interface{}
+ var err error
+
+ if session.statement.RawSQL == "" {
+ if len(session.statement.TableName()) <= 0 {
+ return false, ErrTableNotFound
+ }
+ session.statement.Limit(1)
+ sqlStr, args, err = session.statement.genGetSQL(bean)
+ if err != nil {
+ return false, err
+ }
+ } else {
+ sqlStr = session.statement.RawSQL
+ args = session.statement.RawParams
+ }
+
+ table := session.statement.RefTable
+
+ if session.canCache() && beanValue.Elem().Kind() == reflect.Struct {
+ if cacher := session.engine.getCacher(session.statement.TableName()); cacher != nil &&
+ !session.statement.unscoped {
+ has, err := session.cacheGet(bean, sqlStr, args...)
+ if err != ErrCacheFailed {
+ return has, err
+ }
+ }
+ }
+
+ context := session.statement.context
+ if context != nil {
+ res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args))
+ if res != nil {
+ session.engine.logger.Debug("hit context cache", sqlStr)
+
+ structValue := reflect.Indirect(reflect.ValueOf(bean))
+ structValue.Set(reflect.Indirect(reflect.ValueOf(res)))
+ session.lastSQL = ""
+ session.lastSQLArgs = nil
+ return true, nil
+ }
+ }
+
+ has, err := session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...)
+ if err != nil || !has {
+ return has, err
+ }
+
+ if context != nil {
+ context.Put(fmt.Sprintf("%v-%v", sqlStr, args), bean)
+ }
+
+ return true, nil
+}
+
+func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
+ rows, err := session.queryRows(sqlStr, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ if rows.Err() != nil {
+ return false, rows.Err()
+ }
+ return false, nil
+ }
+
+ switch bean.(type) {
+ case sql.NullInt64, sql.NullBool, sql.NullFloat64, sql.NullString:
+ return true, rows.Scan(&bean)
+ case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString:
+ return true, rows.Scan(bean)
+ case *string:
+ var res sql.NullString
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*string)) = res.String
+ }
+ return true, nil
+ case *int:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*int)) = int(res.Int64)
+ }
+ return true, nil
+ case *int8:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*int8)) = int8(res.Int64)
+ }
+ return true, nil
+ case *int16:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*int16)) = int16(res.Int64)
+ }
+ return true, nil
+ case *int32:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*int32)) = int32(res.Int64)
+ }
+ return true, nil
+ case *int64:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*int64)) = int64(res.Int64)
+ }
+ return true, nil
+ case *uint:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*uint)) = uint(res.Int64)
+ }
+ return true, nil
+ case *uint8:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*uint8)) = uint8(res.Int64)
+ }
+ return true, nil
+ case *uint16:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*uint16)) = uint16(res.Int64)
+ }
+ return true, nil
+ case *uint32:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*uint32)) = uint32(res.Int64)
+ }
+ return true, nil
+ case *uint64:
+ var res sql.NullInt64
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*uint64)) = uint64(res.Int64)
+ }
+ return true, nil
+ case *bool:
+ var res sql.NullBool
+ if err := rows.Scan(&res); err != nil {
+ return true, err
+ }
+ if res.Valid {
+ *(bean.(*bool)) = res.Bool
+ }
+ return true, nil
+ }
+
+ switch beanKind {
+ case reflect.Struct:
+ fields, err := rows.Columns()
+ if err != nil {
+ // WARN: Alougth rows return true, but get fields failed
+ return true, err
+ }
+
+ scanResults, err := session.row2Slice(rows, fields, bean)
+ if err != nil {
+ return false, err
+ }
+ // close it before covert data
+ rows.Close()
+
+ dataStruct := rValue(bean)
+ _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table)
+ if err != nil {
+ return true, err
+ }
+
+ return true, session.executeProcessors()
+ case reflect.Slice:
+ err = rows.ScanSlice(bean)
+ case reflect.Map:
+ err = rows.ScanMap(bean)
+ case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ err = rows.Scan(bean)
+ default:
+ err = rows.Scan(bean)
+ }
+
+ return true, err
+}
+
+func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) {
+ // if has no reftable, then don't use cache currently
+ if !session.canCache() {
+ return false, ErrCacheFailed
+ }
+
+ for _, filter := range session.engine.dialect.Filters() {
+ sqlStr = filter.Do(sqlStr, session.engine.dialect, session.statement.RefTable)
+ }
+ newsql := session.statement.convertIDSQL(sqlStr)
+ if newsql == "" {
+ return false, ErrCacheFailed
+ }
+
+ tableName := session.statement.TableName()
+ cacher := session.engine.getCacher(tableName)
+
+ session.engine.logger.Debug("[cacheGet] find sql:", newsql, args)
+ table := session.statement.RefTable
+ ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
+ if err != nil {
+ var res = make([]string, len(table.PrimaryKeys))
+ rows, err := session.NoCache().queryRows(newsql, args...)
+ if err != nil {
+ return false, err
+ }
+ defer rows.Close()
+
+ if rows.Next() {
+ err = rows.ScanSlice(&res)
+ if err != nil {
+ return false, err
+ }
+ } else {
+ return false, ErrCacheFailed
+ }
+
+ var pk core.PK = make([]interface{}, len(table.PrimaryKeys))
+ for i, col := range table.PKColumns() {
+ if col.SQLType.IsText() {
+ pk[i] = res[i]
+ } else if col.SQLType.IsNumeric() {
+ n, err := strconv.ParseInt(res[i], 10, 64)
+ if err != nil {
+ return false, err
+ }
+ pk[i] = n
+ } else {
+ return false, errors.New("unsupported")
+ }
+ }
+
+ ids = []core.PK{pk}
+ session.engine.logger.Debug("[cacheGet] cache ids:", newsql, ids)
+ err = core.PutCacheSql(cacher, ids, tableName, newsql, args)
+ if err != nil {
+ return false, err
+ }
+ } else {
+ session.engine.logger.Debug("[cacheGet] cache hit sql:", newsql, ids)
+ }
+
+ if len(ids) > 0 {
+ structValue := reflect.Indirect(reflect.ValueOf(bean))
+ id := ids[0]
+ session.engine.logger.Debug("[cacheGet] get bean:", tableName, id)
+ sid, err := id.ToString()
+ if err != nil {
+ return false, err
+ }
+ cacheBean := cacher.GetBean(tableName, sid)
+ if cacheBean == nil {
+ cacheBean = bean
+ has, err = session.nocacheGet(reflect.Struct, table, cacheBean, sqlStr, args...)
+ if err != nil || !has {
+ return has, err
+ }
+
+ session.engine.logger.Debug("[cacheGet] cache bean:", tableName, id, cacheBean)
+ cacher.PutBean(tableName, sid, cacheBean)
+ } else {
+ session.engine.logger.Debug("[cacheGet] cache hit bean:", tableName, id, cacheBean)
+ has = true
+ }
+ structValue.Set(reflect.Indirect(reflect.ValueOf(cacheBean)))
+
+ return has, nil
+ }
+ return false, nil
+}
diff --git a/vendor/xorm.io/xorm/session_insert.go b/vendor/xorm.io/xorm/session_insert.go
new file mode 100644
index 0000000000..1e19ce7a4e
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_insert.go
@@ -0,0 +1,878 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+// Insert insert one or more beans
+func (session *Session) Insert(beans ...interface{}) (int64, error) {
+ var affected int64
+ var err error
+
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ session.autoResetStatement = false
+ defer func() {
+ session.autoResetStatement = true
+ session.resetStatement()
+ }()
+
+ for _, bean := range beans {
+ switch bean.(type) {
+ case map[string]interface{}:
+ cnt, err := session.insertMapInterface(bean.(map[string]interface{}))
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ case []map[string]interface{}:
+ s := bean.([]map[string]interface{})
+ for i := 0; i < len(s); i++ {
+ cnt, err := session.insertMapInterface(s[i])
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ }
+ case map[string]string:
+ cnt, err := session.insertMapString(bean.(map[string]string))
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ case []map[string]string:
+ s := bean.([]map[string]string)
+ for i := 0; i < len(s); i++ {
+ cnt, err := session.insertMapString(s[i])
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ }
+ default:
+ sliceValue := reflect.Indirect(reflect.ValueOf(bean))
+ if sliceValue.Kind() == reflect.Slice {
+ size := sliceValue.Len()
+ if size > 0 {
+ if session.engine.SupportInsertMany() {
+ cnt, err := session.innerInsertMulti(bean)
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ } else {
+ for i := 0; i < size; i++ {
+ cnt, err := session.innerInsert(sliceValue.Index(i).Interface())
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ }
+ }
+ }
+ } else {
+ cnt, err := session.innerInsert(bean)
+ if err != nil {
+ return affected, err
+ }
+ affected += cnt
+ }
+ }
+ }
+
+ return affected, err
+}
+
+func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error) {
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+ if sliceValue.Kind() != reflect.Slice {
+ return 0, errors.New("needs a pointer to a slice")
+ }
+
+ if sliceValue.Len() <= 0 {
+ return 0, errors.New("could not insert a empty slice")
+ }
+
+ if err := session.statement.setRefBean(sliceValue.Index(0).Interface()); err != nil {
+ return 0, err
+ }
+
+ tableName := session.statement.TableName()
+ if len(tableName) <= 0 {
+ return 0, ErrTableNotFound
+ }
+
+ table := session.statement.RefTable
+ size := sliceValue.Len()
+
+ var colNames []string
+ var colMultiPlaces []string
+ var args []interface{}
+ var cols []*core.Column
+
+ for i := 0; i < size; i++ {
+ v := sliceValue.Index(i)
+ vv := reflect.Indirect(v)
+ elemValue := v.Interface()
+ var colPlaces []string
+
+ // handle BeforeInsertProcessor
+ // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
+ for _, closure := range session.beforeClosures {
+ closure(elemValue)
+ }
+
+ if processor, ok := interface{}(elemValue).(BeforeInsertProcessor); ok {
+ processor.BeforeInsert()
+ }
+ // --
+
+ if i == 0 {
+ for _, col := range table.Columns() {
+ ptrFieldValue, err := col.ValueOfV(&vv)
+ if err != nil {
+ return 0, err
+ }
+ fieldValue := *ptrFieldValue
+ if col.IsAutoIncrement && isZero(fieldValue.Interface()) {
+ continue
+ }
+ if col.MapType == core.ONLYFROMDB {
+ continue
+ }
+ if col.IsDeleted {
+ continue
+ }
+ if session.statement.omitColumnMap.contain(col.Name) {
+ continue
+ }
+ if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) {
+ continue
+ }
+ if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime {
+ val, t := session.engine.nowTime(col)
+ args = append(args, val)
+
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ } else if col.IsVersion && session.statement.checkVersion {
+ args = append(args, 1)
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnInt(bean, col, 1)
+ })
+ } else {
+ arg, err := session.value2Interface(col, fieldValue)
+ if err != nil {
+ return 0, err
+ }
+ args = append(args, arg)
+ }
+
+ colNames = append(colNames, col.Name)
+ cols = append(cols, col)
+ colPlaces = append(colPlaces, "?")
+ }
+ } else {
+ for _, col := range cols {
+ ptrFieldValue, err := col.ValueOfV(&vv)
+ if err != nil {
+ return 0, err
+ }
+ fieldValue := *ptrFieldValue
+
+ if col.IsAutoIncrement && isZero(fieldValue.Interface()) {
+ continue
+ }
+ if col.MapType == core.ONLYFROMDB {
+ continue
+ }
+ if col.IsDeleted {
+ continue
+ }
+ if session.statement.omitColumnMap.contain(col.Name) {
+ continue
+ }
+ if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) {
+ continue
+ }
+ if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime {
+ val, t := session.engine.nowTime(col)
+ args = append(args, val)
+
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ } else if col.IsVersion && session.statement.checkVersion {
+ args = append(args, 1)
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnInt(bean, col, 1)
+ })
+ } else {
+ arg, err := session.value2Interface(col, fieldValue)
+ if err != nil {
+ return 0, err
+ }
+ args = append(args, arg)
+ }
+
+ colPlaces = append(colPlaces, "?")
+ }
+ }
+ colMultiPlaces = append(colMultiPlaces, strings.Join(colPlaces, ", "))
+ }
+ cleanupProcessorsClosures(&session.beforeClosures)
+
+ var sql string
+ if session.engine.dialect.DBType() == core.ORACLE {
+ temp := fmt.Sprintf(") INTO %s (%v) VALUES (",
+ session.engine.Quote(tableName),
+ quoteColumns(colNames, session.engine.Quote, ","))
+ sql = fmt.Sprintf("INSERT ALL INTO %s (%v) VALUES (%v) SELECT 1 FROM DUAL",
+ session.engine.Quote(tableName),
+ quoteColumns(colNames, session.engine.Quote, ","),
+ strings.Join(colMultiPlaces, temp))
+ } else {
+ sql = fmt.Sprintf("INSERT INTO %s (%v) VALUES (%v)",
+ session.engine.Quote(tableName),
+ quoteColumns(colNames, session.engine.Quote, ","),
+ strings.Join(colMultiPlaces, "),("))
+ }
+ res, err := session.exec(sql, args...)
+ if err != nil {
+ return 0, err
+ }
+
+ session.cacheInsert(tableName)
+
+ lenAfterClosures := len(session.afterClosures)
+ for i := 0; i < size; i++ {
+ elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface()
+
+ // handle AfterInsertProcessor
+ if session.isAutoCommit {
+ // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
+ for _, closure := range session.afterClosures {
+ closure(elemValue)
+ }
+ if processor, ok := interface{}(elemValue).(AfterInsertProcessor); ok {
+ processor.AfterInsert()
+ }
+ } else {
+ if lenAfterClosures > 0 {
+ if value, has := session.afterInsertBeans[elemValue]; has && value != nil {
+ *value = append(*value, session.afterClosures...)
+ } else {
+ afterClosures := make([]func(interface{}), lenAfterClosures)
+ copy(afterClosures, session.afterClosures)
+ session.afterInsertBeans[elemValue] = &afterClosures
+ }
+ } else {
+ if _, ok := interface{}(elemValue).(AfterInsertProcessor); ok {
+ session.afterInsertBeans[elemValue] = nil
+ }
+ }
+ }
+ }
+
+ cleanupProcessorsClosures(&session.afterClosures)
+ return res.RowsAffected()
+}
+
+// InsertMulti insert multiple records
+func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
+ if sliceValue.Kind() != reflect.Slice {
+ return 0, ErrParamsType
+
+ }
+
+ if sliceValue.Len() <= 0 {
+ return 0, nil
+ }
+
+ return session.innerInsertMulti(rowsSlicePtr)
+}
+
+func (session *Session) innerInsert(bean interface{}) (int64, error) {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return 0, err
+ }
+ if len(session.statement.TableName()) <= 0 {
+ return 0, ErrTableNotFound
+ }
+
+ table := session.statement.RefTable
+
+ // handle BeforeInsertProcessor
+ for _, closure := range session.beforeClosures {
+ closure(bean)
+ }
+ cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used
+
+ if processor, ok := interface{}(bean).(BeforeInsertProcessor); ok {
+ processor.BeforeInsert()
+ }
+
+ colNames, args, err := session.genInsertColumns(bean)
+ if err != nil {
+ return 0, err
+ }
+
+ exprs := session.statement.exprColumns
+ colPlaces := strings.Repeat("?, ", len(colNames))
+ if exprs.Len() <= 0 && len(colPlaces) > 0 {
+ colPlaces = colPlaces[0 : len(colPlaces)-2]
+ }
+
+ var tableName = session.statement.TableName()
+ var output string
+ if session.engine.dialect.DBType() == core.MSSQL && len(table.AutoIncrement) > 0 {
+ output = fmt.Sprintf(" OUTPUT Inserted.%s", table.AutoIncrement)
+ }
+
+ var buf = builder.NewWriter()
+ if _, err := buf.WriteString(fmt.Sprintf("INSERT INTO %s", session.engine.Quote(tableName))); err != nil {
+ return 0, err
+ }
+
+ if len(colPlaces) <= 0 {
+ if session.engine.dialect.DBType() == core.MYSQL {
+ if _, err := buf.WriteString(" VALUES ()"); err != nil {
+ return 0, err
+ }
+ } else {
+ if _, err := buf.WriteString(fmt.Sprintf("%s DEFAULT VALUES", output)); err != nil {
+ return 0, err
+ }
+ }
+ } else {
+ if _, err := buf.WriteString(" ("); err != nil {
+ return 0, err
+ }
+
+ if err := writeStrings(buf, append(colNames, exprs.colNames...), "`", "`"); err != nil {
+ return 0, err
+ }
+
+ if session.statement.cond.IsValid() {
+ if _, err := buf.WriteString(fmt.Sprintf(")%s SELECT ", output)); err != nil {
+ return 0, err
+ }
+
+ if err := session.statement.writeArgs(buf, args); err != nil {
+ return 0, err
+ }
+
+ if len(exprs.args) > 0 {
+ if _, err := buf.WriteString(","); err != nil {
+ return 0, err
+ }
+ }
+ if err := exprs.writeArgs(buf); err != nil {
+ return 0, err
+ }
+
+ if _, err := buf.WriteString(fmt.Sprintf(" FROM %v WHERE ", session.engine.Quote(tableName))); err != nil {
+ return 0, err
+ }
+
+ if err := session.statement.cond.WriteTo(buf); err != nil {
+ return 0, err
+ }
+ } else {
+ buf.Append(args...)
+
+ if _, err := buf.WriteString(fmt.Sprintf(")%s VALUES (%v",
+ output,
+ colPlaces)); err != nil {
+ return 0, err
+ }
+
+ if err := exprs.writeArgs(buf); err != nil {
+ return 0, err
+ }
+
+ if _, err := buf.WriteString(")"); err != nil {
+ return 0, err
+ }
+ }
+ }
+
+ if len(table.AutoIncrement) > 0 && session.engine.dialect.DBType() == core.POSTGRES {
+ if _, err := buf.WriteString(" RETURNING " + session.engine.Quote(table.AutoIncrement)); err != nil {
+ return 0, err
+ }
+ }
+
+ sqlStr := buf.String()
+ args = buf.Args()
+
+ handleAfterInsertProcessorFunc := func(bean interface{}) {
+ if session.isAutoCommit {
+ for _, closure := range session.afterClosures {
+ closure(bean)
+ }
+ if processor, ok := interface{}(bean).(AfterInsertProcessor); ok {
+ processor.AfterInsert()
+ }
+ } else {
+ lenAfterClosures := len(session.afterClosures)
+ if lenAfterClosures > 0 {
+ if value, has := session.afterInsertBeans[bean]; has && value != nil {
+ *value = append(*value, session.afterClosures...)
+ } else {
+ afterClosures := make([]func(interface{}), lenAfterClosures)
+ copy(afterClosures, session.afterClosures)
+ session.afterInsertBeans[bean] = &afterClosures
+ }
+
+ } else {
+ if _, ok := interface{}(bean).(AfterInsertProcessor); ok {
+ session.afterInsertBeans[bean] = nil
+ }
+ }
+ }
+ cleanupProcessorsClosures(&session.afterClosures) // cleanup after used
+ }
+
+ // for postgres, many of them didn't implement lastInsertId, so we should
+ // implemented it ourself.
+ if session.engine.dialect.DBType() == core.ORACLE && len(table.AutoIncrement) > 0 {
+ res, err := session.queryBytes("select seq_atable.currval from dual", args...)
+ if err != nil {
+ return 0, err
+ }
+
+ defer handleAfterInsertProcessorFunc(bean)
+
+ session.cacheInsert(tableName)
+
+ if table.Version != "" && session.statement.checkVersion {
+ verValue, err := table.VersionColumn().ValueOf(bean)
+ if err != nil {
+ session.engine.logger.Error(err)
+ } else if verValue.IsValid() && verValue.CanSet() {
+ session.incrVersionFieldValue(verValue)
+ }
+ }
+
+ if len(res) < 1 {
+ return 0, errors.New("insert no error but not returned id")
+ }
+
+ idByte := res[0][table.AutoIncrement]
+ id, err := strconv.ParseInt(string(idByte), 10, 64)
+ if err != nil || id <= 0 {
+ return 1, err
+ }
+
+ aiValue, err := table.AutoIncrColumn().ValueOf(bean)
+ if err != nil {
+ session.engine.logger.Error(err)
+ }
+
+ if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
+ return 1, nil
+ }
+
+ aiValue.Set(int64ToIntValue(id, aiValue.Type()))
+
+ return 1, nil
+ } else if len(table.AutoIncrement) > 0 && (session.engine.dialect.DBType() == core.POSTGRES || session.engine.dialect.DBType() == core.MSSQL) {
+ res, err := session.queryBytes(sqlStr, args...)
+
+ if err != nil {
+ return 0, err
+ }
+ defer handleAfterInsertProcessorFunc(bean)
+
+ session.cacheInsert(tableName)
+
+ if table.Version != "" && session.statement.checkVersion {
+ verValue, err := table.VersionColumn().ValueOf(bean)
+ if err != nil {
+ session.engine.logger.Error(err)
+ } else if verValue.IsValid() && verValue.CanSet() {
+ session.incrVersionFieldValue(verValue)
+ }
+ }
+
+ if len(res) < 1 {
+ return 0, errors.New("insert successfully but not returned id")
+ }
+
+ idByte := res[0][table.AutoIncrement]
+ id, err := strconv.ParseInt(string(idByte), 10, 64)
+ if err != nil || id <= 0 {
+ return 1, err
+ }
+
+ aiValue, err := table.AutoIncrColumn().ValueOf(bean)
+ if err != nil {
+ session.engine.logger.Error(err)
+ }
+
+ if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
+ return 1, nil
+ }
+
+ aiValue.Set(int64ToIntValue(id, aiValue.Type()))
+
+ return 1, nil
+ } else {
+ res, err := session.exec(sqlStr, args...)
+ if err != nil {
+ return 0, err
+ }
+
+ defer handleAfterInsertProcessorFunc(bean)
+
+ session.cacheInsert(tableName)
+
+ if table.Version != "" && session.statement.checkVersion {
+ verValue, err := table.VersionColumn().ValueOf(bean)
+ if err != nil {
+ session.engine.logger.Error(err)
+ } else if verValue.IsValid() && verValue.CanSet() {
+ session.incrVersionFieldValue(verValue)
+ }
+ }
+
+ if table.AutoIncrement == "" {
+ return res.RowsAffected()
+ }
+
+ var id int64
+ id, err = res.LastInsertId()
+ if err != nil || id <= 0 {
+ return res.RowsAffected()
+ }
+
+ aiValue, err := table.AutoIncrColumn().ValueOf(bean)
+ if err != nil {
+ session.engine.logger.Error(err)
+ }
+
+ if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
+ return res.RowsAffected()
+ }
+
+ aiValue.Set(int64ToIntValue(id, aiValue.Type()))
+
+ return res.RowsAffected()
+ }
+}
+
+// InsertOne insert only one struct into database as a record.
+// The in parameter bean must a struct or a point to struct. The return
+// parameter is inserted and error
+func (session *Session) InsertOne(bean interface{}) (int64, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ return session.innerInsert(bean)
+}
+
+func (session *Session) cacheInsert(table string) error {
+ if !session.statement.UseCache {
+ return nil
+ }
+ cacher := session.engine.getCacher(table)
+ if cacher == nil {
+ return nil
+ }
+ session.engine.logger.Debug("[cache] clear sql:", table)
+ cacher.ClearIds(table)
+ return nil
+}
+
+// genInsertColumns generates insert needed columns
+func (session *Session) genInsertColumns(bean interface{}) ([]string, []interface{}, error) {
+ table := session.statement.RefTable
+ colNames := make([]string, 0, len(table.ColumnsSeq()))
+ args := make([]interface{}, 0, len(table.ColumnsSeq()))
+
+ for _, col := range table.Columns() {
+ if col.MapType == core.ONLYFROMDB {
+ continue
+ }
+
+ if col.IsDeleted {
+ continue
+ }
+
+ if session.statement.omitColumnMap.contain(col.Name) {
+ continue
+ }
+
+ if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) {
+ continue
+ }
+
+ if session.statement.incrColumns.isColExist(col.Name) {
+ continue
+ } else if session.statement.decrColumns.isColExist(col.Name) {
+ continue
+ } else if session.statement.exprColumns.isColExist(col.Name) {
+ continue
+ }
+
+ fieldValuePtr, err := col.ValueOf(bean)
+ if err != nil {
+ return nil, nil, err
+ }
+ fieldValue := *fieldValuePtr
+
+ if col.IsAutoIncrement {
+ switch fieldValue.Type().Kind() {
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64:
+ if fieldValue.Int() == 0 {
+ continue
+ }
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64:
+ if fieldValue.Uint() == 0 {
+ continue
+ }
+ case reflect.String:
+ if len(fieldValue.String()) == 0 {
+ continue
+ }
+ case reflect.Ptr:
+ if fieldValue.Pointer() == 0 {
+ continue
+ }
+ }
+ }
+
+ // !evalphobia! set fieldValue as nil when column is nullable and zero-value
+ if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok {
+ if col.Nullable && isZero(fieldValue.Interface()) {
+ var nilValue *int
+ fieldValue = reflect.ValueOf(nilValue)
+ }
+ }
+
+ if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
+ // if time is non-empty, then set to auto time
+ val, t := session.engine.nowTime(col)
+ args = append(args, val)
+
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ } else if col.IsVersion && session.statement.checkVersion {
+ args = append(args, 1)
+ } else {
+ arg, err := session.value2Interface(col, fieldValue)
+ if err != nil {
+ return colNames, args, err
+ }
+ args = append(args, arg)
+ }
+
+ colNames = append(colNames, col.Name)
+ }
+ return colNames, args, nil
+}
+
+func (session *Session) insertMapInterface(m map[string]interface{}) (int64, error) {
+ if len(m) == 0 {
+ return 0, ErrParamsType
+ }
+
+ tableName := session.statement.TableName()
+ if len(tableName) <= 0 {
+ return 0, ErrTableNotFound
+ }
+
+ var columns = make([]string, 0, len(m))
+ exprs := session.statement.exprColumns
+ for k := range m {
+ if !exprs.isColExist(k) {
+ columns = append(columns, k)
+ }
+ }
+ sort.Strings(columns)
+
+ var args = make([]interface{}, 0, len(m))
+ for _, colName := range columns {
+ 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
+}
+
+func (session *Session) insertMapString(m map[string]string) (int64, error) {
+ if len(m) == 0 {
+ return 0, ErrParamsType
+ }
+
+ tableName := session.statement.TableName()
+ if len(tableName) <= 0 {
+ return 0, ErrTableNotFound
+ }
+
+ var columns = make([]string, 0, len(m))
+ exprs := session.statement.exprColumns
+ for k := range m {
+ if !exprs.isColExist(k) {
+ columns = append(columns, k)
+ }
+ }
+ sort.Strings(columns)
+
+ var args = make([]interface{}, 0, len(m))
+ for _, colName := range columns {
+ 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
+}
diff --git a/vendor/xorm.io/xorm/session_iterate.go b/vendor/xorm.io/xorm/session_iterate.go
new file mode 100644
index 0000000000..ca996c2884
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_iterate.go
@@ -0,0 +1,100 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import "reflect"
+
+// IterFunc only use by Iterate
+type IterFunc func(idx int, bean interface{}) error
+
+// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields
+// are conditions.
+func (session *Session) Rows(bean interface{}) (*Rows, error) {
+ return newRows(session, bean)
+}
+
+// Iterate record by record handle records from table, condiBeans's non-empty fields
+// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
+// map[int64]*Struct
+func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ if session.statement.lastError != nil {
+ return session.statement.lastError
+ }
+
+ if session.statement.bufferSize > 0 {
+ return session.bufferIterate(bean, fun)
+ }
+
+ rows, err := session.Rows(bean)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ i := 0
+ for rows.Next() {
+ b := reflect.New(rows.beanType).Interface()
+ err = rows.Scan(b)
+ if err != nil {
+ return err
+ }
+ err = fun(i, b)
+ if err != nil {
+ return err
+ }
+ i++
+ }
+ return err
+}
+
+// BufferSize sets the buffersize for iterate
+func (session *Session) BufferSize(size int) *Session {
+ session.statement.bufferSize = size
+ return session
+}
+
+func (session *Session) bufferIterate(bean interface{}, fun IterFunc) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ var bufferSize = session.statement.bufferSize
+ var limit = session.statement.LimitN
+ if limit > 0 && bufferSize > limit {
+ bufferSize = limit
+ }
+ var start = session.statement.Start
+ v := rValue(bean)
+ sliceType := reflect.SliceOf(v.Type())
+ var idx = 0
+ for {
+ slice := reflect.New(sliceType)
+ if err := session.Limit(bufferSize, start).find(slice.Interface(), bean); err != nil {
+ return err
+ }
+
+ for i := 0; i < slice.Elem().Len(); i++ {
+ if err := fun(idx, slice.Elem().Index(i).Addr().Interface()); err != nil {
+ return err
+ }
+ idx++
+ }
+
+ start = start + slice.Elem().Len()
+ if limit > 0 && idx+bufferSize > limit {
+ bufferSize = limit - idx
+ }
+
+ if bufferSize <= 0 || slice.Elem().Len() < bufferSize || idx == limit {
+ break
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/session_query.go b/vendor/xorm.io/xorm/session_query.go
new file mode 100644
index 0000000000..21c00b8d7f
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_query.go
@@ -0,0 +1,320 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+func (session *Session) genQuerySQL(sqlOrArgs ...interface{}) (string, []interface{}, error) {
+ if len(sqlOrArgs) > 0 {
+ return convertSQLOrArgs(sqlOrArgs...)
+ }
+
+ if session.statement.RawSQL != "" {
+ return session.statement.RawSQL, session.statement.RawParams, nil
+ }
+
+ if len(session.statement.TableName()) <= 0 {
+ return "", nil, ErrTableNotFound
+ }
+
+ var columnStr = session.statement.ColumnStr
+ if len(session.statement.selectStr) > 0 {
+ columnStr = session.statement.selectStr
+ } else {
+ if session.statement.JoinStr == "" {
+ if columnStr == "" {
+ if session.statement.GroupByStr != "" {
+ columnStr = session.engine.quoteColumns(session.statement.GroupByStr)
+ } else {
+ columnStr = session.statement.genColumnStr()
+ }
+ }
+ } else {
+ if columnStr == "" {
+ if session.statement.GroupByStr != "" {
+ columnStr = session.engine.quoteColumns(session.statement.GroupByStr)
+ } else {
+ columnStr = "*"
+ }
+ }
+ }
+ if columnStr == "" {
+ columnStr = "*"
+ }
+ }
+
+ if err := session.statement.processIDParam(); err != nil {
+ return "", nil, err
+ }
+
+ condSQL, condArgs, err := builder.ToSQL(session.statement.cond)
+ if err != nil {
+ return "", nil, err
+ }
+
+ args := append(session.statement.joinArgs, condArgs...)
+ sqlStr, err := session.statement.genSelectSQL(columnStr, condSQL, true, true)
+ if err != nil {
+ return "", nil, err
+ }
+ // for mssql and use limit
+ qs := strings.Count(sqlStr, "?")
+ if len(args)*2 == qs {
+ args = append(args, args...)
+ }
+
+ return sqlStr, args, nil
+}
+
+// Query runs a raw sql and return records as []map[string][]byte
+func (session *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ sqlStr, args, err := session.genQuerySQL(sqlOrArgs...)
+ if err != nil {
+ return nil, err
+ }
+
+ return session.queryBytes(sqlStr, args...)
+}
+
+func value2String(rawValue *reflect.Value) (str string, err error) {
+ aa := reflect.TypeOf((*rawValue).Interface())
+ vv := reflect.ValueOf((*rawValue).Interface())
+ switch aa.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ str = strconv.FormatInt(vv.Int(), 10)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ str = strconv.FormatUint(vv.Uint(), 10)
+ case reflect.Float32, reflect.Float64:
+ str = strconv.FormatFloat(vv.Float(), 'f', -1, 64)
+ case reflect.String:
+ str = vv.String()
+ case reflect.Array, reflect.Slice:
+ switch aa.Elem().Kind() {
+ case reflect.Uint8:
+ data := rawValue.Interface().([]byte)
+ str = string(data)
+ if str == "\x00" {
+ str = "0"
+ }
+ default:
+ err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
+ }
+ // time type
+ case reflect.Struct:
+ if aa.ConvertibleTo(core.TimeType) {
+ str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano)
+ } else {
+ err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
+ }
+ case reflect.Bool:
+ str = strconv.FormatBool(vv.Bool())
+ case reflect.Complex128, reflect.Complex64:
+ str = fmt.Sprintf("%v", vv.Complex())
+ /* TODO: unsupported types below
+ case reflect.Map:
+ case reflect.Ptr:
+ case reflect.Uintptr:
+ case reflect.UnsafePointer:
+ case reflect.Chan, reflect.Func, reflect.Interface:
+ */
+ default:
+ err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name())
+ }
+ return
+}
+
+func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) {
+ result := make(map[string]string)
+ scanResultContainers := make([]interface{}, len(fields))
+ for i := 0; i < len(fields); i++ {
+ var scanResultContainer interface{}
+ scanResultContainers[i] = &scanResultContainer
+ }
+ if err := rows.Scan(scanResultContainers...); err != nil {
+ return nil, err
+ }
+
+ for ii, key := range fields {
+ rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
+ // if row is null then as empty string
+ if rawValue.Interface() == nil {
+ result[key] = ""
+ continue
+ }
+
+ if data, err := value2String(&rawValue); err == nil {
+ result[key] = data
+ } else {
+ return nil, err
+ }
+ }
+ return result, nil
+}
+
+func row2sliceStr(rows *core.Rows, fields []string) (results []string, err error) {
+ result := make([]string, 0, len(fields))
+ scanResultContainers := make([]interface{}, len(fields))
+ for i := 0; i < len(fields); i++ {
+ var scanResultContainer interface{}
+ scanResultContainers[i] = &scanResultContainer
+ }
+ if err := rows.Scan(scanResultContainers...); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < len(fields); i++ {
+ rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[i]))
+ // if row is null then as empty string
+ if rawValue.Interface() == nil {
+ result = append(result, "")
+ continue
+ }
+
+ if data, err := value2String(&rawValue); err == nil {
+ result = append(result, data)
+ } else {
+ return nil, err
+ }
+ }
+ return result, nil
+}
+
+func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) {
+ fields, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+ for rows.Next() {
+ result, err := row2mapStr(rows, fields)
+ if err != nil {
+ return nil, err
+ }
+ resultsSlice = append(resultsSlice, result)
+ }
+
+ return resultsSlice, nil
+}
+
+func rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) {
+ fields, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+ for rows.Next() {
+ record, err := row2sliceStr(rows, fields)
+ if err != nil {
+ return nil, err
+ }
+ resultsSlice = append(resultsSlice, record)
+ }
+
+ return resultsSlice, nil
+}
+
+// QueryString runs a raw sql and return records as []map[string]string
+func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ sqlStr, args, err := session.genQuerySQL(sqlOrArgs...)
+ if err != nil {
+ return nil, err
+ }
+
+ rows, err := session.queryRows(sqlStr, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ return rows2Strings(rows)
+}
+
+// QuerySliceString runs a raw sql and return records as [][]string
+func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ sqlStr, args, err := session.genQuerySQL(sqlOrArgs...)
+ if err != nil {
+ return nil, err
+ }
+
+ rows, err := session.queryRows(sqlStr, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ return rows2SliceString(rows)
+}
+
+func row2mapInterface(rows *core.Rows, fields []string) (resultsMap map[string]interface{}, err error) {
+ resultsMap = make(map[string]interface{}, len(fields))
+ scanResultContainers := make([]interface{}, len(fields))
+ for i := 0; i < len(fields); i++ {
+ var scanResultContainer interface{}
+ scanResultContainers[i] = &scanResultContainer
+ }
+ if err := rows.Scan(scanResultContainers...); err != nil {
+ return nil, err
+ }
+
+ for ii, key := range fields {
+ resultsMap[key] = reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])).Interface()
+ }
+ return
+}
+
+func rows2Interfaces(rows *core.Rows) (resultsSlice []map[string]interface{}, err error) {
+ fields, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+ for rows.Next() {
+ result, err := row2mapInterface(rows, fields)
+ if err != nil {
+ return nil, err
+ }
+ resultsSlice = append(resultsSlice, result)
+ }
+
+ return resultsSlice, nil
+}
+
+// QueryInterface runs a raw sql and return records as []map[string]interface{}
+func (session *Session) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ sqlStr, args, err := session.genQuerySQL(sqlOrArgs...)
+ if err != nil {
+ return nil, err
+ }
+
+ rows, err := session.queryRows(sqlStr, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ return rows2Interfaces(rows)
+}
diff --git a/vendor/xorm.io/xorm/session_raw.go b/vendor/xorm.io/xorm/session_raw.go
new file mode 100644
index 0000000000..67648ef130
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_raw.go
@@ -0,0 +1,227 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql"
+ "reflect"
+ "time"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) {
+ for _, filter := range session.engine.dialect.Filters() {
+ *sqlStr = filter.Do(*sqlStr, session.engine.dialect, session.statement.RefTable)
+ }
+
+ session.lastSQL = *sqlStr
+ session.lastSQLArgs = paramStr
+}
+
+func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Rows, error) {
+ defer session.resetStatement()
+
+ session.queryPreprocess(&sqlStr, args...)
+
+ if session.engine.showSQL {
+ if session.engine.showExecTime {
+ b4ExecTime := time.Now()
+ defer func() {
+ execDuration := time.Since(b4ExecTime)
+ if len(args) > 0 {
+ session.engine.logger.Infof("[SQL] %s %#v - took: %v", sqlStr, args, execDuration)
+ } else {
+ session.engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration)
+ }
+ }()
+ } else {
+ if len(args) > 0 {
+ session.engine.logger.Infof("[SQL] %v %#v", sqlStr, args)
+ } else {
+ session.engine.logger.Infof("[SQL] %v", sqlStr)
+ }
+ }
+ }
+
+ if session.isAutoCommit {
+ var db *core.DB
+ if session.sessionType == groupSession {
+ db = session.engine.engineGroup.Slave().DB()
+ } else {
+ db = session.DB()
+ }
+
+ if session.prepareStmt {
+ // don't clear stmt since session will cache them
+ stmt, err := session.doPrepare(db, sqlStr)
+ if err != nil {
+ return nil, err
+ }
+
+ rows, err := stmt.QueryContext(session.ctx, args...)
+ if err != nil {
+ return nil, err
+ }
+ return rows, nil
+ }
+
+ rows, err := db.QueryContext(session.ctx, sqlStr, args...)
+ if err != nil {
+ return nil, err
+ }
+ return rows, nil
+ }
+
+ rows, err := session.tx.QueryContext(session.ctx, sqlStr, args...)
+ if err != nil {
+ return nil, err
+ }
+ return rows, nil
+}
+
+func (session *Session) queryRow(sqlStr string, args ...interface{}) *core.Row {
+ return core.NewRow(session.queryRows(sqlStr, args...))
+}
+
+func value2Bytes(rawValue *reflect.Value) ([]byte, error) {
+ str, err := value2String(rawValue)
+ if err != nil {
+ return nil, err
+ }
+ return []byte(str), nil
+}
+
+func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) {
+ result := make(map[string][]byte)
+ scanResultContainers := make([]interface{}, len(fields))
+ for i := 0; i < len(fields); i++ {
+ var scanResultContainer interface{}
+ scanResultContainers[i] = &scanResultContainer
+ }
+ if err := rows.Scan(scanResultContainers...); err != nil {
+ return nil, err
+ }
+
+ for ii, key := range fields {
+ rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
+ //if row is null then ignore
+ if rawValue.Interface() == nil {
+ result[key] = []byte{}
+ continue
+ }
+
+ if data, err := value2Bytes(&rawValue); err == nil {
+ result[key] = data
+ } else {
+ return nil, err // !nashtsai! REVIEW, should return err or just error log?
+ }
+ }
+ return result, nil
+}
+
+func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) {
+ fields, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+ for rows.Next() {
+ result, err := row2map(rows, fields)
+ if err != nil {
+ return nil, err
+ }
+ resultsSlice = append(resultsSlice, result)
+ }
+
+ return resultsSlice, nil
+}
+
+func (session *Session) queryBytes(sqlStr string, args ...interface{}) ([]map[string][]byte, error) {
+ rows, err := session.queryRows(sqlStr, args...)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ return rows2maps(rows)
+}
+
+func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) {
+ defer session.resetStatement()
+
+ session.queryPreprocess(&sqlStr, args...)
+
+ if session.engine.showSQL {
+ if session.engine.showExecTime {
+ b4ExecTime := time.Now()
+ defer func() {
+ execDuration := time.Since(b4ExecTime)
+ if len(args) > 0 {
+ session.engine.logger.Infof("[SQL] %s %#v - took: %v", sqlStr, args, execDuration)
+ } else {
+ session.engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration)
+ }
+ }()
+ } else {
+ if len(args) > 0 {
+ session.engine.logger.Infof("[SQL] %v %#v", sqlStr, args)
+ } else {
+ session.engine.logger.Infof("[SQL] %v", sqlStr)
+ }
+ }
+ }
+
+ if !session.isAutoCommit {
+ return session.tx.ExecContext(session.ctx, sqlStr, args...)
+ }
+
+ if session.prepareStmt {
+ stmt, err := session.doPrepare(session.DB(), sqlStr)
+ if err != nil {
+ return nil, err
+ }
+
+ res, err := stmt.ExecContext(session.ctx, args...)
+ if err != nil {
+ return nil, err
+ }
+ return res, nil
+ }
+
+ return session.DB().ExecContext(session.ctx, sqlStr, args...)
+}
+
+func convertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) {
+ switch sqlOrArgs[0].(type) {
+ case string:
+ return sqlOrArgs[0].(string), sqlOrArgs[1:], nil
+ case *builder.Builder:
+ return sqlOrArgs[0].(*builder.Builder).ToSQL()
+ case builder.Builder:
+ bd := sqlOrArgs[0].(builder.Builder)
+ return bd.ToSQL()
+ }
+
+ return "", nil, ErrUnSupportedType
+}
+
+// Exec raw sql
+func (session *Session) Exec(sqlOrArgs ...interface{}) (sql.Result, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ if len(sqlOrArgs) == 0 {
+ return nil, ErrUnSupportedType
+ }
+
+ sqlStr, args, err := convertSQLOrArgs(sqlOrArgs...)
+ if err != nil {
+ return nil, err
+ }
+
+ return session.exec(sqlStr, args...)
+}
diff --git a/vendor/xorm.io/xorm/session_schema.go b/vendor/xorm.io/xorm/session_schema.go
new file mode 100644
index 0000000000..5e576c29aa
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_schema.go
@@ -0,0 +1,430 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql"
+ "fmt"
+ "strings"
+
+ "xorm.io/core"
+)
+
+// Ping test if database is ok
+func (session *Session) Ping() error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName())
+ return session.DB().PingContext(session.ctx)
+}
+
+// CreateTable create a table according a bean
+func (session *Session) CreateTable(bean interface{}) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ return session.createTable(bean)
+}
+
+func (session *Session) createTable(bean interface{}) error {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+
+ sqlStr := session.statement.genCreateTableSQL()
+ _, err := session.exec(sqlStr)
+ return err
+}
+
+// CreateIndexes create indexes
+func (session *Session) CreateIndexes(bean interface{}) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ return session.createIndexes(bean)
+}
+
+func (session *Session) createIndexes(bean interface{}) error {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+
+ sqls := session.statement.genIndexSQL()
+ for _, sqlStr := range sqls {
+ _, err := session.exec(sqlStr)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// CreateUniques create uniques
+func (session *Session) CreateUniques(bean interface{}) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+ return session.createUniques(bean)
+}
+
+func (session *Session) createUniques(bean interface{}) error {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+
+ sqls := session.statement.genUniqueSQL()
+ for _, sqlStr := range sqls {
+ _, err := session.exec(sqlStr)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// DropIndexes drop indexes
+func (session *Session) DropIndexes(bean interface{}) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ return session.dropIndexes(bean)
+}
+
+func (session *Session) dropIndexes(bean interface{}) error {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return err
+ }
+
+ sqls := session.statement.genDelIndexSQL()
+ for _, sqlStr := range sqls {
+ _, err := session.exec(sqlStr)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// DropTable drop table will drop table if exist, if drop failed, it will return error
+func (session *Session) DropTable(beanOrTableName interface{}) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ return session.dropTable(beanOrTableName)
+}
+
+func (session *Session) dropTable(beanOrTableName interface{}) error {
+ tableName := session.engine.TableName(beanOrTableName)
+ var needDrop = true
+ if !session.engine.dialect.SupportDropIfExists() {
+ sqlStr, args := session.engine.dialect.TableCheckSql(tableName)
+ results, err := session.queryBytes(sqlStr, args...)
+ if err != nil {
+ return err
+ }
+ needDrop = len(results) > 0
+ }
+
+ if needDrop {
+ sqlStr := session.engine.Dialect().DropTableSql(session.engine.TableName(tableName, true))
+ _, err := session.exec(sqlStr)
+ return err
+ }
+ return nil
+}
+
+// IsTableExist if a table is exist
+func (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ tableName := session.engine.TableName(beanOrTableName)
+
+ return session.isTableExist(tableName)
+}
+
+func (session *Session) isTableExist(tableName string) (bool, error) {
+ sqlStr, args := session.engine.dialect.TableCheckSql(tableName)
+ results, err := session.queryBytes(sqlStr, args...)
+ return len(results) > 0, err
+}
+
+// IsTableEmpty if table have any records
+func (session *Session) IsTableEmpty(bean interface{}) (bool, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+ return session.isTableEmpty(session.engine.TableName(bean))
+}
+
+func (session *Session) isTableEmpty(tableName string) (bool, error) {
+ var total int64
+ sqlStr := fmt.Sprintf("select count(*) from %s", session.engine.Quote(session.engine.TableName(tableName, true)))
+ err := session.queryRow(sqlStr).Scan(&total)
+ if err != nil {
+ if err == sql.ErrNoRows {
+ err = nil
+ }
+ return true, err
+ }
+
+ return total == 0, nil
+}
+
+// find if index is exist according cols
+func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) {
+ indexes, err := session.engine.dialect.GetIndexes(tableName)
+ if err != nil {
+ return false, err
+ }
+
+ for _, index := range indexes {
+ if sliceEq(index.Cols, cols) {
+ if unique {
+ return index.Type == core.UniqueType, nil
+ }
+ return index.Type == core.IndexType, nil
+ }
+ }
+ return false, nil
+}
+
+func (session *Session) addColumn(colName string) error {
+ col := session.statement.RefTable.GetColumn(colName)
+ sql, args := session.statement.genAddColumnStr(col)
+ _, err := session.exec(sql, args...)
+ return err
+}
+
+func (session *Session) addIndex(tableName, idxName string) error {
+ index := session.statement.RefTable.Indexes[idxName]
+ sqlStr := session.engine.dialect.CreateIndexSql(tableName, index)
+ _, err := session.exec(sqlStr)
+ return err
+}
+
+func (session *Session) addUnique(tableName, uqeName string) error {
+ index := session.statement.RefTable.Indexes[uqeName]
+ sqlStr := session.engine.dialect.CreateIndexSql(tableName, index)
+ _, err := session.exec(sqlStr)
+ return err
+}
+
+// Sync2 synchronize structs to database tables
+func (session *Session) Sync2(beans ...interface{}) error {
+ engine := session.engine
+
+ if session.isAutoClose {
+ session.isAutoClose = false
+ defer session.Close()
+ }
+
+ tables, err := engine.dialect.GetTables()
+ if err != nil {
+ return err
+ }
+
+ session.autoResetStatement = false
+ defer func() {
+ session.autoResetStatement = true
+ session.resetStatement()
+ }()
+
+ for _, bean := range beans {
+ v := rValue(bean)
+ table, err := engine.mapType(v)
+ if err != nil {
+ return err
+ }
+ var tbName string
+ if len(session.statement.AltTableName) > 0 {
+ tbName = session.statement.AltTableName
+ } else {
+ tbName = engine.TableName(bean)
+ }
+ tbNameWithSchema := engine.tbNameWithSchema(tbName)
+
+ var oriTable *core.Table
+ for _, tb := range tables {
+ if strings.EqualFold(engine.tbNameWithSchema(tb.Name), engine.tbNameWithSchema(tbName)) {
+ oriTable = tb
+ break
+ }
+ }
+
+ // this is a new table
+ if oriTable == nil {
+ err = session.StoreEngine(session.statement.StoreEngine).createTable(bean)
+ if err != nil {
+ return err
+ }
+
+ err = session.createUniques(bean)
+ if err != nil {
+ return err
+ }
+
+ err = session.createIndexes(bean)
+ if err != nil {
+ return err
+ }
+ continue
+ }
+
+ // this will modify an old table
+ if err = engine.loadTableInfo(oriTable); err != nil {
+ return err
+ }
+
+ // check columns
+ for _, col := range table.Columns() {
+ var oriCol *core.Column
+ for _, col2 := range oriTable.Columns() {
+ if strings.EqualFold(col.Name, col2.Name) {
+ oriCol = col2
+ break
+ }
+ }
+
+ // column is not exist on table
+ if oriCol == nil {
+ session.statement.RefTable = table
+ session.statement.tableName = tbNameWithSchema
+ if err = session.addColumn(col.Name); err != nil {
+ return err
+ }
+ continue
+ }
+
+ err = nil
+ expectedType := engine.dialect.SqlType(col)
+ curType := engine.dialect.SqlType(oriCol)
+ if expectedType != curType {
+ if expectedType == core.Text &&
+ strings.HasPrefix(curType, core.Varchar) {
+ // currently only support mysql & postgres
+ if engine.dialect.DBType() == core.MYSQL ||
+ engine.dialect.DBType() == core.POSTGRES {
+ engine.logger.Infof("Table %s column %s change type from %s to %s\n",
+ tbNameWithSchema, col.Name, curType, expectedType)
+ _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col))
+ } else {
+ engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n",
+ tbNameWithSchema, col.Name, curType, expectedType)
+ }
+ } else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) {
+ if engine.dialect.DBType() == core.MYSQL {
+ if oriCol.Length < col.Length {
+ engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n",
+ tbNameWithSchema, col.Name, oriCol.Length, col.Length)
+ _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col))
+ }
+ }
+ } else {
+ if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') {
+ engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s",
+ tbNameWithSchema, col.Name, curType, expectedType)
+ }
+ }
+ } else if expectedType == core.Varchar {
+ if engine.dialect.DBType() == core.MYSQL {
+ if oriCol.Length < col.Length {
+ engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n",
+ tbNameWithSchema, col.Name, oriCol.Length, col.Length)
+ _, err = session.exec(engine.dialect.ModifyColumnSql(tbNameWithSchema, col))
+ }
+ }
+ }
+
+ if col.Default != oriCol.Default {
+ if (col.SQLType.Name == core.Bool || col.SQLType.Name == core.Boolean) &&
+ ((strings.EqualFold(col.Default, "true") && oriCol.Default == "1") ||
+ (strings.EqualFold(col.Default, "false") && oriCol.Default == "0")) {
+ } else {
+ engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s",
+ tbName, col.Name, oriCol.Default, col.Default)
+ }
+ }
+ if col.Nullable != oriCol.Nullable {
+ engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v",
+ tbName, col.Name, oriCol.Nullable, col.Nullable)
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ var foundIndexNames = make(map[string]bool)
+ var addedNames = make(map[string]*core.Index)
+
+ for name, index := range table.Indexes {
+ var oriIndex *core.Index
+ for name2, index2 := range oriTable.Indexes {
+ if index.Equal(index2) {
+ oriIndex = index2
+ foundIndexNames[name2] = true
+ break
+ }
+ }
+
+ if oriIndex != nil {
+ if oriIndex.Type != index.Type {
+ sql := engine.dialect.DropIndexSql(tbNameWithSchema, oriIndex)
+ _, err = session.exec(sql)
+ if err != nil {
+ return err
+ }
+ oriIndex = nil
+ }
+ }
+
+ if oriIndex == nil {
+ addedNames[name] = index
+ }
+ }
+
+ for name2, index2 := range oriTable.Indexes {
+ if _, ok := foundIndexNames[name2]; !ok {
+ sql := engine.dialect.DropIndexSql(tbNameWithSchema, index2)
+ _, err = session.exec(sql)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ for name, index := range addedNames {
+ if index.Type == core.UniqueType {
+ session.statement.RefTable = table
+ session.statement.tableName = tbNameWithSchema
+ err = session.addUnique(tbNameWithSchema, name)
+ } else if index.Type == core.IndexType {
+ session.statement.RefTable = table
+ session.statement.tableName = tbNameWithSchema
+ err = session.addIndex(tbNameWithSchema, name)
+ }
+ if err != nil {
+ return err
+ }
+ }
+
+ // check all the columns which removed from struct fields but left on database tables.
+ for _, colName := range oriTable.ColumnsSeq() {
+ if table.GetColumn(colName) == nil {
+ engine.logger.Warnf("Table %s has column %s but struct has not related field", engine.TableName(oriTable.Name, true), colName)
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/session_stats.go b/vendor/xorm.io/xorm/session_stats.go
new file mode 100644
index 0000000000..c2cac83069
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_stats.go
@@ -0,0 +1,98 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql"
+ "errors"
+ "reflect"
+)
+
+// Count counts the records. bean's non-empty fields
+// are conditions.
+func (session *Session) Count(bean ...interface{}) (int64, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ var sqlStr string
+ var args []interface{}
+ var err error
+ if session.statement.RawSQL == "" {
+ sqlStr, args, err = session.statement.genCountSQL(bean...)
+ if err != nil {
+ return 0, err
+ }
+ } else {
+ sqlStr = session.statement.RawSQL
+ args = session.statement.RawParams
+ }
+
+ var total int64
+ err = session.queryRow(sqlStr, args...).Scan(&total)
+ if err == sql.ErrNoRows || err == nil {
+ return total, nil
+ }
+
+ return 0, err
+}
+
+// sum call sum some column. bean's non-empty fields are conditions.
+func (session *Session) sum(res interface{}, bean interface{}, columnNames ...string) error {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ v := reflect.ValueOf(res)
+ if v.Kind() != reflect.Ptr {
+ return errors.New("need a pointer to a variable")
+ }
+
+ var isSlice = v.Elem().Kind() == reflect.Slice
+ var sqlStr string
+ var args []interface{}
+ var err error
+ if len(session.statement.RawSQL) == 0 {
+ sqlStr, args, err = session.statement.genSumSQL(bean, columnNames...)
+ if err != nil {
+ return err
+ }
+ } else {
+ sqlStr = session.statement.RawSQL
+ args = session.statement.RawParams
+ }
+
+ if isSlice {
+ err = session.queryRow(sqlStr, args...).ScanSlice(res)
+ } else {
+ err = session.queryRow(sqlStr, args...).Scan(res)
+ }
+ if err == sql.ErrNoRows || err == nil {
+ return nil
+ }
+ return err
+}
+
+// Sum call sum some column. bean's non-empty fields are conditions.
+func (session *Session) Sum(bean interface{}, columnName string) (res float64, err error) {
+ return res, session.sum(&res, bean, columnName)
+}
+
+// SumInt call sum some column. bean's non-empty fields are conditions.
+func (session *Session) SumInt(bean interface{}, columnName string) (res int64, err error) {
+ return res, session.sum(&res, bean, columnName)
+}
+
+// Sums call sum some columns. bean's non-empty fields are conditions.
+func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) {
+ var res = make([]float64, len(columnNames), len(columnNames))
+ return res, session.sum(&res, bean, columnNames...)
+}
+
+// SumsInt sum specify columns and return as []int64 instead of []float64
+func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) {
+ var res = make([]int64, len(columnNames), len(columnNames))
+ return res, session.sum(&res, bean, columnNames...)
+}
diff --git a/vendor/xorm.io/xorm/session_tx.go b/vendor/xorm.io/xorm/session_tx.go
new file mode 100644
index 0000000000..ee3d473f95
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_tx.go
@@ -0,0 +1,83 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+// Begin a transaction
+func (session *Session) Begin() error {
+ if session.isAutoCommit {
+ tx, err := session.DB().BeginTx(session.ctx, nil)
+ if err != nil {
+ return err
+ }
+ session.isAutoCommit = false
+ session.isCommitedOrRollbacked = false
+ session.tx = tx
+ session.saveLastSQL("BEGIN TRANSACTION")
+ }
+ return nil
+}
+
+// Rollback When using transaction, you can rollback if any error
+func (session *Session) Rollback() error {
+ if !session.isAutoCommit && !session.isCommitedOrRollbacked {
+ session.saveLastSQL(session.engine.dialect.RollBackStr())
+ session.isCommitedOrRollbacked = true
+ session.isAutoCommit = true
+ return session.tx.Rollback()
+ }
+ return nil
+}
+
+// Commit When using transaction, Commit will commit all operations.
+func (session *Session) Commit() error {
+ if !session.isAutoCommit && !session.isCommitedOrRollbacked {
+ session.saveLastSQL("COMMIT")
+ session.isCommitedOrRollbacked = true
+ session.isAutoCommit = true
+ var err error
+ if err = session.tx.Commit(); err == nil {
+ // handle processors after tx committed
+ closureCallFunc := func(closuresPtr *[]func(interface{}), bean interface{}) {
+ if closuresPtr != nil {
+ for _, closure := range *closuresPtr {
+ closure(bean)
+ }
+ }
+ }
+
+ for bean, closuresPtr := range session.afterInsertBeans {
+ closureCallFunc(closuresPtr, bean)
+
+ if processor, ok := interface{}(bean).(AfterInsertProcessor); ok {
+ processor.AfterInsert()
+ }
+ }
+ for bean, closuresPtr := range session.afterUpdateBeans {
+ closureCallFunc(closuresPtr, bean)
+
+ if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok {
+ processor.AfterUpdate()
+ }
+ }
+ for bean, closuresPtr := range session.afterDeleteBeans {
+ closureCallFunc(closuresPtr, bean)
+
+ if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok {
+ processor.AfterDelete()
+ }
+ }
+ cleanUpFunc := func(slices *map[interface{}]*[]func(interface{})) {
+ if len(*slices) > 0 {
+ *slices = make(map[interface{}]*[]func(interface{}), 0)
+ }
+ }
+ cleanUpFunc(&session.afterInsertBeans)
+ cleanUpFunc(&session.afterUpdateBeans)
+ cleanUpFunc(&session.afterDeleteBeans)
+ }
+ return err
+ }
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/session_update.go b/vendor/xorm.io/xorm/session_update.go
new file mode 100644
index 0000000000..c5c65a452a
--- /dev/null
+++ b/vendor/xorm.io/xorm/session_update.go
@@ -0,0 +1,525 @@
+// Copyright 2016 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, args ...interface{}) error {
+ if table == nil ||
+ session.tx != nil {
+ return ErrCacheFailed
+ }
+
+ oldhead, newsql := session.statement.convertUpdateSQL(sqlStr)
+ if newsql == "" {
+ return ErrCacheFailed
+ }
+ for _, filter := range session.engine.dialect.Filters() {
+ newsql = filter.Do(newsql, session.engine.dialect, table)
+ }
+ session.engine.logger.Debug("[cacheUpdate] new sql", oldhead, newsql)
+
+ var nStart int
+ if len(args) > 0 {
+ if strings.Index(sqlStr, "?") > -1 {
+ nStart = strings.Count(oldhead, "?")
+ } else {
+ // only for pq, TODO: if any other databse?
+ nStart = strings.Count(oldhead, "$")
+ }
+ }
+
+ cacher := session.engine.getCacher(tableName)
+ session.engine.logger.Debug("[cacheUpdate] get cache sql", newsql, args[nStart:])
+ ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:])
+ if err != nil {
+ rows, err := session.NoCache().queryRows(newsql, args[nStart:]...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ ids = make([]core.PK, 0)
+ for rows.Next() {
+ var res = make([]string, len(table.PrimaryKeys))
+ err = rows.ScanSlice(&res)
+ if err != nil {
+ return err
+ }
+ var pk core.PK = make([]interface{}, len(table.PrimaryKeys))
+ for i, col := range table.PKColumns() {
+ if col.SQLType.IsNumeric() {
+ n, err := strconv.ParseInt(res[i], 10, 64)
+ if err != nil {
+ return err
+ }
+ pk[i] = n
+ } else if col.SQLType.IsText() {
+ pk[i] = res[i]
+ } else {
+ return errors.New("not supported")
+ }
+ }
+
+ ids = append(ids, pk)
+ }
+ session.engine.logger.Debug("[cacheUpdate] find updated id", ids)
+ } /*else {
+ session.engine.LogDebug("[xorm:cacheUpdate] del cached sql:", tableName, newsql, args)
+ cacher.DelIds(tableName, genSqlKey(newsql, args))
+ }*/
+
+ for _, id := range ids {
+ sid, err := id.ToString()
+ if err != nil {
+ return err
+ }
+ if bean := cacher.GetBean(tableName, sid); bean != nil {
+ sqls := splitNNoCase(sqlStr, "where", 2)
+ if len(sqls) == 0 || len(sqls) > 2 {
+ return ErrCacheFailed
+ }
+
+ sqls = splitNNoCase(sqls[0], "set", 2)
+ if len(sqls) != 2 {
+ return ErrCacheFailed
+ }
+ kvs := strings.Split(strings.TrimSpace(sqls[1]), ",")
+
+ for idx, kv := range kvs {
+ sps := strings.SplitN(kv, "=", 2)
+ sps2 := strings.Split(sps[0], ".")
+ colName := sps2[len(sps2)-1]
+ // treat quote prefix, suffix and '`' as quotes
+ quotes := append(strings.Split(session.engine.Quote(""), ""), "`")
+ if strings.ContainsAny(colName, strings.Join(quotes, "")) {
+ colName = strings.TrimSpace(eraseAny(colName, quotes...))
+ } else {
+ session.engine.logger.Debug("[cacheUpdate] cannot find column", tableName, colName)
+ return ErrCacheFailed
+ }
+
+ if col := table.GetColumn(colName); col != nil {
+ fieldValue, err := col.ValueOf(bean)
+ if err != nil {
+ session.engine.logger.Error(err)
+ } else {
+ session.engine.logger.Debug("[cacheUpdate] set bean field", bean, colName, fieldValue.Interface())
+ if col.IsVersion && session.statement.checkVersion {
+ session.incrVersionFieldValue(fieldValue)
+ } else {
+ fieldValue.Set(reflect.ValueOf(args[idx]))
+ }
+ }
+ } else {
+ session.engine.logger.Errorf("[cacheUpdate] ERROR: column %v is not table %v's",
+ colName, table.Name)
+ }
+ }
+
+ session.engine.logger.Debug("[cacheUpdate] update cache", tableName, id, bean)
+ cacher.PutBean(tableName, sid, bean)
+ }
+ }
+ session.engine.logger.Debug("[cacheUpdate] clear cached table sql:", tableName)
+ cacher.ClearIds(tableName)
+ return nil
+}
+
+// Update records, bean's non-empty fields are updated contents,
+// condiBean' non-empty filds are conditions
+// CAUTION:
+// 1.bool will defaultly be updated content nor conditions
+// You should call UseBool if you have bool to use.
+// 2.float32 & float64 may be not inexact as conditions
+func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) {
+ if session.isAutoClose {
+ defer session.Close()
+ }
+
+ if session.statement.lastError != nil {
+ return 0, session.statement.lastError
+ }
+
+ v := rValue(bean)
+ t := v.Type()
+
+ var colNames []string
+ var args []interface{}
+
+ // handle before update processors
+ for _, closure := range session.beforeClosures {
+ closure(bean)
+ }
+ cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used
+ if processor, ok := interface{}(bean).(BeforeUpdateProcessor); ok {
+ processor.BeforeUpdate()
+ }
+ // --
+
+ var err error
+ var isMap = t.Kind() == reflect.Map
+ var isStruct = t.Kind() == reflect.Struct
+ if isStruct {
+ if err := session.statement.setRefBean(bean); err != nil {
+ return 0, err
+ }
+
+ if len(session.statement.TableName()) <= 0 {
+ return 0, ErrTableNotFound
+ }
+
+ if session.statement.ColumnStr == "" {
+ colNames, args = session.statement.buildUpdates(bean, false, false,
+ false, false, true)
+ } else {
+ colNames, args, err = session.genUpdateColumns(bean)
+ if err != nil {
+ return 0, err
+ }
+ }
+ } else if isMap {
+ colNames = make([]string, 0)
+ args = make([]interface{}, 0)
+ bValue := reflect.Indirect(reflect.ValueOf(bean))
+
+ for _, v := range bValue.MapKeys() {
+ colNames = append(colNames, session.engine.Quote(v.String())+" = ?")
+ args = append(args, bValue.MapIndex(v).Interface())
+ }
+ } else {
+ return 0, ErrParamsType
+ }
+
+ table := session.statement.RefTable
+
+ if session.statement.UseAutoTime && table != nil && table.Updated != "" {
+ if !session.statement.columnMap.contain(table.Updated) &&
+ !session.statement.omitColumnMap.contain(table.Updated) {
+ colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?")
+ col := table.UpdatedColumn()
+ val, t := session.engine.nowTime(col)
+ args = append(args, val)
+
+ var colName = col.Name
+ if isStruct {
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ }
+ }
+ }
+
+ // for update action to like "column = column + ?"
+ incColumns := session.statement.incrColumns
+ for i, colName := range incColumns.colNames {
+ colNames = append(colNames, session.engine.Quote(colName)+" = "+session.engine.Quote(colName)+" + ?")
+ args = append(args, incColumns.args[i])
+ }
+ // for update action to like "column = column - ?"
+ decColumns := session.statement.decrColumns
+ for i, colName := range decColumns.colNames {
+ colNames = append(colNames, session.engine.Quote(colName)+" = "+session.engine.Quote(colName)+" - ?")
+ args = append(args, decColumns.args[i])
+ }
+ // for update action to like "column = expression"
+ exprColumns := session.statement.exprColumns
+ for i, colName := range exprColumns.colNames {
+ switch tp := exprColumns.args[i].(type) {
+ case string:
+ 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+")")
+ args = append(args, subArgs...)
+ }
+ }
+
+ if err = session.statement.processIDParam(); err != nil {
+ return 0, err
+ }
+
+ var autoCond builder.Cond
+ if !session.statement.noAutoCondition {
+ condBeanIsStruct := false
+ if len(condiBean) > 0 {
+ if c, ok := condiBean[0].(map[string]interface{}); ok {
+ autoCond = builder.Eq(c)
+ } else {
+ ct := reflect.TypeOf(condiBean[0])
+ k := ct.Kind()
+ if k == reflect.Ptr {
+ k = ct.Elem().Kind()
+ }
+ if k == reflect.Struct {
+ var err error
+ autoCond, err = session.statement.buildConds(session.statement.RefTable, condiBean[0], true, true, false, true, false)
+ if err != nil {
+ return 0, err
+ }
+ condBeanIsStruct = true
+ } else {
+ return 0, ErrConditionType
+ }
+ }
+ }
+
+ if !condBeanIsStruct && table != nil {
+ if col := table.DeletedColumn(); col != nil && !session.statement.unscoped { // tag "deleted" is enabled
+ autoCond1 := session.engine.CondDeleted(session.engine.Quote(col.Name))
+
+ if autoCond == nil {
+ autoCond = autoCond1
+ } else {
+ autoCond = autoCond.And(autoCond1)
+ }
+ }
+ }
+ }
+
+ st := &session.statement
+
+ var sqlStr string
+ var condArgs []interface{}
+ var condSQL string
+ cond := session.statement.cond.And(autoCond)
+
+ var doIncVer = (table != nil && table.Version != "" && session.statement.checkVersion)
+ var verValue *reflect.Value
+ if doIncVer {
+ verValue, err = table.VersionColumn().ValueOf(bean)
+ if err != nil {
+ return 0, err
+ }
+
+ cond = cond.And(builder.Eq{session.engine.Quote(table.Version): verValue.Interface()})
+ colNames = append(colNames, session.engine.Quote(table.Version)+" = "+session.engine.Quote(table.Version)+" + 1")
+ }
+
+ condSQL, condArgs, err = builder.ToSQL(cond)
+ if err != nil {
+ return 0, err
+ }
+
+ if len(condSQL) > 0 {
+ condSQL = "WHERE " + condSQL
+ }
+
+ if st.OrderStr != "" {
+ condSQL = condSQL + fmt.Sprintf(" ORDER BY %v", st.OrderStr)
+ }
+
+ var tableName = session.statement.TableName()
+ // TODO: Oracle support needed
+ var top string
+ if st.LimitN > 0 {
+ if st.Engine.dialect.DBType() == core.MYSQL {
+ condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
+ } else if st.Engine.dialect.DBType() == core.SQLITE {
+ tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
+ cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)",
+ session.engine.Quote(tableName), tempCondSQL), condArgs...))
+ condSQL, condArgs, err = builder.ToSQL(cond)
+ if err != nil {
+ return 0, err
+ }
+ if len(condSQL) > 0 {
+ condSQL = "WHERE " + condSQL
+ }
+ } else if st.Engine.dialect.DBType() == core.POSTGRES {
+ tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
+ cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)",
+ session.engine.Quote(tableName), tempCondSQL), condArgs...))
+ condSQL, condArgs, err = builder.ToSQL(cond)
+ if err != nil {
+ return 0, err
+ }
+
+ if len(condSQL) > 0 {
+ condSQL = "WHERE " + condSQL
+ }
+ } else if st.Engine.dialect.DBType() == core.MSSQL {
+ if st.OrderStr != "" && st.Engine.dialect.DBType() == core.MSSQL &&
+ table != nil && len(table.PrimaryKeys) == 1 {
+ cond = builder.Expr(fmt.Sprintf("%s IN (SELECT TOP (%d) %s FROM %v%v)",
+ table.PrimaryKeys[0], st.LimitN, table.PrimaryKeys[0],
+ session.engine.Quote(tableName), condSQL), condArgs...)
+
+ condSQL, condArgs, err = builder.ToSQL(cond)
+ if err != nil {
+ return 0, err
+ }
+ if len(condSQL) > 0 {
+ condSQL = "WHERE " + condSQL
+ }
+ } else {
+ top = fmt.Sprintf("TOP (%d) ", st.LimitN)
+ }
+ }
+ }
+
+ if len(colNames) <= 0 {
+ return 0, errors.New("No content found to be updated")
+ }
+
+ sqlStr = fmt.Sprintf("UPDATE %v%v SET %v %v",
+ top,
+ session.engine.Quote(tableName),
+ strings.Join(colNames, ", "),
+ condSQL)
+
+ res, err := session.exec(sqlStr, append(args, condArgs...)...)
+ if err != nil {
+ return 0, err
+ } else if doIncVer {
+ if verValue != nil && verValue.IsValid() && verValue.CanSet() {
+ session.incrVersionFieldValue(verValue)
+ }
+ }
+
+ if cacher := session.engine.getCacher(tableName); cacher != nil && session.statement.UseCache {
+ // session.cacheUpdate(table, tableName, sqlStr, args...)
+ session.engine.logger.Debug("[cacheUpdate] clear table ", tableName)
+ cacher.ClearIds(tableName)
+ cacher.ClearBeans(tableName)
+ }
+
+ // handle after update processors
+ if session.isAutoCommit {
+ for _, closure := range session.afterClosures {
+ closure(bean)
+ }
+ if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok {
+ session.engine.logger.Debug("[event]", tableName, " has after update processor")
+ processor.AfterUpdate()
+ }
+ } else {
+ lenAfterClosures := len(session.afterClosures)
+ if lenAfterClosures > 0 {
+ if value, has := session.afterUpdateBeans[bean]; has && value != nil {
+ *value = append(*value, session.afterClosures...)
+ } else {
+ afterClosures := make([]func(interface{}), lenAfterClosures)
+ copy(afterClosures, session.afterClosures)
+ // FIXME: if bean is a map type, it will panic because map cannot be as map key
+ session.afterUpdateBeans[bean] = &afterClosures
+ }
+
+ } else {
+ if _, ok := interface{}(bean).(AfterUpdateProcessor); ok {
+ session.afterUpdateBeans[bean] = nil
+ }
+ }
+ }
+ cleanupProcessorsClosures(&session.afterClosures) // cleanup after used
+ // --
+
+ return res.RowsAffected()
+}
+
+func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interface{}, error) {
+ table := session.statement.RefTable
+ colNames := make([]string, 0, len(table.ColumnsSeq()))
+ args := make([]interface{}, 0, len(table.ColumnsSeq()))
+
+ for _, col := range table.Columns() {
+ if !col.IsVersion && !col.IsCreated && !col.IsUpdated {
+ if session.statement.omitColumnMap.contain(col.Name) {
+ continue
+ }
+ }
+ if col.MapType == core.ONLYFROMDB {
+ continue
+ }
+
+ fieldValuePtr, err := col.ValueOf(bean)
+ if err != nil {
+ return nil, nil, err
+ }
+ fieldValue := *fieldValuePtr
+
+ if col.IsAutoIncrement {
+ switch fieldValue.Type().Kind() {
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64:
+ if fieldValue.Int() == 0 {
+ continue
+ }
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64:
+ if fieldValue.Uint() == 0 {
+ continue
+ }
+ case reflect.String:
+ if len(fieldValue.String()) == 0 {
+ continue
+ }
+ case reflect.Ptr:
+ if fieldValue.Pointer() == 0 {
+ continue
+ }
+ }
+ }
+
+ if (col.IsDeleted && !session.statement.unscoped) || col.IsCreated {
+ continue
+ }
+
+ // if only update specify columns
+ if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) {
+ continue
+ }
+
+ if session.statement.incrColumns.isColExist(col.Name) {
+ continue
+ } else if session.statement.decrColumns.isColExist(col.Name) {
+ continue
+ } else if session.statement.exprColumns.isColExist(col.Name) {
+ continue
+ }
+
+ // !evalphobia! set fieldValue as nil when column is nullable and zero-value
+ if _, ok := getFlagForColumn(session.statement.nullableMap, col); ok {
+ if col.Nullable && isZero(fieldValue.Interface()) {
+ var nilValue *int
+ fieldValue = reflect.ValueOf(nilValue)
+ }
+ }
+
+ if col.IsUpdated && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
+ // if time is non-empty, then set to auto time
+ val, t := session.engine.nowTime(col)
+ args = append(args, val)
+
+ var colName = col.Name
+ session.afterClosures = append(session.afterClosures, func(bean interface{}) {
+ col := table.GetColumn(colName)
+ setColumnTime(bean, col, t)
+ })
+ } else if col.IsVersion && session.statement.checkVersion {
+ args = append(args, 1)
+ } else {
+ arg, err := session.value2Interface(col, fieldValue)
+ if err != nil {
+ return colNames, args, err
+ }
+ args = append(args, arg)
+ }
+
+ colNames = append(colNames, session.engine.Quote(col.Name)+" = ?")
+ }
+ return colNames, args, nil
+}
diff --git a/vendor/xorm.io/xorm/statement.go b/vendor/xorm.io/xorm/statement.go
new file mode 100644
index 0000000000..67e352136f
--- /dev/null
+++ b/vendor/xorm.io/xorm/statement.go
@@ -0,0 +1,1256 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "database/sql/driver"
+ "fmt"
+ "reflect"
+ "strings"
+ "time"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+// Statement save all the sql info for executing SQL
+type Statement struct {
+ RefTable *core.Table
+ Engine *Engine
+ Start int
+ LimitN int
+ idParam *core.PK
+ OrderStr string
+ JoinStr string
+ joinArgs []interface{}
+ GroupByStr string
+ HavingStr string
+ ColumnStr string
+ selectStr string
+ useAllCols bool
+ OmitStr string
+ AltTableName string
+ tableName string
+ RawSQL string
+ RawParams []interface{}
+ UseCascade bool
+ UseAutoJoin bool
+ StoreEngine string
+ Charset string
+ UseCache bool
+ UseAutoTime bool
+ noAutoCondition bool
+ IsDistinct bool
+ IsForUpdate bool
+ TableAlias string
+ allUseBool bool
+ checkVersion bool
+ unscoped bool
+ columnMap columnMap
+ omitColumnMap columnMap
+ mustColumnMap map[string]bool
+ nullableMap map[string]bool
+ incrColumns exprParams
+ decrColumns exprParams
+ exprColumns exprParams
+ cond builder.Cond
+ bufferSize int
+ context ContextCache
+ lastError error
+}
+
+// Init reset all the statement's fields
+func (statement *Statement) Init() {
+ statement.RefTable = nil
+ statement.Start = 0
+ statement.LimitN = 0
+ statement.OrderStr = ""
+ statement.UseCascade = true
+ statement.JoinStr = ""
+ statement.joinArgs = make([]interface{}, 0)
+ statement.GroupByStr = ""
+ statement.HavingStr = ""
+ statement.ColumnStr = ""
+ statement.OmitStr = ""
+ statement.columnMap = columnMap{}
+ statement.omitColumnMap = columnMap{}
+ statement.AltTableName = ""
+ statement.tableName = ""
+ statement.idParam = nil
+ statement.RawSQL = ""
+ statement.RawParams = make([]interface{}, 0)
+ statement.UseCache = true
+ statement.UseAutoTime = true
+ statement.noAutoCondition = false
+ statement.IsDistinct = false
+ statement.IsForUpdate = false
+ statement.TableAlias = ""
+ statement.selectStr = ""
+ statement.allUseBool = false
+ statement.useAllCols = false
+ statement.mustColumnMap = make(map[string]bool)
+ statement.nullableMap = make(map[string]bool)
+ statement.checkVersion = true
+ statement.unscoped = false
+ statement.incrColumns = exprParams{}
+ statement.decrColumns = exprParams{}
+ statement.exprColumns = exprParams{}
+ statement.cond = builder.NewCond()
+ statement.bufferSize = 0
+ statement.context = nil
+ statement.lastError = nil
+}
+
+// NoAutoCondition if you do not want convert bean's field as query condition, then use this function
+func (statement *Statement) NoAutoCondition(no ...bool) *Statement {
+ statement.noAutoCondition = true
+ if len(no) > 0 {
+ statement.noAutoCondition = no[0]
+ }
+ return statement
+}
+
+// Alias set the table alias
+func (statement *Statement) Alias(alias string) *Statement {
+ statement.TableAlias = alias
+ return statement
+}
+
+// SQL adds raw sql statement
+func (statement *Statement) SQL(query interface{}, args ...interface{}) *Statement {
+ switch query.(type) {
+ case (*builder.Builder):
+ var err error
+ statement.RawSQL, statement.RawParams, err = query.(*builder.Builder).ToSQL()
+ if err != nil {
+ statement.lastError = err
+ }
+ case string:
+ statement.RawSQL = query.(string)
+ statement.RawParams = args
+ default:
+ statement.lastError = ErrUnSupportedSQLType
+ }
+
+ return statement
+}
+
+// Where add Where statement
+func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement {
+ return statement.And(query, args...)
+}
+
+// And add Where & and statement
+func (statement *Statement) And(query interface{}, args ...interface{}) *Statement {
+ switch query.(type) {
+ case string:
+ cond := builder.Expr(query.(string), args...)
+ statement.cond = statement.cond.And(cond)
+ case map[string]interface{}:
+ queryMap := query.(map[string]interface{})
+ newMap := make(map[string]interface{})
+ for k, v := range queryMap {
+ newMap[statement.Engine.Quote(k)] = v
+ }
+ statement.cond = statement.cond.And(builder.Eq(newMap))
+ case builder.Cond:
+ cond := query.(builder.Cond)
+ statement.cond = statement.cond.And(cond)
+ for _, v := range args {
+ if vv, ok := v.(builder.Cond); ok {
+ statement.cond = statement.cond.And(vv)
+ }
+ }
+ default:
+ statement.lastError = ErrConditionType
+ }
+
+ return statement
+}
+
+// Or add Where & Or statement
+func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement {
+ switch query.(type) {
+ case string:
+ cond := builder.Expr(query.(string), args...)
+ statement.cond = statement.cond.Or(cond)
+ case map[string]interface{}:
+ cond := builder.Eq(query.(map[string]interface{}))
+ statement.cond = statement.cond.Or(cond)
+ case builder.Cond:
+ cond := query.(builder.Cond)
+ statement.cond = statement.cond.Or(cond)
+ for _, v := range args {
+ if vv, ok := v.(builder.Cond); ok {
+ statement.cond = statement.cond.Or(vv)
+ }
+ }
+ default:
+ // TODO: not support condition type
+ }
+ return statement
+}
+
+// In generate "Where column IN (?) " statement
+func (statement *Statement) In(column string, args ...interface{}) *Statement {
+ in := builder.In(statement.Engine.Quote(column), args...)
+ statement.cond = statement.cond.And(in)
+ return statement
+}
+
+// NotIn generate "Where column NOT IN (?) " statement
+func (statement *Statement) NotIn(column string, args ...interface{}) *Statement {
+ notIn := builder.NotIn(statement.Engine.Quote(column), args...)
+ statement.cond = statement.cond.And(notIn)
+ return statement
+}
+
+func (statement *Statement) setRefValue(v reflect.Value) error {
+ var err error
+ statement.RefTable, err = statement.Engine.autoMapType(reflect.Indirect(v))
+ if err != nil {
+ return err
+ }
+ statement.tableName = statement.Engine.TableName(v, true)
+ return nil
+}
+
+func (statement *Statement) setRefBean(bean interface{}) error {
+ var err error
+ statement.RefTable, err = statement.Engine.autoMapType(rValue(bean))
+ if err != nil {
+ return err
+ }
+ statement.tableName = statement.Engine.TableName(bean, true)
+ return nil
+}
+
+// Auto generating update columnes and values according a struct
+func (statement *Statement) buildUpdates(bean interface{},
+ includeVersion, includeUpdated, includeNil,
+ includeAutoIncr, update bool) ([]string, []interface{}) {
+ engine := statement.Engine
+ table := statement.RefTable
+ allUseBool := statement.allUseBool
+ useAllCols := statement.useAllCols
+ mustColumnMap := statement.mustColumnMap
+ nullableMap := statement.nullableMap
+ columnMap := statement.columnMap
+ omitColumnMap := statement.omitColumnMap
+ unscoped := statement.unscoped
+
+ var colNames = make([]string, 0)
+ var args = make([]interface{}, 0)
+ for _, col := range table.Columns() {
+ if !includeVersion && col.IsVersion {
+ continue
+ }
+ if col.IsCreated {
+ continue
+ }
+ if !includeUpdated && col.IsUpdated {
+ continue
+ }
+ if !includeAutoIncr && col.IsAutoIncrement {
+ continue
+ }
+ if col.IsDeleted && !unscoped {
+ continue
+ }
+ if omitColumnMap.contain(col.Name) {
+ continue
+ }
+ if len(columnMap) > 0 && !columnMap.contain(col.Name) {
+ continue
+ }
+
+ if col.MapType == core.ONLYFROMDB {
+ continue
+ }
+
+ if statement.incrColumns.isColExist(col.Name) {
+ continue
+ } else if statement.decrColumns.isColExist(col.Name) {
+ continue
+ } else if statement.exprColumns.isColExist(col.Name) {
+ continue
+ }
+
+ fieldValuePtr, err := col.ValueOf(bean)
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+
+ fieldValue := *fieldValuePtr
+ fieldType := reflect.TypeOf(fieldValue.Interface())
+ if fieldType == nil {
+ continue
+ }
+
+ requiredField := useAllCols
+ includeNil := useAllCols
+
+ if b, ok := getFlagForColumn(mustColumnMap, col); ok {
+ if b {
+ requiredField = true
+ } else {
+ continue
+ }
+ }
+
+ // !evalphobia! set fieldValue as nil when column is nullable and zero-value
+ if b, ok := getFlagForColumn(nullableMap, col); ok {
+ if b && col.Nullable && isZero(fieldValue.Interface()) {
+ var nilValue *int
+ fieldValue = reflect.ValueOf(nilValue)
+ fieldType = reflect.TypeOf(fieldValue.Interface())
+ includeNil = true
+ }
+ }
+
+ var val interface{}
+
+ if fieldValue.CanAddr() {
+ if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+ data, err := structConvert.ToDB()
+ if err != nil {
+ engine.logger.Error(err)
+ } else {
+ val = data
+ }
+ goto APPEND
+ }
+ }
+
+ if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
+ data, err := structConvert.ToDB()
+ if err != nil {
+ engine.logger.Error(err)
+ } else {
+ val = data
+ }
+ goto APPEND
+ }
+
+ if fieldType.Kind() == reflect.Ptr {
+ if fieldValue.IsNil() {
+ if includeNil {
+ args = append(args, nil)
+ colNames = append(colNames, fmt.Sprintf("%v=?", engine.Quote(col.Name)))
+ }
+ continue
+ } else if !fieldValue.IsValid() {
+ continue
+ } else {
+ // dereference ptr type to instance type
+ fieldValue = fieldValue.Elem()
+ fieldType = reflect.TypeOf(fieldValue.Interface())
+ requiredField = true
+ }
+ }
+
+ switch fieldType.Kind() {
+ case reflect.Bool:
+ if allUseBool || requiredField {
+ val = fieldValue.Interface()
+ } else {
+ // if a bool in a struct, it will not be as a condition because it default is false,
+ // please use Where() instead
+ continue
+ }
+ case reflect.String:
+ if !requiredField && fieldValue.String() == "" {
+ continue
+ }
+ // for MyString, should convert to string or panic
+ if fieldType.String() != reflect.String.String() {
+ val = fieldValue.String()
+ } else {
+ val = fieldValue.Interface()
+ }
+ case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64:
+ if !requiredField && fieldValue.Int() == 0 {
+ continue
+ }
+ val = fieldValue.Interface()
+ case reflect.Float32, reflect.Float64:
+ if !requiredField && fieldValue.Float() == 0.0 {
+ continue
+ }
+ val = fieldValue.Interface()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
+ if !requiredField && fieldValue.Uint() == 0 {
+ continue
+ }
+ t := int64(fieldValue.Uint())
+ val = reflect.ValueOf(&t).Interface()
+ case reflect.Struct:
+ if fieldType.ConvertibleTo(core.TimeType) {
+ t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+ if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
+ continue
+ }
+ val = engine.formatColTime(col, t)
+ } else if nulType, ok := fieldValue.Interface().(driver.Valuer); ok {
+ val, _ = nulType.Value()
+ } else {
+ if !col.SQLType.IsJson() {
+ engine.autoMapType(fieldValue)
+ if table, ok := engine.Tables[fieldValue.Type()]; ok {
+ if len(table.PrimaryKeys) == 1 {
+ pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName)
+ // fix non-int pk issues
+ if pkField.IsValid() && (!requiredField && !isZero(pkField.Interface())) {
+ val = pkField.Interface()
+ } else {
+ continue
+ }
+ } else {
+ // TODO: how to handler?
+ panic("not supported")
+ }
+ } else {
+ val = fieldValue.Interface()
+ }
+ } else {
+ // Blank struct could not be as update data
+ if requiredField || !isStructZero(fieldValue) {
+ bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface()))
+ }
+ if col.SQLType.IsText() {
+ val = string(bytes)
+ } else if col.SQLType.IsBlob() {
+ val = bytes
+ }
+ } else {
+ continue
+ }
+ }
+ }
+ case reflect.Array, reflect.Slice, reflect.Map:
+ if !requiredField {
+ if fieldValue == reflect.Zero(fieldType) {
+ continue
+ }
+ if fieldType.Kind() == reflect.Array {
+ if isArrayValueZero(fieldValue) {
+ continue
+ }
+ } else if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
+ continue
+ }
+ }
+
+ if col.SQLType.IsText() {
+ bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = string(bytes)
+ } else if col.SQLType.IsBlob() {
+ var bytes []byte
+ var err error
+ if fieldType.Kind() == reflect.Slice &&
+ fieldType.Elem().Kind() == reflect.Uint8 {
+ if fieldValue.Len() > 0 {
+ val = fieldValue.Bytes()
+ } else {
+ continue
+ }
+ } else if fieldType.Kind() == reflect.Array &&
+ fieldType.Elem().Kind() == reflect.Uint8 {
+ val = fieldValue.Slice(0, 0).Interface()
+ } else {
+ bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface())
+ if err != nil {
+ engine.logger.Error(err)
+ continue
+ }
+ val = bytes
+ }
+ } else {
+ continue
+ }
+ default:
+ val = fieldValue.Interface()
+ }
+
+ APPEND:
+ args = append(args, val)
+ if col.IsPrimaryKey && engine.dialect.DBType() == "ql" {
+ continue
+ }
+ colNames = append(colNames, fmt.Sprintf("%v = ?", engine.Quote(col.Name)))
+ }
+
+ return colNames, args
+}
+
+func (statement *Statement) needTableName() bool {
+ return len(statement.JoinStr) > 0
+}
+
+func (statement *Statement) colName(col *core.Column, tableName string) string {
+ if statement.needTableName() {
+ var nm = tableName
+ if len(statement.TableAlias) > 0 {
+ nm = statement.TableAlias
+ }
+ return statement.Engine.Quote(nm) + "." + statement.Engine.Quote(col.Name)
+ }
+ return statement.Engine.Quote(col.Name)
+}
+
+// TableName return current tableName
+func (statement *Statement) TableName() string {
+ if statement.AltTableName != "" {
+ return statement.AltTableName
+ }
+
+ return statement.tableName
+}
+
+// ID generate "where id = ? " statement or for composite key "where key1 = ? and key2 = ?"
+func (statement *Statement) ID(id interface{}) *Statement {
+ idValue := reflect.ValueOf(id)
+ idType := reflect.TypeOf(idValue.Interface())
+
+ switch idType {
+ case ptrPkType:
+ if pkPtr, ok := (id).(*core.PK); ok {
+ statement.idParam = pkPtr
+ return statement
+ }
+ case pkType:
+ if pk, ok := (id).(core.PK); ok {
+ statement.idParam = &pk
+ return statement
+ }
+ }
+
+ switch idType.Kind() {
+ case reflect.String:
+ statement.idParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()}
+ return statement
+ }
+
+ statement.idParam = &core.PK{id}
+ return statement
+}
+
+// Incr Generate "Update ... Set column = column + arg" statement
+func (statement *Statement) Incr(column string, arg ...interface{}) *Statement {
+ if len(arg) > 0 {
+ statement.incrColumns.addParam(column, arg[0])
+ } else {
+ statement.incrColumns.addParam(column, 1)
+ }
+ return statement
+}
+
+// Decr Generate "Update ... Set column = column - arg" statement
+func (statement *Statement) Decr(column string, arg ...interface{}) *Statement {
+ if len(arg) > 0 {
+ statement.decrColumns.addParam(column, arg[0])
+ } else {
+ statement.decrColumns.addParam(column, 1)
+ }
+ return statement
+}
+
+// SetExpr Generate "Update ... Set column = {expression}" statement
+func (statement *Statement) SetExpr(column string, expression interface{}) *Statement {
+ statement.exprColumns.addParam(column, expression)
+ return statement
+}
+
+func (statement *Statement) col2NewColsWithQuote(columns ...string) []string {
+ newColumns := make([]string, 0)
+ quotes := append(strings.Split(statement.Engine.Quote(""), ""), "`")
+ for _, col := range columns {
+ newColumns = append(newColumns, statement.Engine.Quote(eraseAny(col, quotes...)))
+ }
+ return newColumns
+}
+
+func (statement *Statement) colmap2NewColsWithQuote() []string {
+ newColumns := make([]string, len(statement.columnMap), len(statement.columnMap))
+ copy(newColumns, statement.columnMap)
+ for i := 0; i < len(statement.columnMap); i++ {
+ newColumns[i] = statement.Engine.Quote(newColumns[i])
+ }
+ return newColumns
+}
+
+// Distinct generates "DISTINCT col1, col2 " statement
+func (statement *Statement) Distinct(columns ...string) *Statement {
+ statement.IsDistinct = true
+ statement.Cols(columns...)
+ return statement
+}
+
+// ForUpdate generates "SELECT ... FOR UPDATE" statement
+func (statement *Statement) ForUpdate() *Statement {
+ statement.IsForUpdate = true
+ return statement
+}
+
+// Select replace select
+func (statement *Statement) Select(str string) *Statement {
+ statement.selectStr = str
+ return statement
+}
+
+// Cols generate "col1, col2" statement
+func (statement *Statement) Cols(columns ...string) *Statement {
+ cols := col2NewCols(columns...)
+ for _, nc := range cols {
+ statement.columnMap.add(nc)
+ }
+
+ newColumns := statement.colmap2NewColsWithQuote()
+
+ statement.ColumnStr = strings.Join(newColumns, ", ")
+ statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.quote("*"), "*", -1)
+ return statement
+}
+
+// AllCols update use only: update all columns
+func (statement *Statement) AllCols() *Statement {
+ statement.useAllCols = true
+ return statement
+}
+
+// MustCols update use only: must update columns
+func (statement *Statement) MustCols(columns ...string) *Statement {
+ newColumns := col2NewCols(columns...)
+ for _, nc := range newColumns {
+ statement.mustColumnMap[strings.ToLower(nc)] = true
+ }
+ return statement
+}
+
+// UseBool indicates that use bool fields as update contents and query contiditions
+func (statement *Statement) UseBool(columns ...string) *Statement {
+ if len(columns) > 0 {
+ statement.MustCols(columns...)
+ } else {
+ statement.allUseBool = true
+ }
+ return statement
+}
+
+// Omit do not use the columns
+func (statement *Statement) Omit(columns ...string) {
+ newColumns := col2NewCols(columns...)
+ for _, nc := range newColumns {
+ statement.omitColumnMap = append(statement.omitColumnMap, nc)
+ }
+ statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", ")))
+}
+
+// Nullable Update use only: update columns to null when value is nullable and zero-value
+func (statement *Statement) Nullable(columns ...string) {
+ newColumns := col2NewCols(columns...)
+ for _, nc := range newColumns {
+ statement.nullableMap[strings.ToLower(nc)] = true
+ }
+}
+
+// Top generate LIMIT limit statement
+func (statement *Statement) Top(limit int) *Statement {
+ statement.Limit(limit)
+ return statement
+}
+
+// Limit generate LIMIT start, limit statement
+func (statement *Statement) Limit(limit int, start ...int) *Statement {
+ statement.LimitN = limit
+ if len(start) > 0 {
+ statement.Start = start[0]
+ }
+ return statement
+}
+
+// OrderBy generate "Order By order" statement
+func (statement *Statement) OrderBy(order string) *Statement {
+ if len(statement.OrderStr) > 0 {
+ statement.OrderStr += ", "
+ }
+ statement.OrderStr += order
+ return statement
+}
+
+// Desc generate `ORDER BY xx DESC`
+func (statement *Statement) Desc(colNames ...string) *Statement {
+ var buf strings.Builder
+ if len(statement.OrderStr) > 0 {
+ fmt.Fprint(&buf, statement.OrderStr, ", ")
+ }
+ newColNames := statement.col2NewColsWithQuote(colNames...)
+ fmt.Fprintf(&buf, "%v DESC", strings.Join(newColNames, " DESC, "))
+ statement.OrderStr = buf.String()
+ return statement
+}
+
+// Asc provide asc order by query condition, the input parameters are columns.
+func (statement *Statement) Asc(colNames ...string) *Statement {
+ var buf strings.Builder
+ if len(statement.OrderStr) > 0 {
+ fmt.Fprint(&buf, statement.OrderStr, ", ")
+ }
+ newColNames := statement.col2NewColsWithQuote(colNames...)
+ fmt.Fprintf(&buf, "%v ASC", strings.Join(newColNames, " ASC, "))
+ statement.OrderStr = buf.String()
+ return statement
+}
+
+// Table tempororily set table name, the parameter could be a string or a pointer of struct
+func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
+ v := rValue(tableNameOrBean)
+ t := v.Type()
+ if t.Kind() == reflect.Struct {
+ var err error
+ statement.RefTable, err = statement.Engine.autoMapType(v)
+ if err != nil {
+ statement.Engine.logger.Error(err)
+ return statement
+ }
+ }
+
+ statement.AltTableName = statement.Engine.TableName(tableNameOrBean, true)
+ return statement
+}
+
+// Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
+func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement {
+ var buf strings.Builder
+ if len(statement.JoinStr) > 0 {
+ fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP)
+ } else {
+ fmt.Fprintf(&buf, "%v JOIN ", joinOP)
+ }
+
+ switch tp := tablename.(type) {
+ case builder.Builder:
+ subSQL, subQueryArgs, err := tp.ToSQL()
+ if err != nil {
+ statement.lastError = err
+ return statement
+ }
+ tbs := strings.Split(tp.TableName(), ".")
+ quotes := append(strings.Split(statement.Engine.Quote(""), ""), "`")
+
+ var aliasName = strings.Trim(tbs[len(tbs)-1], strings.Join(quotes, ""))
+ fmt.Fprintf(&buf, "(%s) %s ON %v", subSQL, aliasName, condition)
+ statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
+ case *builder.Builder:
+ subSQL, subQueryArgs, err := tp.ToSQL()
+ if err != nil {
+ statement.lastError = err
+ return statement
+ }
+ tbs := strings.Split(tp.TableName(), ".")
+ quotes := append(strings.Split(statement.Engine.Quote(""), ""), "`")
+
+ var aliasName = strings.Trim(tbs[len(tbs)-1], strings.Join(quotes, ""))
+ fmt.Fprintf(&buf, "(%s) %s ON %v", subSQL, aliasName, condition)
+ statement.joinArgs = append(statement.joinArgs, subQueryArgs...)
+ default:
+ tbName := statement.Engine.TableName(tablename, true)
+ fmt.Fprintf(&buf, "%s ON %v", tbName, condition)
+ }
+
+ statement.JoinStr = buf.String()
+ statement.joinArgs = append(statement.joinArgs, args...)
+ return statement
+}
+
+// GroupBy generate "Group By keys" statement
+func (statement *Statement) GroupBy(keys string) *Statement {
+ statement.GroupByStr = keys
+ return statement
+}
+
+// Having generate "Having conditions" statement
+func (statement *Statement) Having(conditions string) *Statement {
+ statement.HavingStr = fmt.Sprintf("HAVING %v", conditions)
+ return statement
+}
+
+// Unscoped always disable struct tag "deleted"
+func (statement *Statement) Unscoped() *Statement {
+ statement.unscoped = true
+ return statement
+}
+
+func (statement *Statement) genColumnStr() string {
+ if statement.RefTable == nil {
+ return ""
+ }
+
+ var buf strings.Builder
+ columns := statement.RefTable.Columns()
+
+ for _, col := range columns {
+ if statement.omitColumnMap.contain(col.Name) {
+ continue
+ }
+
+ if len(statement.columnMap) > 0 && !statement.columnMap.contain(col.Name) {
+ continue
+ }
+
+ if col.MapType == core.ONLYTODB {
+ continue
+ }
+
+ if buf.Len() != 0 {
+ buf.WriteString(", ")
+ }
+
+ if statement.JoinStr != "" {
+ if statement.TableAlias != "" {
+ buf.WriteString(statement.TableAlias)
+ } else {
+ buf.WriteString(statement.TableName())
+ }
+
+ buf.WriteString(".")
+ }
+
+ statement.Engine.QuoteTo(&buf, col.Name)
+ }
+
+ return buf.String()
+}
+
+func (statement *Statement) genCreateTableSQL() string {
+ return statement.Engine.dialect.CreateTableSql(statement.RefTable, statement.TableName(),
+ statement.StoreEngine, statement.Charset)
+}
+
+func (statement *Statement) genIndexSQL() []string {
+ var sqls []string
+ tbName := statement.TableName()
+ for _, index := range statement.RefTable.Indexes {
+ if index.Type == core.IndexType {
+ sql := statement.Engine.dialect.CreateIndexSql(tbName, index)
+ /*idxTBName := strings.Replace(tbName, ".", "_", -1)
+ idxTBName = strings.Replace(idxTBName, `"`, "", -1)
+ sql := fmt.Sprintf("CREATE INDEX %v ON %v (%v);", quote(indexName(idxTBName, idxName)),
+ quote(tbName), quote(strings.Join(index.Cols, quote(","))))*/
+ sqls = append(sqls, sql)
+ }
+ }
+ return sqls
+}
+
+func uniqueName(tableName, uqeName string) string {
+ return fmt.Sprintf("UQE_%v_%v", tableName, uqeName)
+}
+
+func (statement *Statement) genUniqueSQL() []string {
+ var sqls []string
+ tbName := statement.TableName()
+ for _, index := range statement.RefTable.Indexes {
+ if index.Type == core.UniqueType {
+ sql := statement.Engine.dialect.CreateIndexSql(tbName, index)
+ sqls = append(sqls, sql)
+ }
+ }
+ return sqls
+}
+
+func (statement *Statement) genDelIndexSQL() []string {
+ var sqls []string
+ tbName := statement.TableName()
+ idxPrefixName := strings.Replace(tbName, `"`, "", -1)
+ idxPrefixName = strings.Replace(idxPrefixName, `.`, "_", -1)
+ for idxName, index := range statement.RefTable.Indexes {
+ var rIdxName string
+ if index.Type == core.UniqueType {
+ rIdxName = uniqueName(idxPrefixName, idxName)
+ } else if index.Type == core.IndexType {
+ rIdxName = indexName(idxPrefixName, idxName)
+ }
+ sql := fmt.Sprintf("DROP INDEX %v", statement.Engine.Quote(statement.Engine.TableName(rIdxName, true)))
+ if statement.Engine.dialect.IndexOnTable() {
+ sql += fmt.Sprintf(" ON %v", statement.Engine.Quote(tbName))
+ }
+ sqls = append(sqls, sql)
+ }
+ return sqls
+}
+
+func (statement *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) {
+ quote := statement.Engine.Quote
+ sql := fmt.Sprintf("ALTER TABLE %v ADD %v", quote(statement.TableName()),
+ col.String(statement.Engine.dialect))
+ if statement.Engine.dialect.DBType() == core.MYSQL && len(col.Comment) > 0 {
+ sql += " COMMENT '" + col.Comment + "'"
+ }
+ sql += ";"
+ return sql, []interface{}{}
+}
+
+func (statement *Statement) buildConds(table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, addedTableName bool) (builder.Cond, error) {
+ return statement.Engine.buildConds(table, bean, includeVersion, includeUpdated, includeNil, includeAutoIncr, statement.allUseBool, statement.useAllCols,
+ statement.unscoped, statement.mustColumnMap, statement.TableName(), statement.TableAlias, addedTableName)
+}
+
+func (statement *Statement) mergeConds(bean interface{}) error {
+ if !statement.noAutoCondition {
+ var addedTableName = (len(statement.JoinStr) > 0)
+ autoCond, err := statement.buildConds(statement.RefTable, bean, true, true, false, true, addedTableName)
+ if err != nil {
+ return err
+ }
+ statement.cond = statement.cond.And(autoCond)
+ }
+
+ if err := statement.processIDParam(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (statement *Statement) genConds(bean interface{}) (string, []interface{}, error) {
+ if err := statement.mergeConds(bean); err != nil {
+ return "", nil, err
+ }
+
+ return builder.ToSQL(statement.cond)
+}
+
+func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, error) {
+ v := rValue(bean)
+ isStruct := v.Kind() == reflect.Struct
+ if isStruct {
+ statement.setRefBean(bean)
+ }
+
+ var columnStr = statement.ColumnStr
+ if len(statement.selectStr) > 0 {
+ columnStr = statement.selectStr
+ } else {
+ // TODO: always generate column names, not use * even if join
+ if len(statement.JoinStr) == 0 {
+ if len(columnStr) == 0 {
+ if len(statement.GroupByStr) > 0 {
+ columnStr = statement.Engine.quoteColumns(statement.GroupByStr)
+ } else {
+ columnStr = statement.genColumnStr()
+ }
+ }
+ } else {
+ if len(columnStr) == 0 {
+ if len(statement.GroupByStr) > 0 {
+ columnStr = statement.Engine.quoteColumns(statement.GroupByStr)
+ }
+ }
+ }
+ }
+
+ if len(columnStr) == 0 {
+ columnStr = "*"
+ }
+
+ if isStruct {
+ if err := statement.mergeConds(bean); err != nil {
+ return "", nil, err
+ }
+ } else {
+ if err := statement.processIDParam(); err != nil {
+ return "", nil, err
+ }
+ }
+ condSQL, condArgs, err := builder.ToSQL(statement.cond)
+ if err != nil {
+ return "", nil, err
+ }
+
+ sqlStr, err := statement.genSelectSQL(columnStr, condSQL, true, true)
+ if err != nil {
+ return "", nil, err
+ }
+
+ return sqlStr, append(statement.joinArgs, condArgs...), nil
+}
+
+func (statement *Statement) genCountSQL(beans ...interface{}) (string, []interface{}, error) {
+ var condSQL string
+ var condArgs []interface{}
+ var err error
+ if len(beans) > 0 {
+ statement.setRefBean(beans[0])
+ condSQL, condArgs, err = statement.genConds(beans[0])
+ } else {
+ condSQL, condArgs, err = builder.ToSQL(statement.cond)
+ }
+ if err != nil {
+ return "", nil, err
+ }
+
+ var selectSQL = statement.selectStr
+ if len(selectSQL) <= 0 {
+ if statement.IsDistinct {
+ selectSQL = fmt.Sprintf("count(DISTINCT %s)", statement.ColumnStr)
+ } else {
+ selectSQL = "count(*)"
+ }
+ }
+ sqlStr, err := statement.genSelectSQL(selectSQL, condSQL, false, false)
+ if err != nil {
+ return "", nil, err
+ }
+
+ return sqlStr, append(statement.joinArgs, condArgs...), nil
+}
+
+func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (string, []interface{}, error) {
+ statement.setRefBean(bean)
+
+ var sumStrs = make([]string, 0, len(columns))
+ for _, colName := range columns {
+ if !strings.Contains(colName, " ") && !strings.Contains(colName, "(") {
+ colName = statement.Engine.Quote(colName)
+ }
+ sumStrs = append(sumStrs, fmt.Sprintf("COALESCE(sum(%s),0)", colName))
+ }
+ sumSelect := strings.Join(sumStrs, ", ")
+
+ condSQL, condArgs, err := statement.genConds(bean)
+ if err != nil {
+ return "", nil, err
+ }
+
+ sqlStr, err := statement.genSelectSQL(sumSelect, condSQL, true, true)
+ if err != nil {
+ return "", nil, err
+ }
+
+ return sqlStr, append(statement.joinArgs, condArgs...), nil
+}
+
+func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, needOrderBy bool) (string, error) {
+ var (
+ distinct string
+ dialect = statement.Engine.Dialect()
+ quote = statement.Engine.Quote
+ fromStr = " FROM "
+ top, mssqlCondi, whereStr string
+ )
+ if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") {
+ distinct = "DISTINCT "
+ }
+ if len(condSQL) > 0 {
+ whereStr = " WHERE " + condSQL
+ }
+
+ if dialect.DBType() == core.MSSQL && strings.Contains(statement.TableName(), "..") {
+ fromStr += statement.TableName()
+ } else {
+ fromStr += quote(statement.TableName())
+ }
+
+ if statement.TableAlias != "" {
+ if dialect.DBType() == core.ORACLE {
+ fromStr += " " + quote(statement.TableAlias)
+ } else {
+ fromStr += " AS " + quote(statement.TableAlias)
+ }
+ }
+ if statement.JoinStr != "" {
+ fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr)
+ }
+
+ if dialect.DBType() == core.MSSQL {
+ if statement.LimitN > 0 {
+ top = fmt.Sprintf("TOP %d ", statement.LimitN)
+ }
+ if statement.Start > 0 {
+ var column string
+ if len(statement.RefTable.PKColumns()) == 0 {
+ for _, index := range statement.RefTable.Indexes {
+ if len(index.Cols) == 1 {
+ column = index.Cols[0]
+ break
+ }
+ }
+ if len(column) == 0 {
+ column = statement.RefTable.ColumnsSeq()[0]
+ }
+ } else {
+ column = statement.RefTable.PKColumns()[0].Name
+ }
+ if statement.needTableName() {
+ if len(statement.TableAlias) > 0 {
+ column = statement.TableAlias + "." + column
+ } else {
+ column = statement.TableName() + "." + column
+ }
+ }
+
+ var orderStr string
+ if needOrderBy && len(statement.OrderStr) > 0 {
+ orderStr = " ORDER BY " + statement.OrderStr
+ }
+
+ var groupStr string
+ if len(statement.GroupByStr) > 0 {
+ groupStr = " GROUP BY " + statement.GroupByStr
+ }
+ mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s%s%s))",
+ column, statement.Start, column, fromStr, whereStr, orderStr, groupStr)
+ }
+ }
+
+ var buf strings.Builder
+ fmt.Fprintf(&buf, "SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr)
+ if len(mssqlCondi) > 0 {
+ if len(whereStr) > 0 {
+ fmt.Fprint(&buf, " AND ", mssqlCondi)
+ } else {
+ fmt.Fprint(&buf, " WHERE ", mssqlCondi)
+ }
+ }
+
+ if statement.GroupByStr != "" {
+ fmt.Fprint(&buf, " GROUP BY ", statement.GroupByStr)
+ }
+ if statement.HavingStr != "" {
+ fmt.Fprint(&buf, " ", statement.HavingStr)
+ }
+ if needOrderBy && statement.OrderStr != "" {
+ fmt.Fprint(&buf, " ORDER BY ", statement.OrderStr)
+ }
+ if needLimit {
+ if dialect.DBType() != core.MSSQL && dialect.DBType() != core.ORACLE {
+ if statement.Start > 0 {
+ fmt.Fprintf(&buf, " LIMIT %v OFFSET %v", statement.LimitN, statement.Start)
+ } else if statement.LimitN > 0 {
+ fmt.Fprint(&buf, " LIMIT ", statement.LimitN)
+ }
+ } else if dialect.DBType() == core.ORACLE {
+ if statement.Start != 0 || statement.LimitN != 0 {
+ oldString := buf.String()
+ buf.Reset()
+ rawColStr := columnStr
+ if rawColStr == "*" {
+ rawColStr = "at.*"
+ }
+ fmt.Fprintf(&buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d",
+ columnStr, rawColStr, oldString, statement.Start+statement.LimitN, statement.Start)
+ }
+ }
+ }
+ if statement.IsForUpdate {
+ return dialect.ForUpdateSql(buf.String()), nil
+ }
+
+ return buf.String(), nil
+}
+
+func (statement *Statement) processIDParam() error {
+ if statement.idParam == nil || statement.RefTable == nil {
+ return nil
+ }
+
+ if len(statement.RefTable.PrimaryKeys) != len(*statement.idParam) {
+ return fmt.Errorf("ID condition is error, expect %d primarykeys, there are %d",
+ len(statement.RefTable.PrimaryKeys),
+ len(*statement.idParam),
+ )
+ }
+
+ for i, col := range statement.RefTable.PKColumns() {
+ var colName = statement.colName(col, statement.TableName())
+ statement.cond = statement.cond.And(builder.Eq{colName: (*(statement.idParam))[i]})
+ }
+ return nil
+}
+
+func (statement *Statement) joinColumns(cols []*core.Column, includeTableName bool) string {
+ var colnames = make([]string, len(cols))
+ for i, col := range cols {
+ if includeTableName {
+ colnames[i] = statement.Engine.Quote(statement.TableName()) +
+ "." + statement.Engine.Quote(col.Name)
+ } else {
+ colnames[i] = statement.Engine.Quote(col.Name)
+ }
+ }
+ return strings.Join(colnames, ", ")
+}
+
+func (statement *Statement) convertIDSQL(sqlStr string) string {
+ if statement.RefTable != nil {
+ cols := statement.RefTable.PKColumns()
+ if len(cols) == 0 {
+ return ""
+ }
+
+ colstrs := statement.joinColumns(cols, false)
+ sqls := splitNNoCase(sqlStr, " from ", 2)
+ if len(sqls) != 2 {
+ return ""
+ }
+
+ var top string
+ if statement.LimitN > 0 && statement.Engine.dialect.DBType() == core.MSSQL {
+ top = fmt.Sprintf("TOP %d ", statement.LimitN)
+ }
+
+ newsql := fmt.Sprintf("SELECT %s%s FROM %v", top, colstrs, sqls[1])
+ return newsql
+ }
+ return ""
+}
+
+func (statement *Statement) convertUpdateSQL(sqlStr string) (string, string) {
+ if statement.RefTable == nil || len(statement.RefTable.PrimaryKeys) != 1 {
+ return "", ""
+ }
+
+ colstrs := statement.joinColumns(statement.RefTable.PKColumns(), true)
+ sqls := splitNNoCase(sqlStr, "where", 2)
+ if len(sqls) != 2 {
+ if len(sqls) == 1 {
+ return sqls[0], fmt.Sprintf("SELECT %v FROM %v",
+ colstrs, statement.Engine.Quote(statement.TableName()))
+ }
+ return "", ""
+ }
+
+ var whereStr = sqls[1]
+
+ // TODO: for postgres only, if any other database?
+ var paraStr string
+ if statement.Engine.dialect.DBType() == core.POSTGRES {
+ paraStr = "$"
+ } else if statement.Engine.dialect.DBType() == core.MSSQL {
+ paraStr = ":"
+ }
+
+ if paraStr != "" {
+ if strings.Contains(sqls[1], paraStr) {
+ dollers := strings.Split(sqls[1], paraStr)
+ whereStr = dollers[0]
+ for i, c := range dollers[1:] {
+ ccs := strings.SplitN(c, " ", 2)
+ whereStr += fmt.Sprintf(paraStr+"%v %v", i+1, ccs[1])
+ }
+ }
+ }
+
+ return sqls[0], fmt.Sprintf("SELECT %v FROM %v WHERE %v",
+ colstrs, statement.Engine.Quote(statement.TableName()),
+ whereStr)
+}
diff --git a/vendor/xorm.io/xorm/statement_args.go b/vendor/xorm.io/xorm/statement_args.go
new file mode 100644
index 0000000000..310f24d6b9
--- /dev/null
+++ b/vendor/xorm.io/xorm/statement_args.go
@@ -0,0 +1,170 @@
+// Copyright 2019 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "time"
+
+ "xorm.io/builder"
+ "xorm.io/core"
+)
+
+func quoteNeeded(a interface{}) bool {
+ switch a.(type) {
+ case int, int8, int16, int32, int64:
+ return false
+ case uint, uint8, uint16, uint32, uint64:
+ return false
+ case float32, float64:
+ return false
+ case bool:
+ return false
+ case string:
+ return true
+ case time.Time, *time.Time:
+ return true
+ case builder.Builder, *builder.Builder:
+ return false
+ }
+
+ t := reflect.TypeOf(a)
+ switch t.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return false
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return false
+ case reflect.Float32, reflect.Float64:
+ return false
+ case reflect.Bool:
+ return false
+ case reflect.String:
+ return true
+ }
+
+ return true
+}
+
+func convertStringSingleQuote(arg string) string {
+ return "'" + strings.Replace(arg, "'", "''", -1) + "'"
+}
+
+func convertString(arg string) string {
+ var buf strings.Builder
+ buf.WriteRune('\'')
+ for _, c := range arg {
+ if c == '\\' || c == '\'' {
+ buf.WriteRune('\\')
+ }
+ buf.WriteRune(c)
+ }
+ buf.WriteRune('\'')
+ return buf.String()
+}
+
+func convertArg(arg interface{}, convertFunc func(string) string) string {
+ if quoteNeeded(arg) {
+ argv := fmt.Sprintf("%v", arg)
+ return convertFunc(argv)
+ }
+
+ return fmt.Sprintf("%v", arg)
+}
+
+const insertSelectPlaceHolder = true
+
+func (statement *Statement) writeArg(w *builder.BytesWriter, arg interface{}) error {
+ switch argv := arg.(type) {
+ case bool:
+ if statement.Engine.dialect.DBType() == core.MSSQL {
+ if argv {
+ if _, err := w.WriteString("1"); err != nil {
+ return err
+ }
+ } else {
+ if _, err := w.WriteString("0"); err != nil {
+ return err
+ }
+ }
+ } else {
+ if argv {
+ if _, err := w.WriteString("true"); err != nil {
+ return err
+ }
+ } else {
+ if _, err := w.WriteString("false"); err != nil {
+ return err
+ }
+ }
+ }
+ case *builder.Builder:
+ if _, err := w.WriteString("("); err != nil {
+ return err
+ }
+ if err := argv.WriteTo(w); err != nil {
+ return err
+ }
+ if _, err := w.WriteString(")"); err != nil {
+ return err
+ }
+ default:
+ if insertSelectPlaceHolder {
+ if err := w.WriteByte('?'); err != nil {
+ return err
+ }
+ w.Append(arg)
+ } else {
+ var convertFunc = convertStringSingleQuote
+ if statement.Engine.dialect.DBType() == core.MYSQL {
+ convertFunc = convertString
+ }
+ if _, err := w.WriteString(convertArg(arg, convertFunc)); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (statement *Statement) writeArgs(w *builder.BytesWriter, args []interface{}) error {
+ for i, arg := range args {
+ if err := statement.writeArg(w, arg); err != nil {
+ return err
+ }
+
+ if i+1 != len(args) {
+ if _, err := w.WriteString(","); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func writeStrings(w *builder.BytesWriter, cols []string, leftQuote, rightQuote string) error {
+ for i, colName := range cols {
+ if len(leftQuote) > 0 && colName[0] != '`' {
+ if _, err := w.WriteString(leftQuote); err != nil {
+ return err
+ }
+ }
+ if _, err := w.WriteString(colName); err != nil {
+ return err
+ }
+ if len(rightQuote) > 0 && colName[len(colName)-1] != '`' {
+ if _, err := w.WriteString(rightQuote); err != nil {
+ return err
+ }
+ }
+ if i+1 != len(cols) {
+ if _, err := w.WriteString(","); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/statement_columnmap.go b/vendor/xorm.io/xorm/statement_columnmap.go
new file mode 100644
index 0000000000..b6523b1e7a
--- /dev/null
+++ b/vendor/xorm.io/xorm/statement_columnmap.go
@@ -0,0 +1,35 @@
+// Copyright 2019 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import "strings"
+
+type columnMap []string
+
+func (m columnMap) contain(colName string) bool {
+ if len(m) == 0 {
+ return false
+ }
+
+ n := len(colName)
+ for _, mk := range m {
+ if len(mk) != n {
+ continue
+ }
+ if strings.EqualFold(mk, colName) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (m *columnMap) add(colName string) bool {
+ if m.contain(colName) {
+ return false
+ }
+ *m = append(*m, colName)
+ return true
+}
diff --git a/vendor/xorm.io/xorm/statement_exprparam.go b/vendor/xorm.io/xorm/statement_exprparam.go
new file mode 100644
index 0000000000..4da4f1ea12
--- /dev/null
+++ b/vendor/xorm.io/xorm/statement_exprparam.go
@@ -0,0 +1,117 @@
+// Copyright 2019 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "fmt"
+ "strings"
+
+ "xorm.io/builder"
+)
+
+type ErrUnsupportedExprType struct {
+ tp string
+}
+
+func (err ErrUnsupportedExprType) Error() string {
+ return fmt.Sprintf("Unsupported expression type: %v", err.tp)
+}
+
+type exprParam struct {
+ colName string
+ arg interface{}
+}
+
+type exprParams struct {
+ colNames []string
+ args []interface{}
+}
+
+func (exprs *exprParams) Len() int {
+ return len(exprs.colNames)
+}
+
+func (exprs *exprParams) addParam(colName string, arg interface{}) {
+ exprs.colNames = append(exprs.colNames, colName)
+ exprs.args = append(exprs.args, arg)
+}
+
+func (exprs *exprParams) isColExist(colName string) bool {
+ for _, name := range exprs.colNames {
+ if strings.EqualFold(trimQuote(name), trimQuote(colName)) {
+ return true
+ }
+ }
+ return false
+}
+
+func (exprs *exprParams) getByName(colName string) (exprParam, bool) {
+ for i, name := range exprs.colNames {
+ if strings.EqualFold(name, colName) {
+ return exprParam{name, exprs.args[i]}, true
+ }
+ }
+ return exprParam{}, false
+}
+
+func (exprs *exprParams) writeArgs(w *builder.BytesWriter) error {
+ for i, expr := range exprs.args {
+ switch arg := expr.(type) {
+ case *builder.Builder:
+ if _, err := w.WriteString("("); err != nil {
+ return err
+ }
+ if err := arg.WriteTo(w); err != nil {
+ return err
+ }
+ if _, err := w.WriteString(")"); err != nil {
+ return err
+ }
+ default:
+ if _, err := w.WriteString(fmt.Sprintf("%v", arg)); err != nil {
+ return err
+ }
+ }
+ if i != len(exprs.args)-1 {
+ if _, err := w.WriteString(","); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (exprs *exprParams) writeNameArgs(w *builder.BytesWriter) error {
+ for i, colName := range exprs.colNames {
+ if _, err := w.WriteString(colName); err != nil {
+ return err
+ }
+ if _, err := w.WriteString("="); err != nil {
+ return err
+ }
+
+ switch arg := exprs.args[i].(type) {
+ case *builder.Builder:
+ if _, err := w.WriteString("("); err != nil {
+ return err
+ }
+ if err := arg.WriteTo(w); err != nil {
+ return err
+ }
+ if _, err := w.WriteString("("); err != nil {
+ return err
+ }
+ default:
+ w.Append(exprs.args[i])
+ }
+
+ if i+1 != len(exprs.colNames) {
+ if _, err := w.WriteString(","); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/statement_quote.go b/vendor/xorm.io/xorm/statement_quote.go
new file mode 100644
index 0000000000..e22e0d1471
--- /dev/null
+++ b/vendor/xorm.io/xorm/statement_quote.go
@@ -0,0 +1,19 @@
+// Copyright 2019 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+func trimQuote(s string) string {
+ if len(s) == 0 {
+ return s
+ }
+
+ if s[0] == '`' {
+ s = s[1:]
+ }
+ if len(s) > 0 && s[len(s)-1] == '`' {
+ return s[:len(s)-1]
+ }
+ return s
+}
diff --git a/vendor/xorm.io/xorm/syslogger.go b/vendor/xorm.io/xorm/syslogger.go
new file mode 100644
index 0000000000..11ba01e7bd
--- /dev/null
+++ b/vendor/xorm.io/xorm/syslogger.go
@@ -0,0 +1,89 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !windows,!nacl,!plan9
+
+package xorm
+
+import (
+ "fmt"
+ "log/syslog"
+
+ "xorm.io/core"
+)
+
+var _ core.ILogger = &SyslogLogger{}
+
+// SyslogLogger will be depricated
+type SyslogLogger struct {
+ w *syslog.Writer
+ showSQL bool
+}
+
+// NewSyslogLogger implements core.ILogger
+func NewSyslogLogger(w *syslog.Writer) *SyslogLogger {
+ return &SyslogLogger{w: w}
+}
+
+// Debug log content as Debug
+func (s *SyslogLogger) Debug(v ...interface{}) {
+ s.w.Debug(fmt.Sprint(v...))
+}
+
+// Debugf log content as Debug and format
+func (s *SyslogLogger) Debugf(format string, v ...interface{}) {
+ s.w.Debug(fmt.Sprintf(format, v...))
+}
+
+// Error log content as Error
+func (s *SyslogLogger) Error(v ...interface{}) {
+ s.w.Err(fmt.Sprint(v...))
+}
+
+// Errorf log content as Errorf and format
+func (s *SyslogLogger) Errorf(format string, v ...interface{}) {
+ s.w.Err(fmt.Sprintf(format, v...))
+}
+
+// Info log content as Info
+func (s *SyslogLogger) Info(v ...interface{}) {
+ s.w.Info(fmt.Sprint(v...))
+}
+
+// Infof log content as Infof and format
+func (s *SyslogLogger) Infof(format string, v ...interface{}) {
+ s.w.Info(fmt.Sprintf(format, v...))
+}
+
+// Warn log content as Warn
+func (s *SyslogLogger) Warn(v ...interface{}) {
+ s.w.Warning(fmt.Sprint(v...))
+}
+
+// Warnf log content as Warnf and format
+func (s *SyslogLogger) Warnf(format string, v ...interface{}) {
+ s.w.Warning(fmt.Sprintf(format, v...))
+}
+
+// Level shows log level
+func (s *SyslogLogger) Level() core.LogLevel {
+ return core.LOG_UNKNOWN
+}
+
+// SetLevel always return error, as current log/syslog package doesn't allow to set priority level after syslog.Writer created
+func (s *SyslogLogger) SetLevel(l core.LogLevel) {}
+
+// ShowSQL set if logging SQL
+func (s *SyslogLogger) ShowSQL(show ...bool) {
+ if len(show) == 0 {
+ s.showSQL = true
+ return
+ }
+ s.showSQL = show[0]
+}
+
+// IsShowSQL if logging SQL
+func (s *SyslogLogger) IsShowSQL() bool {
+ return s.showSQL
+}
diff --git a/vendor/xorm.io/xorm/tag.go b/vendor/xorm.io/xorm/tag.go
new file mode 100644
index 0000000000..ec8d5cf05b
--- /dev/null
+++ b/vendor/xorm.io/xorm/tag.go
@@ -0,0 +1,311 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+
+ "xorm.io/core"
+)
+
+type tagContext struct {
+ tagName string
+ params []string
+ preTag, nextTag string
+ table *core.Table
+ col *core.Column
+ fieldValue reflect.Value
+ isIndex bool
+ isUnique bool
+ indexNames map[string]int
+ engine *Engine
+ hasCacheTag bool
+ hasNoCacheTag bool
+ ignoreNext bool
+}
+
+// tagHandler describes tag handler for XORM
+type tagHandler func(ctx *tagContext) error
+
+var (
+ // defaultTagHandlers enumerates all the default tag handler
+ defaultTagHandlers = map[string]tagHandler{
+ "<-": OnlyFromDBTagHandler,
+ "->": OnlyToDBTagHandler,
+ "PK": PKTagHandler,
+ "NULL": NULLTagHandler,
+ "NOT": IgnoreTagHandler,
+ "AUTOINCR": AutoIncrTagHandler,
+ "DEFAULT": DefaultTagHandler,
+ "CREATED": CreatedTagHandler,
+ "UPDATED": UpdatedTagHandler,
+ "DELETED": DeletedTagHandler,
+ "VERSION": VersionTagHandler,
+ "UTC": UTCTagHandler,
+ "LOCAL": LocalTagHandler,
+ "NOTNULL": NotNullTagHandler,
+ "INDEX": IndexTagHandler,
+ "UNIQUE": UniqueTagHandler,
+ "CACHE": CacheTagHandler,
+ "NOCACHE": NoCacheTagHandler,
+ "COMMENT": CommentTagHandler,
+ }
+)
+
+func init() {
+ for k := range core.SqlTypes {
+ defaultTagHandlers[k] = SQLTypeTagHandler
+ }
+}
+
+// IgnoreTagHandler describes ignored tag handler
+func IgnoreTagHandler(ctx *tagContext) error {
+ return nil
+}
+
+// OnlyFromDBTagHandler describes mapping direction tag handler
+func OnlyFromDBTagHandler(ctx *tagContext) error {
+ ctx.col.MapType = core.ONLYFROMDB
+ return nil
+}
+
+// OnlyToDBTagHandler describes mapping direction tag handler
+func OnlyToDBTagHandler(ctx *tagContext) error {
+ ctx.col.MapType = core.ONLYTODB
+ return nil
+}
+
+// PKTagHandler decribes primary key tag handler
+func PKTagHandler(ctx *tagContext) error {
+ ctx.col.IsPrimaryKey = true
+ ctx.col.Nullable = false
+ return nil
+}
+
+// NULLTagHandler describes null tag handler
+func NULLTagHandler(ctx *tagContext) error {
+ ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT")
+ return nil
+}
+
+// NotNullTagHandler describes notnull tag handler
+func NotNullTagHandler(ctx *tagContext) error {
+ ctx.col.Nullable = false
+ return nil
+}
+
+// AutoIncrTagHandler describes autoincr tag handler
+func AutoIncrTagHandler(ctx *tagContext) error {
+ ctx.col.IsAutoIncrement = true
+ /*
+ if len(ctx.params) > 0 {
+ autoStartInt, err := strconv.Atoi(ctx.params[0])
+ if err != nil {
+ return err
+ }
+ ctx.col.AutoIncrStart = autoStartInt
+ } else {
+ ctx.col.AutoIncrStart = 1
+ }
+ */
+ return nil
+}
+
+// DefaultTagHandler describes default tag handler
+func DefaultTagHandler(ctx *tagContext) error {
+ if len(ctx.params) > 0 {
+ ctx.col.Default = ctx.params[0]
+ } else {
+ ctx.col.Default = ctx.nextTag
+ ctx.ignoreNext = true
+ }
+ ctx.col.DefaultIsEmpty = false
+ return nil
+}
+
+// CreatedTagHandler describes created tag handler
+func CreatedTagHandler(ctx *tagContext) error {
+ ctx.col.IsCreated = true
+ return nil
+}
+
+// VersionTagHandler describes version tag handler
+func VersionTagHandler(ctx *tagContext) error {
+ ctx.col.IsVersion = true
+ ctx.col.Default = "1"
+ return nil
+}
+
+// UTCTagHandler describes utc tag handler
+func UTCTagHandler(ctx *tagContext) error {
+ ctx.col.TimeZone = time.UTC
+ return nil
+}
+
+// LocalTagHandler describes local tag handler
+func LocalTagHandler(ctx *tagContext) error {
+ if len(ctx.params) == 0 {
+ ctx.col.TimeZone = time.Local
+ } else {
+ var err error
+ ctx.col.TimeZone, err = time.LoadLocation(ctx.params[0])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// UpdatedTagHandler describes updated tag handler
+func UpdatedTagHandler(ctx *tagContext) error {
+ ctx.col.IsUpdated = true
+ return nil
+}
+
+// DeletedTagHandler describes deleted tag handler
+func DeletedTagHandler(ctx *tagContext) error {
+ ctx.col.IsDeleted = true
+ return nil
+}
+
+// IndexTagHandler describes index tag handler
+func IndexTagHandler(ctx *tagContext) error {
+ if len(ctx.params) > 0 {
+ ctx.indexNames[ctx.params[0]] = core.IndexType
+ } else {
+ ctx.isIndex = true
+ }
+ return nil
+}
+
+// UniqueTagHandler describes unique tag handler
+func UniqueTagHandler(ctx *tagContext) error {
+ if len(ctx.params) > 0 {
+ ctx.indexNames[ctx.params[0]] = core.UniqueType
+ } else {
+ ctx.isUnique = true
+ }
+ return nil
+}
+
+// CommentTagHandler add comment to column
+func CommentTagHandler(ctx *tagContext) error {
+ if len(ctx.params) > 0 {
+ ctx.col.Comment = strings.Trim(ctx.params[0], "' ")
+ }
+ return nil
+}
+
+// SQLTypeTagHandler describes SQL Type tag handler
+func SQLTypeTagHandler(ctx *tagContext) error {
+ ctx.col.SQLType = core.SQLType{Name: ctx.tagName}
+ if len(ctx.params) > 0 {
+ if ctx.tagName == core.Enum {
+ ctx.col.EnumOptions = make(map[string]int)
+ for k, v := range ctx.params {
+ v = strings.TrimSpace(v)
+ v = strings.Trim(v, "'")
+ ctx.col.EnumOptions[v] = k
+ }
+ } else if ctx.tagName == core.Set {
+ ctx.col.SetOptions = make(map[string]int)
+ for k, v := range ctx.params {
+ v = strings.TrimSpace(v)
+ v = strings.Trim(v, "'")
+ ctx.col.SetOptions[v] = k
+ }
+ } else {
+ var err error
+ if len(ctx.params) == 2 {
+ ctx.col.Length, err = strconv.Atoi(ctx.params[0])
+ if err != nil {
+ return err
+ }
+ ctx.col.Length2, err = strconv.Atoi(ctx.params[1])
+ if err != nil {
+ return err
+ }
+ } else if len(ctx.params) == 1 {
+ ctx.col.Length, err = strconv.Atoi(ctx.params[0])
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// ExtendsTagHandler describes extends tag handler
+func ExtendsTagHandler(ctx *tagContext) error {
+ var fieldValue = ctx.fieldValue
+ var isPtr = false
+ switch fieldValue.Kind() {
+ case reflect.Ptr:
+ f := fieldValue.Type().Elem()
+ if f.Kind() == reflect.Struct {
+ fieldPtr := fieldValue
+ fieldValue = fieldValue.Elem()
+ if !fieldValue.IsValid() || fieldPtr.IsNil() {
+ fieldValue = reflect.New(f).Elem()
+ }
+ }
+ isPtr = true
+ fallthrough
+ case reflect.Struct:
+ parentTable, err := ctx.engine.mapType(fieldValue)
+ if err != nil {
+ return err
+ }
+ for _, col := range parentTable.Columns() {
+ col.FieldName = fmt.Sprintf("%v.%v", ctx.col.FieldName, col.FieldName)
+
+ var tagPrefix = ctx.col.FieldName
+ if len(ctx.params) > 0 {
+ col.Nullable = isPtr
+ tagPrefix = ctx.params[0]
+ if col.IsPrimaryKey {
+ col.Name = ctx.col.FieldName
+ col.IsPrimaryKey = false
+ } else {
+ col.Name = fmt.Sprintf("%v%v", tagPrefix, col.Name)
+ }
+ }
+
+ if col.Nullable {
+ col.IsAutoIncrement = false
+ col.IsPrimaryKey = false
+ }
+
+ ctx.table.AddColumn(col)
+ for indexName, indexType := range col.Indexes {
+ addIndex(indexName, ctx.table, col, indexType)
+ }
+ }
+ default:
+ //TODO: warning
+ }
+ return nil
+}
+
+// CacheTagHandler describes cache tag handler
+func CacheTagHandler(ctx *tagContext) error {
+ if !ctx.hasCacheTag {
+ ctx.hasCacheTag = true
+ }
+ return nil
+}
+
+// NoCacheTagHandler describes nocache tag handler
+func NoCacheTagHandler(ctx *tagContext) error {
+ if !ctx.hasNoCacheTag {
+ ctx.hasNoCacheTag = true
+ }
+ return nil
+}
diff --git a/vendor/xorm.io/xorm/test_mssql.sh b/vendor/xorm.io/xorm/test_mssql.sh
new file mode 100644
index 0000000000..7f060cff32
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_mssql.sh
@@ -0,0 +1 @@
+go test -db=mssql -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_mssql_cache.sh b/vendor/xorm.io/xorm/test_mssql_cache.sh
new file mode 100644
index 0000000000..76efd6ca0a
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_mssql_cache.sh
@@ -0,0 +1 @@
+go test -db=mssql -conn_str="server=192.168.1.58;user id=sa;password=123456;database=xorm_test" -cache=true \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_mymysql.sh b/vendor/xorm.io/xorm/test_mymysql.sh
new file mode 100644
index 0000000000..f7780d14fa
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_mymysql.sh
@@ -0,0 +1 @@
+go test -db=mymysql -conn_str="xorm_test/root/" \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_mymysql_cache.sh b/vendor/xorm.io/xorm/test_mymysql_cache.sh
new file mode 100644
index 0000000000..0100286d65
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_mymysql_cache.sh
@@ -0,0 +1 @@
+go test -db=mymysql -conn_str="xorm_test/root/" -cache=true \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_mysql.sh b/vendor/xorm.io/xorm/test_mysql.sh
new file mode 100644
index 0000000000..650e4ee170
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_mysql.sh
@@ -0,0 +1 @@
+go test -db=mysql -conn_str="root:@/xorm_test" \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_mysql_cache.sh b/vendor/xorm.io/xorm/test_mysql_cache.sh
new file mode 100644
index 0000000000..c542e73594
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_mysql_cache.sh
@@ -0,0 +1 @@
+go test -db=mysql -conn_str="root:@/xorm_test" -cache=true \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_postgres.sh b/vendor/xorm.io/xorm/test_postgres.sh
new file mode 100644
index 0000000000..dc1152e0a6
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_postgres.sh
@@ -0,0 +1 @@
+go test -db=postgres -conn_str="dbname=xorm_test sslmode=disable" \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_postgres_cache.sh b/vendor/xorm.io/xorm/test_postgres_cache.sh
new file mode 100644
index 0000000000..462fc948cb
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_postgres_cache.sh
@@ -0,0 +1 @@
+go test -db=postgres -conn_str="dbname=xorm_test sslmode=disable" -cache=true \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_sqlite.sh b/vendor/xorm.io/xorm/test_sqlite.sh
new file mode 100644
index 0000000000..6352b5cb5f
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_sqlite.sh
@@ -0,0 +1 @@
+go test -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_sqlite_cache.sh b/vendor/xorm.io/xorm/test_sqlite_cache.sh
new file mode 100644
index 0000000000..75a054c3f1
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_sqlite_cache.sh
@@ -0,0 +1 @@
+go test -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" -cache=true \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/test_tidb.sh b/vendor/xorm.io/xorm/test_tidb.sh
new file mode 100644
index 0000000000..03d2d6cd82
--- /dev/null
+++ b/vendor/xorm.io/xorm/test_tidb.sh
@@ -0,0 +1 @@
+go test -db=mysql -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true \ No newline at end of file
diff --git a/vendor/xorm.io/xorm/transaction.go b/vendor/xorm.io/xorm/transaction.go
new file mode 100644
index 0000000000..4104103fd5
--- /dev/null
+++ b/vendor/xorm.io/xorm/transaction.go
@@ -0,0 +1,26 @@
+// Copyright 2018 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+// Transaction Execute sql wrapped in a transaction(abbr as tx), tx will automatic commit if no errors occurred
+func (engine *Engine) Transaction(f func(*Session) (interface{}, error)) (interface{}, error) {
+ session := engine.NewSession()
+ defer session.Close()
+
+ if err := session.Begin(); err != nil {
+ return nil, err
+ }
+
+ result, err := f(session)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := session.Commit(); err != nil {
+ return nil, err
+ }
+
+ return result, nil
+}
diff --git a/vendor/xorm.io/xorm/types.go b/vendor/xorm.io/xorm/types.go
new file mode 100644
index 0000000000..c76a546065
--- /dev/null
+++ b/vendor/xorm.io/xorm/types.go
@@ -0,0 +1,16 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import (
+ "reflect"
+
+ "xorm.io/core"
+)
+
+var (
+ ptrPkType = reflect.TypeOf(&core.PK{})
+ pkType = reflect.TypeOf(core.PK{})
+)
diff --git a/vendor/xorm.io/xorm/xorm.go b/vendor/xorm.io/xorm/xorm.go
new file mode 100644
index 0000000000..e1c83b56f2
--- /dev/null
+++ b/vendor/xorm.io/xorm/xorm.go
@@ -0,0 +1,126 @@
+// Copyright 2015 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.8
+
+package xorm
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "reflect"
+ "runtime"
+ "sync"
+ "time"
+
+ "xorm.io/core"
+)
+
+const (
+ // Version show the xorm's version
+ Version string = "0.8.0.1015"
+)
+
+func regDrvsNDialects() bool {
+ providedDrvsNDialects := map[string]struct {
+ dbType core.DbType
+ getDriver func() core.Driver
+ getDialect func() core.Dialect
+ }{
+ "mssql": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }},
+ "odbc": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }}, // !nashtsai! TODO change this when supporting MS Access
+ "mysql": {"mysql", func() core.Driver { return &mysqlDriver{} }, func() core.Dialect { return &mysql{} }},
+ "mymysql": {"mysql", func() core.Driver { return &mymysqlDriver{} }, func() core.Dialect { return &mysql{} }},
+ "postgres": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }},
+ "pgx": {"postgres", func() core.Driver { return &pqDriverPgx{} }, func() core.Dialect { return &postgres{} }},
+ "sqlite3": {"sqlite3", func() core.Driver { return &sqlite3Driver{} }, func() core.Dialect { return &sqlite3{} }},
+ "oci8": {"oracle", func() core.Driver { return &oci8Driver{} }, func() core.Dialect { return &oracle{} }},
+ "goracle": {"oracle", func() core.Driver { return &goracleDriver{} }, func() core.Dialect { return &oracle{} }},
+ }
+
+ for driverName, v := range providedDrvsNDialects {
+ if driver := core.QueryDriver(driverName); driver == nil {
+ core.RegisterDriver(driverName, v.getDriver())
+ core.RegisterDialect(v.dbType, v.getDialect)
+ }
+ }
+ return true
+}
+
+func close(engine *Engine) {
+ engine.Close()
+}
+
+func init() {
+ regDrvsNDialects()
+}
+
+// NewEngine new a db manager according to the parameter. Currently support four
+// drivers
+func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
+ driver := core.QueryDriver(driverName)
+ if driver == nil {
+ return nil, fmt.Errorf("Unsupported driver name: %v", driverName)
+ }
+
+ uri, err := driver.Parse(driverName, dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+
+ dialect := core.QueryDialect(uri.DbType)
+ if dialect == nil {
+ return nil, fmt.Errorf("Unsupported dialect type: %v", uri.DbType)
+ }
+
+ db, err := core.Open(driverName, dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+
+ err = dialect.Init(db, uri, driverName, dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+
+ engine := &Engine{
+ db: db,
+ dialect: dialect,
+ Tables: make(map[reflect.Type]*core.Table),
+ mutex: &sync.RWMutex{},
+ TagIdentifier: "xorm",
+ TZLocation: time.Local,
+ tagHandlers: defaultTagHandlers,
+ cachers: make(map[string]core.Cacher),
+ defaultContext: context.Background(),
+ }
+
+ if uri.DbType == core.SQLITE {
+ engine.DatabaseTZ = time.UTC
+ } else {
+ engine.DatabaseTZ = time.Local
+ }
+
+ logger := NewSimpleLogger(os.Stdout)
+ logger.SetLevel(core.LOG_INFO)
+ engine.SetLogger(logger)
+ engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper)))
+
+ runtime.SetFinalizer(engine, close)
+
+ return engine, nil
+}
+
+// NewEngineWithParams new a db manager with params. The params will be passed to dialect.
+func NewEngineWithParams(driverName string, dataSourceName string, params map[string]string) (*Engine, error) {
+ engine, err := NewEngine(driverName, dataSourceName)
+ engine.dialect.SetParams(params)
+ return engine, err
+}
+
+// Clone clone an engine
+func (engine *Engine) Clone() (*Engine, error) {
+ return NewEngine(engine.DriverName(), engine.DataSourceName())
+}