]> source.dussan.org Git - gitea.git/commitdiff
Fix label count (#8267)
authorLunny Xiao <xiaolunwen@gmail.com>
Tue, 24 Sep 2019 13:22:39 +0000 (21:22 +0800)
committerGitHub <noreply@github.com>
Tue, 24 Sep 2019 13:22:39 +0000 (21:22 +0800)
* fix label count

* fix vendor

* fix import order

* update xorm to fix bug

* fix tests

* fix mssql bug

38 files changed:
go.mod
go.sum
models/issue.go
models/issue_label.go
models/issue_label_test.go
vendor/github.com/go-xorm/xorm/.drone.yml
vendor/github.com/go-xorm/xorm/dialect_postgres.go
vendor/github.com/go-xorm/xorm/engine.go
vendor/github.com/go-xorm/xorm/go.mod
vendor/github.com/go-xorm/xorm/go.sum
vendor/github.com/go-xorm/xorm/interface.go
vendor/github.com/go-xorm/xorm/session_cols.go
vendor/github.com/go-xorm/xorm/session_insert.go
vendor/github.com/go-xorm/xorm/session_update.go
vendor/github.com/go-xorm/xorm/statement.go
vendor/github.com/go-xorm/xorm/statement_args.go [new file with mode: 0644]
vendor/github.com/go-xorm/xorm/statement_columnmap.go [new file with mode: 0644]
vendor/github.com/go-xorm/xorm/statement_exprparam.go [new file with mode: 0644]
vendor/github.com/go-xorm/xorm/statement_quote.go [new file with mode: 0644]
vendor/github.com/go-xorm/xorm/test_mssql.sh
vendor/modules.txt
vendor/xorm.io/builder/.drone.yml
vendor/xorm.io/builder/README.md
vendor/xorm.io/builder/builder.go
vendor/xorm.io/builder/builder_insert.go
vendor/xorm.io/builder/builder_join.go [new file with mode: 0644]
vendor/xorm.io/builder/builder_select.go
vendor/xorm.io/builder/builder_update.go
vendor/xorm.io/builder/cond.go
vendor/xorm.io/builder/cond_eq.go
vendor/xorm.io/builder/cond_expr.go
vendor/xorm.io/builder/cond_neq.go
vendor/xorm.io/builder/doc.go
vendor/xorm.io/builder/error.go
vendor/xorm.io/builder/go.mod
vendor/xorm.io/builder/sql.go
vendor/xorm.io/builder/string_builder.go [deleted file]
vendor/xorm.io/builder/writer.go [new file with mode: 0644]

diff --git a/go.mod b/go.mod
index 0b5dd381de921650bcc77529e1c444db07ac867f..017e3f61561eaad316b5f7d3d81a60a70eb49b56 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -48,7 +48,7 @@ require (
        github.com/go-redis/redis v6.15.2+incompatible
        github.com/go-sql-driver/mysql v1.4.1
        github.com/go-swagger/go-swagger v0.20.1
-       github.com/go-xorm/xorm v0.7.7-0.20190822154023-17592d96b35b
+       github.com/go-xorm/xorm v0.7.8-0.20190924080535-59ed80ce1a67
        github.com/gobwas/glob v0.2.3
        github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561
        github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
@@ -124,6 +124,6 @@ require (
        gopkg.in/testfixtures.v2 v2.5.0
        mvdan.cc/xurls/v2 v2.0.0
        strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a
-       xorm.io/builder v0.3.5
+       xorm.io/builder v0.3.6
        xorm.io/core v0.7.0
 )
diff --git a/go.sum b/go.sum
index a0eff427da2f2fd75f4ad01c597bc55a2c23e8e2..4607de2ce594dcf051fa9f2d0afbc44e2e517a97 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -250,10 +250,11 @@ github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.m
 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/go-xorm/xorm v0.7.6/go.mod h1:nqz2TAsuOHWH2yk4FYWtacCGgdbrcdZ5mF1XadqEHls=
-github.com/go-xorm/xorm v0.7.7-0.20190822154023-17592d96b35b h1:Y0hWUheXDHpIs7BWtJcykO4d1VOsVDKg1PsP5YJwxxM=
-github.com/go-xorm/xorm v0.7.7-0.20190822154023-17592d96b35b/go.mod h1:nqz2TAsuOHWH2yk4FYWtacCGgdbrcdZ5mF1XadqEHls=
+github.com/go-xorm/xorm v0.7.8-0.20190924080535-59ed80ce1a67 h1:mB5RWONyATkQ48+iQZ1lCZNPG3tABilyaEOxDm1QWyU=
+github.com/go-xorm/xorm v0.7.8-0.20190924080535-59ed80ce1a67/go.mod h1:RSsmsVARCy4sayuKWFPaVNQMPYGLNRIK71YIVvgImL0=
 github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561 h1:deE7ritpK04PgtpyVOS2TYcQEld9qLCD5b5EbVNOuLA=
 github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:YgYOrVn3Nj9Tq0EvjmFbphRytDj7JNRoWSStJZWDJTQ=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -340,6 +341,7 @@ github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+Vzrr
 github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jackc/pgx v3.5.0+incompatible h1:BRJ4G3UPtvml5R1ey0biqqGuYUGayMYekm3woO75orY=
 github.com/jackc/pgx v3.5.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
+github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
 github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d h1:ig/iUfDDg06RVW8OMby+GrmW6K2nPO3AFHlEIdvJSd4=
 github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
@@ -818,5 +820,7 @@ strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a h1:8q33Shx
 strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
 xorm.io/builder v0.3.5 h1:EilU39fvWDxjb1cDaELpYhsF+zziRBhew8xk4pngO+A=
 xorm.io/builder v0.3.5/go.mod h1:ZFbByS/KxZI1FKRjL05PyJ4YrK2bcxlUaAxdum5aTR8=
+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.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM=
 xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI=
index c4b15e9ddfd31ba9437e2060eb386c91ad3472ce..77712c0fec72ea8a62c38bfbdd41f1d14b7ee23a 100644 (file)
@@ -760,11 +760,6 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (er
                return err
        }
        for idx := range issue.Labels {
-               if issue.IsClosed {
-                       issue.Labels[idx].NumClosedIssues++
-               } else {
-                       issue.Labels[idx].NumClosedIssues--
-               }
                if err = updateLabel(e, issue.Labels[idx]); err != nil {
                        return err
                }
index f378f62e654ecab6706886d840ec88d66714d62a..dab5ba2827c7076dffe61310e82a5c928cf95750 100644 (file)
@@ -14,6 +14,7 @@ import (
        api "code.gitea.io/gitea/modules/structs"
 
        "github.com/go-xorm/xorm"
+       "xorm.io/builder"
 )
 
 var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
@@ -294,7 +295,20 @@ func GetLabelsByIssueID(issueID int64) ([]*Label, error) {
 }
 
 func updateLabel(e Engine, l *Label) error {
-       _, err := e.ID(l.ID).AllCols().Update(l)
+       _, err := e.ID(l.ID).
+               SetExpr("num_issues",
+                       builder.Select("count(*)").From("issue_label").
+                               Where(builder.Eq{"label_id": l.ID}),
+               ).
+               SetExpr("num_closed_issues",
+                       builder.Select("count(*)").From("issue_label").
+                               InnerJoin("issue", "issue_label.issue_id = issue.id").
+                               Where(builder.Eq{
+                                       "issue_label.label_id": l.ID,
+                                       "issue.is_closed":      true,
+                               }),
+               ).
+               AllCols().Update(l)
        return err
 }
 
@@ -375,10 +389,6 @@ func newIssueLabel(e *xorm.Session, issue *Issue, label *Label, doer *User) (err
                return err
        }
 
-       label.NumIssues++
-       if issue.IsClosed {
-               label.NumClosedIssues++
-       }
        return updateLabel(e, label)
 }
 
@@ -448,10 +458,6 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label, doer *User) (
                return err
        }
 
-       label.NumIssues--
-       if issue.IsClosed {
-               label.NumClosedIssues--
-       }
        return updateLabel(e, label)
 }
 
index 5cdc059cffdf30408406163d22820a9afdebcf24..3cf6cc0e5785b47052b79d8522237bec58375be9 100644 (file)
@@ -205,6 +205,7 @@ func TestNewIssueLabel(t *testing.T) {
                LabelID:  label.ID,
                Content:  "1",
        })
