Browse Source

Add support for offices group sync

Andrea Fazzi 5 years ago
parent
commit
8c086cb7e3
67 changed files with 1259 additions and 2331 deletions
  1. 21 22
      Godeps/Godeps.json
  2. 19 0
      cron/sync/sync.go
  3. 3 3
      vendor/github.com/dgrijalva/jwt-go/README.md
  4. 0 6
      vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md
  5. 0 1
      vendor/github.com/go-sql-driver/mysql/.gitignore
  6. 5 84
      vendor/github.com/go-sql-driver/mysql/.travis.yml
  7. 0 23
      vendor/github.com/go-sql-driver/mysql/AUTHORS
  8. 25 58
      vendor/github.com/go-sql-driver/mysql/README.md
  9. 1 1
      vendor/github.com/go-sql-driver/mysql/appengine.go
  10. 32 116
      vendor/github.com/go-sql-driver/mysql/connection.go
  11. 0 202
      vendor/github.com/go-sql-driver/mysql/connection_go18.go
  12. 3 6
      vendor/github.com/go-sql-driver/mysql/const.go
  13. 2 12
      vendor/github.com/go-sql-driver/mysql/driver.go
  14. 41 79
      vendor/github.com/go-sql-driver/mysql/dsn.go
  15. 73 6
      vendor/github.com/go-sql-driver/mysql/errors.go
  16. 0 140
      vendor/github.com/go-sql-driver/mysql/fields.go
  17. 1 2
      vendor/github.com/go-sql-driver/mysql/infile.go
  18. 86 108
      vendor/github.com/go-sql-driver/mysql/packets.go
  19. 35 142
      vendor/github.com/go-sql-driver/mysql/rows.go
  20. 30 55
      vendor/github.com/go-sql-driver/mysql/statement.go
  21. 2 2
      vendor/github.com/go-sql-driver/mysql/transaction.go
  22. 1 83
      vendor/github.com/go-sql-driver/mysql/utils.go
  23. 0 40
      vendor/github.com/go-sql-driver/mysql/utils_go17.go
  24. 0 49
      vendor/github.com/go-sql-driver/mysql/utils_go18.go
  25. 1 11
      vendor/github.com/gorilla/handlers/cors.go
  26. 3 4
      vendor/github.com/gorilla/mux/.travis.yml
  27. 6 122
      vendor/github.com/gorilla/mux/README.md
  28. 5 12
      vendor/github.com/gorilla/mux/doc.go
  29. 34 63
      vendor/github.com/gorilla/mux/mux.go
  30. 4 14
      vendor/github.com/gorilla/mux/regexp.go
  31. 8 127
      vendor/github.com/gorilla/mux/route.go
  32. 2 1
      vendor/github.com/gorilla/schema/.travis.yml
  33. 10 34
      vendor/github.com/gorilla/schema/README.md
  34. 22 33
      vendor/github.com/gorilla/schema/cache.go
  35. 1 1
      vendor/github.com/gorilla/schema/converter.go
  36. 26 130
      vendor/github.com/gorilla/schema/decoder.go
  37. 3 3
      vendor/github.com/gorilla/schema/doc.go
  38. 0 195
      vendor/github.com/gorilla/schema/encoder.go
  39. 0 2
      vendor/github.com/gorilla/securecookie/README.md
  40. 3 14
      vendor/github.com/gorilla/sessions/README.md
  41. 2 1
      vendor/github.com/gorilla/sessions/doc.go
  42. 13 23
      vendor/github.com/jinzhu/gorm/README.md
  43. 2 2
      vendor/github.com/jinzhu/gorm/association.go
  44. 5 7
      vendor/github.com/jinzhu/gorm/callback.go
  45. 2 1
      vendor/github.com/jinzhu/gorm/callback_create.go
  46. 4 0
      vendor/github.com/jinzhu/gorm/callback_query.go
  47. 7 0
      vendor/github.com/jinzhu/gorm/callback_query_preload.go
  48. 112 41
      vendor/github.com/jinzhu/gorm/callback_save.go
  49. 11 1
      vendor/github.com/jinzhu/gorm/callback_update.go
  50. 22 8
      vendor/github.com/jinzhu/gorm/dialect.go
  51. 28 8
      vendor/github.com/jinzhu/gorm/dialect_common.go
  52. 28 13
      vendor/github.com/jinzhu/gorm/dialect_mysql.go
  53. 20 7
      vendor/github.com/jinzhu/gorm/dialect_postgres.go
  54. 2 2
      vendor/github.com/jinzhu/gorm/dialect_sqlite3.go
  55. 30 0
      vendor/github.com/jinzhu/gorm/docker-compose.yml
  56. 16 0
      vendor/github.com/jinzhu/gorm/errors.go
  57. 27 22
      vendor/github.com/jinzhu/gorm/join_table_handler.go
  58. 44 20
      vendor/github.com/jinzhu/gorm/main.go
  59. 62 30
      vendor/github.com/jinzhu/gorm/model_struct.go
  60. 173 119
      vendor/github.com/jinzhu/gorm/scope.go
  61. 0 7
      vendor/github.com/jinzhu/gorm/search.go
  62. 1 1
      vendor/github.com/jinzhu/gorm/test_all.sh
  63. 1 1
      vendor/github.com/jinzhu/gorm/utils.go
  64. 111 4
      vendor/github.com/jinzhu/gorm/wercker.yml
  65. 0 4
      vendor/github.com/robfig/cron/README.md
  66. 3 3
      vendor/github.com/robfig/cron/doc.go
  67. 25 0
      vendor/gogs.carducci-dante.gov.it/karmen/client/client.go

+ 21 - 22
Godeps/Godeps.json