+       label = AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label)
        assert.EqualValues(t, prevNumIssues+1, label.NumIssues)
 
        // re-add existing IssueLabel
index 94ba92e6088fac7aa60d06903fda7a546237e2b0..6001ec595a8180d3ab7ee943c3c6093053c92b9e 100644 (file)
@@ -103,7 +103,18 @@ steps:
   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"
-  - 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 > coverage.txt
+  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"
+  - 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 > coverage.txt
   when:
     event:
     - push
@@ -134,6 +145,19 @@ services:
     - 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
@@ -167,11 +191,13 @@ 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
-    - GO111MODULE=off go build -v
+    - go build -v
   when:
     event:
     - push
@@ -181,9 +207,10 @@ steps:
   pull: default
   image: golang:1.11
   environment:
+    GO111MODULE: "on"
     GOPROXY: "https://goproxy.cn"
   commands:
-    - GO111MODULE=on go build -v
+    - go build -v
   when:
     event:
     - push
@@ -192,8 +219,10 @@ steps:
 - name: test-sqlite
   pull: default
   image: golang:1.11
+  environment:
+    GO111MODULE: "on"
+    GOPROXY: "https://goproxy.cn"
   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:
@@ -204,6 +233,9 @@ steps:
 - 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"
@@ -215,6 +247,9 @@ steps:
 - 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"
@@ -226,6 +261,9 @@ steps:
 - 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"
@@ -237,6 +275,9 @@ steps:
 - 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"
@@ -248,10 +289,28 @@ steps:
 - 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"
-  - 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 > coverage.txt
+  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"
+  - 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 > coverage.txt
   when:
     event:
     - push
@@ -282,6 +341,19 @@ services:
     - 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
@@ -315,11 +387,13 @@ 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
-    - GO111MODULE=off go build -v
+    - go build -v
   when:
     event:
     - push
@@ -329,9 +403,10 @@ steps:
   pull: default
   image: golang:1.12
   environment:
-    GOPROXY: "https://goproxy.cn" 
+    GO111MODULE: "on"
+    GOPROXY: "https://goproxy.cn"
   commands:
-    - GO111MODULE=on go build -v
+    - go build -v
   when:
     event:
     - push
@@ -340,8 +415,10 @@ steps:
 - name: test-sqlite
   pull: default
   image: golang:1.12
+  environment:
+    GO111MODULE: "on"
+    GOPROXY: "https://goproxy.cn"
   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:
@@ -352,6 +429,9 @@ steps:
 - 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"
@@ -363,6 +443,9 @@ steps:
 - 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"
@@ -374,6 +457,9 @@ steps:
 - 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"
@@ -385,6 +471,9 @@ steps:
 - 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"
@@ -396,10 +485,28 @@ steps:
 - 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"
-  - 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 > coverage.txt
+  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"
+  - go get -u 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 > coverage.txt
   when:
     event:
     - push
@@ -424,6 +531,19 @@ services:
   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
index e1c377a063c5fd5a0e95619eef2a8cb7178eb7b4..3df682e81ac4ce812b4fd6581351b4b909b34585 100644 (file)
@@ -952,7 +952,7 @@ func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) {
 
 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, numeric_precision, numeric_precision_radix ,
+       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
@@ -987,14 +987,14 @@ WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.att
                col.Indexes = make(map[string]int)
 
                var colName, isNullable, dataType string
-               var maxLenStr, colDefault, numPrecision, numRadix *string
+               var maxLenStr, colDefault *string
                var isPK, isUnique bool
-               err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix, &isPK, &isUnique)
+               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, numPrecision, numRadix, isPK, isUnique)
+               // fmt.Println(args, colName, isNullable, dataType, maxLenStr, colDefault, isPK, isUnique)
                var maxLen int
                if maxLenStr != nil {
                        maxLen, err = strconv.Atoi(*maxLenStr)
index ebcab91bc7c72c0dc4478dd272f7e33c9f6f22f2..649fd1e306bc0faee9eabecbc4c0814a20e801ad 100644 (file)
@@ -190,14 +190,14 @@ func (engine *Engine) Quote(value string) string {
                return value
        }
 
-       buf := builder.StringBuilder{}
+       buf := strings.Builder{}
        engine.QuoteTo(&buf, value)
 
        return buf.String()
 }
 
 // QuoteTo quotes string and writes into the buffer