@@ -9,13 +9,13 @@
 		},
 		{
 			"ImportPath": "github.com/dgrijalva/jwt-go",
-			"Comment": "v3.1.0",
-			"Rev": "dbeaa9332f19a944acb5736b4456cfcc02140e29"
+			"Comment": "v3.0.0-17-g2268707",
+			"Rev": "2268707a8f0843315e2004ee4f1d021dc08baedf"
 		},
 		{
 			"ImportPath": "github.com/go-sql-driver/mysql",
-			"Comment": "v1.3-49-gcd4cb90",
-			"Rev": "cd4cb909ce1a31435164be29bf3682031f61539a"
+			"Comment": "v1.3-2-g2e00b5c",
+			"Rev": "2e00b5cd70399450106cec6431c2e2ce3cae5034"
 		},
 		{
 			"ImportPath": "github.com/gocarina/gocsv",
@@ -28,38 +28,37 @@
 		},
 		{
 			"ImportPath": "github.com/gorilla/handlers",
-			"Comment": "v1.3.0",
-			"Rev": "90663712d74cb411cbef281bc1e08c19d1a76145"
+			"Comment": "v1.2.1-1-ga4d79d4",
+			"Rev": "a4d79d4487c2430a17d9dc8a1f74d1a6ed6908ca"
 		},
 		{
 			"ImportPath": "github.com/gorilla/mux",
-			"Comment": "v1.6.0-1-g2d5fef0",
-			"Rev": "2d5fef06b891c971b14aa6f71ca5ab6c03a36e0e"
+			"Comment": "v1.1-27-g757bef9",
+			"Rev": "757bef944d0f21880861c2dd9c871ca543023cba"
 		},
 		{
 			"ImportPath": "github.com/gorilla/schema",
-			"Comment": "v1.0.2-3-gda8e735",
-			"Rev": "da8e73546beca346d03fd40d5b146e06e6b98b7a"
+			"Rev": "0164a00ab4cd01d814d8cd5bf63fd9fcea30e23b"
 		},
 		{
 			"ImportPath": "github.com/gorilla/securecookie",
-			"Comment": "v1.1-6-ge59506c",
-			"Rev": "e59506cc896acb7f7bf732d4fdf5e25f7ccd8983"
+			"Comment": "v1.1-5-gfa5329f",
+			"Rev": "fa5329f913702981df43dcb2a380bac429c810b5"
 		},
 		{
 			"ImportPath": "github.com/gorilla/sessions",
-			"Comment": "v1.1-8-ga3acf13",
-			"Rev": "a3acf13e802c358d65f249324d14ed24aac11370"
+			"Comment": "v1.1",
+			"Rev": "ca9ada44574153444b00d3fd9c8559e4cc95f896"
 		},
 		{
 			"ImportPath": "github.com/jinzhu/gorm",
-			"Comment": "v1.0-168-gc3bb6aa",
-			"Rev": "c3bb6aaa828867eec72dd8571d111e442688f85f"
+			"Comment": "v1.0-250-g48a20a6",
+			"Rev": "48a20a6e9f3f4d26095df82c3337efec6db0a6fc"
 		},
 		{
 			"ImportPath": "github.com/jinzhu/gorm/dialects/mysql",
-			"Comment": "v1.0-168-gc3bb6aa",
-			"Rev": "c3bb6aaa828867eec72dd8571d111e442688f85f"
+			"Comment": "v1.0-250-g48a20a6",
+			"Rev": "48a20a6e9f3f4d26095df82c3337efec6db0a6fc"
 		},
 		{
 			"ImportPath": "github.com/jinzhu/inflection",
@@ -71,8 +70,8 @@
 		},
 		{
 			"ImportPath": "github.com/robfig/cron",
-			"Comment": "v1.1",
-			"Rev": "b41be1df696709bb6395fe435af20370037c0b4c"
+			"Comment": "v1-58-g736158d",
+			"Rev": "736158dc09e10f1911ca3a1e1b01f11b566ce5db"
 		},
 		{
 			"ImportPath": "github.com/sethvargo/go-password/password",
@@ -81,11 +80,11 @@
 		},
 		{
 			"ImportPath": "gogs.carducci-dante.gov.it/karmen/client",
-			"Rev": "876a562a75c249b2afd260bf60a209d204191605"
+			"Rev": "1a53dd7f124000313ae5e190c6b62ee179764d31"
 		},
 		{
 			"ImportPath": "gogs.carducci-dante.gov.it/karmen/ldap",
-			"Rev": "83e2c51c67bc8c5f171ad956e200363b4ca0af00"
+			"Rev": "034d2afa27464b7b5fc304170d521fc63dbefb5c"
 		},
 		{
 			"ImportPath": "gogs.carducci-dante.gov.it/karmen/util/template",

+ 19 - 0
cron/sync/sync.go

@@ -367,6 +367,12 @@ func (syncJob *SyncJob) Run() {
 		log.Println(err)
 	}
 
+	log.Println("Retrieving Offices...")
+	offices, err := karmenClient.GetOffices()
+	if err != nil {
+		log.Println(err)
+	}
+
 	users := make([]orm.User, 0)
 	for _, teacher := range teachers {
 		users = append(users, teacher)
@@ -436,4 +442,17 @@ func (syncJob *SyncJob) Run() {
 		log.Println(result)
 	}
 
+	for _, office := range offices {
+		users := make([]orm.User, 0)
+		for _, administrative := range office.Administratives {
+			users = append(users, administrative)
+		}
+		group := fmt.Sprintf("cn=%s,ou=Segreterie", office.Name)
+		if result, err := syncJob.SyncGroup(ldapClient, users, group); err != nil {
+			panic(err)
+		} else {
+			log.Println(result)
+		}
+	}
+
 }

+ 3 - 3
vendor/github.com/dgrijalva/jwt-go/README.md

@@ -4,7 +4,7 @@ A [go](http://www.golang.org) (or 'golang' for search engine friendliness) imple
 
 **BREAKING CHANGES:*** Version 3.0.0 is here. It includes _a lot_ of changes including a few that break the API.  We've tried to break as few things as possible, so there should just be a few type signature changes.  A full list of breaking changes is available in `VERSION_HISTORY.md`.  See `MIGRATION_GUIDE.md` for more information on updating your code.
 
-**NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage.  See the examples provided.
+**NOTICE:** A vulnerability in JWT was [recently published](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/).  As this library doesn't force users to validate the `alg` is what they expected, it's possible your usage is effected.  There will be an update soon to remedy this, and it will likey require backwards-incompatible changes to the API.  In the short term, please make sure your implementation verifies the `alg` is what you expect.
 
 
 ## What the heck is a JWT?
@@ -74,7 +74,7 @@ It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is
 
 Without going too far down the rabbit hole, here's a description of the interaction of these technologies:
 
-* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth.
+* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to.  For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth.
 * OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token.
 * Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL.
  
@@ -82,4 +82,4 @@ Without going too far down the rabbit hole, here's a description of the interact
 
 Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go).
 
-The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation.
+The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration.  You'll also find several implementation examples in to documentation.

+ 0 - 6
vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md

@@ -1,11 +1,5 @@
 ## `jwt-go` Version History
 
-#### 3.1.0
-
-* Improvements to `jwt` command line tool
-* Added `SkipClaimsValidation` option to `Parser`
-* Documentation updates
-
 #### 3.0.0
 
 * **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code

+ 0 - 1
vendor/github.com/go-sql-driver/mysql/.gitignore

@@ -6,4 +6,3 @@
 Icon?
 ehthumbs.db
 Thumbs.db
-.idea

+ 5 - 84
vendor/github.com/go-sql-driver/mysql/.travis.yml

@@ -1,92 +1,13 @@
 sudo: false
 language: go
 go:
+  - 1.2
+  - 1.3
+  - 1.4
+  - 1.5
+  - 1.6
   - 1.7
-  - 1.8
-  - 1.9
   - tip
 
-before_install:
-  - go get golang.org/x/tools/cmd/cover
-  - go get github.com/mattn/goveralls
-
 before_script:
-  - echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB" | sudo tee -a /etc/mysql/my.cnf
-  - sudo service mysql restart
-  - .travis/wait_mysql.sh
   - mysql -e 'create database gotest;'
-
-matrix:
-  include:
-    - env: DB=MYSQL57
-      sudo: required
-      dist: trusty
-      go: 1.9
-      services:
-        - docker
-      before_install:
-        - go get golang.org/x/tools/cmd/cover
-        - go get github.com/mattn/goveralls
-        - docker pull mysql:5.7
-        - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
-          mysql:5.7 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
-        - sleep 30
-        - cp .travis/docker.cnf ~/.my.cnf
-        - mysql --print-defaults
-        - .travis/wait_mysql.sh
-      before_script:
-        - export MYSQL_TEST_USER=gotest
-        - export MYSQL_TEST_PASS=secret
-        - export MYSQL_TEST_ADDR=127.0.0.1:3307
-        - export MYSQL_TEST_CONCURRENT=1
-
-    - env: DB=MARIA55
-      sudo: required
-      dist: trusty
-      go: 1.9
-      services:
-        - docker
-      before_install:
-        - go get golang.org/x/tools/cmd/cover
-        - go get github.com/mattn/goveralls
-        - docker pull mariadb:5.5
-        - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
-          mariadb:5.5 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
-        - sleep 30
-        - cp .travis/docker.cnf ~/.my.cnf
-        - mysql --print-defaults
-        - .travis/wait_mysql.sh
-      before_script:
-        - export MYSQL_TEST_USER=gotest
-        - export MYSQL_TEST_PASS=secret
-        - export MYSQL_TEST_ADDR=127.0.0.1:3307
-        - export MYSQL_TEST_CONCURRENT=1
-
-    - env: DB=MARIA10_1
-      sudo: required
-      dist: trusty
-      go: 1.9
-      services:
-        - docker
-      before_install:
-        - go get golang.org/x/tools/cmd/cover
-        - go get github.com/mattn/goveralls
-        - docker pull mariadb:10.1
-        - docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
-          mariadb:10.1 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
-        - sleep 30
-        - cp .travis/docker.cnf ~/.my.cnf
-        - mysql --print-defaults
-        - .travis/wait_mysql.sh
-      before_script:
-        - export MYSQL_TEST_USER=gotest
-        - export MYSQL_TEST_PASS=secret
-        - export MYSQL_TEST_ADDR=127.0.0.1:3307
-        - export MYSQL_TEST_CONCURRENT=1
-
-script:
-  - go test -v -covermode=count -coverprofile=coverage.out
-  - go vet ./...
-  - test -z "$(gofmt -d -s . | tee /dev/stderr)"
-after_script:
-  - $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci

+ 0 - 23
vendor/github.com/go-sql-driver/mysql/AUTHORS

@@ -12,56 +12,36 @@
 # Individual Persons
 
 Aaron Hopkins <go-sql-driver at die.net>
-Achille Roussel <achille.roussel at gmail.com>
 Arne Hormann <arnehormann at gmail.com>
-Asta Xie <xiemengjun at gmail.com>
-Bulat Gaifullin <gaifullinbf at gmail.com>
 Carlos Nieto <jose.carlos at menteslibres.net>
 Chris Moos <chris at tech9computers.com>
-Daniel Montoya <dsmontoyam at gmail.com>
 Daniel Nichter <nil at codenode.com>
 Daniël van Eeden <git at myname.nl>
-Dave Protasowski <dprotaso at gmail.com>
 DisposaBoy <disposaboy at dby.me>
 Egor Smolyakov <egorsmkv at gmail.com>
-Evan Shaw <evan at vendhq.com>
 Frederick Mayle <frederickmayle at gmail.com>
 Gustavo Kristic <gkristic at gmail.com>
 Hanno Braun <mail at hannobraun.com>
 Henri Yandell <flamefew at gmail.com>
 Hirotaka Yamamoto <ymmt2005 at gmail.com>
-ICHINOSE Shogo <shogo82148 at gmail.com>
 INADA Naoki <songofacandy at gmail.com>
-Jacek Szwec <szwec.jacek at gmail.com>
 James Harr <james.harr at gmail.com>
-Jeff Hodges <jeff at somethingsimilar.com>
-Jeffrey Charles <jeffreycharles at gmail.com>
 Jian Zhen <zhenjl at gmail.com>
 Joshua Prunier <joshua.prunier at gmail.com>
 Julien Lefevre <julien.lefevr at gmail.com>
 Julien Schmidt <go-sql-driver at julienschmidt.com>
-Justin Li <jli at j-li.net>
-Justin Nuß <nuss.justin at gmail.com>
 Kamil Dziedzic <kamil at klecza.pl>
 Kevin Malachowski <kevin at chowski.com>
 Lennart Rudolph <lrudolph at hmc.edu>
 Leonardo YongUk Kim <dalinaum at gmail.com>
-Linh Tran Tuan <linhduonggnu at gmail.com>
-Lion Yang <lion at aosc.xyz>
 Luca Looz <luca.looz92 at gmail.com>
 Lucas Liu <extrafliu at gmail.com>
 Luke Scott <luke at webconnex.com>
-Maciej Zimnoch <maciej.zimnoch@codilime.com>
 Michael Woolnough <michael.woolnough at gmail.com>
 Nicola Peduzzi <thenikso at gmail.com>
 Olivier Mengué <dolmen at cpan.org>
-oscarzhao <oscarzhaosl at gmail.com>
 Paul Bonser <misterpib at gmail.com>
-Peter Schultz <peter.schultz at classmarkets.com>
-Rebecca Chin <rchin at pivotal.io>
 Runrioter Wung <runrioter at gmail.com>
-Robert Russell <robert at rrbrussell.com>
-Shuode Li <elemount at qq.com>
 Soroush Pour <me at soroushjp.com>
 Stan Putrya <root.vagner at gmail.com>
 Stanley Gunawan <gunawan.stanley at gmail.com>
@@ -73,8 +53,5 @@ Zhenye Xie <xiezhenye at gmail.com>
 # Organizations
 
 Barracuda Networks, Inc.
-Counting Ltd.
 Google Inc.
-Keybase Inc.
-Pivotal Inc.
 Stripe Inc.

+ 25 - 58
vendor/github.com/go-sql-driver/mysql/README.md

@@ -15,9 +15,6 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
       * [Address](#address)
       * [Parameters](#parameters)
       * [Examples](#examples)
-    * [Connection pool and timeouts](#connection-pool-and-timeouts)
-    * [context.Context Support](#contextcontext-support)
-    * [ColumnType Support](#columntype-support)
     * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support)
     * [time.Time support](#timetime-support)
     * [Unicode support](#unicode-support)
@@ -40,7 +37,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
   * Optional placeholder interpolation
 
 ## Requirements
-  * Go 1.7 or higher. We aim to support the 3 latest versions of Go.
+  * Go 1.2 or higher
   * MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
 
 ---------------------------------------
@@ -48,7 +45,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
 ## Installation
 Simple install the package to your [$GOPATH](https://github.com/golang/go/wiki/GOPATH "GOPATH") with the [go tool](https://golang.org/cmd/go/ "go command") from shell:
 ```bash
-$ go get -u github.com/go-sql-driver/mysql
+$ go get github.com/go-sql-driver/mysql
 ```
 Make sure [Git is installed](https://git-scm.com/downloads) on your machine and in your system's `PATH`.
 
@@ -102,8 +99,7 @@ See [net.Dial](https://golang.org/pkg/net/#Dial) for more information which netw
 In general you should use an Unix domain socket if available and TCP otherwise for best performance.
 
 #### Address
-For TCP and UDP networks, addresses have the form `host[:port]`.
-If `port` is omitted, the default port will be used.
+For TCP and UDP networks, addresses have the form `host:port`.
 If `host` is a literal IPv6 address, it must be enclosed in square brackets.
 The functions [net.JoinHostPort](https://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](https://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form.
 
@@ -140,9 +136,9 @@ Default:        false
 ```
 Type:           bool
 Valid Values:   true, false
-Default:        true
+Default:        false
 ```
-`allowNativePasswords=false` disallows the usage of MySQL native password method.
+`allowNativePasswords=true` allows the usage of the mysql native password method.
 
 ##### `allowOldPasswords`
 
@@ -233,10 +229,10 @@ Please keep in mind, that param values must be [url.QueryEscape](https://golang.
 ##### `maxAllowedPacket`
 ```
 Type:          decimal number
-Default:       4194304
+Default:       0
 ```
 
-Max packet size allowed in bytes. The default value is 4 MiB and should be adjusted to match the server settings. `maxAllowedPacket=0` can be used to automatically fetch the `max_allowed_packet` variable from server *on every connection*.
+Max packet size allowed in bytes. Use `maxAllowedPacket=0` to automatically fetch the `max_allowed_packet` variable from server.
 
 ##### `multiStatements`
 
@@ -264,13 +260,13 @@ Default:        false
 ##### `readTimeout`
 
 ```
-Type:           duration
+Type:           decimal number
 Default:        0
 ```
 
-I/O read timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
+I/O read timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
 
-##### `rejectReadOnly`
+##### `strict`
 
 ```
 Type:           bool
@@ -278,37 +274,20 @@ Valid Values:   true, false
 Default:        false
 ```
 
+`strict=true` enables a driver-side strict mode in which MySQL warnings are treated as errors. This mode should not be used in production as it may lead to data corruption in certain situations.
 
-`rejectReadOnly=true` causes the driver to reject read-only connections. This
-is for a possible race condition during an automatic failover, where the mysql
-client gets connected to a read-only replica after the failover.
-
-Note that this should be a fairly rare case, as an automatic failover normally
-happens when the primary is down, and the race condition shouldn't happen
-unless it comes back up online as soon as the failover is kicked off. On the
-other hand, when this happens, a MySQL application can get stuck on a
-read-only connection until restarted. It is however fairly easy to reproduce,
-for example, using a manual failover on AWS Aurora's MySQL-compatible cluster.
-
-If you are not relying on read-only transactions to reject writes that aren't
-supposed to happen, setting this on some MySQL providers (such as AWS Aurora)
-is safer for failovers.
-
-Note that ERROR 1290 can be returned for a `read-only` server and this option will
-cause a retry for that error. However the same error number is used for some
-other cases. You should ensure your application will never cause an ERROR 1290
-except for `read-only` mode when enabling this option.
+A server-side strict mode, which is safe for production use, can be set via the [`sql_mode`](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html) system variable.
 
+By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_notes) to ignore notes.
 
 ##### `timeout`
 
 ```
-Type:           duration
+Type:           decimal number
 Default:        OS default
 ```
 
-Timeout for establishing connections, aka dial timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
-
+*Driver* side connection timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout).
 
 ##### `tls`
 
@@ -320,15 +299,14 @@ Default:        false
 
 `tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
 
-
 ##### `writeTimeout`
 
 ```
-Type:           duration
+Type:           decimal number
 Default:        0
 ```
 
-I/O write timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
+I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
 
 
 ##### System Variables
@@ -339,9 +317,9 @@ Any other parameters are interpreted as system variables:
   * `<string_var>=%27<value>%27`: `SET <string_var>='<value>'`
 
 Rules:
-* The values for string variables must be quoted with `'`.
+* The values for string variables must be quoted with '
 * The values must also be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!
- (which implies values of string variables must be wrapped with `%27`).
+ (which implies values of string variables must be wrapped with `%27`)
 
 Examples:
   * `autocommit=1`: `SET autocommit=1`
@@ -402,18 +380,6 @@ No Database preselected:
 user:password@/
 ```
 
-
-### Connection pool and timeouts
-The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively.
-
-## `ColumnType` Support
-This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported.
-
-## `context.Context` Support
-Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts.
-See [context support in the database/sql package](https://golang.org/doc/go1.8#database_sql) for more details.
-
-
 ### `LOAD DATA LOCAL INFILE` support
 For this feature you need direct access to the package. Therefore you must change the import path (no `_`):
 ```go
@@ -428,7 +394,7 @@ See the [godoc of Go-MySQL-Driver](https://godoc.org/github.com/go-sql-driver/my
 
 
 ### `time.Time` support
-The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your program.
+The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your programm.
 
 However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](https://golang.org/pkg/time/#Location) with the `loc` DSN parameter.
 
@@ -446,6 +412,7 @@ Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAM
 
 See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support.
 
+
 ## Testing / Development
 To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details.
 
@@ -464,13 +431,13 @@ Mozilla summarizes the license scope as follows:
 
 
 That means:
-  * You can **use** the **unchanged** source code both in private and commercially.
-  * When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0).
-  * You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**.
+  * You can **use** the **unchanged** source code both in private and commercially
+  * When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0)
+  * You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**
 
 Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you have further questions regarding the license.
 
-You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE).
+You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE)
 
 ![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow")
 

+ 1 - 1
vendor/github.com/go-sql-driver/mysql/appengine.go

@@ -11,7 +11,7 @@
 package mysql
 
 import (
-	"google.golang.org/appengine/cloudsql"
+	"appengine/cloudsql"
 )
 
 func init() {

+ 32 - 116
vendor/github.com/go-sql-driver/mysql/connection.go

@@ -10,23 +10,12 @@ package mysql
 
 import (
 	"database/sql/driver"
-	"io"
 	"net"
 	"strconv"
 	"strings"
 	"time"
 )
 
-// a copy of context.Context for Go 1.7 and earlier
-type mysqlContext interface {
-	Done() <-chan struct{}
-	Err() error
-
-	// defined in context.Context, but not used in this driver:
-	// Deadline() (deadline time.Time, ok bool)
-	// Value(key interface{}) interface{}
-}
-
 type mysqlConn struct {
 	buf              buffer
 	netConn          net.Conn
@@ -40,14 +29,7 @@ type mysqlConn struct {
 	status           statusFlag
 	sequence         uint8
 	parseTime        bool
-
-	// for context support (Go 1.8+)
-	watching bool
-	watcher  chan<- mysqlContext
-	closech  chan struct{}
-	finished chan<- struct{}
-	canceled atomicError // set non-nil if conn is canceled
-	closed   atomicBool  // set when conn is closed, before closech is closed
+	strict           bool
 }
 
 // Handles parameters set in DSN after the connection is established
@@ -80,41 +62,22 @@ func (mc *mysqlConn) handleParams() (err error) {
 	return
 }
 
-func (mc *mysqlConn) markBadConn(err error) error {
-	if mc == nil {
-		return err
-	}
-	if err != errBadConnNoWrite {
-		return err
-	}
-	return driver.ErrBadConn
-}
-
 func (mc *mysqlConn) Begin() (driver.Tx, error) {
-	return mc.begin(false)
-}
-
-func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
-	if mc.closed.IsSet() {
+	if mc.netConn == nil {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
-	var q string
-	if readOnly {
-		q = "START TRANSACTION READ ONLY"
-	} else {
-		q = "START TRANSACTION"
-	}
-	err := mc.exec(q)
+	err := mc.exec("START TRANSACTION")
 	if err == nil {
 		return &mysqlTx{mc}, err
 	}
-	return nil, mc.markBadConn(err)
+
+	return nil, err
 }
 
 func (mc *mysqlConn) Close() (err error) {
 	// Makes Close idempotent
-	if !mc.closed.IsSet() {
+	if mc.netConn != nil {
 		err = mc.writeCommandPacket(comQuit)
 	}
 
@@ -128,39 +91,26 @@ func (mc *mysqlConn) Close() (err error) {
 // is called before auth or on auth failure because MySQL will have already
 // closed the network connection.
 func (mc *mysqlConn) cleanup() {
-	if !mc.closed.TrySet(true) {
-		return
-	}
-
 	// Makes cleanup idempotent
-	close(mc.closech)
-	if mc.netConn == nil {
-		return
-	}
-	if err := mc.netConn.Close(); err != nil {
-		errLog.Print(err)
-	}
-}
-
-func (mc *mysqlConn) error() error {
-	if mc.closed.IsSet() {
-		if err := mc.canceled.Value(); err != nil {
-			return err
+	if mc.netConn != nil {
+		if err := mc.netConn.Close(); err != nil {
+			errLog.Print(err)
 		}
-		return ErrInvalidConn
+		mc.netConn = nil
 	}
-	return nil
+	mc.cfg = nil
+	mc.buf.nc = nil
 }
 
 func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
-	if mc.closed.IsSet() {
+	if mc.netConn == nil {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
 	// Send command
 	err := mc.writeCommandPacketStr(comStmtPrepare, query)
 	if err != nil {
-		return nil, mc.markBadConn(err)
+		return nil, err
 	}
 
 	stmt := &mysqlStmt{
@@ -194,7 +144,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
 	if buf == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return "", ErrInvalidConn
+		return "", driver.ErrBadConn
 	}
 	buf = buf[:0]
 	argPos := 0
@@ -307,7 +257,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
 }
 
 func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
-	if mc.closed.IsSet() {
+	if mc.netConn == nil {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
@@ -321,6 +271,7 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
 			return nil, err
 		}
 		query = prepared
+		args = nil
 	}
 	mc.affectedRows = 0
 	mc.insertId = 0
@@ -332,43 +283,32 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
 			insertId:     int64(mc.insertId),
 		}, err
 	}
-	return nil, mc.markBadConn(err)
+	return nil, err
 }
 
 // Internal function to execute commands
 func (mc *mysqlConn) exec(query string) error {
 	// Send command
-	if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
-		return mc.markBadConn(err)
-	}
-
-	// Read Result
-	resLen, err := mc.readResultSetHeaderPacket()
+	err := mc.writeCommandPacketStr(comQuery, query)
 	if err != nil {
 		return err
 	}
 
-	if resLen > 0 {
-		// columns
-		if err := mc.readUntilEOF(); err != nil {
+	// Read Result
+	resLen, err := mc.readResultSetHeaderPacket()
+	if err == nil && resLen > 0 {
+		if err = mc.readUntilEOF(); err != nil {
 			return err
 		}
 
-		// rows
-		if err := mc.readUntilEOF(); err != nil {
-			return err
-		}
+		err = mc.readUntilEOF()
 	}
 
-	return mc.discardResults()
+	return err
 }
 
 func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
-	return mc.query(query, args)
-}
-
-func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
-	if mc.closed.IsSet() {
+	if mc.netConn == nil {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
@@ -382,6 +322,7 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error)
 			return nil, err
 		}
 		query = prepared
+		args = nil
 	}
 	// Send command
 	err := mc.writeCommandPacketStr(comQuery, query)
@@ -394,22 +335,15 @@ func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error)
 			rows.mc = mc
 
 			if resLen == 0 {
-				rows.rs.done = true
-
-				switch err := rows.NextResultSet(); err {
-				case nil, io.EOF:
-					return rows, nil
-				default:
-					return nil, err
-				}
+				// no columns, no more data
+				return emptyRows{}, nil
 			}
-
 			// Columns
-			rows.rs.columns, err = mc.readColumns(resLen)
+			rows.columns, err = mc.readColumns(resLen)
 			return rows, err
 		}
 	}
-	return nil, mc.markBadConn(err)
+	return nil, err
 }
 
 // Gets the value of the given MySQL System Variable
@@ -425,7 +359,7 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
 	if err == nil {
 		rows := new(textRows)
 		rows.mc = mc
-		rows.rs.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
+		rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
 
 		if resLen > 0 {
 			// Columns
@@ -441,21 +375,3 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
 	}
 	return nil, err
 }
-
-// finish is called when the query has canceled.
-func (mc *mysqlConn) cancel(err error) {
-	mc.canceled.Set(err)
-	mc.cleanup()
-}
-
-// finish is called when the query has succeeded.
-func (mc *mysqlConn) finish() {
-	if !mc.watching || mc.finished == nil {
-		return
-	}
-	select {
-	case mc.finished <- struct{}{}:
-		mc.watching = false
-	case <-mc.closech:
-	}
-}

+ 0 - 202
vendor/github.com/go-sql-driver/mysql/connection_go18.go

@@ -1,202 +0,0 @@
-// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
-//
-// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-// +build go1.8
-
-package mysql
-
-import (
-	"context"
-	"database/sql"
-	"database/sql/driver"
-)
-
-// Ping implements driver.Pinger interface
-func (mc *mysqlConn) Ping(ctx context.Context) error {
-	if mc.closed.IsSet() {
-		errLog.Print(ErrInvalidConn)
-		return driver.ErrBadConn
-	}
-
-	if err := mc.watchCancel(ctx); err != nil {
-		return err
-	}
-	defer mc.finish()
-
-	if err := mc.writeCommandPacket(comPing); err != nil {
-		return err
-	}
-	if _, err := mc.readResultOK(); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// BeginTx implements driver.ConnBeginTx interface
-func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
-	if err := mc.watchCancel(ctx); err != nil {
-		return nil, err
-	}
-	defer mc.finish()
-
-	if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
-		level, err := mapIsolationLevel(opts.Isolation)
-		if err != nil {
-			return nil, err
-		}
-		err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return mc.begin(opts.ReadOnly)
-}
-
-func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
-	dargs, err := namedValueToValue(args)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := mc.watchCancel(ctx); err != nil {
-		return nil, err
-	}
-
-	rows, err := mc.query(query, dargs)
-	if err != nil {
-		mc.finish()
-		return nil, err
-	}
-	rows.finish = mc.finish
-	return rows, err
-}
-
-func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
-	dargs, err := namedValueToValue(args)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := mc.watchCancel(ctx); err != nil {
-		return nil, err
-	}
-	defer mc.finish()
-
-	return mc.Exec(query, dargs)
-}
-
-func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
-	if err := mc.watchCancel(ctx); err != nil {
-		return nil, err
-	}
-
-	stmt, err := mc.Prepare(query)
-	mc.finish()
-	if err != nil {
-		return nil, err
-	}
-
-	select {
-	default:
-	case <-ctx.Done():
-		stmt.Close()
-		return nil, ctx.Err()
-	}
-	return stmt, nil
-}
-
-func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
-	dargs, err := namedValueToValue(args)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := stmt.mc.watchCancel(ctx); err != nil {
-		return nil, err
-	}
-
-	rows, err := stmt.query(dargs)
-	if err != nil {
-		stmt.mc.finish()
-		return nil, err
-	}
-	rows.finish = stmt.mc.finish
-	return rows, err
-}
-
-func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
-	dargs, err := namedValueToValue(args)
-	if err != nil {
-		return nil, err
-	}
-
-	if err := stmt.mc.watchCancel(ctx); err != nil {
-		return nil, err
-	}
-	defer stmt.mc.finish()
-
-	return stmt.Exec(dargs)
-}
-
-func (mc *mysqlConn) watchCancel(ctx context.Context) error {
-	if mc.watching {
-		// Reach here if canceled,
-		// so the connection is already invalid
-		mc.cleanup()
-		return nil
-	}
-	if ctx.Done() == nil {
-		return nil
-	}
-
-	mc.watching = true
-	select {
-	default:
-	case <-ctx.Done():
-		return ctx.Err()
-	}
-	if mc.watcher == nil {
-		return nil
-	}
-
-	mc.watcher <- ctx
-
-	return nil
-}
-
-func (mc *mysqlConn) startWatcher() {
-	watcher := make(chan mysqlContext, 1)
-	mc.watcher = watcher
-	finished := make(chan struct{})
-	mc.finished = finished
-	go func() {
-		for {
-			var ctx mysqlContext
-			select {
-			case ctx = <-watcher:
-			case <-mc.closech:
-				return
-			}
-
-			select {
-			case <-ctx.Done():
-				mc.cancel(ctx.Err())
-			case <-finished:
-			case <-mc.closech:
-				return
-			}
-		}
-	}()
-}
-
-func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
-	nv.Value, err = converter{}.ConvertValue(nv.Value)
-	return
-}

+ 3 - 6
vendor/github.com/go-sql-driver/mysql/const.go

@@ -9,8 +9,7 @@
 package mysql
 
 const (
-	defaultMaxAllowedPacket = 4 << 20 // 4 MiB
-	minProtocolVersion      = 10
+	minProtocolVersion byte = 10
 	maxPacketSize           = 1<<24 - 1
 	timeFormat              = "2006-01-02 15:04:05.999999"
 )
@@ -88,10 +87,8 @@ const (
 )
 
 // https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
-type fieldType byte
-
 const (
-	fieldTypeDecimal fieldType = iota
+	fieldTypeDecimal byte = iota
 	fieldTypeTiny
 	fieldTypeShort
 	fieldTypeLong
@@ -110,7 +107,7 @@ const (
 	fieldTypeBit
 )
 const (
-	fieldTypeJSON fieldType = iota + 0xf5
+	fieldTypeJSON byte = iota + 0xf5
 	fieldTypeNewDecimal
 	fieldTypeEnum
 	fieldTypeSet

+ 2 - 12
vendor/github.com/go-sql-driver/mysql/driver.go

@@ -4,7 +4,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 
-// Package mysql provides a MySQL driver for Go's database/sql package.
+// Package mysql provides a MySQL driver for Go's database/sql package
 //
 // The driver should be used via the database/sql package:
 //
@@ -22,11 +22,6 @@ import (
 	"net"
 )
 
-// watcher interface is used for context support (From Go 1.8)
-type watcher interface {
-	startWatcher()
-}
-
 // MySQLDriver is exported to make the driver directly accessible.
 // In general the driver is used via the database/sql package.
 type MySQLDriver struct{}
@@ -57,13 +52,13 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
 	mc := &mysqlConn{
 		maxAllowedPacket: maxPacketSize,
 		maxWriteSize:     maxPacketSize - 1,
-		closech:          make(chan struct{}),
 	}
 	mc.cfg, err = ParseDSN(dsn)
 	if err != nil {
 		return nil, err
 	}
 	mc.parseTime = mc.cfg.ParseTime
+	mc.strict = mc.cfg.Strict
 
 	// Connect to Server
 	if dial, ok := dials[mc.cfg.Net]; ok {
@@ -86,11 +81,6 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
 		}
 	}
 
-	// Call startWatcher for context support (From Go 1.8)
-	if s, ok := interface{}(mc).(watcher); ok {
-		s.startWatcher()
-	}
-
 	mc.buf = newBuffer(mc.netConn)
 
 	// Set I/O timeouts

+ 41 - 79
vendor/github.com/go-sql-driver/mysql/dsn.go

@@ -15,7 +15,6 @@ import (
 	"fmt"
 	"net"
 	"net/url"
-	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -28,9 +27,7 @@ var (
 	errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
 )
 
-// Config is a configuration parsed from a DSN string.
-// If a new Config is created instead of being parsed from a DSN string,
-// the NewConfig function should be used, which sets default values.
+// Config is a configuration parsed from a DSN string
 type Config struct {
 	User             string            // Username
 	Passwd           string            // Password (requires User)
@@ -56,45 +53,7 @@ type Config struct {
 	InterpolateParams       bool // Interpolate placeholders into query string
 	MultiStatements         bool // Allow multiple statements in one query
 	ParseTime               bool // Parse time values to time.Time
-	RejectReadOnly          bool // Reject read-only connections
-}
-
-// NewConfig creates a new Config and sets default values.
-func NewConfig() *Config {
-	return &Config{
-		Collation:            defaultCollation,
-		Loc:                  time.UTC,
-		MaxAllowedPacket:     defaultMaxAllowedPacket,
-		AllowNativePasswords: true,
-	}
-}
-
-func (cfg *Config) normalize() error {
-	if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
-		return errInvalidDSNUnsafeCollation
-	}
-
-	// Set default network if empty
-	if cfg.Net == "" {
-		cfg.Net = "tcp"
-	}
-
-	// Set default address if empty
-	if cfg.Addr == "" {
-		switch cfg.Net {
-		case "tcp":
-			cfg.Addr = "127.0.0.1:3306"
-		case "unix":
-			cfg.Addr = "/tmp/mysql.sock"
-		default:
-			return errors.New("default addr for network '" + cfg.Net + "' unknown")
-		}
-
-	} else if cfg.Net == "tcp" {
-		cfg.Addr = ensureHavePort(cfg.Addr)
-	}
-
-	return nil
+	Strict                  bool // Return warnings as errors
 }
 
 // FormatDSN formats the given Config into a DSN string which can be passed to
@@ -143,12 +102,12 @@ func (cfg *Config) FormatDSN() string {
 		}
 	}
 
-	if !cfg.AllowNativePasswords {
+	if cfg.AllowNativePasswords {
 		if hasParam {
-			buf.WriteString("&allowNativePasswords=false")
+			buf.WriteString("&allowNativePasswords=true")
 		} else {
 			hasParam = true
-			buf.WriteString("?allowNativePasswords=false")
+			buf.WriteString("?allowNativePasswords=true")
 		}
 	}
 
@@ -236,12 +195,12 @@ func (cfg *Config) FormatDSN() string {
 		buf.WriteString(cfg.ReadTimeout.String())
 	}
 
-	if cfg.RejectReadOnly {
+	if cfg.Strict {
 		if hasParam {
-			buf.WriteString("&rejectReadOnly=true")
+			buf.WriteString("&strict=true")
 		} else {
 			hasParam = true
-			buf.WriteString("?rejectReadOnly=true")
+			buf.WriteString("?strict=true")
 		}
 	}
 
@@ -275,7 +234,7 @@ func (cfg *Config) FormatDSN() string {
 		buf.WriteString(cfg.WriteTimeout.String())
 	}
 
-	if cfg.MaxAllowedPacket != defaultMaxAllowedPacket {
+	if cfg.MaxAllowedPacket > 0 {
 		if hasParam {
 			buf.WriteString("&maxAllowedPacket=")
 		} else {
@@ -288,12 +247,7 @@ func (cfg *Config) FormatDSN() string {
 
 	// other params
 	if cfg.Params != nil {
-		var params []string
-		for param := range cfg.Params {
-			params = append(params, param)
-		}
-		sort.Strings(params)
-		for _, param := range params {
+		for param, value := range cfg.Params {
 			if hasParam {
 				buf.WriteByte('&')
 			} else {
@@ -303,7 +257,7 @@ func (cfg *Config) FormatDSN() string {
 
 			buf.WriteString(param)
 			buf.WriteByte('=')
-			buf.WriteString(url.QueryEscape(cfg.Params[param]))
+			buf.WriteString(url.QueryEscape(value))
 		}
 	}
 
@@ -313,7 +267,10 @@ func (cfg *Config) FormatDSN() string {
 // ParseDSN parses the DSN string to a Config
 func ParseDSN(dsn string) (cfg *Config, err error) {
 	// New config with some default values
-	cfg = NewConfig()
+	cfg = &Config{
+		Loc:       time.UTC,
+		Collation: defaultCollation,
+	}
 
 	// [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
 	// Find the last '/' (since the password or the net addr might contain a '/')
@@ -381,9 +338,28 @@ func ParseDSN(dsn string) (cfg *Config, err error) {
 		return nil, errInvalidDSNNoSlash
 	}
 
-	if err = cfg.normalize(); err != nil {
-		return nil, err
+	if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
+		return nil, errInvalidDSNUnsafeCollation
+	}
+
+	// Set default network if empty
+	if cfg.Net == "" {
+		cfg.Net = "tcp"
+	}
+
+	// Set default address if empty
+	if cfg.Addr == "" {
+		switch cfg.Net {
+		case "tcp":
+			cfg.Addr = "127.0.0.1:3306"
+		case "unix":
+			cfg.Addr = "/tmp/mysql.sock"
+		default:
+			return nil, errors.New("default addr for network '" + cfg.Net + "' unknown")
+		}
+
 	}
+
 	return
 }
 
@@ -398,6 +374,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 
 		// cfg params
 		switch value := param[1]; param[0] {
+
 		// Disable INFILE whitelist / enable all files
 		case "allowAllFiles":
 			var isBool bool
@@ -495,18 +472,14 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 				return
 			}
 
-		// Reject read-only connections
-		case "rejectReadOnly":
+		// Strict mode
+		case "strict":
 			var isBool bool
-			cfg.RejectReadOnly, isBool = readBool(value)
+			cfg.Strict, isBool = readBool(value)
 			if !isBool {
 				return errors.New("invalid bool value: " + value)
 			}
 
-		// Strict mode
-		case "strict":
-			panic("strict mode has been removed. See https://github.com/go-sql-driver/mysql/wiki/strict-mode")
-
 		// Dial Timeout
 		case "timeout":
 			cfg.Timeout, err = time.ParseDuration(value)
@@ -521,10 +494,6 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 				if boolValue {
 					cfg.TLSConfig = "true"
 					cfg.tls = &tls.Config{}
-					host, _, err := net.SplitHostPort(cfg.Addr)
-					if err == nil {
-						cfg.tls.ServerName = host
-					}
 				} else {
 					cfg.TLSConfig = "false"
 				}
@@ -537,7 +506,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 					return fmt.Errorf("invalid value for TLS config name: %v", err)
 				}
 
-				if tlsConfig := getTLSConfigClone(name); tlsConfig != nil {
+				if tlsConfig, ok := tlsConfigRegister[name]; ok {
 					if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
 						host, _, err := net.SplitHostPort(cfg.Addr)
 						if err == nil {
@@ -577,10 +546,3 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 
 	return
 }
-
-func ensureHavePort(addr string) string {
-	if _, _, err := net.SplitHostPort(addr); err != nil {
-		return net.JoinHostPort(addr, "3306")
-	}
-	return addr
-}

+ 73 - 6
vendor/github.com/go-sql-driver/mysql/errors.go

@@ -9,8 +9,10 @@
 package mysql
 
 import (
+	"database/sql/driver"
 	"errors"
 	"fmt"
+	"io"
 	"log"
 	"os"
 )
@@ -29,12 +31,6 @@ var (
 	ErrPktSyncMul        = errors.New("commands out of sync. Did you run multiple statements at once?")
 	ErrPktTooLarge       = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server")
 	ErrBusyBuffer        = errors.New("busy buffer")
-
-	// errBadConnNoWrite is used for connection errors where nothing was sent to the database yet.
-	// If this happens first in a function starting a database interaction, it should be replaced by driver.ErrBadConn
-	// to trigger a resend.
-	// See https://github.com/go-sql-driver/mysql/pull/302
-	errBadConnNoWrite = errors.New("bad connection")
 )
 
 var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
@@ -63,3 +59,74 @@ type MySQLError struct {
 func (me *MySQLError) Error() string {
 	return fmt.Sprintf("Error %d: %s", me.Number, me.Message)
 }
+
+// MySQLWarnings is an error type which represents a group of one or more MySQL
+// warnings
+type MySQLWarnings []MySQLWarning
+
+func (mws MySQLWarnings) Error() string {
+	var msg string
+	for i, warning := range mws {
+		if i > 0 {
+			msg += "\r\n"
+		}
+		msg += fmt.Sprintf(
+			"%s %s: %s",
+			warning.Level,
+			warning.Code,
+			warning.Message,
+		)
+	}
+	return msg
+}
+
+// MySQLWarning is an error type which represents a single MySQL warning.
+// Warnings are returned in groups only. See MySQLWarnings
+type MySQLWarning struct {
+	Level   string
+	Code    string
+	Message string
+}
+
+func (mc *mysqlConn) getWarnings() (err error) {
+	rows, err := mc.Query("SHOW WARNINGS", nil)
+	if err != nil {
+		return
+	}
+
+	var warnings = MySQLWarnings{}
+	var values = make([]driver.Value, 3)
+
+	for {
+		err = rows.Next(values)
+		switch err {
+		case nil:
+			warning := MySQLWarning{}
+
+			if raw, ok := values[0].([]byte); ok {
+				warning.Level = string(raw)
+			} else {
+				warning.Level = fmt.Sprintf("%s", values[0])
+			}
+			if raw, ok := values[1].([]byte); ok {
+				warning.Code = string(raw)
+			} else {
+				warning.Code = fmt.Sprintf("%s", values[1])
+			}
+			if raw, ok := values[2].([]byte); ok {
+				warning.Message = string(raw)
+			} else {
+				warning.Message = fmt.Sprintf("%s", values[0])
+			}
+
+			warnings = append(warnings, warning)
+
+		case io.EOF:
+			return warnings
+
+		default:
+			rows.Close()
+			return
+		}
+	}
+}

+ 0 - 140
vendor/github.com/go-sql-driver/mysql/fields.go

@@ -1,140 +0,0 @@
-// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
-//
-// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-package mysql
-
-import (
-	"database/sql"
-	"reflect"
-)
-
-var typeDatabaseName = map[fieldType]string{
-	fieldTypeBit:        "BIT",
-	fieldTypeBLOB:       "BLOB",
-	fieldTypeDate:       "DATE",
-	fieldTypeDateTime:   "DATETIME",
-	fieldTypeDecimal:    "DECIMAL",
-	fieldTypeDouble:     "DOUBLE",
-	fieldTypeEnum:       "ENUM",
-	fieldTypeFloat:      "FLOAT",
-	fieldTypeGeometry:   "GEOMETRY",
-	fieldTypeInt24:      "MEDIUMINT",
-	fieldTypeJSON:       "JSON",
-	fieldTypeLong:       "INT",
-	fieldTypeLongBLOB:   "LONGBLOB",
-	fieldTypeLongLong:   "BIGINT",
-	fieldTypeMediumBLOB: "MEDIUMBLOB",
-	fieldTypeNewDate:    "DATE",
-	fieldTypeNewDecimal: "DECIMAL",
-	fieldTypeNULL:       "NULL",
-	fieldTypeSet:        "SET",
-	fieldTypeShort:      "SMALLINT",
-	fieldTypeString:     "CHAR",
-	fieldTypeTime:       "TIME",
-	fieldTypeTimestamp:  "TIMESTAMP",
-	fieldTypeTiny:       "TINYINT",
-	fieldTypeTinyBLOB:   "TINYBLOB",
-	fieldTypeVarChar:    "VARCHAR",
-	fieldTypeVarString:  "VARCHAR",
-	fieldTypeYear:       "YEAR",
-}
-
-var (
-	scanTypeFloat32   = reflect.TypeOf(float32(0))
-	scanTypeFloat64   = reflect.TypeOf(float64(0))
-	scanTypeInt8      = reflect.TypeOf(int8(0))
-	scanTypeInt16     = reflect.TypeOf(int16(0))
-	scanTypeInt32     = reflect.TypeOf(int32(0))
-	scanTypeInt64     = reflect.TypeOf(int64(0))
-	scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
-	scanTypeNullInt   = reflect.TypeOf(sql.NullInt64{})
-	scanTypeNullTime  = reflect.TypeOf(NullTime{})
-	scanTypeUint8     = reflect.TypeOf(uint8(0))
-	scanTypeUint16    = reflect.TypeOf(uint16(0))
-	scanTypeUint32    = reflect.TypeOf(uint32(0))
-	scanTypeUint64    = reflect.TypeOf(uint64(0))
-	scanTypeRawBytes  = reflect.TypeOf(sql.RawBytes{})
-	scanTypeUnknown   = reflect.TypeOf(new(interface{}))
-)
-
-type mysqlField struct {
-	tableName string
-	name      string
-	length    uint32
-	flags     fieldFlag
-	fieldType fieldType
-	decimals  byte
-}
-
-func (mf *mysqlField) scanType() reflect.Type {
-	switch mf.fieldType {
-	case fieldTypeTiny:
-		if mf.flags&flagNotNULL != 0 {
-			if mf.flags&flagUnsigned != 0 {
-				return scanTypeUint8
-			}
-			return scanTypeInt8
-		}
-		return scanTypeNullInt
-
-	case fieldTypeShort, fieldTypeYear:
-		if mf.flags&flagNotNULL != 0 {
-			if mf.flags&flagUnsigned != 0 {
-				return scanTypeUint16
-			}
-			return scanTypeInt16
-		}
-		return scanTypeNullInt
-
-	case fieldTypeInt24, fieldTypeLong:
-		if mf.flags&flagNotNULL != 0 {
-			if mf.flags&flagUnsigned != 0 {
-				return scanTypeUint32
-			}
-			return scanTypeInt32
-		}
-		return scanTypeNullInt
-
-	case fieldTypeLongLong:
-		if mf.flags&flagNotNULL != 0 {
-			if mf.flags&flagUnsigned != 0 {
-				return scanTypeUint64
-			}
-			return scanTypeInt64
-		}
-		return scanTypeNullInt
-
-	case fieldTypeFloat:
-		if mf.flags&flagNotNULL != 0 {
-			return scanTypeFloat32
-		}
-		return scanTypeNullFloat
-
-	case fieldTypeDouble:
-		if mf.flags&flagNotNULL != 0 {
-			return scanTypeFloat64
-		}
-		return scanTypeNullFloat
-
-	case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar,
-		fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB,
-		fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB,
-		fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON,
-		fieldTypeTime:
-		return scanTypeRawBytes
-
-	case fieldTypeDate, fieldTypeNewDate,
-		fieldTypeTimestamp, fieldTypeDateTime:
-		// NullTime is always returned for more consistent behavior as it can
-		// handle both cases of parseTime regardless if the field is nullable.
-		return scanTypeNullTime
-
-	default:
-		return scanTypeUnknown
-	}
-}

+ 1 - 2
vendor/github.com/go-sql-driver/mysql/infile.go

@@ -147,8 +147,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
 	}
 
 	// send content packets
-	// if packetSize == 0, the Reader contains no data
-	if err == nil && packetSize > 0 {
+	if err == nil {
 		data := make([]byte, 4+packetSize)
 		var n int
 		for err == nil {

+ 86 - 108
vendor/github.com/go-sql-driver/mysql/packets.go

@@ -30,12 +30,9 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
 		// read packet header
 		data, err := mc.buf.readNext(4)
 		if err != nil {
-			if cerr := mc.canceled.Value(); cerr != nil {
-				return nil, cerr
-			}
 			errLog.Print(err)
 			mc.Close()
-			return nil, ErrInvalidConn
+			return nil, driver.ErrBadConn
 		}
 
 		// packet length [24 bit]
@@ -57,7 +54,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
 			if prevData == nil {
 				errLog.Print(ErrMalformPkt)
 				mc.Close()
-				return nil, ErrInvalidConn
+				return nil, driver.ErrBadConn
 			}
 
 			return prevData, nil
@@ -66,12 +63,9 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
 		// read packet body [pktLen bytes]
 		data, err = mc.buf.readNext(pktLen)
 		if err != nil {
-			if cerr := mc.canceled.Value(); cerr != nil {
-				return nil, cerr
-			}
 			errLog.Print(err)
 			mc.Close()
-			return nil, ErrInvalidConn
+			return nil, driver.ErrBadConn
 		}
 
 		// return data if this was the last packet
@@ -131,20 +125,11 @@ func (mc *mysqlConn) writePacket(data []byte) error {
 
 		// Handle error
 		if err == nil { // n != len(data)
-			mc.cleanup()
 			errLog.Print(ErrMalformPkt)
 		} else {
-			if cerr := mc.canceled.Value(); cerr != nil {
-				return cerr
-			}
-			if n == 0 && pktLen == len(data)-4 {
-				// only for the first loop iteration when nothing was written yet
-				return errBadConnNoWrite
-			}
-			mc.cleanup()
 			errLog.Print(err)
 		}
-		return ErrInvalidConn
+		return driver.ErrBadConn
 	}
 }
 
@@ -278,7 +263,7 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
 	if data == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return errBadConnNoWrite
+		return driver.ErrBadConn
 	}
 
 	// ClientFlags [32 bit]
@@ -356,9 +341,7 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
 // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
 func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
 	// User password
-	// https://dev.mysql.com/doc/internals/en/old-password-authentication.html
-	// Old password authentication only need and will need 8-byte challenge.
-	scrambleBuff := scrambleOldPassword(cipher[:8], []byte(mc.cfg.Passwd))
+	scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.Passwd))
 
 	// Calculate the packet length and add a tailing 0
 	pktLen := len(scrambleBuff) + 1
@@ -366,7 +349,7 @@ func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
 	if data == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return errBadConnNoWrite
+		return driver.ErrBadConn
 	}
 
 	// Add the scrambled password [null terminated string]
@@ -385,7 +368,7 @@ func (mc *mysqlConn) writeClearAuthPacket() error {
 	if data == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return errBadConnNoWrite
+		return driver.ErrBadConn
 	}
 
 	// Add the clear password [null terminated string]
@@ -398,9 +381,7 @@ func (mc *mysqlConn) writeClearAuthPacket() error {
 //  Native password authentication method
 // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
 func (mc *mysqlConn) writeNativeAuthPacket(cipher []byte) error {
-	// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
-	// Native password authentication only need and will need 20-byte challenge.
-	scrambleBuff := scramblePassword(cipher[0:20], []byte(mc.cfg.Passwd))
+	scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd))
 
 	// Calculate the packet length and add a tailing 0
 	pktLen := len(scrambleBuff)
@@ -408,7 +389,7 @@ func (mc *mysqlConn) writeNativeAuthPacket(cipher []byte) error {
 	if data == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return errBadConnNoWrite
+		return driver.ErrBadConn
 	}
 
 	// Add the scramble
@@ -429,7 +410,7 @@ func (mc *mysqlConn) writeCommandPacket(command byte) error {
 	if data == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return errBadConnNoWrite
+		return driver.ErrBadConn
 	}
 
 	// Add command byte
@@ -448,7 +429,7 @@ func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error {
 	if data == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return errBadConnNoWrite
+		return driver.ErrBadConn
 	}
 
 	// Add command byte
@@ -469,7 +450,7 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
 	if data == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return errBadConnNoWrite
+		return driver.ErrBadConn
 	}
 
 	// Add command byte
@@ -503,26 +484,25 @@ func (mc *mysqlConn) readResultOK() ([]byte, error) {
 			if len(data) > 1 {
 				pluginEndIndex := bytes.IndexByte(data, 0x00)
 				plugin := string(data[1:pluginEndIndex])
-				cipher := data[pluginEndIndex+1:]
+				cipher := data[pluginEndIndex+1 : len(data)-1]
 
-				switch plugin {
-				case "mysql_old_password":
+				if plugin == "mysql_old_password" {
 					// using old_passwords
 					return cipher, ErrOldPassword
-				case "mysql_clear_password":
+				} else if plugin == "mysql_clear_password" {
 					// using clear text password
 					return cipher, ErrCleartextPassword
-				case "mysql_native_password":
+				} else if plugin == "mysql_native_password" {
 					// using mysql default authentication method
 					return cipher, ErrNativePassword
-				default:
+				} else {
 					return cipher, ErrUnknownPlugin
 				}
+			} else {
+				// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
+				return nil, ErrOldPassword
 			}
 
-			// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
-			return nil, ErrOldPassword
-
 		default: // Error otherwise
 			return nil, mc.handleErrorPacket(data)
 		}
@@ -570,22 +550,6 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error {
 	// Error Number [16 bit uint]
 	errno := binary.LittleEndian.Uint16(data[1:3])
 
-	// 1792: ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION
-	// 1290: ER_OPTION_PREVENTS_STATEMENT (returned by Aurora during failover)
-	if (errno == 1792 || errno == 1290) && mc.cfg.RejectReadOnly {
-		// Oops; we are connected to a read-only connection, and won't be able
-		// to issue any write statements. Since RejectReadOnly is configured,
-		// we throw away this connection hoping this one would have write
-		// permission. This is specifically for a possible race condition
-		// during failover (e.g. on AWS Aurora). See README.md for more.
-		//
-		// We explicitly close the connection before returning
-		// driver.ErrBadConn to ensure that `database/sql` purges this
-		// connection and initiates a new one for next statement next time.
-		mc.Close()
-		return driver.ErrBadConn
-	}
-
 	pos := 3
 
 	// SQL State [optional: # + 5bytes string]
@@ -620,12 +584,19 @@ func (mc *mysqlConn) handleOkPacket(data []byte) error {
 
 	// server_status [2 bytes]
 	mc.status = readStatus(data[1+n+m : 1+n+m+2])
-	if mc.status&statusMoreResultsExists != 0 {
-		return nil
+	if err := mc.discardResults(); err != nil {
+		return err
 	}
 
 	// warning count [2 bytes]
+	if !mc.strict {
+		return nil
+	}
 
+	pos := 1 + n + m + 2
+	if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 {
+		return mc.getWarnings()
+	}
 	return nil
 }
 
@@ -700,14 +671,11 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
 
 		// Filler [uint8]
 		// Charset [charset, collation uint8]
-		pos += n + 1 + 2
-
 		// Length [uint32]
-		columns[i].length = binary.LittleEndian.Uint32(data[pos : pos+4])
-		pos += 4
+		pos += n + 1 + 2 + 4
 
 		// Field type [uint8]
-		columns[i].fieldType = fieldType(data[pos])
+		columns[i].fieldType = data[pos]
 		pos++
 
 		// Flags [uint16]
@@ -730,10 +698,6 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
 func (rows *textRows) readRow(dest []driver.Value) error {
 	mc := rows.mc
 
-	if rows.rs.done {
-		return io.EOF
-	}
-
 	data, err := mc.readPacket()
 	if err != nil {
 		return err
@@ -743,11 +707,15 @@ func (rows *textRows) readRow(dest []driver.Value) error {
 	if data[0] == iEOF && len(data) == 5 {
 		// server_status [2 bytes]
 		rows.mc.status = readStatus(data[3:])
-		rows.rs.done = true
-		if !rows.HasNextResultSet() {
-			rows.mc = nil
+		err = rows.mc.discardResults()
+		if err == nil {
+			err = io.EOF
+		} else {
+			// connection unusable
+			rows.mc.Close()
 		}
-		return io.EOF
+		rows.mc = nil
+		return err
 	}
 	if data[0] == iERR {
 		rows.mc = nil
@@ -768,7 +736,7 @@ func (rows *textRows) readRow(dest []driver.Value) error {
 				if !mc.parseTime {
 					continue
 				} else {
-					switch rows.rs.columns[i].fieldType {
+					switch rows.columns[i].fieldType {
 					case fieldTypeTimestamp, fieldTypeDateTime,
 						fieldTypeDate, fieldTypeNewDate:
 						dest[i], err = parseDateTime(
@@ -840,7 +808,14 @@ func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) {
 		// Reserved [8 bit]
 
 		// Warning count [16 bit uint]
+		if !stmt.mc.strict {
+			return columnCount, nil
+		}
 
+		// Check for warnings count > 0, only available in MySQL > 4.1
+		if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 {
+			return columnCount, stmt.mc.getWarnings()
+		}
 		return columnCount, nil
 	}
 	return 0, err
@@ -925,7 +900,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 	if data == nil {
 		// can not take the buffer. Something must be wrong with the connection
 		errLog.Print(ErrBusyBuffer)
-		return errBadConnNoWrite
+		return driver.ErrBadConn
 	}
 
 	// command [1 byte]
@@ -984,7 +959,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 			// build NULL-bitmap
 			if arg == nil {
 				nullMask[i/8] |= 1 << (uint(i) & 7)
-				paramTypes[i+i] = byte(fieldTypeNULL)
+				paramTypes[i+i] = fieldTypeNULL
 				paramTypes[i+i+1] = 0x00
 				continue
 			}
@@ -992,7 +967,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 			// cache types and values
 			switch v := arg.(type) {
 			case int64:
-				paramTypes[i+i] = byte(fieldTypeLongLong)
+				paramTypes[i+i] = fieldTypeLongLong
 				paramTypes[i+i+1] = 0x00
 
 				if cap(paramValues)-len(paramValues)-8 >= 0 {
@@ -1008,7 +983,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				}
 
 			case float64:
-				paramTypes[i+i] = byte(fieldTypeDouble)
+				paramTypes[i+i] = fieldTypeDouble
 				paramTypes[i+i+1] = 0x00
 
 				if cap(paramValues)-len(paramValues)-8 >= 0 {
@@ -1024,7 +999,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				}
 
 			case bool:
-				paramTypes[i+i] = byte(fieldTypeTiny)
+				paramTypes[i+i] = fieldTypeTiny
 				paramTypes[i+i+1] = 0x00
 
 				if v {
@@ -1036,7 +1011,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 			case []byte:
 				// Common case (non-nil value) first
 				if v != nil {
-					paramTypes[i+i] = byte(fieldTypeString)
+					paramTypes[i+i] = fieldTypeString
 					paramTypes[i+i+1] = 0x00
 
 					if len(v) < mc.maxAllowedPacket-pos-len(paramValues)-(len(args)-(i+1))*64 {
@@ -1054,11 +1029,11 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 
 				// Handle []byte(nil) as a NULL value
 				nullMask[i/8] |= 1 << (uint(i) & 7)
-				paramTypes[i+i] = byte(fieldTypeNULL)
+				paramTypes[i+i] = fieldTypeNULL
 				paramTypes[i+i+1] = 0x00
 
 			case string:
-				paramTypes[i+i] = byte(fieldTypeString)
+				paramTypes[i+i] = fieldTypeString
 				paramTypes[i+i+1] = 0x00
 
 				if len(v) < mc.maxAllowedPacket-pos-len(paramValues)-(len(args)-(i+1))*64 {
@@ -1073,22 +1048,20 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				}
 
 			case time.Time:
-				paramTypes[i+i] = byte(fieldTypeString)
+				paramTypes[i+i] = fieldTypeString
 				paramTypes[i+i+1] = 0x00
 
-				var a [64]byte
-				var b = a[:0]
-
+				var val []byte
 				if v.IsZero() {
-					b = append(b, "0000-00-00"...)
+					val = []byte("0000-00-00")
 				} else {
-					b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat)
+					val = []byte(v.In(mc.cfg.Loc).Format(timeFormat))
 				}
 
 				paramValues = appendLengthEncodedInteger(paramValues,
-					uint64(len(b)),
+					uint64(len(val)),
 				)
-				paramValues = append(paramValues, b...)
+				paramValues = append(paramValues, val...)
 
 			default:
 				return fmt.Errorf("can not convert type: %T", arg)
@@ -1124,6 +1097,8 @@ func (mc *mysqlConn) discardResults() error {
 			if err := mc.readUntilEOF(); err != nil {
 				return err
 			}
+		} else {
+			mc.status &^= statusMoreResultsExists
 		}
 	}
 	return nil
@@ -1141,17 +1116,20 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 		// EOF Packet
 		if data[0] == iEOF && len(data) == 5 {
 			rows.mc.status = readStatus(data[3:])
-			rows.rs.done = true
-			if !rows.HasNextResultSet() {
-				rows.mc = nil
+			err = rows.mc.discardResults()
+			if err == nil {
+				err = io.EOF
+			} else {
+				// connection unusable
+				rows.mc.Close()
 			}
-			return io.EOF
+			rows.mc = nil
+			return err
 		}
-		mc := rows.mc
 		rows.mc = nil
 
 		// Error otherwise
-		return mc.handleErrorPacket(data)
+		return rows.mc.handleErrorPacket(data)
 	}
 
 	// NULL-bitmap,  [(column-count + 7 + 2) / 8 bytes]
@@ -1167,14 +1145,14 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 		}
 
 		// Convert to byte-coded string
-		switch rows.rs.columns[i].fieldType {
+		switch rows.columns[i].fieldType {
 		case fieldTypeNULL:
 			dest[i] = nil
 			continue
 
 		// Numeric Types
 		case fieldTypeTiny:
-			if rows.rs.columns[i].flags&flagUnsigned != 0 {
+			if rows.columns[i].flags&flagUnsigned != 0 {
 				dest[i] = int64(data[pos])
 			} else {
 				dest[i] = int64(int8(data[pos]))
@@ -1183,7 +1161,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 			continue
 
 		case fieldTypeShort, fieldTypeYear:
-			if rows.rs.columns[i].flags&flagUnsigned != 0 {
+			if rows.columns[i].flags&flagUnsigned != 0 {
 				dest[i] = int64(binary.LittleEndian.Uint16(data[pos : pos+2]))
 			} else {
 				dest[i] = int64(int16(binary.LittleEndian.Uint16(data[pos : pos+2])))
@@ -1192,7 +1170,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 			continue
 
 		case fieldTypeInt24, fieldTypeLong:
-			if rows.rs.columns[i].flags&flagUnsigned != 0 {
+			if rows.columns[i].flags&flagUnsigned != 0 {
 				dest[i] = int64(binary.LittleEndian.Uint32(data[pos : pos+4]))
 			} else {
 				dest[i] = int64(int32(binary.LittleEndian.Uint32(data[pos : pos+4])))
@@ -1201,7 +1179,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 			continue
 
 		case fieldTypeLongLong:
-			if rows.rs.columns[i].flags&flagUnsigned != 0 {
+			if rows.columns[i].flags&flagUnsigned != 0 {
 				val := binary.LittleEndian.Uint64(data[pos : pos+8])
 				if val > math.MaxInt64 {
 					dest[i] = uint64ToString(val)
@@ -1215,7 +1193,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 			continue
 
 		case fieldTypeFloat:
-			dest[i] = math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4]))
+			dest[i] = float32(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4])))
 			pos += 4
 			continue
 
@@ -1255,10 +1233,10 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 			case isNull:
 				dest[i] = nil
 				continue
-			case rows.rs.columns[i].fieldType == fieldTypeTime:
+			case rows.columns[i].fieldType == fieldTypeTime:
 				// database/sql does not support an equivalent to TIME, return a string
 				var dstlen uint8
-				switch decimals := rows.rs.columns[i].decimals; decimals {
+				switch decimals := rows.columns[i].decimals; decimals {
 				case 0x00, 0x1f:
 					dstlen = 8
 				case 1, 2, 3, 4, 5, 6:
@@ -1266,7 +1244,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 				default:
 					return fmt.Errorf(
 						"protocol error, illegal decimals value %d",
-						rows.rs.columns[i].decimals,
+						rows.columns[i].decimals,
 					)
 				}
 				dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true)
@@ -1274,10 +1252,10 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 				dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
 			default:
 				var dstlen uint8
-				if rows.rs.columns[i].fieldType == fieldTypeDate {
+				if rows.columns[i].fieldType == fieldTypeDate {
 					dstlen = 10
 				} else {
-					switch decimals := rows.rs.columns[i].decimals; decimals {
+					switch decimals := rows.columns[i].decimals; decimals {
 					case 0x00, 0x1f:
 						dstlen = 19
 					case 1, 2, 3, 4, 5, 6:
@@ -1285,7 +1263,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 					default:
 						return fmt.Errorf(
 							"protocol error, illegal decimals value %d",
-							rows.rs.columns[i].decimals,
+							rows.columns[i].decimals,
 						)
 					}
 				}
@@ -1301,7 +1279,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 
 		// Please report if this happens!
 		default:
-			return fmt.Errorf("unknown field type %d", rows.rs.columns[i].fieldType)
+			return fmt.Errorf("unknown field type %d", rows.columns[i].fieldType)
 		}
 	}
 

+ 35 - 142
vendor/github.com/go-sql-driver/mysql/rows.go

@@ -11,20 +11,19 @@ package mysql
 import (
 	"database/sql/driver"
 	"io"
-	"math"
-	"reflect"
 )
 
-type resultSet struct {
-	columns     []mysqlField
-	columnNames []string
-	done        bool
+type mysqlField struct {
+	tableName string
+	name      string
+	flags     fieldFlag
+	fieldType byte
+	decimals  byte
 }
 
 type mysqlRows struct {
-	mc     *mysqlConn
-	rs     resultSet
-	finish func()
+	mc      *mysqlConn
+	columns []mysqlField
 }
 
 type binaryRows struct {
@@ -35,89 +34,37 @@ type textRows struct {
 	mysqlRows
 }
 
-func (rows *mysqlRows) Columns() []string {
-	if rows.rs.columnNames != nil {
-		return rows.rs.columnNames
-	}
+type emptyRows struct{}
 
-	columns := make([]string, len(rows.rs.columns))
+func (rows *mysqlRows) Columns() []string {
+	columns := make([]string, len(rows.columns))
 	if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias {
 		for i := range columns {
-			if tableName := rows.rs.columns[i].tableName; len(tableName) > 0 {
-				columns[i] = tableName + "." + rows.rs.columns[i].name
+			if tableName := rows.columns[i].tableName; len(tableName) > 0 {
+				columns[i] = tableName + "." + rows.columns[i].name
 			} else {
-				columns[i] = rows.rs.columns[i].name
+				columns[i] = rows.columns[i].name
 			}
 		}
 	} else {
 		for i := range columns {
-			columns[i] = rows.rs.columns[i].name
+			columns[i] = rows.columns[i].name
 		}
 	}
-
-	rows.rs.columnNames = columns
 	return columns
 }
 
-func (rows *mysqlRows) ColumnTypeDatabaseTypeName(i int) string {
-	if name, ok := typeDatabaseName[rows.rs.columns[i].fieldType]; ok {
-		return name
-	}
-	return ""
-}
-
-// func (rows *mysqlRows) ColumnTypeLength(i int) (length int64, ok bool) {
-// 	return int64(rows.rs.columns[i].length), true
-// }
-
-func (rows *mysqlRows) ColumnTypeNullable(i int) (nullable, ok bool) {
-	return rows.rs.columns[i].flags&flagNotNULL == 0, true
-}
-
-func (rows *mysqlRows) ColumnTypePrecisionScale(i int) (int64, int64, bool) {
-	column := rows.rs.columns[i]
-	decimals := int64(column.decimals)
-
-	switch column.fieldType {
-	case fieldTypeDecimal, fieldTypeNewDecimal:
-		if decimals > 0 {
-			return int64(column.length) - 2, decimals, true
-		}
-		return int64(column.length) - 1, decimals, true
-	case fieldTypeTimestamp, fieldTypeDateTime, fieldTypeTime:
-		return decimals, decimals, true
-	case fieldTypeFloat, fieldTypeDouble:
-		if decimals == 0x1f {
-			return math.MaxInt64, math.MaxInt64, true
-		}
-		return math.MaxInt64, decimals, true
-	}
-
-	return 0, 0, false
-}
-
-func (rows *mysqlRows) ColumnTypeScanType(i int) reflect.Type {
-	return rows.rs.columns[i].scanType()
-}
-
-func (rows *mysqlRows) Close() (err error) {
-	if f := rows.finish; f != nil {
-		f()
-		rows.finish = nil
-	}
-
+func (rows *mysqlRows) Close() error {
 	mc := rows.mc
 	if mc == nil {
 		return nil
 	}
-	if err := mc.error(); err != nil {
-		return err
+	if mc.netConn == nil {
+		return ErrInvalidConn
 	}
 
 	// Remove unread packets from stream
-	if !rows.rs.done {
-		err = mc.readUntilEOF()
-	}
+	err := mc.readUntilEOF()
 	if err == nil {
 		if err = mc.discardResults(); err != nil {
 			return err
@@ -128,66 +75,10 @@ func (rows *mysqlRows) Close() (err error) {
 	return err
 }
 
-func (rows *mysqlRows) HasNextResultSet() (b bool) {
-	if rows.mc == nil {
-		return false
-	}
-	return rows.mc.status&statusMoreResultsExists != 0
-}
-
-func (rows *mysqlRows) nextResultSet() (int, error) {
-	if rows.mc == nil {
-		return 0, io.EOF
-	}
-	if err := rows.mc.error(); err != nil {
-		return 0, err
-	}
-
-	// Remove unread packets from stream
-	if !rows.rs.done {
-		if err := rows.mc.readUntilEOF(); err != nil {
-			return 0, err
-		}
-		rows.rs.done = true
-	}
-
-	if !rows.HasNextResultSet() {
-		rows.mc = nil
-		return 0, io.EOF
-	}
-	rows.rs = resultSet{}
-	return rows.mc.readResultSetHeaderPacket()
-}
-
-func (rows *mysqlRows) nextNotEmptyResultSet() (int, error) {
-	for {
-		resLen, err := rows.nextResultSet()
-		if err != nil {
-			return 0, err
-		}
-
-		if resLen > 0 {
-			return resLen, nil
-		}
-
-		rows.rs.done = true
-	}
-}
-
-func (rows *binaryRows) NextResultSet() error {
-	resLen, err := rows.nextNotEmptyResultSet()
-	if err != nil {
-		return err
-	}
-
-	rows.rs.columns, err = rows.mc.readColumns(resLen)
-	return err
-}
-
 func (rows *binaryRows) Next(dest []driver.Value) error {
 	if mc := rows.mc; mc != nil {
-		if err := mc.error(); err != nil {
-			return err
+		if mc.netConn == nil {
+			return ErrInvalidConn
 		}
 
 		// Fetch next row from stream
@@ -196,20 +87,10 @@ func (rows *binaryRows) Next(dest []driver.Value) error {
 	return io.EOF
 }
 
-func (rows *textRows) NextResultSet() (err error) {
-	resLen, err := rows.nextNotEmptyResultSet()
-	if err != nil {
-		return err
-	}
-
-	rows.rs.columns, err = rows.mc.readColumns(resLen)
-	return err
-}
-
 func (rows *textRows) Next(dest []driver.Value) error {
 	if mc := rows.mc; mc != nil {
-		if err := mc.error(); err != nil {
-			return err
+		if mc.netConn == nil {
+			return ErrInvalidConn
 		}
 
 		// Fetch next row from stream
@@ -217,3 +98,15 @@ func (rows *textRows) Next(dest []driver.Value) error {
 	}
 	return io.EOF
 }
+
+func (rows emptyRows) Columns() []string {
+	return nil
+}
+
+func (rows emptyRows) Close() error {
+	return nil
+}
+
+func (rows emptyRows) Next(dest []driver.Value) error {
+	return io.EOF
+}

+ 30 - 55
vendor/github.com/go-sql-driver/mysql/statement.go

@@ -11,7 +11,6 @@ package mysql
 import (
 	"database/sql/driver"
 	"fmt"
-	"io"
 	"reflect"
 	"strconv"
 )
@@ -20,10 +19,11 @@ type mysqlStmt struct {
 	mc         *mysqlConn
 	id         uint32
 	paramCount int
+	columns    []mysqlField // cached from the first query
 }
 
 func (stmt *mysqlStmt) Close() error {
-	if stmt.mc == nil || stmt.mc.closed.IsSet() {
+	if stmt.mc == nil || stmt.mc.netConn == nil {
 		// driver.Stmt.Close can be called more than once, thus this function
 		// has to be idempotent.
 		// See also Issue #450 and golang/go#16019.
@@ -45,14 +45,14 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
 }
 
 func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
-	if stmt.mc.closed.IsSet() {
+	if stmt.mc.netConn == nil {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
 	// Send command
 	err := stmt.writeExecutePacket(args)
 	if err != nil {
-		return nil, stmt.mc.markBadConn(err)
+		return nil, err
 	}
 
 	mc := stmt.mc
@@ -62,45 +62,37 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
 
 	// Read Result
 	resLen, err := mc.readResultSetHeaderPacket()
-	if err != nil {
-		return nil, err
-	}
-
-	if resLen > 0 {
-		// Columns
-		if err = mc.readUntilEOF(); err != nil {
-			return nil, err
+	if err == nil {
+		if resLen > 0 {
+			// Columns
+			err = mc.readUntilEOF()
+			if err != nil {
+				return nil, err
+			}
+
+			// Rows
+			err = mc.readUntilEOF()
 		}
-
-		// Rows
-		if err := mc.readUntilEOF(); err != nil {
-			return nil, err
+		if err == nil {
+			return &mysqlResult{
+				affectedRows: int64(mc.affectedRows),
+				insertId:     int64(mc.insertId),
+			}, nil
 		}
 	}
 
-	if err := mc.discardResults(); err != nil {
-		return nil, err
-	}
-
-	return &mysqlResult{
-		affectedRows: int64(mc.affectedRows),
-		insertId:     int64(mc.insertId),
-	}, nil
+	return nil, err
 }
 
 func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
-	return stmt.query(args)
-}
-
-func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
-	if stmt.mc.closed.IsSet() {
+	if stmt.mc.netConn == nil {
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 	}
 	// Send command
 	err := stmt.writeExecutePacket(args)
 	if err != nil {
-		return nil, stmt.mc.markBadConn(err)
+		return nil, err
 	}
 
 	mc := stmt.mc
@@ -115,15 +107,14 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
 
 	if resLen > 0 {
 		rows.mc = mc
-		rows.rs.columns, err = mc.readColumns(resLen)
-	} else {
-		rows.rs.done = true
-
-		switch err := rows.NextResultSet(); err {
-		case nil, io.EOF:
-			return rows, nil
-		default:
-			return nil, err
+		// Columns
+		// If not cached, read them and cache them
+		if stmt.columns == nil {
+			rows.columns, err = mc.readColumns(resLen)
+			stmt.columns = rows.columns
+		} else {
+			rows.columns = stmt.columns
+			err = mc.readUntilEOF()
 		}
 	}
 
@@ -137,12 +128,6 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
 		return v, nil
 	}
 
-	if v != nil {
-		if valuer, ok := v.(driver.Valuer); ok {
-			return valuer.Value()
-		}
-	}
-
 	rv := reflect.ValueOf(v)
 	switch rv.Kind() {
 	case reflect.Ptr:
@@ -163,16 +148,6 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
 		return int64(u64), nil
 	case reflect.Float32, reflect.Float64:
 		return rv.Float(), nil
-	case reflect.Bool:
-		return rv.Bool(), nil
-	case reflect.Slice:
-		ek := rv.Type().Elem().Kind()
-		if ek == reflect.Uint8 {
-			return rv.Bytes(), nil
-		}
-		return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek)
-	case reflect.String:
-		return rv.String(), nil
 	}
 	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
 }

+ 2 - 2
vendor/github.com/go-sql-driver/mysql/transaction.go

@@ -13,7 +13,7 @@ type mysqlTx struct {
 }
 
 func (tx *mysqlTx) Commit() (err error) {
-	if tx.mc == nil || tx.mc.closed.IsSet() {
+	if tx.mc == nil || tx.mc.netConn == nil {
 		return ErrInvalidConn
 	}
 	err = tx.mc.exec("COMMIT")
@@ -22,7 +22,7 @@ func (tx *mysqlTx) Commit() (err error) {
 }
 
 func (tx *mysqlTx) Rollback() (err error) {
-	if tx.mc == nil || tx.mc.closed.IsSet() {
+	if tx.mc == nil || tx.mc.netConn == nil {
 		return ErrInvalidConn
 	}
 	err = tx.mc.exec("ROLLBACK")

+ 1 - 83
vendor/github.com/go-sql-driver/mysql/utils.go

@@ -16,21 +16,16 @@ import (
 	"fmt"
 	"io"
 	"strings"
-	"sync"
-	"sync/atomic"
 	"time"
 )
 
 var (
-	tlsConfigLock     sync.RWMutex
 	tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
 )
 
 // RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
 // Use the key as a value in the DSN where tls=value.
 //
-// Note: The tls.Config provided to needs to be exclusively owned by the driver after registering.
-//
 //  rootCertPool := x509.NewCertPool()
 //  pem, err := ioutil.ReadFile("/path/ca-cert.pem")
 //  if err != nil {
@@ -56,32 +51,19 @@ func RegisterTLSConfig(key string, config *tls.Config) error {
 		return fmt.Errorf("key '%s' is reserved", key)
 	}
 
-	tlsConfigLock.Lock()
 	if tlsConfigRegister == nil {
 		tlsConfigRegister = make(map[string]*tls.Config)
 	}
 
 	tlsConfigRegister[key] = config
-	tlsConfigLock.Unlock()
 	return nil
 }
 
 // DeregisterTLSConfig removes the tls.Config associated with key.
 func DeregisterTLSConfig(key string) {
-	tlsConfigLock.Lock()
 	if tlsConfigRegister != nil {
 		delete(tlsConfigRegister, key)
 	}
-	tlsConfigLock.Unlock()
-}
-
-func getTLSConfigClone(key string) (config *tls.Config) {
-	tlsConfigLock.RLock()
-	if v, ok := tlsConfigRegister[key]; ok {
-		config = cloneTLSConfig(v)
-	}
-	tlsConfigLock.RUnlock()
-	return
 }
 
 // Returns the bool value of the input.
@@ -566,8 +548,8 @@ func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
 	if len(b) == 0 {
 		return 0, true, 1
 	}
-
 	switch b[0] {
+
 	// 251: NULL
 	case 0xfb:
 		return 0, true, 1
@@ -756,67 +738,3 @@ func escapeStringQuotes(buf []byte, v string) []byte {
 
 	return buf[:pos]
 }
-
-/******************************************************************************
-*                               Sync utils                                    *
-******************************************************************************/
-
-// noCopy may be embedded into structs which must not be copied
-// after the first use.
-//
-// See https://github.com/golang/go/issues/8005#issuecomment-190753527
-// for details.
-type noCopy struct{}
-
-// Lock is a no-op used by -copylocks checker from `go vet`.
-func (*noCopy) Lock() {}
-
-// atomicBool is a wrapper around uint32 for usage as a boolean value with
-// atomic access.
-type atomicBool struct {
-	_noCopy noCopy
-	value   uint32
-}
-
-// IsSet returns wether the current boolean value is true
-func (ab *atomicBool) IsSet() bool {
-	return atomic.LoadUint32(&ab.value) > 0
-}
-
-// Set sets the value of the bool regardless of the previous value
-func (ab *atomicBool) Set(value bool) {
-	if value {
-		atomic.StoreUint32(&ab.value, 1)
-	} else {
-		atomic.StoreUint32(&ab.value, 0)
-	}
-}
-
-// TrySet sets the value of the bool and returns wether the value changed
-func (ab *atomicBool) TrySet(value bool) bool {
-	if value {
-		return atomic.SwapUint32(&ab.value, 1) == 0
-	}
-	return atomic.SwapUint32(&ab.value, 0) > 0
-}
-
-// atomicBool is a wrapper for atomically accessed error values
-type atomicError struct {
-	_noCopy noCopy
-	value   atomic.Value
-}
-
-// Set sets the error value regardless of the previous value.
-// The value must not be nil
-func (ae *atomicError) Set(value error) {
-	ae.value.Store(value)
-}
-
-// Value returns the current error value
-func (ae *atomicError) Value() error {
-	if v := ae.value.Load(); v != nil {
-		// this will panic if the value doesn't implement the error interface
-		return v.(error)
-	}
-	return nil
-}

+ 0 - 40
vendor/github.com/go-sql-driver/mysql/utils_go17.go

@@ -1,40 +0,0 @@
-// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
-//
-// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-// +build go1.7
-// +build !go1.8
-
-package mysql
-
-import "crypto/tls"
-
-func cloneTLSConfig(c *tls.Config) *tls.Config {
-	return &tls.Config{
-		Rand:                        c.Rand,
-		Time:                        c.Time,
-		Certificates:                c.Certificates,
-		NameToCertificate:           c.NameToCertificate,
-		GetCertificate:              c.GetCertificate,
-		RootCAs:                     c.RootCAs,
-		NextProtos:                  c.NextProtos,
-		ServerName:                  c.ServerName,
-		ClientAuth:                  c.ClientAuth,
-		ClientCAs:                   c.ClientCAs,
-		InsecureSkipVerify:          c.InsecureSkipVerify,
-		CipherSuites:                c.CipherSuites,
-		PreferServerCipherSuites:    c.PreferServerCipherSuites,
-		SessionTicketsDisabled:      c.SessionTicketsDisabled,
-		SessionTicketKey:            c.SessionTicketKey,
-		ClientSessionCache:          c.ClientSessionCache,
-		MinVersion:                  c.MinVersion,
-		MaxVersion:                  c.MaxVersion,
-		CurvePreferences:            c.CurvePreferences,
-		DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
-		Renegotiation:               c.Renegotiation,
-	}
-}

+ 0 - 49
vendor/github.com/go-sql-driver/mysql/utils_go18.go

@@ -1,49 +0,0 @@
-// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
-//
-// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-// +build go1.8
-
-package mysql
-
-import (
-	"crypto/tls"
-	"database/sql"
-	"database/sql/driver"
-	"errors"
-)
-
-func cloneTLSConfig(c *tls.Config) *tls.Config {
-	return c.Clone()
-}
-
-func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
-	dargs := make([]driver.Value, len(named))
-	for n, param := range named {
-		if len(param.Name) > 0 {
-			// TODO: support the use of Named Parameters #561
-			return nil, errors.New("mysql: driver does not support the use of Named Parameters")
-		}
-		dargs[n] = param.Value
-	}
-	return dargs, nil
-}
-
-func mapIsolationLevel(level driver.IsolationLevel) (string, error) {
-	switch sql.IsolationLevel(level) {
-	case sql.LevelRepeatableRead:
-		return "REPEATABLE READ", nil
-	case sql.LevelReadCommitted:
-		return "READ COMMITTED", nil
-	case sql.LevelReadUncommitted:
-		return "READ UNCOMMITTED", nil
-	case sql.LevelSerializable:
-		return "SERIALIZABLE", nil
-	default:
-		return "", errors.New("mysql: unsupported isolation level: " + string(level))
-	}
-}

+ 1 - 11
vendor/github.com/gorilla/handlers/cors.go

@@ -110,17 +110,7 @@ func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set(corsVaryHeader, corsOriginHeader)
 	}
 
-	returnOrigin := origin
-	for _, o := range ch.allowedOrigins {
-		// A configuration of * is different than explicitly setting an allowed
-		// origin. Returning arbitrary origin headers an an access control allow
-		// origin header is unsafe and is not required by any use case.
-		if o == corsOriginMatchAll {
-			returnOrigin = "*"
-			break
-		}
-	}
-	w.Header().Set(corsAllowOriginHeader, returnOrigin)
+	w.Header().Set(corsAllowOriginHeader, origin)
 
 	if r.Method == corsOptionMethod {
 		return

+ 3 - 4
vendor/github.com/gorilla/mux/.travis.yml

@@ -3,13 +3,12 @@ sudo: false
 
 matrix:
   include:
+    - go: 1.2
+    - go: 1.3
+    - go: 1.4
     - go: 1.5
     - go: 1.6
     - go: 1.7
-    - go: 1.8
-    - go: 1.9
-    - go: tip
-  allow_failures:
     - go: tip
 
 install:

+ 6 - 122
vendor/github.com/gorilla/mux/README.md

@@ -2,7 +2,6 @@ gorilla/mux
 ===
 [![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
 [![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
-[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge)
 
 ![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
 
@@ -15,7 +14,7 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv
 
 * It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
 * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
-* URL hosts, paths and query values can have variables with an optional regular expression.
+* URL hosts and paths can have variables with an optional regular expression.
 * Registered URLs can be built, or "reversed", which helps maintaining references to resources.
 * Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching.
 
@@ -26,7 +25,6 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv
 * [Matching Routes](#matching-routes)
 * [Static Files](#static-files)
 * [Registered URLs](#registered-urls)
-* [Walking Routes](#walking-routes)
 * [Full Example](#full-example)
 
 ---
@@ -67,11 +65,8 @@ r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
 The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:
 
 ```go
-func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) {
-	vars := mux.Vars(r)
-	w.WriteHeader(http.StatusOK)
-	fmt.Fprintf(w, "Category: %v\n", vars["category"])
-}
+vars := mux.Vars(request)
+category := vars["category"]
 ```
 
 And this is all you need to know about the basic usage. More advanced options are explained below.
@@ -135,14 +130,6 @@ r.HandleFunc("/products", ProductsHandler).
   Schemes("http")
 ```
 
-Routes are tested in the order they were added to the router. If two routes match, the first one wins:
-
-```go
-r := mux.NewRouter()
-r.HandleFunc("/specific", specificHandler)
-r.PathPrefix("/").Handler(catchAllHandler)
-```
-
 Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting".
 
 For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it:
@@ -176,64 +163,6 @@ s.HandleFunc("/{key}/", ProductHandler)
 // "/products/{key}/details"
 s.HandleFunc("/{key}/details", ProductDetailsHandler)
 ```
-### Listing Routes
-
-Routes on a mux can be listed using the Router.Walk method—useful for generating documentation:
-
-```go
-package main
-
-import (
-    "fmt"
-    "net/http"
-    "strings"
-
-    "github.com/gorilla/mux"
-)
-
-func handler(w http.ResponseWriter, r *http.Request) {
-    return
-}
-
-func main() {
-    r := mux.NewRouter()
-    r.HandleFunc("/", handler)
-    r.HandleFunc("/products", handler).Methods("POST")
-    r.HandleFunc("/articles", handler).Methods("GET")
-    r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT")
-    r.HandleFunc("/authors", handler).Queries("surname", "{surname}")
-    r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
-        t, err := route.GetPathTemplate()
-        if err != nil {
-            return err
-        }
-        qt, err := route.GetQueriesTemplates()
-        if err != nil {
-            return err
-        }
-        // p will contain regular expression is compatible with regular expression in Perl, Python, and other languages.
-        // for instance the regular expression for path '/articles/{id}' will be '^/articles/(?P<v0>[^/]+)$'
-        p, err := route.GetPathRegexp()
-        if err != nil {
-            return err
-        }
-        // qr will contain a list of regular expressions with the same semantics as GetPathRegexp,
-        // just applied to the Queries pairs instead, e.g., 'Queries("surname", "{surname}") will return
-        // {"^surname=(?P<v0>.*)$}. Where each combined query pair will have an entry in the list.
-        qr, err := route.GetQueriesRegexp()
-        if err != nil {
-            return err
-        }
-        m, err := route.GetMethods()
-        if err != nil {
-            return err
-        }
-        fmt.Println(strings.Join(m, ","), strings.Join(qt, ","), strings.Join(qr, ","), t, p)
-        return nil
-    })
-    http.Handle("/", r)
-}
-```
 
 ### Static Files
 
@@ -288,21 +217,19 @@ url, err := r.Get("article").URL("category", "technology", "id", "42")
 "/articles/technology/42"
 ```
 
-This also works for host and query value variables:
+This also works for host variables:
 
 ```go
 r := mux.NewRouter()
 r.Host("{subdomain}.domain.com").
   Path("/articles/{category}/{id:[0-9]+}").
-  Queries("filter", "{filter}").
   HandlerFunc(ArticleHandler).
   Name("article")
 
-// url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla"
+// url.String() will be "http://news.domain.com/articles/technology/42"
 url, err := r.Get("article").URL("subdomain", "news",
                                  "category", "technology",
-                                 "id", "42",
-                                 "filter", "gorilla")
+                                 "id", "42")
 ```
 
 All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match.
@@ -340,49 +267,6 @@ url, err := r.Get("article").URL("subdomain", "news",
                                  "id", "42")
 ```
 
-### Walking Routes
-
-The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example,
-the following prints all of the registered routes:
-
-```go
-r := mux.NewRouter()
-r.HandleFunc("/", handler)
-r.HandleFunc("/products", handler).Methods("POST")
-r.HandleFunc("/articles", handler).Methods("GET")
-r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT")
-r.HandleFunc("/authors", handler).Queries("surname", "{surname}")
-r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
-    t, err := route.GetPathTemplate()
-    if err != nil {
-        return err
-    }
-    qt, err := route.GetQueriesTemplates()
-    if err != nil {
-        return err
-    }
-    // p will contain a regular expression that is compatible with regular expressions in Perl, Python, and other languages.
-    // For example, the regular expression for path '/articles/{id}' will be '^/articles/(?P<v0>[^/]+)$'.
-    p, err := route.GetPathRegexp()
-    if err != nil {
-        return err
-    }
-    // qr will contain a list of regular expressions with the same semantics as GetPathRegexp,
-    // just applied to the Queries pairs instead, e.g., 'Queries("surname", "{surname}") will return
-    // {"^surname=(?P<v0>.*)$}. Where each combined query pair will have an entry in the list.
-    qr, err := route.GetQueriesRegexp()
-    if err != nil {
-        return err
-    }
-    m, err := route.GetMethods()
-    if err != nil {
-        return err
-    }
-    fmt.Println(strings.Join(m, ","), strings.Join(qt, ","), strings.Join(qr, ","), t, p)
-    return nil
-})
-```
-
 ## Full Example
 
 Here's a complete, runnable example of a small `mux` based server:

+ 5 - 12
vendor/github.com/gorilla/mux/doc.go

@@ -12,8 +12,8 @@ or other conditions. The main features are:
 
 	* Requests can be matched based on URL host, path, path prefix, schemes,
 	  header and query values, HTTP methods or using custom matchers.
-	* URL hosts, paths and query values can have variables with an optional
-	  regular expression.
+	* URL hosts and paths can have variables with an optional regular
+	  expression.
 	* Registered URLs can be built, or "reversed", which helps maintaining
 	  references to resources.
 	* Routes can be used as subrouters: nested routes are only tested if the
@@ -57,11 +57,6 @@ calling mux.Vars():
 	vars := mux.Vars(request)
 	category := vars["category"]
 
-Note that if any capturing groups are present, mux will panic() during parsing. To prevent
-this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to
-"/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably
-when capturing groups were present.
-
 And this is all you need to know about the basic usage. More advanced options
 are explained below.
 
@@ -188,20 +183,18 @@ key/value pairs for the route variables. For the previous route, we would do:
 
 	"/articles/technology/42"
 
-This also works for host and query value variables:
+This also works for host variables:
 
 	r := mux.NewRouter()
 	r.Host("{subdomain}.domain.com").
 	  Path("/articles/{category}/{id:[0-9]+}").
-	  Queries("filter", "{filter}").
 	  HandlerFunc(ArticleHandler).
 	  Name("article")
 
-	// url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla"
+	// url.String() will be "http://news.domain.com/articles/technology/42"
 	url, err := r.Get("article").URL("subdomain", "news",
 	                                 "category", "technology",
-	                                 "id", "42",
-	                                 "filter", "gorilla")
+	                                 "id", "42")
 
 All variables defined in the route are required, and their values must
 conform to the corresponding patterns. These requirements guarantee that a

+ 34 - 63
vendor/github.com/gorilla/mux/mux.go

@@ -10,11 +10,7 @@ import (
 	"net/http"
 	"path"
 	"regexp"
-)
-
-var (
-	ErrMethodMismatch = errors.New("method is not allowed")
-	ErrNotFound       = errors.New("no matching route was found")
+	"strings"
 )
 
 // NewRouter returns a new router instance.
@@ -43,10 +39,6 @@ func NewRouter() *Router {
 type Router struct {
 	// Configurable Handler to be used when no route matches.
 	NotFoundHandler http.Handler
-
-	// Configurable Handler to be used when the request method does not match the route.
-	MethodNotAllowedHandler http.Handler
-
 	// Parent route, if this is a subrouter.
 	parent parentRoute
 	// Routes to be matched, in order.
@@ -65,17 +57,7 @@ type Router struct {
 	useEncodedPath bool
 }
 
-// Match attempts to match the given request against the router's registered routes.
-//
-// If the request matches a route of this router or one of its subrouters the Route,
-// Handler, and Vars fields of the the match argument are filled and this function
-// returns true.
-//
-// If the request does not match any of this router's or its subrouters' routes
-// then this function returns false. If available, a reason for the match failure
-// will be filled in the match argument's MatchErr field. If the match failure type
-// (eg: not found) has a registered handler, the handler is assigned to the Handler
-// field of the match argument.
+// Match matches registered routes against the request.
 func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
 	for _, route := range r.routes {
 		if route.Match(req, match) {
@@ -83,23 +65,11 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
 		}
 	}
 
-	if match.MatchErr == ErrMethodMismatch {
-		if r.MethodNotAllowedHandler != nil {
-			match.Handler = r.MethodNotAllowedHandler
-			return true
-		} else {
-			return false
-		}
-	}
-
 	// Closest match for a router (includes sub-routers)
 	if r.NotFoundHandler != nil {
 		match.Handler = r.NotFoundHandler
-		match.MatchErr = ErrNotFound
 		return true
 	}
-
-	match.MatchErr = ErrNotFound
 	return false
 }
 
@@ -111,7 +81,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 	if !r.skipClean {
 		path := req.URL.Path
 		if r.useEncodedPath {
-			path = req.URL.EscapedPath()
+			path = getPath(req)
 		}
 		// Clean path to canonical form and redirect.
 		if p := cleanPath(path); p != path {
@@ -135,15 +105,9 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 		req = setVars(req, match.Vars)
 		req = setCurrentRoute(req, match.Route)
 	}
-
-	if handler == nil && match.MatchErr == ErrMethodMismatch {
-		handler = methodNotAllowedHandler()
-	}
-
 	if handler == nil {
 		handler = http.NotFoundHandler()
 	}
-
 	if !r.KeepContext {
 		defer contextClear(req)
 	}
@@ -196,6 +160,10 @@ func (r *Router) SkipClean(value bool) *Router {
 // UseEncodedPath tells the router to match the encoded original path
 // to the routes.
 // For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".
+// This behavior has the drawback of needing to match routes against
+// r.RequestURI instead of r.URL.Path. Any modifications (such as http.StripPrefix)
+// to r.URL.Path will not affect routing when this flag is on and thus may
+// induce unintended behavior.
 //
 // If not called, the router will match the unencoded path to the routes.
 // For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to"
@@ -208,13 +176,6 @@ func (r *Router) UseEncodedPath() *Router {
 // parentRoute
 // ----------------------------------------------------------------------------
 
-func (r *Router) getBuildScheme() string {
-	if r.parent != nil {
-		return r.parent.getBuildScheme()
-	}
-	return ""
-}
-
 // getNamedRoutes returns the map where named routes are registered.
 func (r *Router) getNamedRoutes() map[string]*Route {
 	if r.namedRoutes == nil {
@@ -338,6 +299,10 @@ type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
 
 func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
 	for _, t := range r.routes {
+		if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" {
+			continue
+		}
+
 		err := walkFn(t, r, ancestors)
 		if err == SkipRouter {
 			continue
@@ -347,12 +312,10 @@ func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
 		}
 		for _, sr := range t.matchers {
 			if h, ok := sr.(*Router); ok {
-				ancestors = append(ancestors, t)
 				err := h.walk(walkFn, ancestors)
 				if err != nil {
 					return err
 				}
-				ancestors = ancestors[:len(ancestors)-1]
 			}
 		}
 		if h, ok := t.handler.(*Router); ok {
@@ -376,11 +339,6 @@ type RouteMatch struct {
 	Route   *Route
 	Handler http.Handler
 	Vars    map[string]string
-
-	// MatchErr is set to appropriate matching error
-	// It is set to ErrMethodMismatch if there is a mismatch in
-	// the request method and route method
-	MatchErr error
 }
 
 type contextKey int
@@ -422,6 +380,28 @@ func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
 // Helpers
 // ----------------------------------------------------------------------------
 
+// getPath returns the escaped path if possible; doing what URL.EscapedPath()
+// which was added in go1.5 does
+func getPath(req *http.Request) string {
+	if req.RequestURI != "" {
+		// Extract the path from RequestURI (which is escaped unlike URL.Path)
+		// as detailed here as detailed in https://golang.org/pkg/net/url/#URL
+		// for < 1.5 server side workaround
+		// http://localhost/path/here?v=1 -> /path/here
+		path := req.RequestURI
+		path = strings.TrimPrefix(path, req.URL.Scheme+`://`)
+		path = strings.TrimPrefix(path, req.URL.Host)
+		if i := strings.LastIndex(path, "?"); i > -1 {
+			path = path[:i]
+		}
+		if i := strings.LastIndex(path, "#"); i > -1 {
+			path = path[:i]
+		}
+		return path
+	}
+	return req.URL.Path
+}
+
 // cleanPath returns the canonical path for p, eliminating . and .. elements.
 // Borrowed from the net/http package.
 func cleanPath(p string) string {
@@ -478,7 +458,7 @@ func mapFromPairsToString(pairs ...string) (map[string]string, error) {
 	return m, nil
 }
 
-// mapFromPairsToRegex converts variadic string parameters to a
+// mapFromPairsToRegex converts variadic string paramers to a
 // string to regex map.
 func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
 	length, err := checkPairs(pairs...)
@@ -560,12 +540,3 @@ func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]s
 	}
 	return true
 }
-
-// methodNotAllowed replies to the request with an HTTP status code 405.
-func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
-	w.WriteHeader(http.StatusMethodNotAllowed)
-}
-
-// methodNotAllowedHandler returns a simple request handler
-// that replies to each request with a status code 405.
-func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) }

+ 4 - 14
vendor/github.com/gorilla/mux/regexp.go

@@ -35,7 +35,7 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
 	// Now let's parse it.
 	defaultPattern := "[^/]+"
 	if matchQuery {
-		defaultPattern = ".*"
+		defaultPattern = "[^?&]*"
 	} else if matchHost {
 		defaultPattern = "[^.]+"
 		matchPrefix = false
@@ -109,13 +109,6 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
 	if errCompile != nil {
 		return nil, errCompile
 	}
-
-	// Check for capturing groups which used to work in older versions
-	if reg.NumSubexp() != len(idxs)/2 {
-		panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) +
-			"Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)")
-	}
-
 	// Done!
 	return &routeRegexp{
 		template:       template,
@@ -141,7 +134,7 @@ type routeRegexp struct {
 	matchQuery bool
 	// The strictSlash value defined on the route, but disabled if PathPrefix was used.
 	strictSlash bool
-	// Determines whether to use encoded req.URL.EnscapedPath() or unencoded
+	// Determines whether to use encoded path from getPath function or unencoded
 	// req.URL.Path for path matching
 	useEncodedPath bool
 	// Expanded regexp.
@@ -162,7 +155,7 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
 		}
 		path := req.URL.Path
 		if r.useEncodedPath {
-			path = req.URL.EscapedPath()
+			path = getPath(req)
 		}
 		return r.regexp.MatchString(path)
 	}
@@ -178,9 +171,6 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
 		if !ok {
 			return "", fmt.Errorf("mux: missing route variable %q", v)
 		}
-		if r.matchQuery {
-			value = url.QueryEscape(value)
-		}
 		urlValues[k] = value
 	}
 	rv := fmt.Sprintf(r.reverse, urlValues...)
@@ -272,7 +262,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
 	}
 	path := req.URL.Path
 	if r.useEncodedPath {
-		path = req.URL.EscapedPath()
+		path = getPath(req)
 	}
 	// Store path variables.
 	if v.path != nil {

+ 8 - 127
vendor/github.com/gorilla/mux/route.go

@@ -31,8 +31,6 @@ type Route struct {
 	skipClean bool
 	// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
 	useEncodedPath bool
-	// The scheme used when building URLs.
-	buildScheme string
 	// If true, this route never matches: it is only used to build URLs.
 	buildOnly bool
 	// The name used to build URLs.
@@ -52,31 +50,12 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
 	if r.buildOnly || r.err != nil {
 		return false
 	}
-
-	var matchErr error
-
 	// Match everything.
 	for _, m := range r.matchers {
 		if matched := m.Match(req, match); !matched {
-			if _, ok := m.(methodMatcher); ok {
-				matchErr = ErrMethodMismatch
-				continue
-			}
-			matchErr = nil
 			return false
 		}
 	}
-
-	if matchErr != nil {
-		match.MatchErr = matchErr
-		return false
-	}
-
-	if match.MatchErr == ErrMethodMismatch {
-		// We found a route which matches request method, clear MatchErr
-		match.MatchErr = nil
-	}
-
 	// Yay, we have a match. Let's collect some info about it.
 	if match.Route == nil {
 		match.Route = r
@@ -87,7 +66,6 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
 	if match.Vars == nil {
 		match.Vars = make(map[string]string)
 	}
-
 	// Set variables.
 	if r.regexp != nil {
 		r.regexp.setMatch(req, match, r)
@@ -175,7 +153,7 @@ func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery
 	}
 	r.regexp = r.getRegexpGroup()
 	if !matchHost && !matchQuery {
-		if len(tpl) > 0 && tpl[0] != '/' {
+		if len(tpl) == 0 || tpl[0] != '/' {
 			return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
 		}
 		if r.regexp.path != nil {
@@ -416,9 +394,6 @@ func (r *Route) Schemes(schemes ...string) *Route {
 	for k, v := range schemes {
 		schemes[k] = strings.ToLower(v)
 	}
-	if r.buildScheme == "" && len(schemes) > 0 {
-		r.buildScheme = schemes[0]
-	}
 	return r.addMatcher(schemeMatcher(schemes))
 }
 
@@ -502,33 +477,22 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) {
 		return nil, err
 	}
 	var scheme, host, path string
-	queries := make([]string, 0, len(r.regexp.queries))
 	if r.regexp.host != nil {
+		// Set a default scheme.
+		scheme = "http"
 		if host, err = r.regexp.host.url(values); err != nil {
 			return nil, err
 		}
-		scheme = "http"
-		if s := r.getBuildScheme(); s != "" {
-			scheme = s
-		}
 	}
 	if r.regexp.path != nil {
 		if path, err = r.regexp.path.url(values); err != nil {
 			return nil, err
 		}
 	}
-	for _, q := range r.regexp.queries {
-		var query string
-		if query, err = q.url(values); err != nil {
-			return nil, err
-		}
-		queries = append(queries, query)
-	}
 	return &url.URL{
-		Scheme:   scheme,
-		Host:     host,
-		Path:     path,
-		RawQuery: strings.Join(queries, "&"),
+		Scheme: scheme,
+		Host:   host,
+		Path:   path,
 	}, nil
 }
 
@@ -550,14 +514,10 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
 	if err != nil {
 		return nil, err
 	}
-	u := &url.URL{
+	return &url.URL{
 		Scheme: "http",
 		Host:   host,
-	}
-	if s := r.getBuildScheme(); s != "" {
-		u.Scheme = s
-	}
-	return u, nil
+	}, nil
 }
 
 // URLPath builds the path part of the URL for a route. See Route.URL().
@@ -598,74 +558,6 @@ func (r *Route) GetPathTemplate() (string, error) {
 	return r.regexp.path.template, nil
 }
 
-// GetPathRegexp returns the expanded regular expression used to match route path.
-// This is useful for building simple REST API documentation and for instrumentation
-// against third-party services.
-// An error will be returned if the route does not define a path.
-func (r *Route) GetPathRegexp() (string, error) {
-	if r.err != nil {
-		return "", r.err
-	}
-	if r.regexp == nil || r.regexp.path == nil {
-		return "", errors.New("mux: route does not have a path")
-	}
-	return r.regexp.path.regexp.String(), nil
-}
-
-// GetQueriesRegexp returns the expanded regular expressions used to match the
-// route queries.
-// This is useful for building simple REST API documentation and for instrumentation
-// against third-party services.
-// An empty list will be returned if the route does not have queries.
-func (r *Route) GetQueriesRegexp() ([]string, error) {
-	if r.err != nil {
-		return nil, r.err
-	}
-	if r.regexp == nil || r.regexp.queries == nil {
-		return nil, errors.New("mux: route doesn't have queries")
-	}
-	var queries []string
-	for _, query := range r.regexp.queries {
-		queries = append(queries, query.regexp.String())
-	}
-	return queries, nil
-}
-
-// GetQueriesTemplates returns the templates used to build the
-// query matching.
-// This is useful for building simple REST API documentation and for instrumentation
-// against third-party services.
-// An empty list will be returned if the route does not define queries.
-func (r *Route) GetQueriesTemplates() ([]string, error) {
-	if r.err != nil {
-		return nil, r.err
-	}
-	if r.regexp == nil || r.regexp.queries == nil {
-		return nil, errors.New("mux: route doesn't have queries")
-	}
-	var queries []string
-	for _, query := range r.regexp.queries {
-		queries = append(queries, query.template)
-	}
-	return queries, nil
-}
-
-// GetMethods returns the methods the route matches against
-// This is useful for building simple REST API documentation and for instrumentation
-// against third-party services.
-// An empty list will be returned if route does not have methods.
-func (r *Route) GetMethods() ([]string, error) {
-	if r.err != nil {
-		return nil, r.err
-	}
-	for _, m := range r.matchers {
-		if methods, ok := m.(methodMatcher); ok {
-			return []string(methods), nil
-		}
-	}
-	return nil, nil
-}
-
 // GetHostTemplate returns the template used to build the
 // route match.
 // This is useful for building simple REST API documentation and for instrumentation
@@ -707,22 +599,11 @@ func (r *Route) buildVars(m map[string]string) map[string]string {
 
 // parentRoute allows routes to know about parent host and path definitions.
 type parentRoute interface {
-	getBuildScheme() string
 	getNamedRoutes() map[string]*Route
 	getRegexpGroup() *routeRegexpGroup
 	buildVars(map[string]string) map[string]string
 }
 
-func (r *Route) getBuildScheme() string {
-	if r.buildScheme != "" {
-		return r.buildScheme
-	}
-	if r.parent != nil {
-		return r.parent.getBuildScheme()
-	}
-	return ""
-}
-
 // getNamedRoutes returns the map where named routes are registered.
 func (r *Route) getNamedRoutes() map[string]*Route {
 	if r.parent == nil {

+ 2 - 1
vendor/github.com/gorilla/schema/.travis.yml

@@ -3,10 +3,11 @@ sudo: false
 
 matrix:
   include:
+    - go: 1.3
+    - go: 1.4
     - go: 1.5
     - go: 1.6
     - go: 1.7
-    - go: 1.8
     - go: tip
   allow_failures:
     - go: tip

+ 10 - 34
vendor/github.com/gorilla/schema/README.md

@@ -1,17 +1,15 @@
 schema
 ======
 [![GoDoc](https://godoc.org/github.com/gorilla/schema?status.svg)](https://godoc.org/github.com/gorilla/schema) [![Build Status](https://travis-ci.org/gorilla/schema.png?branch=master)](https://travis-ci.org/gorilla/schema)
-[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/schema/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/schema?badge)
 
-
-Package gorilla/schema converts structs to and from form values.
+Package gorilla/schema fills a struct with form values.
 
 ## Example
 
 Here's a quick example: we parse POST form values and then decode them into a struct:
 
 ```go
-// Set a Decoder instance as a package global, because it caches
+// Set a Decoder instance as a package global, because it caches 
 // meta-data about structs, and an instance can be shared safely.
 var decoder = schema.NewDecoder()
 
@@ -27,9 +25,9 @@ func MyHandler(w http.ResponseWriter, r *http.Request) {
     }
 
     var person Person
-
+    
     // r.PostForm is a map of our POST form values
-    err = decoder.Decode(&person, r.PostForm)
+    err := decoder.Decode(&person, r.PostForm)
     if err != nil {
         // Handle error
     }
@@ -38,39 +36,17 @@ func MyHandler(w http.ResponseWriter, r *http.Request) {
 }
 ```
 
-Conversely, contents of a struct can be encoded into form values. Here's a variant of the previous example using the Encoder:
-
-```go
-var encoder = schema.NewEncoder()
-
-func MyHttpRequest() {
-    person := Person{"Jane Doe", "555-5555"}
-    form := url.Values{}
-
-    err := encoder.Encode(person, form)
-
-    if err != nil {
-        // Handle error
-    }
-
-    // Use form values, for example, with an http client
-    client := new(http.Client)
-    res, err := client.PostForm("http://my-api.test", form)
-}
-
-```
-
 To define custom names for fields, use a struct tag "schema". To not populate certain fields, use a dash for the name and it will be ignored:
 
 ```go
 type Person struct {
-    Name  string `schema:"name,required"`  // custom name, must be supplied
-    Phone string `schema:"phone"`          // custom name
-    Admin bool   `schema:"-"`              // this field is never set
+    Name  string `schema:"name"`  // custom name
+    Phone string `schema:"phone"` // custom name
+    Admin bool   `schema:"-"`     // this field is never set
 }
 ```
 
-The supported field types in the struct are:
+The supported field types in the destination struct are:
 
 * bool
 * float variants (float32, float64)
@@ -83,8 +59,8 @@ The supported field types in the struct are:
 
 Unsupported types are simply ignored, however custom types can be registered to be converted.
 
-More examples are available on the Gorilla website: https://www.gorillatoolkit.org/pkg/schema
+More examples are available on the Gorilla website: http://www.gorillatoolkit.org/pkg/schema
 
-## License
+## License 
 
 BSD licensed. See the LICENSE file for details.

+ 22 - 33
vendor/github.com/gorilla/schema/cache.go

@@ -18,9 +18,13 @@ var invalidPath = errors.New("schema: invalid path")
 func newCache() *cache {
 	c := cache{
 		m:       make(map[reflect.Type]*structInfo),
+		conv:    make(map[reflect.Kind]Converter),
 		regconv: make(map[reflect.Type]Converter),
 		tag:     "schema",
 	}
+	for k, v := range converters {
+		c.conv[k] = v
+	}
 	return &c
 }
 
@@ -28,15 +32,11 @@ func newCache() *cache {
 type cache struct {
 	l       sync.RWMutex
 	m       map[reflect.Type]*structInfo
+	conv    map[reflect.Kind]Converter
 	regconv map[reflect.Type]Converter
 	tag     string
 }
 
-// registerConverter registers a converter function for a custom type.
-func (c *cache) registerConverter(value interface{}, converterFunc Converter) {
-	c.regconv[reflect.TypeOf(value)] = converterFunc
-}
-
 // parsePath parses a path in dotted notation verifying that it is a valid
 // path to a struct field.
 //
@@ -63,7 +63,7 @@ func (c *cache) parsePath(p string, t reflect.Type) ([]pathPart, error) {
 		}
 		// Valid field. Append index.
 		path = append(path, field.name)
-		if field.isSliceOfStructs && (!field.unmarshalerInfo.IsValid || (field.unmarshalerInfo.IsValid && field.unmarshalerInfo.IsSliceElement)) {
+		if field.ss {
 			// Parse a special case: slices of structs.
 			// i+1 must be the slice index.
 			//
@@ -138,12 +138,7 @@ func (c *cache) create(t reflect.Type, info *structInfo) *structInfo {
 				ft = ft.Elem()
 			}
 			if ft.Kind() == reflect.Struct {
-				bef := len(info.fields)
 				c.create(ft, info)
-				for _, fi := range info.fields[bef:len(info.fields)] {
-					// exclude required check because duplicated to embedded field
-					fi.isRequired = false
-				}
 			}
 		}
 		c.createField(field, info)
@@ -162,7 +157,6 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
 	// First let's get the basic type.
 	isSlice, isStruct := false, false
 	ft := field.Type
-	m := isTextUnmarshaler(reflect.Zero(ft))
 	if ft.Kind() == reflect.Ptr {
 		ft = ft.Elem()
 	}
@@ -179,26 +173,28 @@ func (c *cache) createField(field reflect.StructField, info *structInfo) {
 		}
 	}
 	if isStruct = ft.Kind() == reflect.Struct; !isStruct {
-		if c.converter(ft) == nil && builtinConverters[ft.Kind()] == nil {
+		if conv := c.conv[ft.Kind()]; conv == nil {
 			// Type is not supported.
 			return
 		}
 	}
 
 	info.fields = append(info.fields, &fieldInfo{
-		typ:              field.Type,
-		name:             field.Name,
-		alias:            alias,
-		unmarshalerInfo:  m,
-		isSliceOfStructs: isSlice && isStruct,
-		isAnonymous:      field.Anonymous,
-		isRequired:       options.Contains("required"),
+		typ:      field.Type,
+		name:     field.Name,
+		ss:       isSlice && isStruct,
+		alias:    alias,
+		required: options.Contains("required"),
 	})
 }
 
 // converter returns the converter for a type.
 func (c *cache) converter(t reflect.Type) Converter {
-	return c.regconv[t]
+	conv := c.regconv[t]
+	if conv == nil {
+		conv = c.conv[t.Kind()]
+	}
+	return conv
 }
 
 // ----------------------------------------------------------------------------
@@ -217,18 +213,11 @@ func (i *structInfo) get(alias string) *fieldInfo {
 }
 
 type fieldInfo struct {
-	typ reflect.Type
-	// name is the field name in the struct.
-	name  string
-	alias string
-	// unmarshalerInfo contains information regarding the
-	// encoding.TextUnmarshaler implementation of the field type.
-	unmarshalerInfo unmarshaler
-	// isSliceOfStructs indicates if the field type is a slice of structs.
-	isSliceOfStructs bool
-	// isAnonymous indicates whether the field is embedded in the struct.
-	isAnonymous bool
-	isRequired  bool
+	typ      reflect.Type
+	name     string // field name in the struct.
+	ss       bool   // true if this is a slice of structs.
+	alias    string
+	required bool // tag option
 }
 
 type pathPart struct {

+ 1 - 1
vendor/github.com/gorilla/schema/converter.go

@@ -30,7 +30,7 @@ var (
 )
 
 // Default converters for basic types.
-var builtinConverters = map[reflect.Kind]Converter{
+var converters = map[reflect.Kind]Converter{
 	boolType:    convertBool,
 	float32Type: convertFloat32,
 	float64Type: convertFloat64,

+ 26 - 130
vendor/github.com/gorilla/schema/decoder.go

@@ -56,7 +56,7 @@ func (d *Decoder) IgnoreUnknownKeys(i bool) {
 
 // RegisterConverter registers a converter function for a custom type.
 func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) {
-	d.cache.registerConverter(value, converterFunc)
+	d.cache.regconv[reflect.TypeOf(value)] = converterFunc
 }
 
 // Decode decodes a map[string][]string to a struct.
@@ -90,7 +90,7 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
 	return d.checkRequired(t, src, "")
 }
 
-// checkRequired checks whether required fields are empty
+// checkRequired checks whether requred field empty
 //
 // check type t recursively if t has struct fields, and prefix is same as parsePath: in dotted notation
 //
@@ -106,17 +106,10 @@ func (d *Decoder) checkRequired(t reflect.Type, src map[string][]string, prefix
 		if f.typ.Kind() == reflect.Struct {
 			err := d.checkRequired(f.typ, src, prefix+f.alias+".")
 			if err != nil {
-				if !f.isAnonymous {
-					return err
-				}
-				// check embedded parent field.
-				err2 := d.checkRequired(f.typ, src, prefix)
-				if err2 != nil {
-					return err
-				}
+				return err
 			}
 		}
-		if f.isRequired {
+		if f.required {
 			key := f.alias
 			if prefix != "" {
 				key = prefix + key
@@ -153,6 +146,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
 		}
 		v = v.FieldByName(name)
 	}
+
 	// Don't even bother for unexported fields.
 	if !v.CanSet() {
 		return nil
@@ -184,8 +178,7 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
 
 	// Get the converter early in case there is one for a slice type.
 	conv := d.cache.converter(t)
-	m := isTextUnmarshaler(v)
-	if conv == nil && t.Kind() == reflect.Slice && m.IsSliceElement {
+	if conv == nil && t.Kind() == reflect.Slice {
 		var items []reflect.Value
 		elemT := t.Elem()
 		isPtrElem := elemT.Kind() == reflect.Ptr
@@ -196,12 +189,9 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
 		// Try to get a converter for the element type.
 		conv := d.cache.converter(elemT)
 		if conv == nil {
-			conv = builtinConverters[elemT.Kind()]
-			if conv == nil {
-				// As we are not dealing with slice of structs here, we don't need to check if the type
-				// implements TextUnmarshaler interface
-				return fmt.Errorf("schema: converter not found for %v", elemT)
-			}
+			// As we are not dealing with slice of structs here, we don't need to check if the type
+			// implements TextUnmarshaler interface
+			return fmt.Errorf("schema: converter not found for %v", elemT)
 		}
 
 		for key, value := range values {
@@ -209,26 +199,6 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
 				if d.zeroEmpty {
 					items = append(items, reflect.Zero(elemT))
 				}
-			} else if m.IsValid {
-				u := reflect.New(elemT)
-				if m.IsSliceElementPtr {
-					u = reflect.New(reflect.PtrTo(elemT).Elem())
-				}
-				if err := u.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)); err != nil {
-					return ConversionError{
-						Key:   path,
-						Type:  t,
-						Index: key,
-						Err:   err,
-					}
-				}
-				if m.IsSliceElementPtr {
-					items = append(items, u.Elem().Addr())
-				} else if u.Kind() == reflect.Ptr {
-					items = append(items, u.Elem())
-				} else {
-					items = append(items, u)
-				}
 			} else if item := conv(value); item.IsValid() {
 				if isPtrElem {
 					ptr := reflect.New(elemT)
@@ -283,7 +253,11 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
 			val = values[len(values)-1]
 		}
 
-		if conv != nil {
+		if val == "" {
+			if d.zeroEmpty {
+				v.Set(reflect.Zero(t))
+			}
+		} else if conv != nil {
 			if value := conv(val); value.IsValid() {
 				v.Set(value.Convert(t))
 			} else {
@@ -293,22 +267,16 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
 					Index: -1,
 				}
 			}
-		} else if m.IsValid {
-			if m.IsPtr {
-				u := reflect.New(v.Type())
-				if err := u.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(val)); err != nil {
-					return ConversionError{
-						Key:   path,
-						Type:  t,
-						Index: -1,
-						Err:   err,
-					}
-				}
-				v.Set(reflect.Indirect(u))
-			} else {
-				// If the value implements the encoding.TextUnmarshaler interface
-				// apply UnmarshalText as the converter
-				if err := m.Unmarshaler.UnmarshalText([]byte(val)); err != nil {
+		} else {
+			// When there's no registered conversion for the custom type, we will check if the type
+			// implements the TextUnmarshaler interface. As the UnmarshalText function should be applied
+			// to the pointer of the type, we convert the value to pointer.
+			if v.CanAddr() {
+				v = v.Addr()
+			}
+
+			if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
+				if err := u.UnmarshalText([]byte(val)); err != nil {
 					return ConversionError{
 						Key:   path,
 						Type:  t,
@@ -316,87 +284,15 @@ func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart, values
 						Err:   err,
 					}
 				}
-			}
-		} else if val == "" {
-			if d.zeroEmpty {
-				v.Set(reflect.Zero(t))
-			}
-		} else if conv := builtinConverters[t.Kind()]; conv != nil {
-			if value := conv(val); value.IsValid() {
-				v.Set(value.Convert(t))
+
 			} else {
-				return ConversionError{
-					Key:   path,
-					Type:  t,
-					Index: -1,
-				}
+				return fmt.Errorf("schema: converter not found for %v", t)
 			}
-		} else {
-			return fmt.Errorf("schema: converter not found for %v", t)
 		}
 	}
 	return nil
 }
 
-func isTextUnmarshaler(v reflect.Value) unmarshaler {
-	// Create a new unmarshaller instance
-	m := unmarshaler{}
-	if m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler); m.IsValid {
-		return m
-	}
-	// As the UnmarshalText function should be applied to the pointer of the
-	// type, we check that type to see if it implements the necessary
-	// method.
-	if m.Unmarshaler, m.IsValid = reflect.New(v.Type()).Interface().(encoding.TextUnmarshaler); m.IsValid {
-		m.IsPtr = true
-		return m
-	}
-
-	// if v is []T or *[]T create new T
-	t := v.Type()
-	if t.Kind() == reflect.Ptr {
-		t = t.Elem()
-	}
-	if t.Kind() == reflect.Slice {
-		// Check if the slice implements encoding.TextUnmarshaller
-		if m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler); m.IsValid {
-			return m
-		}
-		// If t is a pointer slice, check if its elements implement
-		// encoding.TextUnmarshaler
-		m.IsSliceElement = true
-		if t = t.Elem(); t.Kind() == reflect.Ptr {
-			t = reflect.PtrTo(t.Elem())
-			v = reflect.Zero(t)
-			m.IsSliceElementPtr = true
-			m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler)
-			return m
-		}
-	}
-
-	v = reflect.New(t)
-	m.Unmarshaler, m.IsValid = v.Interface().(encoding.TextUnmarshaler)
-	return m
-}
-
-// TextUnmarshaler helpers ----------------------------------------------------
-// unmarshaller contains information about a TextUnmarshaler type
-type unmarshaler struct {
-	Unmarshaler encoding.TextUnmarshaler
-	// IsValid indicates whether the resolved type indicated by the other
-	// flags implements the encoding.TextUnmarshaler interface.
-	IsValid bool
-	// IsPtr indicates that the resolved type is the pointer of the original
-	// type.
-	IsPtr bool
-	// IsSliceElement indicates that the resolved type is a slice element of
-	// the original type.
-	IsSliceElement bool
-	// IsSliceElementPtr indicates that the resolved type is a pointer to a
-	// slice element of the original type.
-	IsSliceElementPtr bool
-}
-
 // Errors ---------------------------------------------------------------------
 
 // ConversionError stores information about a failed conversion.

+ 3 - 3
vendor/github.com/gorilla/schema/doc.go

@@ -24,7 +24,7 @@ The basic usage is really simple. Given this struct:
 
 This is just a simple example and it doesn't make a lot of sense to create
 the map manually. Typically it will come from a http.Request object and
-will be of type url.Values, http.Request.Form, or http.Request.MultipartForm:
+will be of type url.Values: http.Request.Form or http.Request.MultipartForm:
 
 	func MyHandler(w http.ResponseWriter, r *http.Request) {
 		err := r.ParseForm()
@@ -45,7 +45,7 @@ will be of type url.Values, http.Request.Form, or http.Request.MultipartForm:
 	}
 
 Note: it is a good idea to set a Decoder instance as a package global,
-because it caches meta-data about structs, and an instance can be shared safely:
+because it caches meta-data about structs, and a instance can be shared safely:
 
 	var decoder = schema.NewDecoder()
 
@@ -121,7 +121,7 @@ field, we could not translate multiple values to it if we did not use an
 index for the parent struct.
 
 There's also the possibility to create a custom type that implements the
-TextUnmarshaler interface, and in this case there's no need to register
+TextUnmarshaler interface, and in this case there's no need to registry
 a converter, like:
 
 	type Person struct {

+ 0 - 195
vendor/github.com/gorilla/schema/encoder.go

@@ -1,195 +0,0 @@
-package schema
-
-import (
-	"errors"
-	"fmt"
-	"reflect"
-	"strconv"
-)
-
-type encoderFunc func(reflect.Value) string
-
-// Encoder encodes values from a struct into url.Values.
-type Encoder struct {
-	cache  *cache
-	regenc map[reflect.Type]encoderFunc
-}
-
-// NewEncoder returns a new Encoder with defaults.
-func NewEncoder() *Encoder {
-	return &Encoder{cache: newCache(), regenc: make(map[reflect.Type]encoderFunc)}
-}
-
-// Encode encodes a struct into map[string][]string.
-//
-// Intended for use with url.Values.
-func (e *Encoder) Encode(src interface{}, dst map[string][]string) error {
-	v := reflect.ValueOf(src)
-
-	return e.encode(v, dst)
-}
-
-// RegisterEncoder registers a converter for encoding a custom type.
-func (e *Encoder) RegisterEncoder(value interface{}, encoder func(reflect.Value) string) {
-	e.regenc[reflect.TypeOf(value)] = encoder
-}
-
-// SetAliasTag changes the tag used to locate custom field aliases.
-// The default tag is "schema".
-func (e *Encoder) SetAliasTag(tag string) {
-	e.cache.tag = tag
-}
-
-// isValidStructPointer test if input value is a valid struct pointer.
-func isValidStructPointer(v reflect.Value) bool {
-	return v.Type().Kind() == reflect.Ptr && v.Elem().IsValid() && v.Elem().Type().Kind() == reflect.Struct
-}
-
-func isZero(v reflect.Value) bool {
-	switch v.Kind() {
-	case reflect.Func:
-	case reflect.Map, reflect.Slice:
-		return v.IsNil() || v.Len() == 0
-	case reflect.Array:
-		z := true
-		for i := 0; i < v.Len(); i++ {
-			z = z && isZero(v.Index(i))
-		}
-		return z
-	case reflect.Struct:
-		z := true
-		for i := 0; i < v.NumField(); i++ {
-			z = z && isZero(v.Field(i))
-		}
-		return z
-	}
-	// Compare other types directly:
-	z := reflect.Zero(v.Type())
-	return v.Interface() == z.Interface()
-}
-
-func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error {
-	if v.Kind() == reflect.Ptr {
-		v = v.Elem()
-	}
-	if v.Kind() != reflect.Struct {
-		return errors.New("schema: interface must be a struct")
-	}
-	t := v.Type()
-
-	errors := MultiError{}
-
-	for i := 0; i < v.NumField(); i++ {
-		name, opts := fieldAlias(t.Field(i), e.cache.tag)
-		if name == "-" {
-			continue
-		}
-
-		// Encode struct pointer types if the field is a valid pointer and a struct.
-		if isValidStructPointer(v.Field(i)) {
-			e.encode(v.Field(i).Elem(), dst)
-			continue
-		}
-
-		encFunc := typeEncoder(v.Field(i).Type(), e.regenc)
-
-		// Encode non-slice types and custom implementations immediately.
-		if encFunc != nil {
-			value := encFunc(v.Field(i))
-			if opts.Contains("omitempty") && isZero(v.Field(i)) {
-				continue
-			}
-
-			dst[name] = append(dst[name], value)
-			continue
-		}
-
-		if v.Field(i).Type().Kind() == reflect.Struct {
-			e.encode(v.Field(i), dst)
-			continue
-		}
-
-		if v.Field(i).Type().Kind() == reflect.Slice {
-			encFunc = typeEncoder(v.Field(i).Type().Elem(), e.regenc)
-		}
-
-		if encFunc == nil {
-			errors[v.Field(i).Type().String()] = fmt.Errorf("schema: encoder not found for %v", v.Field(i))
-			continue
-		}
-
-		// Encode a slice.
-		if v.Field(i).Len() == 0 && opts.Contains("omitempty") {
-			continue
-		}
-
-		dst[name] = []string{}
-		for j := 0; j < v.Field(i).Len(); j++ {
-			dst[name] = append(dst[name], encFunc(v.Field(i).Index(j)))
-		}
-	}
-
-	if len(errors) > 0 {
-		return errors
-	}
-	return nil
-}
-
-func typeEncoder(t reflect.Type, reg map[reflect.Type]encoderFunc) encoderFunc {
-	if f, ok := reg[t]; ok {
-		return f
-	}
-
-	switch t.Kind() {
-	case reflect.Bool:
-		return encodeBool
-	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		return encodeInt
-	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
-		return encodeUint
-	case reflect.Float32:
-		return encodeFloat32
-	case reflect.Float64:
-		return encodeFloat64
-	case reflect.Ptr:
-		f := typeEncoder(t.Elem(), reg)
-		return func(v reflect.Value) string {
-			if v.IsNil() {
-				return "null"
-			}
-			return f(v.Elem())
-		}
-	case reflect.String:
-		return encodeString
-	default:
-		return nil
-	}
-}
-
-func encodeBool(v reflect.Value) string {
-	return strconv.FormatBool(v.Bool())
-}
-
-func encodeInt(v reflect.Value) string {
-	return strconv.FormatInt(int64(v.Int()), 10)
-}
-
-func encodeUint(v reflect.Value) string {
-	return strconv.FormatUint(uint64(v.Uint()), 10)
-}
-
-func encodeFloat(v reflect.Value, bits int) string {
-	return strconv.FormatFloat(v.Float(), 'f', 6, bits)
-}
-
-func encodeFloat32(v reflect.Value) string {
-	return encodeFloat(v, 32)
-}
-
-func encodeFloat64(v reflect.Value) string {
-	return encodeFloat(v, 64)
-}
-
-func encodeString(v reflect.Value) string {
-	return v.String()
-}

+ 0 - 2
vendor/github.com/gorilla/securecookie/README.md

@@ -1,8 +1,6 @@
 securecookie
 ============
 [![GoDoc](https://godoc.org/github.com/gorilla/securecookie?status.svg)](https://godoc.org/github.com/gorilla/securecookie) [![Build Status](https://travis-ci.org/gorilla/securecookie.png?branch=master)](https://travis-ci.org/gorilla/securecookie)
-[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/securecookie/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/securecookie?badge)
-
 
 securecookie encodes and decodes authenticated and optionally encrypted 
 cookie values.

+ 3 - 14
vendor/github.com/gorilla/sessions/README.md

@@ -1,8 +1,6 @@
 sessions
 ========
 [![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.png?branch=master)](https://travis-ci.org/gorilla/sessions)
-[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge)
-
 
 gorilla/sessions provides cookie and filesystem sessions and infrastructure for
 custom session backends.
@@ -44,22 +42,16 @@ Let's start with an example that shows the sessions API in a nutshell:
 
 First we initialize a session store calling `NewCookieStore()` and passing a
 secret key used to authenticate the session. Inside the handler, we call
-`store.Get()` to retrieve an existing session or create a new one. Then we set
-some session values in session.Values, which is a `map[interface{}]interface{}`.
+`store.Get()` to retrieve an existing session or a new one. Then we set some
+session values in session.Values, which is a `map[interface{}]interface{}`.
 And finally we call `session.Save()` to save the session in the response.
 
 Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
 with
 [`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler)
-or else you will leak memory! An easy way to do this is to wrap the top-level
+as or else you will leak memory! An easy way to do this is to wrap the top-level
 mux when calling http.ListenAndServe:
 
-```go
-	http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
-```
-
-The ClearHandler function is provided by the gorilla/context package.
-
 More examples are available [on the Gorilla
 website](http://www.gorillatoolkit.org/pkg/sessions).
 
@@ -71,19 +63,16 @@ Other implementations of the `sessions.Store` interface:
 * [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt
 * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase
 * [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS
-* [github.com/savaki/dynastore](https://github.com/savaki/dynastore) - DynamoDB on AWS (Official AWS library)
 * [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache
 * [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine
 * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB
 * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL
-* [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster
 * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL
 * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis
 * [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB
 * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak
 * [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite
 * [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite)
-* [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql
 
 ## License
 

+ 2 - 1
vendor/github.com/gorilla/sessions/doc.go

@@ -29,7 +29,8 @@ Let's start with an example that shows the sessions API in a nutshell:
 	var store = sessions.NewCookieStore([]byte("something-very-secret"))
 
 	func MyHandler(w http.ResponseWriter, r *http.Request) {
-		// Get a session. Get() always returns a session, even if empty.
+		// Get a session. We're ignoring the error resulted from decoding an
+		// existing session: Get() always returns a session, even if empty.
 		session, err := store.Get(r, "session-name")
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)

+ 13 - 23
vendor/github.com/jinzhu/gorm/README.md

@@ -2,15 +2,19 @@
 
 The fantastic ORM library for Golang, aims to be developer friendly.
 
-[![Join the chat at https://gitter.im/jinzhu/gorm](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jinzhu/gorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[![wercker status](https://app.wercker.com/status/0cb7bb1039e21b74f8274941428e0921/s/master "wercker status")](https://app.wercker.com/project/bykey/0cb7bb1039e21b74f8274941428e0921)
+[![go report card](https://goreportcard.com/badge/github.com/jinzhu/gorm "go report card")](https://goreportcard.com/report/github.com/jinzhu/gorm)
+[![wercker status](https://app.wercker.com/status/8596cace912c9947dd9c8542ecc8cb8b/s/master "wercker status")](https://app.wercker.com/project/byKey/8596cace912c9947dd9c8542ecc8cb8b)
+[![Join the chat at https://gitter.im/jinzhu/gorm](https://img.shields.io/gitter/room/jinzhu/gorm.svg)](https://gitter.im/jinzhu/gorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Open Collective Backer](https://opencollective.com/gorm/tiers/backer/badge.svg?label=backer&color=brightgreen "Open Collective Backer")](https://opencollective.com/gorm)
+[![Open Collective Sponsor](https://opencollective.com/gorm/tiers/sponsor/badge.svg?label=sponsor&color=brightgreen "Open Collective Sponsor")](https://opencollective.com/gorm)
+[![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)
 [![GoDoc](https://godoc.org/github.com/jinzhu/gorm?status.svg)](https://godoc.org/github.com/jinzhu/gorm)
 
 ## Overview
 
 * Full-Featured ORM (almost)
 * Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism)
-* Callbacks (Before/After Create/Save/Update/Delete/Find)
+* Hooks (Before/After Create/Save/Update/Delete/Find)
 * Preloading (eager loading)
 * Transactions
 * Composite Primary Key
@@ -23,28 +27,14 @@ The fantastic ORM library for Golang, aims to be developer friendly.
 
 ## Getting Started
 
-* GORM Guides [jinzhu.github.com/gorm](http://jinzhu.github.io/gorm)
+* GORM Guides [http://gorm.io](http://gorm.io)
 
-## Upgrading To V1.0
+## Contributing
 
-* [CHANGELOG](http://jinzhu.github.io/gorm/changelog.html)
-
-## Supporting the project
-
-[![http://patreon.com/jinzhu](http://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png)](http://patreon.com/jinzhu)
-
-## Author
-
-**jinzhu**
-
-* <http://github.com/jinzhu>
-* <wosmvp@gmail.com>
-* <http://twitter.com/zhangjinzhu>
-
-## Contributors
-
-https://github.com/jinzhu/gorm/graphs/contributors
+[You can help to deliver a better GORM, check out things you can do](http://gorm.io/contribute.html)
 
 ## License
 
-Released under the [MIT License](https://github.com/jinzhu/gorm/blob/master/License).
+© Jinzhu, 2013~time.Now
+
+Released under the [MIT License](https://github.com/jinzhu/gorm/blob/master/License)

+ 2 - 2
vendor/github.com/jinzhu/gorm/association.go

@@ -107,7 +107,7 @@ func (association *Association) Replace(values ...interface{}) *Association {
 			if sourcePrimaryKeys := scope.getColumnAsArray(sourceForeignFieldNames, scope.Value); len(sourcePrimaryKeys) > 0 {
 				newDB = newDB.Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(sourcePrimaryKeys)), toQueryValues(sourcePrimaryKeys)...)
 
-				association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB, relationship))
+				association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB))
 			}
 		} else if relationship.Kind == "has_one" || relationship.Kind == "has_many" {
 			// has_one or has_many relations, set foreign key to be nil (TODO or delete them?)
@@ -173,7 +173,7 @@ func (association *Association) Delete(values ...interface{}) *Association {
 		sql := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(deletingPrimaryKeys))
 		newDB = newDB.Where(sql, toQueryValues(deletingPrimaryKeys)...)
 
-		association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB, relationship))
+		association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB))
 	} else {
 		var foreignKeyMap = map[string]interface{}{}
 		for _, foreignKey := range relationship.ForeignDBNames {

+ 5 - 7
vendor/github.com/jinzhu/gorm/callback.go

@@ -1,8 +1,6 @@
 package gorm
 
-import (
-	"fmt"
-)
+import "log"
 
 // DefaultCallback default callbacks defined by gorm
 var DefaultCallback = &Callback{}
@@ -95,7 +93,7 @@ func (cp *CallbackProcessor) Before(callbackName string) *CallbackProcessor {
 func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *Scope)) {
 	if cp.kind == "row_query" {
 		if cp.before == "" && cp.after == "" && callbackName != "gorm:row_query" {
-			fmt.Printf("Registing RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility...\n", callbackName)
+			log.Printf("Registing RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility...\n", callbackName)
 			cp.before = "gorm:row_query"
 		}
 	}
@@ -109,7 +107,7 @@ func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *
 // Remove a registered callback
 //     db.Callback().Create().Remove("gorm:update_time_stamp_when_create")
 func (cp *CallbackProcessor) Remove(callbackName string) {
-	fmt.Printf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum())
+	log.Printf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum())
 	cp.name = callbackName
 	cp.remove = true
 	cp.parent.processors = append(cp.parent.processors, cp)
@@ -122,7 +120,7 @@ func (cp *CallbackProcessor) Remove(callbackName string) {
 //		   scope.SetColumn("Updated", now)
 //     })
 func (cp *CallbackProcessor) Replace(callbackName string, callback func(scope *Scope)) {
-	fmt.Printf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum())
+	log.Printf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum())
 	cp.name = callbackName
 	cp.processor = &callback
 	cp.replace = true
@@ -161,7 +159,7 @@ func sortProcessors(cps []*CallbackProcessor) []*func(scope *Scope) {
 	for _, cp := range cps {
 		// show warning message the callback name already exists
 		if index := getRIndex(allNames, cp.name); index > -1 && !cp.replace && !cp.remove {
-			fmt.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
+			log.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
 		}
 		allNames = append(allNames, cp.name)
 	}

+ 2 - 1
vendor/github.com/jinzhu/gorm/callback_create.go

@@ -97,8 +97,9 @@ func createCallback(scope *Scope) {
 
 		if len(columns) == 0 {
 			scope.Raw(fmt.Sprintf(
-				"INSERT INTO %v DEFAULT VALUES%v%v",
+				"INSERT INTO %v %v%v%v",
 				quotedTableName,
+				scope.Dialect().DefaultValueStr(),
 				addExtraSpaceIfExist(extraOption),
 				addExtraSpaceIfExist(lastInsertIDReturningSuffix),
 			))

+ 4 - 0
vendor/github.com/jinzhu/gorm/callback_query.go

@@ -15,6 +15,10 @@ func init() {
 
 // queryCallback used to query data from database
 func queryCallback(scope *Scope) {
+	if _, skip := scope.InstanceGet("gorm:skip_query_callback"); skip {
+		return
+	}
+
 	defer scope.trace(NowFunc())
 
 	var (

+ 7 - 0
vendor/github.com/jinzhu/gorm/callback_query_preload.go

@@ -10,6 +10,9 @@ import (
 
 // preloadCallback used to preload associations
 func preloadCallback(scope *Scope) {
+	if _, skip := scope.InstanceGet("gorm:skip_query_callback"); skip {
+		return
+	}
 
 	if _, ok := scope.Get("gorm:auto_preload"); ok {
 		autoPreload(scope)
@@ -324,6 +327,10 @@ func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface
 
 		scope.scan(rows, columns, append(fields, joinTableFields...))
 
+		scope.New(elem.Addr().Interface()).
+			InstanceSet("gorm:skip_query_callback", true).
+			callCallbacks(scope.db.parent.callbacks.queries)
+
 		var foreignKeys = make([]interface{}, len(sourceKeys))
 		// generate hashed forkey keys in join table
 		for idx, joinTableField := range joinTableFields {

+ 112 - 41
vendor/github.com/jinzhu/gorm/callback_save.go

@@ -1,6 +1,9 @@
 package gorm
 
-import "reflect"
+import (
+	"reflect"
+	"strings"
+)
 
 func beginTransactionCallback(scope *Scope) {
 	scope.Begin()
@@ -10,31 +13,81 @@ func commitOrRollbackTransactionCallback(scope *Scope) {
 	scope.CommitOrRollback()
 }
 
-func saveFieldAsAssociation(scope *Scope, field *Field) (bool, *Relationship) {
+func saveAssociationCheck(scope *Scope, field *Field) (autoUpdate bool, autoCreate bool, saveReference bool, r *Relationship) {
+	checkTruth := func(value interface{}) bool {
+		if v, ok := value.(bool); ok && !v {
+			return false
+		}
+
+		if v, ok := value.(string); ok {
+			v = strings.ToLower(v)
+			if v == "false" || v != "skip" {
+				return false
+			}
+		}
+
+		return true
+	}
+
 	if scope.changeableField(field) && !field.IsBlank && !field.IsIgnored {
-		if value, ok := field.TagSettings["SAVE_ASSOCIATIONS"]; !ok || (value != "false" && value != "skip") {
-			if relationship := field.Relationship; relationship != nil {
-				return true, relationship
+		if r = field.Relationship; r != nil {
+			autoUpdate, autoCreate, saveReference = true, true, true
+
+			if value, ok := scope.Get("gorm:save_associations"); ok {
+				autoUpdate = checkTruth(value)
+				autoCreate = autoUpdate
+			} else if value, ok := field.TagSettings["SAVE_ASSOCIATIONS"]; ok {
+				autoUpdate = checkTruth(value)
+				autoCreate = autoUpdate
+			}
+
+			if value, ok := scope.Get("gorm:association_autoupdate"); ok {
+				autoUpdate = checkTruth(value)
+			} else if value, ok := field.TagSettings["ASSOCIATION_AUTOUPDATE"]; ok {
+				autoUpdate = checkTruth(value)
+			}
+
+			if value, ok := scope.Get("gorm:association_autocreate"); ok {
+				autoCreate = checkTruth(value)
+			} else if value, ok := field.TagSettings["ASSOCIATION_AUTOCREATE"]; ok {
+				autoCreate = checkTruth(value)
+			}
+
+			if value, ok := scope.Get("gorm:association_save_reference"); ok {
+				saveReference = checkTruth(value)
+			} else if value, ok := field.TagSettings["ASSOCIATION_SAVE_REFERENCE"]; ok {
+				saveReference = checkTruth(value)
 			}
 		}
 	}
-	return false, nil
+
+	return
 }
 
 func saveBeforeAssociationsCallback(scope *Scope) {
-	if !scope.shouldSaveAssociations() {
-		return
-	}
 	for _, field := range scope.Fields() {
-		if ok, relationship := saveFieldAsAssociation(scope, field); ok && relationship.Kind == "belongs_to" {
+		autoUpdate, autoCreate, saveReference, relationship := saveAssociationCheck(scope, field)
+
+		if relationship != nil && relationship.Kind == "belongs_to" {
 			fieldValue := field.Field.Addr().Interface()
-			scope.Err(scope.NewDB().Save(fieldValue).Error)
-			if len(relationship.ForeignFieldNames) != 0 {
-				// set value's foreign key
-				for idx, fieldName := range relationship.ForeignFieldNames {
-					associationForeignName := relationship.AssociationForeignDBNames[idx]
-					if foreignField, ok := scope.New(fieldValue).FieldByName(associationForeignName); ok {
-						scope.Err(scope.SetColumn(fieldName, foreignField.Field.Interface()))
+			newScope := scope.New(fieldValue)
+
+			if newScope.PrimaryKeyZero() {
+				if autoCreate {
+					scope.Err(scope.NewDB().Save(fieldValue).Error)
+				}
+			} else if autoUpdate {
+				scope.Err(scope.NewDB().Save(fieldValue).Error)
+			}
+
+			if saveReference {
+				if len(relationship.ForeignFieldNames) != 0 {
+					// set value's foreign key
+					for idx, fieldName := range relationship.ForeignFieldNames {
+						associationForeignName := relationship.AssociationForeignDBNames[idx]
+						if foreignField, ok := scope.New(fieldValue).FieldByName(associationForeignName); ok {
+							scope.Err(scope.SetColumn(fieldName, foreignField.Field.Interface()))
+						}
 					}
 				}
 			}
@@ -43,12 +96,10 @@ func saveBeforeAssociationsCallback(scope *Scope) {
 }
 
 func saveAfterAssociationsCallback(scope *Scope) {
-	if !scope.shouldSaveAssociations() {
-		return
-	}
 	for _, field := range scope.Fields() {
-		if ok, relationship := saveFieldAsAssociation(scope, field); ok &&
-			(relationship.Kind == "has_one" || relationship.Kind == "has_many" || relationship.Kind == "many_to_many") {
+		autoUpdate, autoCreate, saveReference, relationship := saveAssociationCheck(scope, field)
+
+		if relationship != nil && (relationship.Kind == "has_one" || relationship.Kind == "has_many" || relationship.Kind == "many_to_many") {
 			value := field.Field
 
 			switch value.Kind() {
@@ -58,41 +109,61 @@ func saveAfterAssociationsCallback(scope *Scope) {
 					elem := value.Index(i).Addr().Interface()
 					newScope := newDB.NewScope(elem)
 
-					if relationship.JoinTableHandler == nil && len(relationship.ForeignFieldNames) != 0 {
-						for idx, fieldName := range relationship.ForeignFieldNames {
-							associationForeignName := relationship.AssociationForeignDBNames[idx]
-							if f, ok := scope.FieldByName(associationForeignName); ok {
-								scope.Err(newScope.SetColumn(fieldName, f.Field.Interface()))
+					if saveReference {
+						if relationship.JoinTableHandler == nil && len(relationship.ForeignFieldNames) != 0 {
+							for idx, fieldName := range relationship.ForeignFieldNames {
+								associationForeignName := relationship.AssociationForeignDBNames[idx]
+								if f, ok := scope.FieldByName(associationForeignName); ok {
+									scope.Err(newScope.SetColumn(fieldName, f.Field.Interface()))
+								}
 							}
 						}
-					}
 
-					if relationship.PolymorphicType != "" {
-						scope.Err(newScope.SetColumn(relationship.PolymorphicType, relationship.PolymorphicValue))
+						if relationship.PolymorphicType != "" {
+							scope.Err(newScope.SetColumn(relationship.PolymorphicType, relationship.PolymorphicValue))
+						}
 					}
 
-					scope.Err(newDB.Save(elem).Error)
+					if newScope.PrimaryKeyZero() {
+						if autoCreate {
+							scope.Err(newDB.Save(elem).Error)
+						}
+					} else if autoUpdate {
+						scope.Err(newDB.Save(elem).Error)
+					}
 
-					if joinTableHandler := relationship.JoinTableHandler; joinTableHandler != nil {
-						scope.Err(joinTableHandler.Add(joinTableHandler, newDB, scope.Value, newScope.Value))
+					if !scope.New(newScope.Value).PrimaryKeyZero() && saveReference {
+						if joinTableHandler := relationship.JoinTableHandler; joinTableHandler != nil {
+							scope.Err(joinTableHandler.Add(joinTableHandler, newDB, scope.Value, newScope.Value))
+						}
 					}
 				}
 			default:
 				elem := value.Addr().Interface()
 				newScope := scope.New(elem)
-				if len(relationship.ForeignFieldNames) != 0 {
-					for idx, fieldName := range relationship.ForeignFieldNames {
-						associationForeignName := relationship.AssociationForeignDBNames[idx]
-						if f, ok := scope.FieldByName(associationForeignName); ok {
-							scope.Err(newScope.SetColumn(fieldName, f.Field.Interface()))
+
+				if saveReference {
+					if len(relationship.ForeignFieldNames) != 0 {
+						for idx, fieldName := range relationship.ForeignFieldNames {
+							associationForeignName := relationship.AssociationForeignDBNames[idx]
+							if f, ok := scope.FieldByName(associationForeignName); ok {
+								scope.Err(newScope.SetColumn(fieldName, f.Field.Interface()))
+							}
 						}
 					}
+
+					if relationship.PolymorphicType != "" {
+						scope.Err(newScope.SetColumn(relationship.PolymorphicType, relationship.PolymorphicValue))
+					}
 				}
 
-				if relationship.PolymorphicType != "" {
-					scope.Err(newScope.SetColumn(relationship.PolymorphicType, relationship.PolymorphicValue))
+				if newScope.PrimaryKeyZero() {
+					if autoCreate {
+						scope.Err(scope.NewDB().Save(elem).Error)
+					}
+				} else if autoUpdate {
+					scope.Err(scope.NewDB().Save(elem).Error)
 				}
-				scope.Err(scope.NewDB().Save(elem).Error)
 			}
 		}
 	}

+ 11 - 1
vendor/github.com/jinzhu/gorm/callback_update.go

@@ -3,6 +3,7 @@ package gorm
 import (
 	"errors"
 	"fmt"
+	"sort"
 	"strings"
 )
 
@@ -59,7 +60,16 @@ func updateCallback(scope *Scope) {
 		var sqls []string
 
 		if updateAttrs, ok := scope.InstanceGet("gorm:update_attrs"); ok {
-			for column, value := range updateAttrs.(map[string]interface{}) {
+			// Sort the column names so that the generated SQL is the same every time.
+			updateMap := updateAttrs.(map[string]interface{})
+			var columns []string
+			for c := range updateMap {
+				columns = append(columns, c)
+			}
+			sort.Strings(columns)
+
+			for _, column := range columns {
+				value := updateMap[column]
 				sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(column), scope.AddToVars(value)))
 			}
 		} else {

+ 22 - 8
vendor/github.com/jinzhu/gorm/dialect.go

@@ -33,6 +33,8 @@ type Dialect interface {
 	HasTable(tableName string) bool
 	// HasColumn check has column or not
 	HasColumn(tableName string, columnName string) bool
+	// ModifyColumn modify column's type
+	ModifyColumn(tableName string, columnName string, typ string) error
 
 	// LimitAndOffsetSQL return generated SQL with Limit and Offset, as mssql has special case
 	LimitAndOffsetSQL(limit, offset interface{}) string
@@ -40,9 +42,11 @@ type Dialect interface {
 	SelectFromDummyTable() string
 	// LastInsertIdReturningSuffix most dbs support LastInsertId, but postgres needs to use `RETURNING`
 	LastInsertIDReturningSuffix(tableName, columnName string) string
+	// DefaultValueStr
+	DefaultValueStr() string
 
-	// BuildForeignKeyName returns a foreign key name for the given table, field and reference
-	BuildForeignKeyName(tableName, field, dest string) string
+	// BuildKeyName returns a valid key name (foreign key, index key) for the given table, field and reference
+	BuildKeyName(kind, tableName string, fields ...string) string
 
 	// CurrentDatabase return current database name
 	CurrentDatabase() string
@@ -90,14 +94,16 @@ var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fiel
 	}
 
 	// Get scanner's real value
-	var getScannerValue func(reflect.Value)
-	getScannerValue = func(value reflect.Value) {
-		fieldValue = value
-		if _, isScanner := reflect.New(fieldValue.Type()).Interface().(sql.Scanner); isScanner && fieldValue.Kind() == reflect.Struct {
-			getScannerValue(fieldValue.Field(0))
+	if dataType == "" {
+		var getScannerValue func(reflect.Value)
+		getScannerValue = func(value reflect.Value) {
+			fieldValue = value
+			if _, isScanner := reflect.New(fieldValue.Type()).Interface().(sql.Scanner); isScanner && fieldValue.Kind() == reflect.Struct {
+				getScannerValue(fieldValue.Field(0))
+			}
 		}
+		getScannerValue(fieldValue)
 	}
-	getScannerValue(fieldValue)
 
 	// Default Size
 	if num, ok := field.TagSettings["SIZE"]; ok {
@@ -114,3 +120,11 @@ var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fiel
 
 	return fieldValue, dataType, size, strings.TrimSpace(additionalType)
 }
+
+func currentDatabaseAndTable(dialect Dialect, tableName string) (string, string) {
+	if strings.Contains(tableName, ".") {
+		splitStrings := strings.SplitN(tableName, ".", 2)
+		return splitStrings[0], splitStrings[1]
+	}
+	return dialect.CurrentDatabase(), tableName
+}

+ 28 - 8
vendor/github.com/jinzhu/gorm/dialect_common.go

@@ -38,6 +38,13 @@ func (commonDialect) Quote(key string) string {
 	return fmt.Sprintf(`"%s"`, key)
 }
 
+func (s *commonDialect) fieldCanAutoIncrement(field *StructField) bool {
+	if value, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
+		return strings.ToLower(value) != "false"
+	}
+	return field.IsPrimaryKey
+}
+
 func (s *commonDialect) DataTypeOf(field *StructField) string {
 	var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s)
 
@@ -46,13 +53,13 @@ func (s *commonDialect) DataTypeOf(field *StructField) string {
 		case reflect.Bool:
 			sqlType = "BOOLEAN"
 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
+			if s.fieldCanAutoIncrement(field) {
 				sqlType = "INTEGER AUTO_INCREMENT"
 			} else {
 				sqlType = "INTEGER"
 			}
 		case reflect.Int64, reflect.Uint64:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
+			if s.fieldCanAutoIncrement(field) {
 				sqlType = "BIGINT AUTO_INCREMENT"
 			} else {
 				sqlType = "BIGINT"
@@ -92,7 +99,8 @@ func (s *commonDialect) DataTypeOf(field *StructField) string {
 
 func (s commonDialect) HasIndex(tableName string, indexName string) bool {
 	var count int
-	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema = ? AND table_name = ? AND index_name = ?", s.CurrentDatabase(), tableName, indexName).Scan(&count)
+	currentDatabase, tableName := currentDatabaseAndTable(&s, tableName)
+	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema = ? AND table_name = ? AND index_name = ?", currentDatabase, tableName, indexName).Scan(&count)
 	return count > 0
 }
 
@@ -107,16 +115,23 @@ func (s commonDialect) HasForeignKey(tableName string, foreignKeyName string) bo
 
 func (s commonDialect) HasTable(tableName string) bool {
 	var count int
-	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_name = ?", s.CurrentDatabase(), tableName).Scan(&count)
+	currentDatabase, tableName := currentDatabaseAndTable(&s, tableName)
+	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_name = ?", currentDatabase, tableName).Scan(&count)
 	return count > 0
 }
 
 func (s commonDialect) HasColumn(tableName string, columnName string) bool {
 	var count int
-	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = ? AND table_name = ? AND column_name = ?", s.CurrentDatabase(), tableName, columnName).Scan(&count)
+	currentDatabase, tableName := currentDatabaseAndTable(&s, tableName)
+	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = ? AND table_name = ? AND column_name = ?", currentDatabase, tableName, columnName).Scan(&count)
 	return count > 0
 }
 
+func (s commonDialect) ModifyColumn(tableName string, columnName string, typ string) error {
+	_, err := s.db.Exec(fmt.Sprintf("ALTER TABLE %v ALTER COLUMN %v TYPE %v", tableName, columnName, typ))
+	return err
+}
+
 func (s commonDialect) CurrentDatabase() (name string) {
 	s.db.QueryRow("SELECT DATABASE()").Scan(&name)
 	return
@@ -144,9 +159,14 @@ func (commonDialect) LastInsertIDReturningSuffix(tableName, columnName string) s
 	return ""
 }
 
-func (DefaultForeignKeyNamer) BuildForeignKeyName(tableName, field, dest string) string {
-	keyName := fmt.Sprintf("%s_%s_%s_foreign", tableName, field, dest)
-	keyName = regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(keyName, "_")
+func (commonDialect) DefaultValueStr() string {
+	return "DEFAULT VALUES"
+}
+
+// BuildKeyName returns a valid key name (foreign key, index key) for the given table, field and reference
+func (DefaultForeignKeyNamer) BuildKeyName(kind, tableName string, fields ...string) string {
+	keyName := fmt.Sprintf("%s_%s_%s", kind, tableName, strings.Join(fields, "_"))
+	keyName = regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString(keyName, "_")
 	return keyName
 }
 

+ 28 - 13
vendor/github.com/jinzhu/gorm/dialect_mysql.go

@@ -44,42 +44,42 @@ func (s *mysql) DataTypeOf(field *StructField) string {
 		case reflect.Bool:
 			sqlType = "boolean"
 		case reflect.Int8:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "tinyint AUTO_INCREMENT"
 			} else {
 				sqlType = "tinyint"
 			}
 		case reflect.Int, reflect.Int16, reflect.Int32:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "int AUTO_INCREMENT"
 			} else {
 				sqlType = "int"
 			}
 		case reflect.Uint8:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "tinyint unsigned AUTO_INCREMENT"
 			} else {
 				sqlType = "tinyint unsigned"
 			}
 		case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "int unsigned AUTO_INCREMENT"
 			} else {
 				sqlType = "int unsigned"
 			}
 		case reflect.Int64:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "bigint AUTO_INCREMENT"
 			} else {
 				sqlType = "bigint"
 			}
 		case reflect.Uint64:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "bigint unsigned AUTO_INCREMENT"
 			} else {
@@ -95,10 +95,15 @@ func (s *mysql) DataTypeOf(field *StructField) string {
 			}
 		case reflect.Struct:
 			if _, ok := dataValue.Interface().(time.Time); ok {
+				precision := ""
+				if p, ok := field.TagSettings["PRECISION"]; ok {
+					precision = fmt.Sprintf("(%s)", p)
+				}
+
 				if _, ok := field.TagSettings["NOT NULL"]; ok {
-					sqlType = "timestamp"
+					sqlType = fmt.Sprintf("timestamp%v", precision)
 				} else {
-					sqlType = "timestamp NULL"
+					sqlType = fmt.Sprintf("timestamp%v NULL", precision)
 				}
 			}
 		default:
@@ -127,6 +132,11 @@ func (s mysql) RemoveIndex(tableName string, indexName string) error {
 	return err
 }
 
+func (s mysql) ModifyColumn(tableName string, columnName string, typ string) error {
+	_, err := s.db.Exec(fmt.Sprintf("ALTER TABLE %v MODIFY COLUMN %v %v", tableName, columnName, typ))
+	return err
+}
+
 func (s mysql) LimitAndOffsetSQL(limit, offset interface{}) (sql string) {
 	if limit != nil {
 		if parsedLimit, err := strconv.ParseInt(fmt.Sprint(limit), 0, 0); err == nil && parsedLimit >= 0 {
@@ -144,7 +154,8 @@ func (s mysql) LimitAndOffsetSQL(limit, offset interface{}) (sql string) {
 
 func (s mysql) HasForeignKey(tableName string, foreignKeyName string) bool {
 	var count int
-	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=? AND TABLE_NAME=? AND CONSTRAINT_NAME=? AND CONSTRAINT_TYPE='FOREIGN KEY'", s.CurrentDatabase(), tableName, foreignKeyName).Scan(&count)
+	currentDatabase, tableName := currentDatabaseAndTable(&s, tableName)
+	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=? AND TABLE_NAME=? AND CONSTRAINT_NAME=? AND CONSTRAINT_TYPE='FOREIGN KEY'", currentDatabase, tableName, foreignKeyName).Scan(&count)
 	return count > 0
 }
 
@@ -157,8 +168,8 @@ func (mysql) SelectFromDummyTable() string {
 	return "FROM DUAL"
 }
 
-func (s mysql) BuildForeignKeyName(tableName, field, dest string) string {
-	keyName := s.commonDialect.BuildForeignKeyName(tableName, field, dest)
+func (s mysql) BuildKeyName(kind, tableName string, fields ...string) string {
+	keyName := s.commonDialect.BuildKeyName(kind, tableName, fields...)
 	if utf8.RuneCountInString(keyName) <= 64 {
 		return keyName
 	}
@@ -166,11 +177,15 @@ func (s mysql) BuildForeignKeyName(tableName, field, dest string) string {
 	h.Write([]byte(keyName))
 	bs := h.Sum(nil)
 
-	// sha1 is 40 digits, keep first 24 characters of destination
-	destRunes := []rune(regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(dest, "_"))
+	// sha1 is 40 characters, keep first 24 characters of destination
+	destRunes := []rune(regexp.MustCompile("[^a-zA-Z0-9]+").ReplaceAllString(fields[0], "_"))
 	if len(destRunes) > 24 {
 		destRunes = destRunes[:24]
 	}
 
 	return fmt.Sprintf("%s%x", string(destRunes), bs)
 }
+
+func (mysql) DefaultValueStr() string {
+	return "VALUES()"
+}

+ 20 - 7
vendor/github.com/jinzhu/gorm/dialect_postgres.go

@@ -1,6 +1,7 @@
 package gorm
 
 import (
+	"encoding/json"
 	"fmt"
 	"reflect"
 	"strings"
@@ -13,6 +14,7 @@ type postgres struct {
 
 func init() {
 	RegisterDialect("postgres", &postgres{})
+	RegisterDialect("cloudsqlpostgres", &postgres{})
 }
 
 func (postgres) GetName() string {
@@ -31,14 +33,14 @@ func (s *postgres) DataTypeOf(field *StructField) string {
 		case reflect.Bool:
 			sqlType = "boolean"
 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uintptr:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "serial"
 			} else {
 				sqlType = "integer"
 			}
 		case reflect.Int64, reflect.Uint32, reflect.Uint64:
-			if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "bigserial"
 			} else {
@@ -67,8 +69,14 @@ func (s *postgres) DataTypeOf(field *StructField) string {
 		default:
 			if IsByteArrayOrSlice(dataValue) {
 				sqlType = "bytea"
-			} else if isUUID(dataValue) {
-				sqlType = "uuid"
+
+				if isUUID(dataValue) {
+					sqlType = "uuid"
+				}
+
+				if isJSON(dataValue) {
+					sqlType = "jsonb"
+				}
 			}
 		}
 	}
@@ -85,7 +93,7 @@ func (s *postgres) DataTypeOf(field *StructField) string {
 
 func (s postgres) HasIndex(tableName string, indexName string) bool {
 	var count int
-	s.db.QueryRow("SELECT count(*) FROM pg_indexes WHERE tablename = $1 AND indexname = $2", tableName, indexName).Scan(&count)
+	s.db.QueryRow("SELECT count(*) FROM pg_indexes WHERE tablename = $1 AND indexname = $2 AND schemaname = CURRENT_SCHEMA()", tableName, indexName).Scan(&count)
 	return count > 0
 }
 
@@ -97,13 +105,13 @@ func (s postgres) HasForeignKey(tableName string, foreignKeyName string) bool {
 
 func (s postgres) HasTable(tableName string) bool {
 	var count int
-	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.tables WHERE table_name = $1 AND table_type = 'BASE TABLE'", tableName).Scan(&count)
+	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.tables WHERE table_name = $1 AND table_type = 'BASE TABLE' AND table_schema = CURRENT_SCHEMA()", tableName).Scan(&count)
 	return count > 0
 }
 
 func (s postgres) HasColumn(tableName string, columnName string) bool {
 	var count int
-	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_name = $1 AND column_name = $2", tableName, columnName).Scan(&count)
+	s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_name = $1 AND column_name = $2 AND table_schema = CURRENT_SCHEMA()", tableName, columnName).Scan(&count)
 	return count > 0
 }
 
@@ -128,3 +136,8 @@ func isUUID(value reflect.Value) bool {
 	lower := strings.ToLower(typename)
 	return "uuid" == lower || "guid" == lower
 }
+
+func isJSON(value reflect.Value) bool {
+	_, ok := value.Interface().(json.RawMessage)
+	return ok
+}

+ 2 - 2
vendor/github.com/jinzhu/gorm/dialect_sqlite3.go

@@ -28,14 +28,14 @@ func (s *sqlite3) DataTypeOf(field *StructField) string {
 		case reflect.Bool:
 			sqlType = "bool"
 		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
-			if field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "integer primary key autoincrement"
 			} else {
 				sqlType = "integer"
 			}
 		case reflect.Int64, reflect.Uint64:
-			if field.IsPrimaryKey {
+			if s.fieldCanAutoIncrement(field) {
 				field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
 				sqlType = "integer primary key autoincrement"
 			} else {

+ 30 - 0
vendor/github.com/jinzhu/gorm/docker-compose.yml

@@ -0,0 +1,30 @@
+version: '3'
+
+services:
+  mysql:
+    image: 'mysql:latest'
+    ports:
+      - 9910:3306
+    environment:
+      - MYSQL_DATABASE=gorm
+      - MYSQL_USER=gorm
+      - MYSQL_PASSWORD=gorm
+      - MYSQL_RANDOM_ROOT_PASSWORD="yes"
+  postgres:
+    image: 'postgres:latest'
+    ports:
+      - 9920:5432
+    environment:
+      - POSTGRES_USER=gorm
+      - POSTGRES_DB=gorm
+      - POSTGRES_PASSWORD=gorm
+  mssql:
+    image: 'mcmoe/mssqldocker:latest'
+    ports:
+      - 9930:1433
+    environment:
+      - ACCEPT_EULA=Y
+      - SA_PASSWORD=LoremIpsum86
+      - MSSQL_DB=gorm
+      - MSSQL_USER=gorm
+      - MSSQL_PASSWORD=LoremIpsum86

+ 16 - 0
vendor/github.com/jinzhu/gorm/errors.go

@@ -21,6 +21,18 @@ var (
 // Errors contains all happened errors
 type Errors []error
 
+// IsRecordNotFoundError returns current error has record not found error or not
+func IsRecordNotFoundError(err error) bool {
+	if errs, ok := err.(Errors); ok {
+		for _, err := range errs {
+			if err == ErrRecordNotFound {
+				return true
+			}
+		}
+	}
+	return err == ErrRecordNotFound
+}
+
 // GetErrors gets all happened errors
 func (errs Errors) GetErrors() []error {
 	return errs
@@ -29,6 +41,10 @@ func (errs Errors) GetErrors() []error {
 // Add adds an error
 func (errs Errors) Add(newErrors ...error) Errors {
 	for _, err := range newErrors {
+		if err == nil {
+			continue
+		}
+
 		if errors, ok := err.(Errors); ok {
 			errs = errs.Add(errors...)
 		} else {

+ 27 - 22
vendor/github.com/jinzhu/gorm/join_table_handler.go

@@ -79,41 +79,43 @@ func (s *JoinTableHandler) Setup(relationship *Relationship, tableName string, s
 
 // Table return join table's table name
 func (s JoinTableHandler) Table(db *DB) string {
-	return s.TableName
+	return DefaultTableNameHandler(db, s.TableName)
 }
 
-func (s JoinTableHandler) getSearchMap(db *DB, sources ...interface{}) map[string]interface{} {
-	values := map[string]interface{}{}
-
+func (s JoinTableHandler) updateConditionMap(conditionMap map[string]interface{}, db *DB, joinTableSources []JoinTableSource, sources ...interface{}) {
 	for _, source := range sources {
 		scope := db.NewScope(source)
 		modelType := scope.GetModelStruct().ModelType
 
-		if s.Source.ModelType == modelType {
-			for _, foreignKey := range s.Source.ForeignKeys {
-				if field, ok := scope.FieldByName(foreignKey.AssociationDBName); ok {
-					values[foreignKey.DBName] = field.Field.Interface()
-				}
-			}
-		} else if s.Destination.ModelType == modelType {
-			for _, foreignKey := range s.Destination.ForeignKeys {
-				if field, ok := scope.FieldByName(foreignKey.AssociationDBName); ok {
-					values[foreignKey.DBName] = field.Field.Interface()
+		for _, joinTableSource := range joinTableSources {
+			if joinTableSource.ModelType == modelType {
+				for _, foreignKey := range joinTableSource.ForeignKeys {
+					if field, ok := scope.FieldByName(foreignKey.AssociationDBName); ok {
+						conditionMap[foreignKey.DBName] = field.Field.Interface()
+					}
 				}
+				break
 			}
 		}
 	}
-	return values
 }
 
 // Add create relationship in join table for source and destination
 func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source interface{}, destination interface{}) error {
-	scope := db.NewScope("")
-	searchMap := s.getSearchMap(db, source, destination)
+	var (
+		scope        = db.NewScope("")
+		conditionMap = map[string]interface{}{}
+	)
+
+	// Update condition map for source
+	s.updateConditionMap(conditionMap, db, []JoinTableSource{s.Source}, source)
+
+	// Update condition map for destination
+	s.updateConditionMap(conditionMap, db, []JoinTableSource{s.Destination}, destination)
 
 	var assignColumns, binVars, conditions []string
 	var values []interface{}
-	for key, value := range searchMap {
+	for key, value := range conditionMap {
 		assignColumns = append(assignColumns, scope.Quote(key))
 		binVars = append(binVars, `?`)
 		conditions = append(conditions, fmt.Sprintf("%v = ?", scope.Quote(key)))
@@ -141,12 +143,15 @@ func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source
 // Delete delete relationship in join table for sources
 func (s JoinTableHandler) Delete(handler JoinTableHandlerInterface, db *DB, sources ...interface{}) error {
 	var (
-		scope      = db.NewScope(nil)
-		conditions []string
-		values     []interface{}
+		scope        = db.NewScope(nil)
+		conditions   []string
+		values       []interface{}
+		conditionMap = map[string]interface{}{}
 	)
 
-	for key, value := range s.getSearchMap(db, sources...) {
+	s.updateConditionMap(conditionMap, db, []JoinTableSource{s.Source, s.Destination}, sources...)
+
+	for key, value := range conditionMap {
 		conditions = append(conditions, fmt.Sprintf("%v = ?", scope.Quote(key)))
 		values = append(values, value)
 	}

+ 44 - 20
vendor/github.com/jinzhu/gorm/main.go

@@ -177,6 +177,15 @@ func (s *DB) QueryExpr() *expr {
 	return Expr(scope.SQL, scope.SQLVars...)
 }
 
+// SubQuery returns the query as sub query
+func (s *DB) SubQuery() *expr {
+	scope := s.NewScope(s.Value)
+	scope.InstanceSet("skip_bindvar", true)
+	scope.prepareQuerySQL()
+
+	return Expr(fmt.Sprintf("(%v)", scope.SQL), scope.SQLVars...)
+}
+
 // Where return a new relation, filter records with given conditions, accepts `map`, `struct` or `string` as conditions, refer http://jinzhu.github.io/gorm/crud.html#query
 func (s *DB) Where(query interface{}, args ...interface{}) *DB {
 	return s.clone().search.Where(query, args...).db
@@ -274,15 +283,22 @@ func (s *DB) Assign(attrs ...interface{}) *DB {
 
 // First find first record that match given conditions, order by primary key
 func (s *DB) First(out interface{}, where ...interface{}) *DB {
-	newScope := s.clone().NewScope(out)
+	newScope := s.NewScope(out)
 	newScope.Search.Limit(1)
 	return newScope.Set("gorm:order_by_primary_key", "ASC").
 		inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
 }
 
+// Take return a record that match given conditions, the order will depend on the database implementation
+func (s *DB) Take(out interface{}, where ...interface{}) *DB {
+	newScope := s.NewScope(out)
+	newScope.Search.Limit(1)
+	return newScope.inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
+}
+
 // Last find last record that match given conditions, order by primary key
 func (s *DB) Last(out interface{}, where ...interface{}) *DB {
-	newScope := s.clone().NewScope(out)
+	newScope := s.NewScope(out)
 	newScope.Search.Limit(1)
 	return newScope.Set("gorm:order_by_primary_key", "DESC").
 		inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
@@ -290,12 +306,12 @@ func (s *DB) Last(out interface{}, where ...interface{}) *DB {
 
 // Find find records that match given conditions
 func (s *DB) Find(out interface{}, where ...interface{}) *DB {
-	return s.clone().NewScope(out).inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
+	return s.NewScope(out).inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db
 }
 
 // Scan scan value to a struct
 func (s *DB) Scan(dest interface{}) *DB {
-	return s.clone().NewScope(s.Value).Set("gorm:query_destination", dest).callCallbacks(s.parent.callbacks.queries).db
+	return s.NewScope(s.Value).Set("gorm:query_destination", dest).callCallbacks(s.parent.callbacks.queries).db
 }
 
 // Row return `*sql.Row` with given conditions
@@ -311,8 +327,8 @@ func (s *DB) Rows() (*sql.Rows, error) {
 // ScanRows scan `*sql.Rows` to give struct
 func (s *DB) ScanRows(rows *sql.Rows, result interface{}) error {
 	var (
-		clone        = s.clone()
-		scope        = clone.NewScope(result)
+		scope        = s.NewScope(result)
+		clone        = scope.db
 		columns, err = rows.Columns()
 	)
 
@@ -337,7 +353,7 @@ func (s *DB) Count(value interface{}) *DB {
 
 // Related get related associations
 func (s *DB) Related(value interface{}, foreignKeys ...string) *DB {
-	return s.clone().NewScope(s.Value).related(value, foreignKeys...).db
+	return s.NewScope(s.Value).related(value, foreignKeys...).db
 }
 
 // FirstOrInit find first matched record or initialize a new one with given conditions (only works with struct, map conditions)
@@ -377,7 +393,7 @@ func (s *DB) Update(attrs ...interface{}) *DB {
 
 // Updates update attributes with callbacks, refer: https://jinzhu.github.io/gorm/crud.html#update
 func (s *DB) Updates(values interface{}, ignoreProtectedAttrs ...bool) *DB {
-	return s.clone().NewScope(s.Value).
+	return s.NewScope(s.Value).
 		Set("gorm:ignore_protected_attrs", len(ignoreProtectedAttrs) > 0).
 		InstanceSet("gorm:update_interface", values).
 		callCallbacks(s.parent.callbacks.updates).db
@@ -390,7 +406,7 @@ func (s *DB) UpdateColumn(attrs ...interface{}) *DB {
 
 // UpdateColumns update attributes without callbacks, refer: https://jinzhu.github.io/gorm/crud.html#update
 func (s *DB) UpdateColumns(values interface{}) *DB {
-	return s.clone().NewScope(s.Value).
+	return s.NewScope(s.Value).
 		Set("gorm:update_column", true).
 		Set("gorm:save_associations", false).
 		InstanceSet("gorm:update_interface", values).
@@ -399,7 +415,7 @@ func (s *DB) UpdateColumns(values interface{}) *DB {
 
 // Save update value in database, if the value doesn't have primary key, will insert it
 func (s *DB) Save(value interface{}) *DB {
-	scope := s.clone().NewScope(value)
+	scope := s.NewScope(value)
 	if !scope.PrimaryKeyZero() {
 		newDB := scope.callCallbacks(s.parent.callbacks.updates).db
 		if newDB.Error == nil && newDB.RowsAffected == 0 {
@@ -412,13 +428,13 @@ func (s *DB) Save(value interface{}) *DB {
 
 // Create insert the value into database
 func (s *DB) Create(value interface{}) *DB {
-	scope := s.clone().NewScope(value)
+	scope := s.NewScope(value)
 	return scope.callCallbacks(s.parent.callbacks.creates).db
 }
 
 // Delete delete value match given conditions, if the value has primary key, then will including the primary key as condition
 func (s *DB) Delete(value interface{}, where ...interface{}) *DB {
-	return s.clone().NewScope(value).inlineCondition(where...).callCallbacks(s.parent.callbacks.deletes).db
+	return s.NewScope(value).inlineCondition(where...).callCallbacks(s.parent.callbacks.deletes).db
 }
 
 // Raw use raw sql as conditions, won't run it unless invoked by other methods
@@ -429,8 +445,8 @@ func (s *DB) Raw(sql string, values ...interface{}) *DB {
 
 // Exec execute raw sql
 func (s *DB) Exec(sql string, values ...interface{}) *DB {
-	scope := s.clone().NewScope(nil)
-	generatedSQL := scope.buildWhereCondition(map[string]interface{}{"query": sql, "args": values})
+	scope := s.NewScope(nil)
+	generatedSQL := scope.buildCondition(map[string]interface{}{"query": sql, "args": values}, true)
 	generatedSQL = strings.TrimSuffix(strings.TrimPrefix(generatedSQL, "("), ")")
 	scope.Raw(generatedSQL)
 	return scope.Exec().db
@@ -495,7 +511,7 @@ func (s *DB) Rollback() *DB {
 
 // NewRecord check if value's primary key is blank
 func (s *DB) NewRecord(value interface{}) bool {
-	return s.clone().NewScope(value).PrimaryKeyZero()
+	return s.NewScope(value).PrimaryKeyZero()
 }
 
 // RecordNotFound check if returning ErrRecordNotFound error
@@ -544,7 +560,7 @@ func (s *DB) DropTableIfExists(values ...interface{}) *DB {
 // HasTable check has table or not
 func (s *DB) HasTable(value interface{}) bool {
 	var (
-		scope     = s.clone().NewScope(value)
+		scope     = s.NewScope(value)
 		tableName string
 	)
 
@@ -570,14 +586,14 @@ func (s *DB) AutoMigrate(values ...interface{}) *DB {
 
 // ModifyColumn modify column to type
 func (s *DB) ModifyColumn(column string, typ string) *DB {
-	scope := s.clone().NewScope(s.Value)
+	scope := s.NewScope(s.Value)
 	scope.modifyColumn(column, typ)
 	return scope.db
 }
 
 // DropColumn drop a column
 func (s *DB) DropColumn(column string) *DB {
-	scope := s.clone().NewScope(s.Value)
+	scope := s.NewScope(s.Value)
 	scope.dropColumn(column)
 	return scope.db
 }
@@ -598,7 +614,7 @@ func (s *DB) AddUniqueIndex(indexName string, columns ...string) *DB {
 
 // RemoveIndex remove index with name
 func (s *DB) RemoveIndex(indexName string) *DB {
-	scope := s.clone().NewScope(s.Value)
+	scope := s.NewScope(s.Value)
 	scope.removeIndex(indexName)
 	return scope.db
 }
@@ -606,11 +622,19 @@ func (s *DB) RemoveIndex(indexName string) *DB {
 // AddForeignKey Add foreign key to the given scope, e.g:
 //     db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")
 func (s *DB) AddForeignKey(field string, dest string, onDelete string, onUpdate string) *DB {
-	scope := s.clone().NewScope(s.Value)
+	scope := s.NewScope(s.Value)
 	scope.addForeignKey(field, dest, onDelete, onUpdate)
 	return scope.db
 }
 
+// RemoveForeignKey Remove foreign key from the given scope, e.g:
+//     db.Model(&User{}).RemoveForeignKey("city_id", "cities(id)")
+func (s *DB) RemoveForeignKey(field string, dest string) *DB {
+	scope := s.clone().NewScope(s.Value)
+	scope.removeForeignKey(field, dest)
+	return scope.db
+}
+
 // Association start `Association Mode` to handler relations things easir in that mode, refer: https://jinzhu.github.io/gorm/associations.html#association-mode
 func (s *DB) Association(column string) *Association {
 	var err error

+ 62 - 30
vendor/github.com/jinzhu/gorm/model_struct.go

@@ -249,11 +249,13 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
 							)
 
 							if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" {
-								foreignKeys = strings.Split(field.TagSettings["FOREIGNKEY"], ",")
+								foreignKeys = strings.Split(foreignKey, ",")
 							}
 
-							if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
-								associationForeignKeys = strings.Split(field.TagSettings["ASSOCIATIONFOREIGNKEY"], ",")
+							if foreignKey := field.TagSettings["ASSOCIATION_FOREIGNKEY"]; foreignKey != "" {
+								associationForeignKeys = strings.Split(foreignKey, ",")
+							} else if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
+								associationForeignKeys = strings.Split(foreignKey, ",")
 							}
 
 							for elemType.Kind() == reflect.Slice || elemType.Kind() == reflect.Ptr {
@@ -264,37 +266,65 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
 								if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
 									relationship.Kind = "many_to_many"
 
-									// if no foreign keys defined with tag
-									if len(foreignKeys) == 0 {
-										for _, field := range modelStruct.PrimaryFields {
-											foreignKeys = append(foreignKeys, field.DBName)
+									{ // Foreign Keys for Source
+										joinTableDBNames := []string{}
+
+										if foreignKey := field.TagSettings["JOINTABLE_FOREIGNKEY"]; foreignKey != "" {
+											joinTableDBNames = strings.Split(foreignKey, ",")
 										}
-									}
 
-									for _, foreignKey := range foreignKeys {
-										if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil {
-											// source foreign keys (db names)
-											relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.DBName)
-											// join table foreign keys for source
-											joinTableDBName := ToDBName(reflectType.Name()) + "_" + foreignField.DBName
-											relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBName)
+										// if no foreign keys defined with tag
+										if len(foreignKeys) == 0 {
+											for _, field := range modelStruct.PrimaryFields {
+												foreignKeys = append(foreignKeys, field.DBName)
+											}
 										}
-									}
 
-									// if no association foreign keys defined with tag
-									if len(associationForeignKeys) == 0 {
-										for _, field := range toScope.PrimaryFields() {
-											associationForeignKeys = append(associationForeignKeys, field.DBName)
+										for idx, foreignKey := range foreignKeys {
+											if foreignField := getForeignField(foreignKey, modelStruct.StructFields); foreignField != nil {
+												// source foreign keys (db names)
+												relationship.ForeignFieldNames = append(relationship.ForeignFieldNames, foreignField.DBName)
+
+												// setup join table foreign keys for source
+												if len(joinTableDBNames) > idx {
+													// if defined join table's foreign key
+													relationship.ForeignDBNames = append(relationship.ForeignDBNames, joinTableDBNames[idx])
+												} else {
+													defaultJointableForeignKey := ToDBName(reflectType.Name()) + "_" + foreignField.DBName
+													relationship.ForeignDBNames = append(relationship.ForeignDBNames, defaultJointableForeignKey)
+												}
+											}
 										}
 									}
 
-									for _, name := range associationForeignKeys {
-										if field, ok := toScope.FieldByName(name); ok {
-											// association foreign keys (db names)
-											relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.DBName)
-											// join table foreign keys for association
-											joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName
-											relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
+									{ // Foreign Keys for Association (Destination)
+										associationJoinTableDBNames := []string{}
+
+										if foreignKey := field.TagSettings["ASSOCIATION_JOINTABLE_FOREIGNKEY"]; foreignKey != "" {
+											associationJoinTableDBNames = strings.Split(foreignKey, ",")
+										}
+
+										// if no association foreign keys defined with tag
+										if len(associationForeignKeys) == 0 {
+											for _, field := range toScope.PrimaryFields() {
+												associationForeignKeys = append(associationForeignKeys, field.DBName)
+											}
+										}
+
+										for idx, name := range associationForeignKeys {
+											if field, ok := toScope.FieldByName(name); ok {
+												// association foreign keys (db names)
+												relationship.AssociationForeignFieldNames = append(relationship.AssociationForeignFieldNames, field.DBName)
+
+												// setup join table foreign keys for association
+												if len(associationJoinTableDBNames) > idx {
+													relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, associationJoinTableDBNames[idx])
+												} else {
+													// join table foreign keys for association
+													joinTableDBName := ToDBName(elemType.Name()) + "_" + field.DBName
+													relationship.AssociationForeignDBNames = append(relationship.AssociationForeignDBNames, joinTableDBName)
+												}
+											}
 										}
 									}
 
@@ -399,11 +429,13 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
 							)
 
 							if foreignKey := field.TagSettings["FOREIGNKEY"]; foreignKey != "" {
-								tagForeignKeys = strings.Split(field.TagSettings["FOREIGNKEY"], ",")
+								tagForeignKeys = strings.Split(foreignKey, ",")
 							}
 
-							if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
-								tagAssociationForeignKeys = strings.Split(field.TagSettings["ASSOCIATIONFOREIGNKEY"], ",")
+							if foreignKey := field.TagSettings["ASSOCIATION_FOREIGNKEY"]; foreignKey != "" {
+								tagAssociationForeignKeys = strings.Split(foreignKey, ",")
+							} else if foreignKey := field.TagSettings["ASSOCIATIONFOREIGNKEY"]; foreignKey != "" {
+								tagAssociationForeignKeys = strings.Split(foreignKey, ",")
 							}
 
 							if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {

+ 173 - 119
vendor/github.com/jinzhu/gorm/scope.go

@@ -1,16 +1,15 @@
 package gorm
 
 import (
+	"bytes"
 	"database/sql"
 	"database/sql/driver"
 	"errors"
 	"fmt"
+	"reflect"
 	"regexp"
-	"strconv"
 	"strings"
 	"time"
-
-	"reflect"
 )
 
 // Scope contain current operation's information when you perform any operation on the database
@@ -116,6 +115,9 @@ func (scope *Scope) Fields() []*Field {
 			if isStruct {
 				fieldValue := indirectScopeValue
 				for _, name := range structField.Names {
+					if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
+						fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
+					}
 					fieldValue = reflect.Indirect(fieldValue).FieldByName(name)
 				}
 				fields = append(fields, &Field{StructField: structField, Field: fieldValue, IsBlank: isBlank(fieldValue)})
@@ -460,7 +462,7 @@ func (scope *Scope) callMethod(methodName string, reflectValue reflect.Value) {
 var (
 	columnRegexp        = regexp.MustCompile("^[a-zA-Z\\d]+(\\.[a-zA-Z\\d]+)*$") // only match string like `name`, `users.name`
 	isNumberRegexp      = regexp.MustCompile("^\\s*\\d+\\s*$")                   // match if string is number
-	comparisonRegexp    = regexp.MustCompile("(?i) (=|<>|>|<|LIKE|IS|IN) ")
+	comparisonRegexp    = regexp.MustCompile("(?i) (=|<>|(>|<)(=?)|LIKE|IS|IN) ")
 	countingQueryRegexp = regexp.MustCompile("(?i)^count(.+)$")
 )
 
@@ -521,134 +523,143 @@ func (scope *Scope) primaryCondition(value interface{}) string {
 	return fmt.Sprintf("(%v.%v = %v)", scope.QuotedTableName(), scope.Quote(scope.PrimaryKey()), value)
 }
 
-func (scope *Scope) buildWhereCondition(clause map[string]interface{}) (str string) {
+func (scope *Scope) buildCondition(clause map[string]interface{}, include bool) (str string) {
+	var (
+		quotedTableName  = scope.QuotedTableName()
+		quotedPrimaryKey = scope.Quote(scope.PrimaryKey())
+		equalSQL         = "="
+		inSQL            = "IN"
+	)
+
+	// If building not conditions
+	if !include {
+		equalSQL = "<>"
+		inSQL = "NOT IN"
+	}
+
 	switch value := clause["query"].(type) {
+	case sql.NullInt64:
+		return fmt.Sprintf("(%v.%v %s %v)", quotedTableName, quotedPrimaryKey, equalSQL, value.Int64)
+	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+		return fmt.Sprintf("(%v.%v %s %v)", quotedTableName, quotedPrimaryKey, equalSQL, value)
+	case []int, []int8, []int16, []int32, []int64, []uint, []uint8, []uint16, []uint32, []uint64, []string, []interface{}:
+		if !include && reflect.ValueOf(value).Len() == 0 {
+			return
+		}
+		str = fmt.Sprintf("(%v.%v %s (?))", quotedTableName, quotedPrimaryKey, inSQL)
+		clause["args"] = []interface{}{value}
 	case string:
 		if isNumberRegexp.MatchString(value) {
-			return scope.primaryCondition(scope.AddToVars(value))
-		} else if value != "" {
-			str = fmt.Sprintf("(%v)", value)
+			return fmt.Sprintf("(%v.%v %s %v)", quotedTableName, quotedPrimaryKey, equalSQL, scope.AddToVars(value))
+		}
+
+		if value != "" {
+			if !include {
+				if comparisonRegexp.MatchString(value) {
+					str = fmt.Sprintf("NOT (%v)", value)
+				} else {
+					str = fmt.Sprintf("(%v.%v NOT IN (?))", quotedTableName, scope.Quote(value))
+				}
+			} else {
+				str = fmt.Sprintf("(%v)", value)
+			}
 		}
-	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, sql.NullInt64:
-		return scope.primaryCondition(scope.AddToVars(value))
-	case []int, []int8, []int16, []int32, []int64, []uint, []uint8, []uint16, []uint32, []uint64, []string, []interface{}:
-		str = fmt.Sprintf("(%v.%v IN (?))", scope.QuotedTableName(), scope.Quote(scope.PrimaryKey()))
-		clause["args"] = []interface{}{value}
 	case map[string]interface{}:
 		var sqls []string
 		for key, value := range value {
 			if value != nil {
-				sqls = append(sqls, fmt.Sprintf("(%v.%v = %v)", scope.QuotedTableName(), scope.Quote(key), scope.AddToVars(value)))
+				sqls = append(sqls, fmt.Sprintf("(%v.%v %s %v)", quotedTableName, scope.Quote(key), equalSQL, scope.AddToVars(value)))
 			} else {
-				sqls = append(sqls, fmt.Sprintf("(%v.%v IS NULL)", scope.QuotedTableName(), scope.Quote(key)))
+				if !include {
+					sqls = append(sqls, fmt.Sprintf("(%v.%v IS NOT NULL)", quotedTableName, scope.Quote(key)))
+				} else {
+					sqls = append(sqls, fmt.Sprintf("(%v.%v IS NULL)", quotedTableName, scope.Quote(key)))
+				}
 			}
 		}
 		return strings.Join(sqls, " AND ")
 	case interface{}:
 		var sqls []string
 		newScope := scope.New(value)
+
+		if len(newScope.Fields()) == 0 {
+			scope.Err(fmt.Errorf("invalid query condition: %v", value))
+			return
+		}
+
 		for _, field := range newScope.Fields() {
 			if !field.IsIgnored && !field.IsBlank {
-				sqls = append(sqls, fmt.Sprintf("(%v.%v = %v)", scope.QuotedTableName(), scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface())))
+				sqls = append(sqls, fmt.Sprintf("(%v.%v %s %v)", quotedTableName, scope.Quote(field.DBName), equalSQL, scope.AddToVars(field.Field.Interface())))
 			}
 		}
 		return strings.Join(sqls, " AND ")
+	default:
+		scope.Err(fmt.Errorf("invalid query condition: %v", value))
+		return
 	}
 
+	replacements := []string{}
 	args := clause["args"].([]interface{})
 	for _, arg := range args {
+		var err error
 		switch reflect.ValueOf(arg).Kind() {
 		case reflect.Slice: // For where("id in (?)", []int64{1,2})
-			if bytes, ok := arg.([]byte); ok {
-				str = strings.Replace(str, "?", scope.AddToVars(bytes), 1)
+			if scanner, ok := interface{}(arg).(driver.Valuer); ok {
+				arg, err = scanner.Value()
+				replacements = append(replacements, scope.AddToVars(arg))
+			} else if b, ok := arg.([]byte); ok {
+				replacements = append(replacements, scope.AddToVars(b))
+			} else if as, ok := arg.([][]interface{}); ok {
+				var tempMarks []string
+				for _, a := range as {
+					var arrayMarks []string
+					for _, v := range a {
+						arrayMarks = append(arrayMarks, scope.AddToVars(v))
+					}
+
+					if len(arrayMarks) > 0 {
+						tempMarks = append(tempMarks, fmt.Sprintf("(%v)", strings.Join(arrayMarks, ",")))
+					}
+				}
+
+				if len(tempMarks) > 0 {
+					replacements = append(replacements, strings.Join(tempMarks, ","))
+				}
 			} else if values := reflect.ValueOf(arg); values.Len() > 0 {
 				var tempMarks []string
 				for i := 0; i < values.Len(); i++ {
 					tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
 				}
-				str = strings.Replace(str, "?", strings.Join(tempMarks, ","), 1)
+				replacements = append(replacements, strings.Join(tempMarks, ","))
 			} else {
-				str = strings.Replace(str, "?", scope.AddToVars(Expr("NULL")), 1)
+				replacements = append(replacements, scope.AddToVars(Expr("NULL")))
 			}
 		default:
 			if valuer, ok := interface{}(arg).(driver.Valuer); ok {
-				arg, _ = valuer.Value()
+				arg, err = valuer.Value()
 			}
 
-			str = strings.Replace(str, "?", scope.AddToVars(arg), 1)
+			replacements = append(replacements, scope.AddToVars(arg))
 		}
-	}
-	return
-}
-
-func (scope *Scope) buildNotCondition(clause map[string]interface{}) (str string) {
-	var notEqualSQL string
-	var primaryKey = scope.PrimaryKey()
 
-	switch value := clause["query"].(type) {
-	case string:
-		if isNumberRegexp.MatchString(value) {
-			id, _ := strconv.Atoi(value)
-			return fmt.Sprintf("(%v <> %v)", scope.Quote(primaryKey), id)
-		} else if comparisonRegexp.MatchString(value) {
-			str = fmt.Sprintf(" NOT (%v) ", value)
-			notEqualSQL = fmt.Sprintf("NOT (%v)", value)
-		} else {
-			str = fmt.Sprintf("(%v.%v NOT IN (?))", scope.QuotedTableName(), scope.Quote(value))
-			notEqualSQL = fmt.Sprintf("(%v.%v <> ?)", scope.QuotedTableName(), scope.Quote(value))
-		}
-	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, sql.NullInt64:
-		return fmt.Sprintf("(%v.%v <> %v)", scope.QuotedTableName(), scope.Quote(primaryKey), value)
-	case []int, []int8, []int16, []int32, []int64, []uint, []uint8, []uint16, []uint32, []uint64, []string:
-		if reflect.ValueOf(value).Len() > 0 {
-			str = fmt.Sprintf("(%v.%v NOT IN (?))", scope.QuotedTableName(), scope.Quote(primaryKey))
-			clause["args"] = []interface{}{value}
-		} else {
-			return ""
-		}
-	case map[string]interface{}:
-		var sqls []string
-		for key, value := range value {
-			if value != nil {
-				sqls = append(sqls, fmt.Sprintf("(%v.%v <> %v)", scope.QuotedTableName(), scope.Quote(key), scope.AddToVars(value)))
-			} else {
-				sqls = append(sqls, fmt.Sprintf("(%v.%v IS NOT NULL)", scope.QuotedTableName(), scope.Quote(key)))
-			}
-		}
-		return strings.Join(sqls, " AND ")
-	case interface{}:
-		var sqls []string
-		var newScope = scope.New(value)
-		for _, field := range newScope.Fields() {
-			if !field.IsBlank {
-				sqls = append(sqls, fmt.Sprintf("(%v.%v <> %v)", scope.QuotedTableName(), scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface())))
-			}
+		if err != nil {
+			scope.Err(err)
 		}
-		return strings.Join(sqls, " AND ")
 	}
 
-	args := clause["args"].([]interface{})
-	for _, arg := range args {
-		switch reflect.ValueOf(arg).Kind() {
-		case reflect.Slice: // For where("id in (?)", []int64{1,2})
-			if bytes, ok := arg.([]byte); ok {
-				str = strings.Replace(str, "?", scope.AddToVars(bytes), 1)
-			} else if values := reflect.ValueOf(arg); values.Len() > 0 {
-				var tempMarks []string
-				for i := 0; i < values.Len(); i++ {
-					tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
-				}
-				str = strings.Replace(str, "?", strings.Join(tempMarks, ","), 1)
-			} else {
-				str = strings.Replace(str, "?", scope.AddToVars(Expr("NULL")), 1)
-			}
-		default:
-			if scanner, ok := interface{}(arg).(driver.Valuer); ok {
-				arg, _ = scanner.Value()
-			}
-			str = strings.Replace(notEqualSQL, "?", scope.AddToVars(arg), 1)
+	buff := bytes.NewBuffer([]byte{})
+	i := 0
+	for _, s := range str {
+		if s == '?' {
+			buff.WriteString(replacements[i])
+			i++
+		} else {
+			buff.WriteRune(s)
 		}
 	}
+
+	str = buff.String()
+
 	return
 }
 
@@ -661,6 +672,7 @@ func (scope *Scope) buildSelectQuery(clause map[string]interface{}) (str string)
 	}
 
 	args := clause["args"].([]interface{})
+	replacements := []string{}
 	for _, arg := range args {
 		switch reflect.ValueOf(arg).Kind() {
 		case reflect.Slice:
@@ -669,14 +681,28 @@ func (scope *Scope) buildSelectQuery(clause map[string]interface{}) (str string)
 			for i := 0; i < values.Len(); i++ {
 				tempMarks = append(tempMarks, scope.AddToVars(values.Index(i).Interface()))
 			}
-			str = strings.Replace(str, "?", strings.Join(tempMarks, ","), 1)
+			replacements = append(replacements, strings.Join(tempMarks, ","))
 		default:
 			if valuer, ok := interface{}(arg).(driver.Valuer); ok {
 				arg, _ = valuer.Value()
 			}
-			str = strings.Replace(str, "?", scope.AddToVars(arg), 1)
+			replacements = append(replacements, scope.AddToVars(arg))
 		}
 	}
+
+	buff := bytes.NewBuffer([]byte{})
+	i := 0
+	for pos := range str {
+		if str[pos] == '?' {
+			buff.WriteString(replacements[i])
+			i++
+		} else {
+			buff.WriteByte(str[pos])
+		}
+	}
+
+	str = buff.String()
+
 	return
 }
 
@@ -700,19 +726,19 @@ func (scope *Scope) whereSQL() (sql string) {
 	}
 
 	for _, clause := range scope.Search.whereConditions {
-		if sql := scope.buildWhereCondition(clause); sql != "" {
+		if sql := scope.buildCondition(clause, true); sql != "" {
 			andConditions = append(andConditions, sql)
 		}
 	}
 
 	for _, clause := range scope.Search.orConditions {
-		if sql := scope.buildWhereCondition(clause); sql != "" {
+		if sql := scope.buildCondition(clause, true); sql != "" {
 			orConditions = append(orConditions, sql)
 		}
 	}
 
 	for _, clause := range scope.Search.notConditions {
-		if sql := scope.buildNotCondition(clause); sql != "" {
+		if sql := scope.buildCondition(clause, false); sql != "" {
 			andConditions = append(andConditions, sql)
 		}
 	}
@@ -786,7 +812,7 @@ func (scope *Scope) havingSQL() string {
 
 	var andConditions []string
 	for _, clause := range scope.Search.havingConditions {
-		if sql := scope.buildWhereCondition(clause); sql != "" {
+		if sql := scope.buildCondition(clause, true); sql != "" {
 			andConditions = append(andConditions, sql)
 		}
 	}
@@ -802,7 +828,7 @@ func (scope *Scope) havingSQL() string {
 func (scope *Scope) joinsSQL() string {
 	var joinConditions []string
 	for _, clause := range scope.Search.joinConditions {
-		if sql := scope.buildWhereCondition(clause); sql != "" {
+		if sql := scope.buildCondition(clause, true); sql != "" {
 			joinConditions = append(joinConditions, strings.TrimSuffix(strings.TrimPrefix(sql, "("), ")"))
 		}
 	}
@@ -924,14 +950,34 @@ func (scope *Scope) initialize() *Scope {
 	return scope
 }
 
+func (scope *Scope) isQueryForColumn(query interface{}, column string) bool {
+	queryStr := strings.ToLower(fmt.Sprint(query))
+	if queryStr == column {
+		return true
+	}
+
+	if strings.HasSuffix(queryStr, "as "+column) {
+		return true
+	}
+
+	if strings.HasSuffix(queryStr, "as "+scope.Quote(column)) {
+		return true
+	}
+
+	return false
+}
+
 func (scope *Scope) pluck(column string, value interface{}) *Scope {
 	dest := reflect.Indirect(reflect.ValueOf(value))
-	scope.Search.Select(column)
 	if dest.Kind() != reflect.Slice {
 		scope.Err(fmt.Errorf("results should be a slice, not %s", dest.Kind()))
 		return scope
 	}
 
+	if query, ok := scope.Search.selects["query"]; !ok || !scope.isQueryForColumn(query, column) {
+		scope.Search.Select(column)
+	}
+
 	rows, err := scope.rows()
 	if scope.Err(err) == nil {
 		defer rows.Close()
@@ -950,7 +996,12 @@ func (scope *Scope) pluck(column string, value interface{}) *Scope {
 
 func (scope *Scope) count(value interface{}) *Scope {
 	if query, ok := scope.Search.selects["query"]; !ok || !countingQueryRegexp.MatchString(fmt.Sprint(query)) {
-		scope.Search.Select("count(*)")
+		if len(scope.Search.group) != 0 {
+			scope.Search.Select("count(*) FROM ( SELECT count(*) as name ")
+			scope.Search.group += " ) AS count_table"
+		} else {
+			scope.Search.Select("count(*)")
+		}
 	}
 	scope.Search.ignoreOrderQuery = true
 	scope.Err(scope.row().Scan(value))
@@ -993,18 +1044,6 @@ func (scope *Scope) changeableField(field *Field) bool {
 	return true
 }
 
-func (scope *Scope) shouldSaveAssociations() bool {
-	if saveAssociations, ok := scope.Get("gorm:save_associations"); ok {
-		if v, ok := saveAssociations.(bool); ok && !v {
-			return false
-		}
-		if v, ok := saveAssociations.(string); ok && (v != "skip") {
-			return false
-		}
-	}
-	return true && !scope.HasError()
-}
-
 func (scope *Scope) related(value interface{}, foreignKeys ...string) *Scope {
 	toScope := scope.db.NewScope(value)
 	tx := scope.db.Set("gorm:association:source", scope.Value)
@@ -1059,7 +1098,7 @@ func (scope *Scope) getTableOptions() string {
 	if !ok {
 		return ""
 	}
-	return tableOptions.(string)
+	return " " + tableOptions.(string)
 }
 
 func (scope *Scope) createJoinTable(field *StructField) {
@@ -1092,7 +1131,7 @@ func (scope *Scope) createJoinTable(field *StructField) {
 				}
 			}
 
-			scope.Err(scope.NewDB().Exec(fmt.Sprintf("CREATE TABLE %v (%v, PRIMARY KEY (%v)) %s", scope.Quote(joinTable), strings.Join(sqlTypes, ","), strings.Join(primaryKeys, ","), scope.getTableOptions())).Error)
+			scope.Err(scope.NewDB().Exec(fmt.Sprintf("CREATE TABLE %v (%v, PRIMARY KEY (%v))%s", scope.Quote(joinTable), strings.Join(sqlTypes, ","), strings.Join(primaryKeys, ","), scope.getTableOptions())).Error)
 		}
 		scope.NewDB().Table(joinTable).AutoMigrate(joinTableHandler)
 	}
@@ -1127,19 +1166,19 @@ func (scope *Scope) createTable() *Scope {
 		primaryKeyStr = fmt.Sprintf(", PRIMARY KEY (%v)", strings.Join(primaryKeys, ","))
 	}
 
-	scope.Raw(fmt.Sprintf("CREATE TABLE %v (%v %v) %s", scope.QuotedTableName(), strings.Join(tags, ","), primaryKeyStr, scope.getTableOptions())).Exec()
+	scope.Raw(fmt.Sprintf("CREATE TABLE %v (%v %v)%s", scope.QuotedTableName(), strings.Join(tags, ","), primaryKeyStr, scope.getTableOptions())).Exec()
 
 	scope.autoIndex()
 	return scope
 }
 
 func (scope *Scope) dropTable() *Scope {
-	scope.Raw(fmt.Sprintf("DROP TABLE %v", scope.QuotedTableName())).Exec()
+	scope.Raw(fmt.Sprintf("DROP TABLE %v%s", scope.QuotedTableName(), scope.getTableOptions())).Exec()
 	return scope
 }
 
 func (scope *Scope) modifyColumn(column string, typ string) {
-	scope.Raw(fmt.Sprintf("ALTER TABLE %v MODIFY %v %v", scope.QuotedTableName(), scope.Quote(column), typ)).Exec()
+	scope.db.AddError(scope.Dialect().ModifyColumn(scope.QuotedTableName(), scope.Quote(column), typ))
 }
 
 func (scope *Scope) dropColumn(column string) {
@@ -1165,7 +1204,8 @@ func (scope *Scope) addIndex(unique bool, indexName string, column ...string) {
 }
 
 func (scope *Scope) addForeignKey(field string, dest string, onDelete string, onUpdate string) {
-	keyName := scope.Dialect().BuildForeignKeyName(scope.TableName(), field, dest)
+	// Compatible with old generated key
+	keyName := scope.Dialect().BuildKeyName(scope.TableName(), field, dest, "foreign")
 
 	if scope.Dialect().HasForeignKey(scope.TableName(), keyName) {
 		return
@@ -1174,6 +1214,16 @@ func (scope *Scope) addForeignKey(field string, dest string, onDelete string, on
 	scope.Raw(fmt.Sprintf(query, scope.QuotedTableName(), scope.quoteIfPossible(keyName), scope.quoteIfPossible(field), dest, onDelete, onUpdate)).Exec()
 }
 
+func (scope *Scope) removeForeignKey(field string, dest string) {
+	keyName := scope.Dialect().BuildKeyName(scope.TableName(), field, dest)
+
+	if !scope.Dialect().HasForeignKey(scope.TableName(), keyName) {
+		return
+	}
+	var query = `ALTER TABLE %s DROP CONSTRAINT %s;`
+	scope.Raw(fmt.Sprintf(query, scope.QuotedTableName(), scope.quoteIfPossible(keyName))).Exec()
+}
+
 func (scope *Scope) removeIndex(indexName string) {
 	scope.Dialect().RemoveIndex(scope.TableName(), indexName)
 }
@@ -1209,7 +1259,7 @@ func (scope *Scope) autoIndex() *Scope {
 
 			for _, name := range names {
 				if name == "INDEX" || name == "" {
-					name = fmt.Sprintf("idx_%v_%v", scope.TableName(), field.DBName)
+					name = scope.Dialect().BuildKeyName("idx", scope.TableName(), field.DBName)
 				}
 				indexes[name] = append(indexes[name], field.DBName)
 			}
@@ -1220,7 +1270,7 @@ func (scope *Scope) autoIndex() *Scope {
 
 			for _, name := range names {
 				if name == "UNIQUE_INDEX" || name == "" {
-					name = fmt.Sprintf("uix_%v_%v", scope.TableName(), field.DBName)
+					name = scope.Dialect().BuildKeyName("uix", scope.TableName(), field.DBName)
 				}
 				uniqueIndexes[name] = append(uniqueIndexes[name], field.DBName)
 			}
@@ -1228,11 +1278,15 @@ func (scope *Scope) autoIndex() *Scope {
 	}
 
 	for name, columns := range indexes {
-		scope.NewDB().Model(scope.Value).AddIndex(name, columns...)
+		if db := scope.NewDB().Table(scope.TableName()).Model(scope.Value).AddIndex(name, columns...); db.Error != nil {
+			scope.db.AddError(db.Error)
+		}
 	}
 
 	for name, columns := range uniqueIndexes {
-		scope.NewDB().Model(scope.Value).AddUniqueIndex(name, columns...)
+		if db := scope.NewDB().Table(scope.TableName()).Model(scope.Value).AddUniqueIndex(name, columns...); db.Error != nil {
+			scope.db.AddError(db.Error)
+		}
 	}
 
 	return scope

+ 0 - 7
vendor/github.com/jinzhu/gorm/search.go

@@ -2,7 +2,6 @@ package gorm
 
 import (
 	"fmt"
-	"regexp"
 )
 
 type search struct {
@@ -73,13 +72,7 @@ func (s *search) Order(value interface{}, reorder ...bool) *search {
 	return s
 }
 
-var distinctSQLRegexp = regexp.MustCompile(`(?i)distinct[^a-z]+[a-z]+`)
-
 func (s *search) Select(query interface{}, args ...interface{}) *search {
-	if distinctSQLRegexp.MatchString(fmt.Sprint(query)) {
-		s.ignoreOrderQuery = true
-	}
-
 	s.selects = map[string]interface{}{"query": query, "args": args}
 	return s
 }

+ 1 - 1
vendor/github.com/jinzhu/gorm/test_all.sh

@@ -1,5 +1,5 @@
 dialects=("postgres" "mysql" "mssql" "sqlite")
 
 for dialect in "${dialects[@]}" ; do
-    GORM_DIALECT=${dialect} go test
+    DEBUG=false GORM_DIALECT=${dialect} go test
 done

+ 1 - 1
vendor/github.com/jinzhu/gorm/utils.go

@@ -23,7 +23,7 @@ var NowFunc = func() time.Time {
 }
 
 // Copied from golint
-var commonInitialisms = []string{"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UI", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"}
+var commonInitialisms = []string{"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UID", "UI", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"}
 var commonInitialismsReplacer *strings.Replacer
 
 var goSrcRegexp = regexp.MustCompile(`jinzhu/gorm/.*.go`)

+ 111 - 4
vendor/github.com/jinzhu/gorm/wercker.yml

@@ -2,17 +2,79 @@
 box: golang
 
 services:
-    - id: mariadb:10.0
+    - name: mariadb
+      id: mariadb:latest
       env:
           MYSQL_DATABASE: gorm
           MYSQL_USER: gorm
           MYSQL_PASSWORD: gorm
           MYSQL_RANDOM_ROOT_PASSWORD: "yes"
-    - id: postgres
+    - name: mysql
+      id: mysql:8
+      env:
+          MYSQL_DATABASE: gorm
+          MYSQL_USER: gorm
+          MYSQL_PASSWORD: gorm
+          MYSQL_RANDOM_ROOT_PASSWORD: "yes"
+    - name: mysql57
+      id: mysql:5.7
+      env:
+          MYSQL_DATABASE: gorm
+          MYSQL_USER: gorm
+          MYSQL_PASSWORD: gorm
+          MYSQL_RANDOM_ROOT_PASSWORD: "yes"
+    - name: mysql56
+      id: mysql:5.6
+      env:
+          MYSQL_DATABASE: gorm
+          MYSQL_USER: gorm
+          MYSQL_PASSWORD: gorm
+          MYSQL_RANDOM_ROOT_PASSWORD: "yes"
+    - name: mysql55
+      id: mysql:5.5
+      env:
+          MYSQL_DATABASE: gorm
+          MYSQL_USER: gorm
+          MYSQL_PASSWORD: gorm
+          MYSQL_RANDOM_ROOT_PASSWORD: "yes"
+    - name: postgres
+      id: postgres:latest
+      env:
+          POSTGRES_USER: gorm
+          POSTGRES_PASSWORD: gorm
+          POSTGRES_DB: gorm
+    - name: postgres96
+      id: postgres:9.6
+      env:
+          POSTGRES_USER: gorm
+          POSTGRES_PASSWORD: gorm
+          POSTGRES_DB: gorm
+    - name: postgres95
+      id: postgres:9.5
+      env:
+          POSTGRES_USER: gorm
+          POSTGRES_PASSWORD: gorm
+          POSTGRES_DB: gorm
+    - name: postgres94
+      id: postgres:9.4
       env:
           POSTGRES_USER: gorm
           POSTGRES_PASSWORD: gorm
           POSTGRES_DB: gorm
+    - name: postgres93
+      id: postgres:9.3
+      env:
+          POSTGRES_USER: gorm
+          POSTGRES_PASSWORD: gorm
+          POSTGRES_DB: gorm
+    - name: mssql
+      id: mcmoe/mssqldocker:latest
+      env:
+        ACCEPT_EULA: Y
+        SA_PASSWORD: LoremIpsum86
+        MSSQL_DB: gorm
+        MSSQL_USER: gorm
+        MSSQL_PASSWORD: LoremIpsum86
 
 # The steps that will be executed in the build pipeline
 build:
@@ -42,12 +104,57 @@ build:
                 code: |
                     go test ./...
 
+        - script:
+                name: test mariadb
+                code: |
+                    GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mariadb:3306)/gorm?charset=utf8&parseTime=True" go test ./...
+
         - script:
                 name: test mysql
                 code: |
-                    GORM_DIALECT=mysql GORM_DBADDRESS=mariadb:3306 go test ./...
+                    GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql:3306)/gorm?charset=utf8&parseTime=True" go test ./...
+
+        - script:
+                name: test mysql5.7
+                code: |
+                    GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql57:3306)/gorm?charset=utf8&parseTime=True" go test ./...
+
+        - script:
+                name: test mysql5.6
+                code: |
+                    GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql56:3306)/gorm?charset=utf8&parseTime=True" go test ./...
+
+        - script:
+                name: test mysql5.5
+                code: |
+                    GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql55:3306)/gorm?charset=utf8&parseTime=True" go test ./...
 
         - script:
                 name: test postgres
                 code: |
-                    GORM_DIALECT=postgres GORM_DBHOST=postgres go test ./...
+                    GORM_DIALECT=postgres GORM_DSN="host=postgres user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
+
+        - script:
+                name: test postgres96
+                code: |
+                    GORM_DIALECT=postgres GORM_DSN="host=postgres96 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
+
+        - script:
+                name: test postgres95
+                code: |
+                    GORM_DIALECT=postgres GORM_DSN="host=postgres95 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
+
+        - script:
+                name: test postgres94
+                code: |
+                    GORM_DIALECT=postgres GORM_DSN="host=postgres94 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
+
+        - script:
+                name: test postgres93
+                code: |
+                    GORM_DIALECT=postgres GORM_DSN="host=postgres93 user=gorm password=gorm DB.name=gorm port=5432 sslmode=disable" go test ./...
+
+        - script:
+                name: test mssql
+                code: |
+                    GORM_DIALECT=mssql GORM_DSN="sqlserver://gorm:LoremIpsum86@mssql:1433?database=gorm" go test ./...

+ 0 - 4
vendor/github.com/robfig/cron/README.md

@@ -1,6 +1,2 @@
 [![GoDoc](http://godoc.org/github.com/robfig/cron?status.png)](http://godoc.org/github.com/robfig/cron) 
 [![Build Status](https://travis-ci.org/robfig/cron.svg?branch=master)](https://travis-ci.org/robfig/cron)
-
-# cron
-
-Documentation here: https://godoc.org/github.com/robfig/cron

+ 3 - 3
vendor/github.com/robfig/cron/doc.go

@@ -78,7 +78,7 @@ You may use one of several pre-defined schedules in place of a cron expression.
 	-----                  | -----------                                | -------------
 	@yearly (or @annually) | Run once a year, midnight, Jan. 1st        | 0 0 0 1 1 *
 	@monthly               | Run once a month, midnight, first of month | 0 0 0 1 * *
-	@weekly                | Run once a week, midnight between Sat/Sun  | 0 0 0 * * 0
+	@weekly                | Run once a week, midnight on Sunday        | 0 0 0 * * 0
 	@daily (or @midnight)  | Run once a day, midnight                   | 0 0 0 * * *
 	@hourly                | Run once an hour, beginning of hour        | 0 0 * * * *
 
@@ -92,8 +92,8 @@ or cron is run. This is supported by formatting the cron spec like this:
 where "duration" is a string accepted by time.ParseDuration
 (http://golang.org/pkg/time/#ParseDuration).
 
-For example, "@every 1h30m10s" would indicate a schedule that activates after
-1 hour, 30 minutes, 10 seconds, and then every interval after that.
+For example, "@every 1h30m10s" would indicate a schedule that activates immediately,
+and then every 1 hour, 30 minutes, 10 seconds.
 
 Note: The interval does not take the job runtime into account.  For example,
 if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes,

+ 25 - 0
vendor/gogs.carducci-dante.gov.it/karmen/client/client.go

@@ -261,6 +261,31 @@ func (c *Client) GetDepartments() ([]*orm.Department, error) {
 	return departments, nil
 }
 
+func (c *Client) GetOffices() ([]*orm.Office, error) {
+	var (
+		response renderer.JsonResponse
+		offices  []*orm.Office
+	)
+
+	data, err := c.SendRequest("GET", "/api/offices?format=json", nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if err := json.Unmarshal(data, &response); err != nil {
+		return nil, err
+	}
+
+	if string(response.Error) != "" {
+		return nil, errors.New(string(response.Error))
+	}
+
+	if err := json.Unmarshal(response.Result, &offices); err != nil {
+		return nil, err
+	}
+	return offices, nil
+}
+
 func (c *Client) GetClasses() ([]*orm.Class, error) {
 	var (
 		response renderer.JsonResponse