-func (engine *Engine) QuoteTo(buf *builder.StringBuilder, value string) {
+func (engine *Engine) QuoteTo(buf *strings.Builder, value string) {
        if buf == nil {
                return
        }
@@ -729,7 +729,7 @@ func (engine *Engine) Decr(column string, arg ...interface{}) *Session {
 }
 
 // SetExpr provides a update string like "column = {expression}"
-func (engine *Engine) SetExpr(column string, expression string) *Session {
+func (engine *Engine) SetExpr(column string, expression interface{}) *Session {
        session := engine.NewSession()
        session.isAutoClose = true
        return session.SetExpr(column, expression)
index 9a30e7978f3c5514b891cb0bda19479e0025c199..eb0d71ba1acad36910a652f1b868b1cd40371c9e 100644 (file)
@@ -1,19 +1,20 @@
 module github.com/go-xorm/xorm
 
+go 1.11
+
 require (
        github.com/cockroachdb/apd v1.1.0 // indirect
        github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4
        github.com/go-sql-driver/mysql v1.4.1
+       github.com/gofrs/uuid v3.2.0+incompatible // indirect
        github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
-       github.com/jackc/pgx v3.3.0+incompatible
-       github.com/kr/pretty v0.1.0 // indirect
+       github.com/jackc/pgx v3.6.0+incompatible
        github.com/lib/pq v1.0.0
        github.com/mattn/go-sqlite3 v1.10.0
        github.com/pkg/errors v0.8.1 // indirect
-       github.com/satori/go.uuid v1.2.0 // indirect
        github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
        github.com/stretchr/testify v1.3.0
        github.com/ziutek/mymysql v1.5.4
-       xorm.io/builder v0.3.5
+       xorm.io/builder v0.3.6
        xorm.io/core v0.7.0
 )
index 370dcd0b6083a851d2d482b9a8ec2ff608774fe0..15f16fbc69c73cf9bfa9f24ec484c2d372166bc1 100644 (file)
@@ -28,6 +28,8 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
 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/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 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=
@@ -48,18 +50,13 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
 github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
-github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90=
-github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
+github.com/jackc/pgx v3.6.0+incompatible h1:bJeo4JdVbDAW8KB2m8XkFeo8CPipREoG37BwEoKGz+Q=
+github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 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=
@@ -84,8 +81,6 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 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/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
-github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -133,6 +128,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 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/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 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=
@@ -162,7 +158,7 @@ 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.5 h1:EilU39fvWDxjb1cDaELpYhsF+zziRBhew8xk4pngO+A=
-xorm.io/builder v0.3.5/go.mod h1:ZFbByS/KxZI1FKRjL05PyJ4YrK2bcxlUaAxdum5aTR8=
+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.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM=
 xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI=
index 0928f66a9ad04f6fd083374dbff2402dadd245d9..a564db12608fef24b31602cac7cddbcc4eacffe9 100644 (file)
@@ -54,7 +54,7 @@ type Interface interface {
        QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error)
        QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error)
        Rows(bean interface{}) (*Rows, error)
-       SetExpr(string, string) *Session
+       SetExpr(string, interface{}) *Session
        SQL(interface{}, ...interface{}) *Session
        Sum(bean interface{}, colName string) (float64, error)
        SumInt(bean interface{}, colName string) (int64, error)
index dc3befcf6b9893c8435fafd7387176a7b78a5bd5..1558074f3de10ceedeaa6e3845989ff91cdf73cb 100644 (file)
@@ -12,49 +12,6 @@ import (
        "xorm.io/core"
 )
 
-type incrParam struct {
-       colName string
-       arg     interface{}
-}
-
-type decrParam struct {
-       colName string
-       arg     interface{}
-}
-
-type exprParam struct {
-       colName string
-       expr    string
-}
-
-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
-}
-
 func setColumnInt(bean interface{}, col *core.Column, t int64) {
        v, err := col.ValueOf(bean)
        if err != nil {
@@ -132,7 +89,7 @@ func (session *Session) Decr(column string, arg ...interface{}) *Session {
 }
 
 // SetExpr provides a query string like "column = {expression}"
-func (session *Session) SetExpr(column string, expression string) *Session {
+func (session *Session) SetExpr(column string, expression interface{}) *Session {
        session.statement.SetExpr(column, expression)
        return session
 }
index 24b328314aafac2c90a9cea44867dfdf9e3a50d8..de6452909cf15f29f9c498d0b9ecba133f5b8f17 100644 (file)
@@ -340,74 +340,96 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
        if err != nil {
                return 0, err
        }
-       // insert expr columns, override if exists
-       exprColumns := session.statement.getExpr()
-       exprColVals := make([]string, 0, len(exprColumns))
-       for _, v := range exprColumns {
-               // remove the expr columns
-               for i, colName := range colNames {
-                       if colName == strings.Trim(v.colName, "`") {
-                               colNames = append(colNames[:i], colNames[i+1:]...)
-                               args = append(args[:i], args[i+1:]...)
-                       }
-               }
-
-               // append expr column to the end
-               colNames = append(colNames, v.colName)
-               exprColVals = append(exprColVals, v.expr)
-       }
 
-       colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns))
-       if len(exprColVals) > 0 {
-               colPlaces = colPlaces + strings.Join(exprColVals, ", ")
-       } else {
-               if len(colPlaces) > 0 {
-                       colPlaces = colPlaces[0 : len(colPlaces)-2]
-               }
+       exprs := session.statement.exprColumns
+       colPlaces := strings.Repeat("?, ", len(colNames))
+       if exprs.Len() <= 0 && len(colPlaces) > 0 {
+               colPlaces = colPlaces[0 : len(colPlaces)-2]
        }
 
-       var sqlStr string
        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)
        }
 
-       if len(colPlaces) > 0 {
-               if session.statement.cond.IsValid() {
-                       condSQL, condArgs, err := builder.ToSQL(session.statement.cond)
-                       if err != nil {
+       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
                        }
-
-                       sqlStr = fmt.Sprintf("INSERT INTO %s (%v)%s SELECT %v FROM %v WHERE %v",
-                               session.engine.Quote(tableName),
-                               quoteColumns(colNames, session.engine.Quote, ","),
-                               output,
-                               colPlaces,
-                               session.engine.Quote(tableName),
-                               condSQL,
-                       )
-                       args = append(args, condArgs...)
                } else {
-                       sqlStr = fmt.Sprintf("INSERT INTO %s (%v)%s VALUES (%v)",
-                               session.engine.Quote(tableName),
-                               quoteColumns(colNames, session.engine.Quote, ","),
-                               output,
-                               colPlaces)
+                       if _, err := buf.WriteString(fmt.Sprintf("%s DEFAULT VALUES", output)); err != nil {
+                               return 0, err
+                       }
                }
        } else {
-               if session.engine.dialect.DBType() == core.MYSQL {
-                       sqlStr = fmt.Sprintf("INSERT INTO %s VALUES ()", session.engine.Quote(tableName))
+               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 {
-                       sqlStr = fmt.Sprintf("INSERT INTO %s%s DEFAULT VALUES", session.engine.Quote(tableName), output)
+                       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 {
-               sqlStr = sqlStr + " RETURNING " + session.engine.Quote(table.AutoIncrement)
+               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 {
@@ -611,9 +633,11 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac
                        continue
                }
 
-               if _, ok := session.statement.incrColumns[col.Name]; ok {
+               if session.statement.incrColumns.isColExist(col.Name) {
                        continue
-               } else if _, ok := session.statement.decrColumns[col.Name]; ok {
+               } else if session.statement.decrColumns.isColExist(col.Name) {
+                       continue
+               } else if session.statement.exprColumns.isColExist(col.Name) {
                        continue
                }
 
@@ -688,46 +712,66 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err
        }
 
        var columns = make([]string, 0, len(m))
+       exprs := session.statement.exprColumns
        for k := range m {
-               columns = append(columns, k)
+               if !exprs.isColExist(k) {
+                       columns = append(columns, k)
+               }
        }
        sort.Strings(columns)
 
-       qm := strings.Repeat("?,", len(columns))
-
        var args = make([]interface{}, 0, len(m))
        for _, colName := range columns {
                args = append(args, m[colName])
        }
 
-       // insert expr columns, override if exists
-       exprColumns := session.statement.getExpr()
-       for _, col := range exprColumns {
-               columns = append(columns, strings.Trim(col.colName, "`"))
-               qm = qm + col.expr + ","
-       }
+       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
+               }
 
-       qm = qm[:len(qm)-1]
+               if err := writeStrings(w, append(columns, exprs.colNames...), "`", "`"); err != nil {
+                       return 0, err
+               }
 
-       var sql string
+               if _, err := w.WriteString(") SELECT "); err != nil {
+                       return 0, err
+               }
 
-       if session.statement.cond.IsValid() {
-               condSQL, condArgs, err := builder.ToSQL(session.statement.cond)
-               if err != nil {
+               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
                }
-               sql = fmt.Sprintf("INSERT INTO %s (`%s`) SELECT %s FROM %s WHERE %s",
-                       session.engine.Quote(tableName),
-                       strings.Join(columns, "`,`"),
-                       qm,
-                       session.engine.Quote(tableName),
-                       condSQL,
-               )
-               args = append(args, condArgs...)
        } else {
-               sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm)
+               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
        }
@@ -754,8 +798,11 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) {
        }
 
        var columns = make([]string, 0, len(m))
+       exprs := session.statement.exprColumns
        for k := range m {
-               columns = append(columns, k)
+               if !exprs.isColExist(k) {
+                       columns = append(columns, k)
+               }
        }
        sort.Strings(columns)
 
@@ -764,37 +811,53 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) {
                args = append(args, m[colName])
        }
 
-       qm := strings.Repeat("?,", len(columns))
+       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
+               }
 
-       // insert expr columns, override if exists
-       exprColumns := session.statement.getExpr()
-       for _, col := range exprColumns {
-               columns = append(columns, strings.Trim(col.colName, "`"))
-               qm = qm + col.expr + ","
-       }
+               if err := writeStrings(w, append(columns, exprs.colNames...), "`", "`"); err != nil {
+                       return 0, err
+               }
 
-       qm = qm[:len(qm)-1]
+               if _, err := w.WriteString(") SELECT "); err != nil {
+                       return 0, err
+               }
 
-       var sql string
+               if err := session.statement.writeArgs(w, args); err != nil {
+                       return 0, err
+               }
 
-       if session.statement.cond.IsValid() {
-               qm = "(" + qm[:len(qm)-1] + ")"
-               condSQL, condArgs, err := builder.ToSQL(session.statement.cond)
-               if err != nil {
+               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
                }
-               sql = fmt.Sprintf("INSERT INTO %s (`%s`) SELECT %s FROM %s WHERE %s",
-                       session.engine.Quote(tableName),
-                       strings.Join(columns, "`,`"),
-                       qm,
-                       session.engine.Quote(tableName),
-                       condSQL,
-               )
-               args = append(args, condArgs...)
        } else {
-               sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm)
+               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
        }
index 85b0bb0bf81af4283421496cedc8d5b83c89afb2..c5c65a452a173c1fb23c5bbd5581412668e5932a 100644 (file)
@@ -223,21 +223,31 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
        }
 
        // for update action to like "column = column + ?"
-       incColumns := session.statement.getInc()
-       for _, v := range incColumns {
-               colNames = append(colNames, session.engine.Quote(v.colName)+" = "+session.engine.Quote(v.colName)+" + ?")
-               args = append(args, v.arg)
+       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.getDec()
-       for _, v := range decColumns {
-               colNames = append(colNames, session.engine.Quote(v.colName)+" = "+session.engine.Quote(v.colName)+" - ?")
-               args = append(args, v.arg)
+       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.getExpr()
-       for _, v := range exprColumns {
-               colNames = append(colNames, session.engine.Quote(v.colName)+" = "+v.expr)
+       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 {
@@ -468,14 +478,17 @@ func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interfac
                        continue
                }
 
-               if len(session.statement.columnMap) > 0 {
-                       if !session.statement.columnMap.contain(col.Name) {
-                               continue
-                       } else if _, ok := session.statement.incrColumns[col.Name]; ok {
-                               continue
-                       } else if _, ok := session.statement.decrColumns[col.Name]; ok {
-                               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
index 585378a8b1bb645aa06fbcd396de3d3523eac032..3cc0831eec42a437690cb2689a00495ba6391c51 100644 (file)
@@ -52,9 +52,9 @@ type Statement struct {
        omitColumnMap   columnMap
        mustColumnMap   map[string]bool
        nullableMap     map[string]bool
-       incrColumns     map[string]incrParam
-       decrColumns     map[string]decrParam
-       exprColumns     map[string]exprParam
+       incrColumns     exprParams
+       decrColumns     exprParams
+       exprColumns     exprParams
        cond            builder.Cond
        bufferSize      int
        context         ContextCache
@@ -94,9 +94,9 @@ func (statement *Statement) Init() {
        statement.nullableMap = make(map[string]bool)
        statement.checkVersion = true
        statement.unscoped = false
-       statement.incrColumns = make(map[string]incrParam)
-       statement.decrColumns = make(map[string]decrParam)
-       statement.exprColumns = make(map[string]exprParam)
+       statement.incrColumns = exprParams{}
+       statement.decrColumns = exprParams{}
+       statement.exprColumns = exprParams{}
        statement.cond = builder.NewCond()
        statement.bufferSize = 0
        statement.context = nil
@@ -534,48 +534,30 @@ func (statement *Statement) ID(id interface{}) *Statement {
 
 // Incr Generate  "Update ... Set column = column + arg" statement
 func (statement *Statement) Incr(column string, arg ...interface{}) *Statement {
-       k := strings.ToLower(column)
        if len(arg) > 0 {
-               statement.incrColumns[k] = incrParam{column, arg[0]}
+               statement.incrColumns.addParam(column, arg[0])
        } else {
-               statement.incrColumns[k] = incrParam{column, 1}
+               statement.incrColumns.addParam(column, 1)
        }
        return statement
 }
 
 // Decr Generate  "Update ... Set column = column - arg" statement
 func (statement *Statement) Decr(column string, arg ...interface{}) *Statement {
-       k := strings.ToLower(column)
        if len(arg) > 0 {
-               statement.decrColumns[k] = decrParam{column, arg[0]}
+               statement.decrColumns.addParam(column, arg[0])
        } else {
-               statement.decrColumns[k] = decrParam{column, 1}
+               statement.decrColumns.addParam(column, 1)
        }
        return statement
 }
 
 // SetExpr Generate  "Update ... Set column = {expression}" statement
-func (statement *Statement) SetExpr(column string, expression string) *Statement {
-       k := strings.ToLower(column)
-       statement.exprColumns[k] = exprParam{column, expression}
+func (statement *Statement) SetExpr(column string, expression interface{}) *Statement {
+       statement.exprColumns.addParam(column, expression)
        return statement
 }
 
-// Generate  "Update ... Set column = column + arg" statement
-func (statement *Statement) getInc() map[string]incrParam {
-       return statement.incrColumns
-}
-
-// Generate  "Update ... Set column = column - arg" statement
-func (statement *Statement) getDec() map[string]decrParam {
-       return statement.decrColumns
-}
-
-// Generate  "Update ... Set column = {expression}" statement
-func (statement *Statement) getExpr() map[string]exprParam {
-       return statement.exprColumns
-}
-
 func (statement *Statement) col2NewColsWithQuote(columns ...string) []string {
        newColumns := make([]string, 0)
        quotes := append(strings.Split(statement.Engine.Quote(""), ""), "`")
@@ -695,7 +677,7 @@ func (statement *Statement) OrderBy(order string) *Statement {
 
 // Desc generate `ORDER BY xx DESC`
 func (statement *Statement) Desc(colNames ...string) *Statement {
-       var buf builder.StringBuilder
+       var buf strings.Builder
        if len(statement.OrderStr) > 0 {
                fmt.Fprint(&buf, statement.OrderStr, ", ")
        }
@@ -707,7 +689,7 @@ func (statement *Statement) Desc(colNames ...string) *Statement {
 
 // Asc provide asc order by query condition, the input parameters are columns.
 func (statement *Statement) Asc(colNames ...string) *Statement {
-       var buf builder.StringBuilder
+       var buf strings.Builder
        if len(statement.OrderStr) > 0 {
                fmt.Fprint(&buf, statement.OrderStr, ", ")
        }
@@ -736,7 +718,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *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 builder.StringBuilder
+       var buf strings.Builder
        if len(statement.JoinStr) > 0 {
                fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP)
        } else {
@@ -801,7 +783,7 @@ func (statement *Statement) genColumnStr() string {
                return ""
        }
 
-       var buf builder.StringBuilder
+       var buf strings.Builder
        columns := statement.RefTable.Columns()
 
        for _, col := range columns {
@@ -1118,7 +1100,7 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, n
                }
        }
 
-       var buf builder.StringBuilder
+       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 {
diff --git a/vendor/github.com/go-xorm/xorm/statement_args.go b/vendor/github.com/go-xorm/xorm/statement_args.go
new file mode 100644 (file)
index 0000000..4ce336f
--- /dev/null
@@ -0,0 +1,97 @@
+// 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"
+
+       "xorm.io/builder"
+       "xorm.io/core"
+)
+
+func (statement *Statement) writeArg(w *builder.BytesWriter, arg interface{}) error {
+       switch argv := arg.(type) {
+       case string:
+               if _, err := w.WriteString("'" + argv + "'"); err != nil {
+                       return err
+               }
+       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 _, err := w.WriteString(fmt.Sprintf("%v", argv)); 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/github.com/go-xorm/xorm/statement_columnmap.go b/vendor/github.com/go-xorm/xorm/statement_columnmap.go
new file mode 100644 (file)
index 0000000..b6523b1
--- /dev/null
@@ -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/github.com/go-xorm/xorm/statement_exprparam.go b/vendor/github.com/go-xorm/xorm/statement_exprparam.go
new file mode 100644 (file)
index 0000000..0cddca0
--- /dev/null
@@ -0,0 +1,112 @@
+// 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 _, 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
+                       }
+               }
+       }
+       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/github.com/go-xorm/xorm/statement_quote.go b/vendor/github.com/go-xorm/xorm/statement_quote.go
new file mode 100644 (file)
index 0000000..e22e0d1
--- /dev/null
@@ -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
+}
index 7f060cff32cdaac7822520d4d8c7cc00cea26245..e26e164118993f7783f99261fd5be642d7cafac7 100644 (file)
@@ -1 +1 @@
-go test -db=mssql -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test"
\ No newline at end of file
+go test -db=mssql -conn_str="server=localhost;user id=sa;password=MwantsaSecurePassword1;database=xorm_test"
\ No newline at end of file
index e300a9bbe40ea317e288fd61cf8e4b8b1963ea89..56df013a5d2d53202823cccde538f3c39011f9f6 100644 (file)
@@ -198,7 +198,7 @@ github.com/go-swagger/go-swagger/cmd/swagger/commands/initcmd
 github.com/go-swagger/go-swagger/codescan
 github.com/go-swagger/go-swagger/generator
 github.com/go-swagger/go-swagger/scan
-# github.com/go-xorm/xorm v0.7.7-0.20190822154023-17592d96b35b
+# github.com/go-xorm/xorm v0.7.8-0.20190924080535-59ed80ce1a67
 github.com/go-xorm/xorm
 # github.com/gobwas/glob v0.2.3
 github.com/gobwas/glob
@@ -612,7 +612,7 @@ gopkg.in/yaml.v2
 mvdan.cc/xurls/v2
 # strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a
 strk.kbt.io/projects/go/libravatar
-# xorm.io/builder v0.3.5
+# xorm.io/builder v0.3.6
 xorm.io/builder
 # xorm.io/core v0.7.0
 xorm.io/core
index ca40377721ebc3486ef02fad040276613d299724..557dbf66f08f9f2a9f57aa80dda6fd768019fb64 100644 (file)
@@ -1,37 +1,90 @@
+---
+kind: pipeline
+name: go1.10
+
 workspace:
   base: /go
-  path: src/github.com/go-xorm/builder
-
-clone:
-  git:
-    image: plugins/git:next
-    depth: 50
-    tags: true
-
-matrix:
-  GO_VERSION:
-    - 1.8
-    - 1.9
-    - 1.10
-    - 1.11
-
-pipeline:
-  test:
-    image: golang:${GO_VERSION}
-    commands:
-      - go get -u github.com/golang/lint/golint
-      - go get -u github.com/stretchr/testify/assert
-      - go get -u github.com/go-xorm/sqlfiddle
-      - golint ./...
-      - go test -v -race -coverprofile=coverage.txt -covermode=atomic
-    when:
-      event: [ push, tag, pull_request ]
-
-codecov:
-    image: robertstettner/drone-codecov
-    group: build
-    secrets: [ codecov_token ]
-    files:
-      - coverage.txt
-    when:
-      event: [ push, pull_request ]
\ No newline at end of file
+  path: src/xorm.io/builder
+
+steps:
+- name: test
+  pull: default
+  image: golang:1.10
+  commands:
+  - go get -u golang.org/x/lint/golint
+  - go get -u github.com/stretchr/testify/assert
+  - go get -u github.com/go-xorm/sqlfiddle
+  - golint ./...
+  - go vet
+  - go test -v -race -coverprofile=coverage.txt -covermode=atomic
+  when:
+    event:
+    - push
+    - tag
+    - pull_request
+
+---
+kind: pipeline
+name: go1.11
+
+steps:
+- name: test
+  pull: default
+  image: golang:1.11
+  commands:
+  - go get -u golang.org/x/lint/golint
+  - golint ./...
+  - go vet
+  - go test -v -race -coverprofile=coverage.txt -covermode=atomic
+  environment:
+    GOPROXY: https://goproxy.cn
+    GO111MODULE: "on"
+  when:
+    event:
+    - push
+    - tag
+    - pull_request
+
+---
+kind: pipeline
+name: go1.12
+
+steps:
+- name: test
+  pull: default
+  image: golang:1.12
+  commands:
+  - go get -u golang.org/x/lint/golint
+  - golint ./...
+  - go vet
+  - go test -v -race -coverprofile=coverage.txt -covermode=atomic
+  environment:
+    GOPROXY: https://goproxy.cn
+    GO111MODULE: "on"
+  when:
+    event:
+    - push
+    - tag
+    - pull_request
+
+---
+kind: pipeline
+name: go1.13
+
+steps:
+- name: test
+  pull: default
+  image: golang:1.13
+  commands:
+  - go get -u golang.org/x/lint/golint
+  - golint ./...
+  - go vet
+  - go test -v -race -coverprofile=coverage.txt -covermode=atomic
+  environment:
+    GOPROXY: https://goproxy.cn
+    GO111MODULE: "on"
+  when:
+    event:
+    - push
+    - tag
+    - pull_request
\ No newline at end of file
index cf516d1fd58b63150efeb810482c62bdc791c246..53e5403203ade6eec386597ff093b564699ec6cb 100644 (file)
@@ -1,13 +1,13 @@
 # SQL builder
 
-[![GitCI.cn](https://gitci.cn/api/badges/go-xorm/builder/status.svg)](https://gitci.cn/go-xorm/builder)  [![codecov](https://codecov.io/gh/go-xorm/builder/branch/master/graph/badge.svg)](https://codecov.io/gh/go-xorm/builder)
-[![](https://goreportcard.com/badge/github.com/go-xorm/builder)](https://goreportcard.com/report/github.com/go-xorm/builder)
+[![Build Status](https://drone.gitea.com/api/badges/xorm/builder/status.svg)](https://drone.gitea.com/xorm/builder) [![](http://gocover.io/_badge/xorm.io/builder)](http://gocover.io/xorm.io/builder)
+[![](https://goreportcard.com/badge/xorm.io/builder)](https://goreportcard.com/report/xorm.io/builder)
 
 Package builder is a lightweight and fast SQL builder for Go and XORM.
 
 Make sure you have installed Go 1.8+ and then:
 
-    go get github.com/go-xorm/builder
+    go get xorm.io/builder
 
 # Insert
 
@@ -71,7 +71,7 @@ sql, args, err := Select("*").From("a").Where(Eq{"status": "1"}).
 * `Eq` is a redefine of a map, you can give one or more conditions to `Eq`
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(Eq{"a":1})
 // a=? [1]
@@ -90,7 +90,7 @@ sql, args, _ := ToSQL(Eq{"b": 1, "c":[]int{2, 3}})
 * `Neq` is the same to `Eq`
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(Neq{"a":1})
 // a<>? [1]
@@ -109,7 +109,7 @@ sql, args, _ := ToSQL(Neq{"b": 1, "c":[]int{2, 3}})
 * `Gt`, `Gte`, `Lt`, `Lte`
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(Gt{"a", 1}.And(Gte{"b", 2}))
 // a>? AND b>=? [1, 2]
@@ -120,7 +120,7 @@ sql, args, _ := ToSQL(Lt{"a", 1}.Or(Lte{"b", 2}))
 * `Like`
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(Like{"a", "c"})
 // a LIKE ? [%c%]
@@ -129,7 +129,7 @@ sql, args, _ := ToSQL(Like{"a", "c"})
 * `Expr` you can customerize your sql with `Expr`
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(Expr("a = ? ", 1))
 // a = ? [1]
@@ -140,7 +140,7 @@ sql, args, _ := ToSQL(Eq{"a": Expr("select id from table where c = ?", 1)})
 * `In` and `NotIn`
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(In("a", 1, 2, 3))
 // a IN (?,?,?) [1,2,3]
@@ -153,7 +153,7 @@ sql, args, _ := ToSQL(In("a", Expr("select id from b where c = ?", 1))))
 * `IsNull` and `NotNull`
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(IsNull{"a"})
 // a IS NULL []
@@ -164,7 +164,7 @@ sql, args, _ := ToSQL(NotNull{"b"})
 * `And(conds ...Cond)`, And can connect one or more condtions via And
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(And(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
 // a=? AND b LIKE ? AND d<>? [1, %c%, 2]
@@ -173,7 +173,7 @@ sql, args, _ := ToSQL(And(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
 * `Or(conds ...Cond)`, Or can connect one or more conditions via Or
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(Or(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
 // a=? OR b LIKE ? OR d<>? [1, %c%, 2]
@@ -184,7 +184,7 @@ sql, args, _ := ToSQL(Or(Eq{"a":1}, And(Like{"b", "c"}, Neq{"d", 2})))
 * `Between`
 
 ```Go
-import . "github.com/go-xorm/builder"
+import . "xorm.io/builder"
 
 sql, args, _ := ToSQL(Between{"a", 1, 2})
 // a BETWEEN 1 AND 2
index ffe86d4dcb56f97a732b9580267c4cd21487b398..4f142228430f8786b038b323b69910434b03992f 100644 (file)
@@ -7,7 +7,6 @@ package builder
 import (
        sql2 "database/sql"
        "fmt"
-       "sort"
 )
 
 type optype byte
@@ -21,6 +20,7 @@ const (
        unionType                // union
 )
 
+// all databasees
 const (
        POSTGRES = "postgres"
        SQLITE   = "sqlite3"
@@ -31,7 +31,7 @@ const (
 
 type join struct {
        joinType  string
-       joinTable string
+       joinTable interface{}
        joinCond  Cond
 }
 
@@ -60,7 +60,7 @@ type Builder struct {
        limitation *limit
        insertCols []string
        insertVals []interface{}
-       updates    []Eq
+       updates    []UpdateCond
        orderBy    string
        groupBy    string
        having     string
@@ -143,18 +143,6 @@ func (b *Builder) Into(tableName string) *Builder {
        return b
 }
 
-// Join sets join table and conditions
-func (b *Builder) Join(joinType, joinTable string, joinCond interface{}) *Builder {
-       switch joinCond.(type) {
-       case Cond:
-               b.joins = append(b.joins, join{joinType, joinTable, joinCond.(Cond)})
-       case string:
-               b.joins = append(b.joins, join{joinType, joinTable, Expr(joinCond.(string))})
-       }
-
-       return b
-}
-
 // Union sets union conditions
 func (b *Builder) Union(unionTp string, unionCond *Builder) *Builder {
        var builder *Builder
@@ -199,31 +187,6 @@ func (b *Builder) Limit(limitN int, offset ...int) *Builder {
        return b
 }
 
-// InnerJoin sets inner join
-func (b *Builder) InnerJoin(joinTable string, joinCond interface{}) *Builder {
-       return b.Join("INNER", joinTable, joinCond)
-}
-
-// LeftJoin sets left join SQL
-func (b *Builder) LeftJoin(joinTable string, joinCond interface{}) *Builder {
-       return b.Join("LEFT", joinTable, joinCond)
-}
-
-// RightJoin sets right join SQL
-func (b *Builder) RightJoin(joinTable string, joinCond interface{}) *Builder {
-       return b.Join("RIGHT", joinTable, joinCond)
-}
-
-// CrossJoin sets cross join SQL
-func (b *Builder) CrossJoin(joinTable string, joinCond interface{}) *Builder {
-       return b.Join("CROSS", joinTable, joinCond)
-}
-
-// FullJoin sets full join SQL
-func (b *Builder) FullJoin(joinTable string, joinCond interface{}) *Builder {
-       return b.Join("FULL", joinTable, joinCond)
-}
-
 // Select sets select SQL
 func (b *Builder) Select(cols ...string) *Builder {
        b.selects = cols
@@ -245,68 +208,12 @@ func (b *Builder) Or(cond Cond) *Builder {
        return b
 }
 
-type insertColsSorter struct {
-       cols []string
-       vals []interface{}
-}
-
-func (s insertColsSorter) Len() int {
-       return len(s.cols)
-}
-func (s insertColsSorter) Swap(i, j int) {
-       s.cols[i], s.cols[j] = s.cols[j], s.cols[i]
-       s.vals[i], s.vals[j] = s.vals[j], s.vals[i]
-}
-
-func (s insertColsSorter) Less(i, j int) bool {
-       return s.cols[i] < s.cols[j]
-}
-
-// Insert sets insert SQL
-func (b *Builder) Insert(eq ...interface{}) *Builder {
-       if len(eq) > 0 {
-               var paramType = -1
-               for _, e := range eq {
-                       switch t := e.(type) {
-                       case Eq:
-                               if paramType == -1 {
-                                       paramType = 0
-                               }
-                               if paramType != 0 {
-                                       break
-                               }
-                               for k, v := range t {
-                                       b.insertCols = append(b.insertCols, k)
-                                       b.insertVals = append(b.insertVals, v)
-                               }
-                       case string:
-                               if paramType == -1 {
-                                       paramType = 1
-                               }
-                               if paramType != 1 {
-                                       break
-                               }
-                               b.insertCols = append(b.insertCols, t)
-                       }
-               }
-       }
-
-       if len(b.insertCols) == len(b.insertVals) {
-               sort.Sort(insertColsSorter{
-                       cols: b.insertCols,
-                       vals: b.insertVals,
-               })
-       }
-       b.optype = insertType
-       return b
-}
-
 // Update sets update SQL
-func (b *Builder) Update(updates ...Eq) *Builder {
-       b.updates = make([]Eq, 0, len(updates))
+func (b *Builder) Update(updates ...Cond) *Builder {
+       b.updates = make([]UpdateCond, 0, len(updates))
        for _, update := range updates {
-               if update.IsValid() {
-                       b.updates = append(b.updates, update)
+               if u, ok := update.(UpdateCond); ok && u.IsValid() {
+                       b.updates = append(b.updates, u)
                }
        }
        b.optype = updateType
@@ -354,7 +261,7 @@ func (b *Builder) ToSQL() (string, []interface{}, error) {
                }
        }
 
-       var sql = w.writer.String()
+       var sql = w.String()
        var err error
 
        switch b.dialect {
@@ -383,12 +290,12 @@ func (b *Builder) ToSQL() (string, []interface{}, error) {
        return sql, w.args, nil
 }
 
-// ToBoundSQL
+// ToBoundSQL generated a bound SQL string
 func (b *Builder) ToBoundSQL() (string, error) {
        w := NewWriter()
        if err := b.WriteTo(w); err != nil {
                return "", err
        }
 
-       return ConvertToBoundSQL(w.writer.String(), w.args)
+       return ConvertToBoundSQL(w.String(), w.args)
 }
index 202cad51d846a0002177991a8d4d9b925e3d3427..9558a8acad5782dfbfb4e61365ef326530327324 100644 (file)
@@ -7,6 +7,7 @@ package builder
 import (
        "bytes"
        "fmt"
+       "sort"
 )
 
 // Insert creates an insert Builder
@@ -87,3 +88,60 @@ func (b *Builder) insertWriteTo(w Writer) error {
 
        return nil
 }
+
+type insertColsSorter struct {
+       cols []string
+       vals []interface{}
+}
+
+func (s insertColsSorter) Len() int {
+       return len(s.cols)
+}
+
+func (s insertColsSorter) Swap(i, j int) {
+       s.cols[i], s.cols[j] = s.cols[j], s.cols[i]
+       s.vals[i], s.vals[j] = s.vals[j], s.vals[i]
+}
+
+func (s insertColsSorter) Less(i, j int) bool {
+       return s.cols[i] < s.cols[j]
+}
+
+// Insert sets insert SQL
+func (b *Builder) Insert(eq ...interface{}) *Builder {
+       if len(eq) > 0 {
+               var paramType = -1
+               for _, e := range eq {
+                       switch t := e.(type) {
+                       case Eq:
+                               if paramType == -1 {
+                                       paramType = 0
+                               }
+                               if paramType != 0 {
+                                       break
+                               }
+                               for k, v := range t {
+                                       b.insertCols = append(b.insertCols, k)
+                                       b.insertVals = append(b.insertVals, v)
+                               }
+                       case string:
+                               if paramType == -1 {
+                                       paramType = 1
+                               }
+                               if paramType != 1 {
+                                       break
+                               }
+                               b.insertCols = append(b.insertCols, t)
+                       }
+               }
+       }
+
+       if len(b.insertCols) == len(b.insertVals) {
+               sort.Sort(insertColsSorter{
+                       cols: b.insertCols,
+                       vals: b.insertVals,
+               })
+       }
+       b.optype = insertType
+       return b
+}
diff --git a/vendor/xorm.io/builder/builder_join.go b/vendor/xorm.io/builder/builder_join.go
new file mode 100644 (file)
index 0000000..a6604c5
--- /dev/null
@@ -0,0 +1,42 @@
+// 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 builder
+
+// InnerJoin sets inner join
+func (b *Builder) InnerJoin(joinTable, joinCond interface{}) *Builder {
+       return b.Join("INNER", joinTable, joinCond)
+}
+
+// LeftJoin sets left join SQL
+func (b *Builder) LeftJoin(joinTable, joinCond interface{}) *Builder {
+       return b.Join("LEFT", joinTable, joinCond)
+}
+
+// RightJoin sets right join SQL
+func (b *Builder) RightJoin(joinTable, joinCond interface{}) *Builder {
+       return b.Join("RIGHT", joinTable, joinCond)
+}
+
+// CrossJoin sets cross join SQL
+func (b *Builder) CrossJoin(joinTable, joinCond interface{}) *Builder {
+       return b.Join("CROSS", joinTable, joinCond)
+}
+
+// FullJoin sets full join SQL
+func (b *Builder) FullJoin(joinTable, joinCond interface{}) *Builder {
+       return b.Join("FULL", joinTable, joinCond)
+}
+
+// Join sets join table and conditions
+func (b *Builder) Join(joinType string, joinTable, joinCond interface{}) *Builder {
+       switch joinCond.(type) {
+       case Cond:
+               b.joins = append(b.joins, join{joinType, joinTable, joinCond.(Cond)})
+       case string:
+               b.joins = append(b.joins, join{joinType, joinTable, Expr(joinCond.(string))})
+       }
+
+       return b
+}
index c33b38698be3176eeb40527184b1636ca42b1da1..814d1b5afb3f5d76e922c4a0108300dc36dc60c7 100644 (file)
@@ -80,8 +80,21 @@ func (b *Builder) selectWriteTo(w Writer) error {
        }
 
        for _, v := range b.joins {
-               if _, err := fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable); err != nil {
-                       return err
+               b, ok := v.joinTable.(*Builder)
+               if ok {
+                       if _, err := fmt.Fprintf(w, " %s JOIN (", v.joinType); err != nil {
+                               return err
+                       }
+                       if err := b.WriteTo(w); err != nil {
+                               return err
+                       }
+                       if _, err := fmt.Fprintf(w, ") ON "); err != nil {
+                               return err
+                       }
+               } else {
+                       if _, err := fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable); err != nil {
+                               return err
+                       }
                }
 
                if err := v.joinCond.WriteTo(w); err != nil {
index 37b455152688091789471620ff8c94579e682b37..9b6e10bc959f0eb9cfe45ee1b3d21dc407d46b79 100644 (file)
@@ -8,8 +8,14 @@ import (
        "fmt"
 )
 
+// UpdateCond defines an interface that cond could be used with update
+type UpdateCond interface {
+       IsValid() bool
+       OpWriteTo(op string, w Writer) error
+}
+
 // Update creates an update Builder
-func Update(updates ...Eq) *Builder {
+func Update(updates ...Cond) *Builder {
        builder := &Builder{cond: NewCond()}
        return builder.Update(updates...)
 }
@@ -27,7 +33,8 @@ func (b *Builder) updateWriteTo(w Writer) error {
        }
 
        for i, s := range b.updates {
-               if err := s.opWriteTo(",", w); err != nil {
+
+               if err := s.OpWriteTo(",", w); err != nil {
                        return err
                }
 
index e44173bbd5a8f03438f9128f1010418c3bcf7745..149f5d8cf7d81d3b44c9ba941aa233bab062fc57 100644 (file)
@@ -4,42 +4,6 @@
 
 package builder
 
-import (
-       "io"
-)
-
-// Writer defines the interface
-type Writer interface {
-       io.Writer
-       Append(...interface{})
-}
-
-var _ Writer = NewWriter()
-
-// BytesWriter implments Writer and save SQL in bytes.Buffer
-type BytesWriter struct {
-       writer *StringBuilder
-       args   []interface{}
-}
-
-// NewWriter creates a new string writer
-func NewWriter() *BytesWriter {
-       w := &BytesWriter{
-               writer: &StringBuilder{},
-       }
-       return w
-}
-
-// Write writes data to Writer
-func (s *BytesWriter) Write(buf []byte) (int, error) {
-       return s.writer.Write(buf)
-}
-
-// Append appends args to Writer
-func (s *BytesWriter) Append(args ...interface{}) {
-       s.args = append(s.args, args...)
-}
-
 // Cond defines an interface
 type Cond interface {
        WriteTo(Writer) error
index 79d795e6dd2f5a0cf500b66b7d1003523534c060..32f04d5d4a969be1bb760d9361d736ca23e87128 100644 (file)
@@ -20,7 +20,8 @@ type Eq map[string]interface{}
 
 var _ Cond = Eq{}
 
-func (eq Eq) opWriteTo(op string, w Writer) error {
+// OpWriteTo writes conditions with special operator
+func (eq Eq) OpWriteTo(op string, w Writer) error {
        var i = 0
        for _, k := range eq.sortedKeys() {
                v := eq[k]
@@ -81,7 +82,7 @@ func (eq Eq) opWriteTo(op string, w Writer) error {
 
 // WriteTo writes SQL to Writer
 func (eq Eq) WriteTo(w Writer) error {
-       return eq.opWriteTo(" AND ", w)
+       return eq.OpWriteTo(" AND ", w)
 }
 
 // And implements And with other conditions
@@ -101,7 +102,7 @@ func (eq Eq) IsValid() bool {
 
 // sortedKeys returns all keys of this Eq sorted with sort.Strings.
 // It is used internally for consistent ordering when generating
-// SQL, see https://github.com/go-xorm/builder/issues/10
+// SQL, see https://gitea.com/xorm/builder/issues/10
 func (eq Eq) sortedKeys() []string {
        keys := make([]string, 0, len(eq))
        for key := range eq {
index e5ed572b1503fb0ac4bf700878e7207c5b800d70..8288aa044095ae1485a619b5fd0f759d9955f8ff 100644 (file)
@@ -18,6 +18,10 @@ func Expr(sql string, args ...interface{}) Cond {
        return expr{sql, args}
 }
 
+func (expr expr) OpWriteTo(op string, w Writer) error {
+       return expr.WriteTo(w)
+}
+
 func (expr expr) WriteTo(w Writer) error {
        if _, err := fmt.Fprint(w, expr.sql); err != nil {
                return err
index 3a8f3136d9d62ac15eccebac5c56390ec4547878..687c59fc2e9a5573332251cd6ca1394bc0159380 100644 (file)
@@ -83,7 +83,7 @@ func (neq Neq) IsValid() bool {
 
 // sortedKeys returns all keys of this Neq sorted with sort.Strings.
 // It is used internally for consistent ordering when generating
-// SQL, see https://github.com/go-xorm/builder/issues/10
+// SQL, see https://gitea.com/xorm/builder/issues/10
 func (neq Neq) sortedKeys() []string {
        keys := make([]string, 0, len(neq))
        for key := range neq {
index 162b150f101b3a17c3b9739e934f8678beefb931..6e7dd452b86c9c516f23b29bb0daf0ce3031e61c 100644 (file)
@@ -8,13 +8,13 @@ Package builder is a simple and powerful sql builder for Go.
 
 Make sure you have installed Go 1.1+ and then:
 
-    go get github.com/go-xorm/builder
+    go get xorm.io/builder
 
 WARNNING: Currently, only query conditions are supported. Below is the supported conditions.
 
 1. Eq is a redefine of a map, you can give one or more conditions to Eq
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(Eq{"a":1})
     // a=? [1]
@@ -31,7 +31,7 @@ WARNNING: Currently, only query conditions are supported. Below is the supported
 
 2. Neq is the same to Eq
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(Neq{"a":1})
     // a<>? [1]
@@ -48,7 +48,7 @@ WARNNING: Currently, only query conditions are supported. Below is the supported
 
 3. Gt, Gte, Lt, Lte
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(Gt{"a", 1}.And(Gte{"b", 2}))
     // a>? AND b>=? [1, 2]
@@ -57,14 +57,14 @@ WARNNING: Currently, only query conditions are supported. Below is the supported
 
 4. Like
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(Like{"a", "c"})
     // a LIKE ? [%c%]
 
 5. Expr you can customerize your sql with Expr
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(Expr("a = ? ", 1))
     // a = ? [1]
@@ -73,7 +73,7 @@ WARNNING: Currently, only query conditions are supported. Below is the supported
 
 6. In and NotIn
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(In("a", 1, 2, 3))
     // a IN (?,?,?) [1,2,3]
@@ -84,7 +84,7 @@ WARNNING: Currently, only query conditions are supported. Below is the supported
 
 7. IsNull and NotNull
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(IsNull{"a"})
     // a IS NULL []
@@ -93,14 +93,14 @@ WARNNING: Currently, only query conditions are supported. Below is the supported
 
 8. And(conds ...Cond), And can connect one or more condtions via AND
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(And(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
     // a=? AND b LIKE ? AND d<>? [1, %c%, 2]
 
 9. Or(conds ...Cond), Or can connect one or more conditions via Or
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(Or(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
     // a=? OR b LIKE ? OR d<>? [1, %c%, 2]
@@ -109,7 +109,7 @@ WARNNING: Currently, only query conditions are supported. Below is the supported
 
 10. Between
 
-    import . "github.com/go-xorm/builder"
+    import . "xorm.io/builder"
 
     sql, args, _ := ToSQL(Between("a", 1, 2))
     // a BETWEEN 1 AND 2
index d830ee9955716a0ff0c2296235555e1c65a40da1..b0ded29f2111ecd5347ff83123bd73acbf40ce02 100644 (file)
@@ -17,9 +17,9 @@ var (
        ErrNeedMoreArguments = errors.New("Need more sql arguments")
        // ErrNoTableName no table name
        ErrNoTableName = errors.New("No table indicated")
-       // ErrNoColumnToInsert no column to update
+       // ErrNoColumnToUpdate no column to update
        ErrNoColumnToUpdate = errors.New("No column(s) to update")
-       // ErrNoColumnToInsert no column to update
+       // ErrNoColumnToInsert no column to insert
        ErrNoColumnToInsert = errors.New("No column(s) to insert")
        // ErrNotSupportDialectType not supported dialect type error
        ErrNotSupportDialectType = errors.New("Not supported dialect type")
index 35e43b329ff23441241f0dc44ba9594734f8427c..9c730113fb52b16e1d8be2d6f07514aec92fb90a 100644 (file)
@@ -1,5 +1,7 @@
 module xorm.io/builder
 
+go 1.11
+
 require (
        github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a
        github.com/stretchr/testify v1.3.0
index 08342427686281c70d47806af3adbc06ef4e071d..4250fea18d1652eb1663bf94c6ebb5f7e8f664ec 100644 (file)
@@ -8,6 +8,7 @@ import (
        sql2 "database/sql"
        "fmt"
        "reflect"
+       "strings"
        "time"
 )
 
@@ -20,7 +21,7 @@ func condToSQL(cond Cond) (string, []interface{}, error) {
        if err := cond.WriteTo(w); err != nil {
                return "", nil, err
        }
-       return w.writer.String(), w.args, nil
+       return w.String(), w.args, nil
 }
 
 func condToBoundSQL(cond Cond) (string, error) {
@@ -32,7 +33,7 @@ func condToBoundSQL(cond Cond) (string, error) {
        if err := cond.WriteTo(w); err != nil {
                return "", err
        }
-       return ConvertToBoundSQL(w.writer.String(), w.args)
+       return ConvertToBoundSQL(w.String(), w.args)
 }
 
 // ToSQL convert a builder or conditions to SQL and args
@@ -92,7 +93,7 @@ func noSQLQuoteNeeded(a interface{}) bool {
 
 // ConvertToBoundSQL will convert SQL and args to a bound SQL
 func ConvertToBoundSQL(sql string, args []interface{}) (string, error) {
-       buf := StringBuilder{}
+       buf := strings.Builder{}
        var i, j, start int
        for ; i < len(sql); i++ {
                if sql[i] == '?' {
@@ -114,7 +115,10 @@ func ConvertToBoundSQL(sql string, args []interface{}) (string, error) {
                        if noSQLQuoteNeeded(arg) {
                                _, err = fmt.Fprint(&buf, arg)
                        } else {
-                               _, err = fmt.Fprintf(&buf, "'%v'", arg)
+                               // replace ' -> '' (standard replacement) to avoid critical SQL injection,
+                               // NOTICE: may allow some injection like % (or _) in LIKE query
+                               _, err = fmt.Fprintf(&buf, "'%v'", strings.Replace(fmt.Sprintf("%v", arg), "'",
+                                       "''", -1))
                        }
                        if err != nil {
                                return "", err
@@ -131,7 +135,7 @@ func ConvertToBoundSQL(sql string, args []interface{}) (string, error) {
 
 // ConvertPlaceholder replaces ? to $1, $2 ... or :1, :2 ... according prefix
 func ConvertPlaceholder(sql, prefix string) (string, error) {
-       buf := StringBuilder{}
+       buf := strings.Builder{}
        var i, j, start int
        for ; i < len(sql); i++ {
                if sql[i] == '?' {
diff --git a/vendor/xorm.io/builder/string_builder.go b/vendor/xorm.io/builder/string_builder.go
deleted file mode 100644 (file)
index d4de871..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2017 The Go 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 builder
-
-import (
-       "unicode/utf8"
-       "unsafe"
-)
-
-// A StringBuilder is used to efficiently build a string using Write methods.
-// It minimizes memory copying. The zero value is ready to use.
-// Do not copy a non-zero Builder.
-type StringBuilder struct {
-       addr *StringBuilder // of receiver, to detect copies by value
-       buf  []byte
-}
-
-// noescape hides a pointer from escape analysis.  noescape is
-// the identity function but escape analysis doesn't think the
-// output depends on the input. noescape is inlined and currently
-// compiles down to zero instructions.
-// USE CAREFULLY!
-// This was copied from the runtime; see issues 23382 and 7921.
-//go:nosplit
-func noescape(p unsafe.Pointer) unsafe.Pointer {
-       x := uintptr(p)
-       return unsafe.Pointer(x ^ 0)
-}
-
-func (b *StringBuilder) copyCheck() {
-       if b.addr == nil {
-               // This hack works around a failing of Go's escape analysis
-               // that was causing b to escape and be heap allocated.
-               // See issue 23382.
-               // TODO: once issue 7921 is fixed, this should be reverted to
-               // just "b.addr = b".
-               b.addr = (*StringBuilder)(noescape(unsafe.Pointer(b)))
-       } else if b.addr != b {
-               panic("strings: illegal use of non-zero Builder copied by value")
-       }
-}
-
-// String returns the accumulated string.
-func (b *StringBuilder) String() string {
-       return *(*string)(unsafe.Pointer(&b.buf))
-}
-
-// Len returns the number of accumulated bytes; b.Len() == len(b.String()).
-func (b *StringBuilder) Len() int { return len(b.buf) }
-
-// Reset resets the Builder to be empty.
-func (b *StringBuilder) Reset() {
-       b.addr = nil
-       b.buf = nil
-}
-
-// grow copies the buffer to a new, larger buffer so that there are at least n
-// bytes of capacity beyond len(b.buf).
-func (b *StringBuilder) grow(n int) {
-       buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
-       copy(buf, b.buf)
-       b.buf = buf
-}
-
-// Grow grows b's capacity, if necessary, to guarantee space for
-// another n bytes. After Grow(n), at least n bytes can be written to b
-// without another allocation. If n is negative, Grow panics.
-func (b *StringBuilder) Grow(n int) {
-       b.copyCheck()
-       if n < 0 {
-               panic("strings.Builder.Grow: negative count")
-       }
-       if cap(b.buf)-len(b.buf) < n {
-               b.grow(n)
-       }
-}
-
-// Write appends the contents of p to b's buffer.
-// Write always returns len(p), nil.
-func (b *StringBuilder) Write(p []byte) (int, error) {
-       b.copyCheck()
-       b.buf = append(b.buf, p...)
-       return len(p), nil
-}
-
-// WriteByte appends the byte c to b's buffer.
-// The returned error is always nil.
-func (b *StringBuilder) WriteByte(c byte) error {
-       b.copyCheck()
-       b.buf = append(b.buf, c)
-       return nil
-}
-
-// WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer.
-// It returns the length of r and a nil error.
-func (b *StringBuilder) WriteRune(r rune) (int, error) {
-       b.copyCheck()
-       if r < utf8.RuneSelf {
-               b.buf = append(b.buf, byte(r))
-               return 1, nil
-       }
-       l := len(b.buf)
-       if cap(b.buf)-l < utf8.UTFMax {
-               b.grow(utf8.UTFMax)
-       }
-       n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r)
-       b.buf = b.buf[:l+n]
-       return n, nil
-}
-
-// WriteString appends the contents of s to b's buffer.
-// It returns the length of s and a nil error.
-func (b *StringBuilder) WriteString(s string) (int, error) {
-       b.copyCheck()
-       b.buf = append(b.buf, s...)
-       return len(s), nil
-}
diff --git a/vendor/xorm.io/builder/writer.go b/vendor/xorm.io/builder/writer.go
new file mode 100644 (file)
index 0000000..fb4fae5
--- /dev/null
@@ -0,0 +1,42 @@
+// 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 builder
+
+import (
+       "io"
+       "strings"
+)
+
+// Writer defines the interface
+type Writer interface {
+       io.Writer
+       Append(...interface{})
+}
+
+var _ Writer = NewWriter()
+
+// BytesWriter implments Writer and save SQL in bytes.Buffer
+type BytesWriter struct {
+       *strings.Builder
+       args []interface{}
+}
+
+// NewWriter creates a new string writer
+func NewWriter() *BytesWriter {
+       w := &BytesWriter{
+               Builder: &strings.Builder{},
+       }
+       return w
+}
+
+// Append appends args to Writer
+func (w *BytesWriter) Append(args ...interface{}) {
+       w.args = append(w.args, args...)
+}
+
+// Args returns args
+func (w *BytesWriter) Args() []interface{} {
+       return w.args
+}