summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost/gorp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattermost/gorp')
-rw-r--r--vendor/github.com/mattermost/gorp/.gitignore9
-rw-r--r--vendor/github.com/mattermost/gorp/.travis.yml33
-rw-r--r--vendor/github.com/mattermost/gorp/CONTRIBUTING.md34
-rw-r--r--vendor/github.com/mattermost/gorp/LICENSE22
-rw-r--r--vendor/github.com/mattermost/gorp/README.md801
-rw-r--r--vendor/github.com/mattermost/gorp/column.go83
-rw-r--r--vendor/github.com/mattermost/gorp/db.go803
-rw-r--r--vendor/github.com/mattermost/gorp/dialect.go114
-rw-r--r--vendor/github.com/mattermost/gorp/dialect_mysql.go173
-rw-r--r--vendor/github.com/mattermost/gorp/dialect_mysql_test.go204
-rw-r--r--vendor/github.com/mattermost/gorp/dialect_oracle.go148
-rw-r--r--vendor/github.com/mattermost/gorp/dialect_postgres.go149
-rw-r--r--vendor/github.com/mattermost/gorp/dialect_sqlite.go121
-rw-r--r--vendor/github.com/mattermost/gorp/dialect_sqlserver.go154
-rw-r--r--vendor/github.com/mattermost/gorp/errors.go38
-rw-r--r--vendor/github.com/mattermost/gorp/gorp.go615
-rw-r--r--vendor/github.com/mattermost/gorp/gorp_suite_test.go13
-rw-r--r--vendor/github.com/mattermost/gorp/gorp_test.go2696
-rw-r--r--vendor/github.com/mattermost/gorp/hooks.go49
-rw-r--r--vendor/github.com/mattermost/gorp/index.go56
-rw-r--r--vendor/github.com/mattermost/gorp/lockerror.go63
-rw-r--r--vendor/github.com/mattermost/gorp/logging.go44
-rw-r--r--vendor/github.com/mattermost/gorp/nulltypes.go58
-rw-r--r--vendor/github.com/mattermost/gorp/select.go383
-rw-r--r--vendor/github.com/mattermost/gorp/table.go247
-rw-r--r--vendor/github.com/mattermost/gorp/table_bindings.go312
-rwxr-xr-xvendor/github.com/mattermost/gorp/test_all.sh41
-rw-r--r--vendor/github.com/mattermost/gorp/transaction.go219
28 files changed, 7682 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/gorp/.gitignore b/vendor/github.com/mattermost/gorp/.gitignore
new file mode 100644
index 000000000..d18c8ca70
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/.gitignore
@@ -0,0 +1,9 @@
+_test
+*.test
+_testmain.go
+_obj
+*~
+*.6
+6.out
+gorptest.bin
+tmp
diff --git a/vendor/github.com/mattermost/gorp/.travis.yml b/vendor/github.com/mattermost/gorp/.travis.yml
new file mode 100644
index 000000000..d35c77e71
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/.travis.yml
@@ -0,0 +1,33 @@
+language: go
+go:
+- 1.6
+- 1.7
+- tip
+
+matrix:
+ allow_failures:
+ - go: tip
+
+services:
+- mysql
+- postgres
+- sqlite3
+
+env:
+ global:
+ - secure: RriLxF6+2yMl67hdVv8ImXlu0h62mhcpqjaOgYNU+IEbUQ7hx96CKY6gkpYubW3BgApvF5RH6j3+HKvh2kGp0XhDOYOQCODfBSaSipZ5Aa5RKjsEYLtuVIobvJ80awR9hUeql69+WXs0/s72WThG0qTbOUY4pqHWfteeY235hWM=
+
+before_script:
+- mysql -e "CREATE DATABASE gorptest;"
+- mysql -u root -e "GRANT ALL ON gorptest.* TO gorptest@localhost IDENTIFIED BY 'gorptest'"
+- psql -c "CREATE DATABASE gorptest;" -U postgres
+- psql -c "CREATE USER "gorptest" WITH SUPERUSER PASSWORD 'gorptest';" -U postgres
+- go get github.com/lib/pq
+- go get github.com/mattn/go-sqlite3
+- go get github.com/ziutek/mymysql/godrv
+- go get github.com/go-sql-driver/mysql
+- go get golang.org/x/tools/cmd/cover
+- go get github.com/mattn/goveralls
+- go get github.com/onsi/ginkgo/ginkgo
+
+script: ./test_all.sh
diff --git a/vendor/github.com/mattermost/gorp/CONTRIBUTING.md b/vendor/github.com/mattermost/gorp/CONTRIBUTING.md
new file mode 100644
index 000000000..7bc145fd7
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/CONTRIBUTING.md
@@ -0,0 +1,34 @@
+# Contributions are very welcome!
+
+## First: Create an Issue
+
+Even if your fix is simple, we'd like to have an issue to relate to
+the PR. Discussion about the architecture and value can go on the
+issue, leaving PR comments exclusively for coding style.
+
+## Second: Make Your PR
+
+- Fork the `master` branch
+- Make your change
+- Make a PR against the `master` branch
+
+You don't need to wait for comments on the issue before making your
+PR. If you do wait for comments, you'll have a better chance of
+getting your PR accepted the first time around, but it's not
+necessary.
+
+## Third: Be Patient
+
+- If your change breaks backward compatibility, this becomes
+ especially true.
+
+We all have lives and jobs, and many of us are no longer on projects
+that make use of `gorp`. We will get back to you, but it might take a
+while.
+
+## Fourth: Consider Becoming a Maintainer
+
+We really do need help. We will likely ask you for help after a good
+PR, but if we don't, please create an issue requesting maintainership.
+Considering how few of us are currently active, we are unlikely to
+refuse good help.
diff --git a/vendor/github.com/mattermost/gorp/LICENSE b/vendor/github.com/mattermost/gorp/LICENSE
new file mode 100644
index 000000000..b661111d0
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2012 James Cooper <james@bitmechanic.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/mattermost/gorp/README.md b/vendor/github.com/mattermost/gorp/README.md
new file mode 100644
index 000000000..53ee245d3
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/README.md
@@ -0,0 +1,801 @@
+# Go Relational Persistence
+
+[![build status](https://img.shields.io/travis/go-gorp/gorp.svg)](http://travis-ci.org/go-gorp/gorp)
+[![code coverage](https://img.shields.io/coveralls/go-gorp/gorp.svg)](https://coveralls.io/r/go-gorp/gorp)
+[![issues](https://img.shields.io/github/issues/go-gorp/gorp.svg)](https://github.com/go-gorp/gorp/issues)
+[![godoc v1](https://img.shields.io/badge/godoc-v1-375EAB.svg)](https://godoc.org/gopkg.in/gorp.v1)
+[![godoc v2](https://img.shields.io/badge/godoc-v2-375EAB.svg)](https://godoc.org/gopkg.in/gorp.v2)
+[![godoc bleeding edge](https://img.shields.io/badge/godoc-bleeding--edge-375EAB.svg)](https://godoc.org/github.com/go-gorp/gorp)
+
+### Update 2016-11-13: Future versions
+
+As many of the maintainers have become busy with other projects,
+progress toward the ever-elusive v2 has slowed to the point that we're
+only occasionally making progress outside of merging pull requests.
+In the interest of continuing to release, I'd like to lean toward a
+more maintainable path forward.
+
+For the moment, I am releasing a v2 tag with the current feature set
+from master, as some of those features have been actively used and
+relied on by more than one project. Our next goal is to continue
+cleaning up the code base with non-breaking changes as much as
+possible, but if/when a breaking change is needed, we'll just release
+new versions. This allows us to continue development at whatever pace
+we're capable of, without delaying the release of features or refusing
+PRs.
+
+## Introduction
+
+I hesitate to call gorp an ORM. Go doesn't really have objects, at
+least not in the classic Smalltalk/Java sense. There goes the "O".
+gorp doesn't know anything about the relationships between your
+structs (at least not yet). So the "R" is questionable too (but I use
+it in the name because, well, it seemed more clever).
+
+The "M" is alive and well. Given some Go structs and a database, gorp
+should remove a fair amount of boilerplate busy-work from your code.
+
+I hope that gorp saves you time, minimizes the drudgery of getting
+data in and out of your database, and helps your code focus on
+algorithms, not infrastructure.
+
+* Bind struct fields to table columns via API or tag
+* Support for embedded structs
+* Support for transactions
+* Forward engineer db schema from structs (great for unit tests)
+* Pre/post insert/update/delete hooks
+* Automatically generate insert/update/delete statements for a struct
+* Automatic binding of auto increment PKs back to struct after insert
+* Delete by primary key(s)
+* Select by primary key(s)
+* Optional trace sql logging
+* Bind arbitrary SQL queries to a struct
+* Bind slice to SELECT query results without type assertions
+* Use positional or named bind parameters in custom SELECT queries
+* Optional optimistic locking using a version column (for
+ update/deletes)
+
+## Installation
+
+Use `go get` or your favorite vendoring tool, using whichever import
+path you'd like.
+
+## Versioning
+
+We use semantic version tags. Feel free to import through `gopkg.in`
+(e.g. `gopkg.in/gorp.v2`) to get the latest tag for a major version,
+or check out the tag using your favorite vendoring tool.
+
+Development is not very active right now, but we have plans to
+restructure `gorp` as we continue to move toward a more extensible
+system. Whenever a breaking change is needed, the major version will
+be bumped.
+
+The `master` branch is where all development is done, and breaking
+changes may happen from time to time. That said, if you want to live
+on the bleeding edge and are comfortable updating your code when we
+make a breaking change, you may use `github.com/go-gorp/gorp` as your
+import path.
+
+Check the version tags to see what's available. We'll make a good
+faith effort to add badges for new versions, but we make no
+guarantees.
+
+## Supported Go versions
+
+This package is guaranteed to be compatible with the latest 2 major
+versions of Go.
+
+Any earlier versions are only supported on a best effort basis and can
+be dropped any time. Go has a great compatibility promise. Upgrading
+your program to a newer version of Go should never really be a
+problem.
+
+## Migration guide
+
+#### Pre-v2 to v2
+Automatic mapping of the version column used in optimistic locking has
+been removed as it could cause problems if the type was not int. The
+version column must now explicitly be set with
+`tablemap.SetVersionCol()`.
+
+## Help/Support
+
+Use our [`gitter` channel](https://gitter.im/go-gorp/gorp). We used
+to use IRC, but with most of us being pulled in many directions, we
+often need the email notifications from `gitter` to yell at us to sign
+in.
+
+## Quickstart
+
+```go
+package main
+
+import (
+ "database/sql"
+ "gopkg.in/gorp.v1"
+ _ "github.com/mattn/go-sqlite3"
+ "log"
+ "time"
+)
+
+func main() {
+ // initialize the DbMap
+ dbmap := initDb()
+ defer dbmap.Db.Close()
+
+ // delete any existing rows
+ err := dbmap.TruncateTables()
+ checkErr(err, "TruncateTables failed")
+
+ // create two posts
+ p1 := newPost("Go 1.1 released!", "Lorem ipsum lorem ipsum")
+ p2 := newPost("Go 1.2 released!", "Lorem ipsum lorem ipsum")
+
+ // insert rows - auto increment PKs will be set properly after the insert
+ err = dbmap.Insert(&p1, &p2)
+ checkErr(err, "Insert failed")
+
+ // use convenience SelectInt
+ count, err := dbmap.SelectInt("select count(*) from posts")
+ checkErr(err, "select count(*) failed")
+ log.Println("Rows after inserting:", count)
+
+ // update a row
+ p2.Title = "Go 1.2 is better than ever"
+ count, err = dbmap.Update(&p2)
+ checkErr(err, "Update failed")
+ log.Println("Rows updated:", count)
+
+ // fetch one row - note use of "post_id" instead of "Id" since column is aliased
+ //
+ // Postgres users should use $1 instead of ? placeholders
+ // See 'Known Issues' below
+ //
+ err = dbmap.SelectOne(&p2, "select * from posts where post_id=?", p2.Id)
+ checkErr(err, "SelectOne failed")
+ log.Println("p2 row:", p2)
+
+ // fetch all rows
+ var posts []Post
+ _, err = dbmap.Select(&posts, "select * from posts order by post_id")
+ checkErr(err, "Select failed")
+ log.Println("All rows:")
+ for x, p := range posts {
+ log.Printf(" %d: %v\n", x, p)
+ }
+
+ // delete row by PK
+ count, err = dbmap.Delete(&p1)
+ checkErr(err, "Delete failed")
+ log.Println("Rows deleted:", count)
+
+ // delete row manually via Exec
+ _, err = dbmap.Exec("delete from posts where post_id=?", p2.Id)
+ checkErr(err, "Exec failed")
+
+ // confirm count is zero
+ count, err = dbmap.SelectInt("select count(*) from posts")
+ checkErr(err, "select count(*) failed")
+ log.Println("Row count - should be zero:", count)
+
+ log.Println("Done!")
+}
+
+type Post struct {
+ // db tag lets you specify the column name if it differs from the struct field
+ Id int64 `db:"post_id"`
+ Created int64
+ Title string `db:",size:50"` // Column size set to 50
+ Body string `db:"article_body,size:1024"` // Set both column name and size
+}
+
+func newPost(title, body string) Post {
+ return Post{
+ Created: time.Now().UnixNano(),
+ Title: title,
+ Body: body,
+ }
+}
+
+func initDb() *gorp.DbMap {
+ // connect to db using standard Go database/sql API
+ // use whatever database/sql driver you wish
+ db, err := sql.Open("sqlite3", "/tmp/post_db.bin")
+ checkErr(err, "sql.Open failed")
+
+ // construct a gorp DbMap
+ dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
+
+ // add a table, setting the table name to 'posts' and
+ // specifying that the Id property is an auto incrementing PK
+ dbmap.AddTableWithName(Post{}, "posts").SetKeys(true, "Id")
+
+ // create the table. in a production system you'd generally
+ // use a migration tool, or create the tables via scripts
+ err = dbmap.CreateTablesIfNotExists()
+ checkErr(err, "Create tables failed")
+
+ return dbmap
+}
+
+func checkErr(err error, msg string) {
+ if err != nil {
+ log.Fatalln(msg, err)
+ }
+}
+```
+
+## Examples
+
+### Mapping structs to tables
+
+First define some types:
+
+```go
+type Invoice struct {
+ Id int64
+ Created int64
+ Updated int64
+ Memo string
+ PersonId int64
+}
+
+type Person struct {
+ Id int64
+ Created int64
+ Updated int64
+ FName string
+ LName string
+}
+
+// Example of using tags to alias fields to column names
+// The 'db' value is the column name
+//
+// A hyphen will cause gorp to skip this field, similar to the
+// Go json package.
+//
+// This is equivalent to using the ColMap methods:
+//
+// table := dbmap.AddTableWithName(Product{}, "product")
+// table.ColMap("Id").Rename("product_id")
+// table.ColMap("Price").Rename("unit_price")
+// table.ColMap("IgnoreMe").SetTransient(true)
+//
+// You can optionally declare the field to be a primary key and/or autoincrement
+//
+type Product struct {
+ Id int64 `db:"product_id, primarykey, autoincrement"`
+ Price int64 `db:"unit_price"`
+ IgnoreMe string `db:"-"`
+}
+```
+
+Then create a mapper, typically you'd do this one time at app startup:
+
+```go
+// connect to db using standard Go database/sql API
+// use whatever database/sql driver you wish
+db, err := sql.Open("mymysql", "tcp:localhost:3306*mydb/myuser/mypassword")
+
+// construct a gorp DbMap
+dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
+
+// register the structs you wish to use with gorp
+// you can also use the shorter dbmap.AddTable() if you
+// don't want to override the table name
+//
+// SetKeys(true) means we have a auto increment primary key, which
+// will get automatically bound to your struct post-insert
+//
+t1 := dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id")
+t2 := dbmap.AddTableWithName(Person{}, "person_test").SetKeys(true, "Id")
+t3 := dbmap.AddTableWithName(Product{}, "product_test").SetKeys(true, "Id")
+```
+
+### Struct Embedding
+
+gorp supports embedding structs. For example:
+
+```go
+type Names struct {
+ FirstName string
+ LastName string
+}
+
+type WithEmbeddedStruct struct {
+ Id int64
+ Names
+}
+
+es := &WithEmbeddedStruct{-1, Names{FirstName: "Alice", LastName: "Smith"}}
+err := dbmap.Insert(es)
+```
+
+See the `TestWithEmbeddedStruct` function in `gorp_test.go` for a full example.
+
+### Create/Drop Tables ###
+
+Automatically create / drop registered tables. This is useful for unit tests
+but is entirely optional. You can of course use gorp with tables created manually,
+or with a separate migration tool (like [goose](https://bitbucket.org/liamstask/goose) or [migrate](https://github.com/mattes/migrate)).
+
+```go
+// create all registered tables
+dbmap.CreateTables()
+
+// same as above, but uses "if not exists" clause to skip tables that are
+// already defined
+dbmap.CreateTablesIfNotExists()
+
+// drop
+dbmap.DropTables()
+```
+
+### SQL Logging
+
+Optionally you can pass in a logger to trace all SQL statements.
+I recommend enabling this initially while you're getting the feel for what
+gorp is doing on your behalf.
+
+Gorp defines a `GorpLogger` interface that Go's built in `log.Logger` satisfies.
+However, you can write your own `GorpLogger` implementation, or use a package such
+as `glog` if you want more control over how statements are logged.
+
+```go
+// Will log all SQL statements + args as they are run
+// The first arg is a string prefix to prepend to all log messages
+dbmap.TraceOn("[gorp]", log.New(os.Stdout, "myapp:", log.Lmicroseconds))
+
+// Turn off tracing
+dbmap.TraceOff()
+```
+
+### Insert
+
+```go
+// Must declare as pointers so optional callback hooks
+// can operate on your data, not copies
+inv1 := &Invoice{0, 100, 200, "first order", 0}
+inv2 := &Invoice{0, 100, 200, "second order", 0}
+
+// Insert your rows
+err := dbmap.Insert(inv1, inv2)
+
+// Because we called SetKeys(true) on Invoice, the Id field
+// will be populated after the Insert() automatically
+fmt.Printf("inv1.Id=%d inv2.Id=%d\n", inv1.Id, inv2.Id)
+```
+
+### Update
+
+Continuing the above example, use the `Update` method to modify an Invoice:
+
+```go
+// count is the # of rows updated, which should be 1 in this example
+count, err := dbmap.Update(inv1)
+```
+
+### Delete
+
+If you have primary key(s) defined for a struct, you can use the `Delete`
+method to remove rows:
+
+```go
+count, err := dbmap.Delete(inv1)
+```
+
+### Select by Key
+
+Use the `Get` method to fetch a single row by primary key. It returns
+nil if no row is found.
+
+```go
+// fetch Invoice with Id=99
+obj, err := dbmap.Get(Invoice{}, 99)
+inv := obj.(*Invoice)
+```
+
+### Ad Hoc SQL
+
+#### SELECT
+
+`Select()` and `SelectOne()` provide a simple way to bind arbitrary queries to a slice
+or a single struct.
+
+```go
+// Select a slice - first return value is not needed when a slice pointer is passed to Select()
+var posts []Post
+_, err := dbmap.Select(&posts, "select * from post order by id")
+
+// You can also use primitive types
+var ids []string
+_, err := dbmap.Select(&ids, "select id from post")
+
+// Select a single row.
+// Returns an error if no row found, or if more than one row is found
+var post Post
+err := dbmap.SelectOne(&post, "select * from post where id=?", id)
+```
+
+Want to do joins? Just write the SQL and the struct. gorp will bind them:
+
+```go
+// Define a type for your join
+// It *must* contain all the columns in your SELECT statement
+//
+// The names here should match the aliased column names you specify
+// in your SQL - no additional binding work required. simple.
+//
+type InvoicePersonView struct {
+ InvoiceId int64
+ PersonId int64
+ Memo string
+ FName string
+}
+
+// Create some rows
+p1 := &Person{0, 0, 0, "bob", "smith"}
+dbmap.Insert(p1)
+
+// notice how we can wire up p1.Id to the invoice easily
+inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id}
+dbmap.Insert(inv1)
+
+// Run your query
+query := "select i.Id InvoiceId, p.Id PersonId, i.Memo, p.FName " +
+ "from invoice_test i, person_test p " +
+ "where i.PersonId = p.Id"
+
+// pass a slice to Select()
+var list []InvoicePersonView
+_, err := dbmap.Select(&list, query)
+
+// this should test true
+expected := InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName}
+if reflect.DeepEqual(list[0], expected) {
+ fmt.Println("Woot! My join worked!")
+}
+```
+
+#### SELECT string or int64
+
+gorp provides a few convenience methods for selecting a single string or int64.
+
+```go
+// select single int64 from db (use $1 instead of ? for postgresql)
+i64, err := dbmap.SelectInt("select count(*) from foo where blah=?", blahVal)
+
+// select single string from db:
+s, err := dbmap.SelectStr("select name from foo where blah=?", blahVal)
+
+```
+
+#### Named bind parameters
+
+You may use a map or struct to bind parameters by name. This is currently
+only supported in SELECT queries.
+
+```go
+_, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age", map[string]interface{}{
+ "name": "Rob",
+ "age": 31,
+})
+```
+
+#### UPDATE / DELETE
+
+You can execute raw SQL if you wish. Particularly good for batch operations.
+
+```go
+res, err := dbmap.Exec("delete from invoice_test where PersonId=?", 10)
+```
+
+### Transactions
+
+You can batch operations into a transaction:
+
+```go
+func InsertInv(dbmap *DbMap, inv *Invoice, per *Person) error {
+ // Start a new transaction
+ trans, err := dbmap.Begin()
+ if err != nil {
+ return err
+ }
+
+ trans.Insert(per)
+ inv.PersonId = per.Id
+ trans.Insert(inv)
+
+ // if the commit is successful, a nil error is returned
+ return trans.Commit()
+}
+```
+
+### Hooks
+
+Use hooks to update data before/after saving to the db. Good for timestamps:
+
+```go
+// implement the PreInsert and PreUpdate hooks
+func (i *Invoice) PreInsert(s gorp.SqlExecutor) error {
+ i.Created = time.Now().UnixNano()
+ i.Updated = i.Created
+ return nil
+}
+
+func (i *Invoice) PreUpdate(s gorp.SqlExecutor) error {
+ i.Updated = time.Now().UnixNano()
+ return nil
+}
+
+// You can use the SqlExecutor to cascade additional SQL
+// Take care to avoid cycles. gorp won't prevent them.
+//
+// Here's an example of a cascading delete
+//
+func (p *Person) PreDelete(s gorp.SqlExecutor) error {
+ query := "delete from invoice_test where PersonId=?"
+
+ _, err := s.Exec(query, p.Id)
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+```
+
+Full list of hooks that you can implement:
+
+ PostGet
+ PreInsert
+ PostInsert
+ PreUpdate
+ PostUpdate
+ PreDelete
+ PostDelete
+
+ All have the same signature. for example:
+
+ func (p *MyStruct) PostUpdate(s gorp.SqlExecutor) error
+
+### Optimistic Locking
+
+#### Note that this behaviour has changed in v2. See [Migration Guide](#migration-guide).
+
+gorp provides a simple optimistic locking feature, similar to Java's
+JPA, that will raise an error if you try to update/delete a row whose
+`version` column has a value different than the one in memory. This
+provides a safe way to do "select then update" style operations
+without explicit read and write locks.
+
+```go
+// Version is an auto-incremented number, managed by gorp
+// If this property is present on your struct, update
+// operations will be constrained
+//
+// For example, say we defined Person as:
+
+type Person struct {
+ Id int64
+ Created int64
+ Updated int64
+ FName string
+ LName string
+
+ // automatically used as the Version col
+ // use table.SetVersionCol("columnName") to map a different
+ // struct field as the version field
+ Version int64
+}
+
+p1 := &Person{0, 0, 0, "Bob", "Smith", 0}
+dbmap.Insert(p1) // Version is now 1
+
+obj, err := dbmap.Get(Person{}, p1.Id)
+p2 := obj.(*Person)
+p2.LName = "Edwards"
+dbmap.Update(p2) // Version is now 2
+
+p1.LName = "Howard"
+
+// Raises error because p1.Version == 1, which is out of date
+count, err := dbmap.Update(p1)
+_, ok := err.(gorp.OptimisticLockError)
+if ok {
+ // should reach this statement
+
+ // in a real app you might reload the row and retry, or
+ // you might propegate this to the user, depending on the desired
+ // semantics
+ fmt.Printf("Tried to update row with stale data: %v\n", err)
+} else {
+ // some other db error occurred - log or return up the stack
+ fmt.Printf("Unknown db err: %v\n", err)
+}
+```
+### Adding INDEX(es) on column(s) beyond the primary key ###
+
+Indexes are frequently critical for performance. Here is how to add
+them to your tables.
+
+NB: SqlServer and Oracle need testing and possible adjustment to the
+CreateIndexSuffix() and DropIndexSuffix() methods to make AddIndex()
+work for them.
+
+In the example below we put an index both on the Id field, and on the
+AcctId field.
+
+```
+type Account struct {
+ Id int64
+ AcctId string // e.g. this might be a long uuid for portability
+}
+
+// indexType (the 2nd param to AddIndex call) is "Btree" or "Hash" for MySQL.
+// demonstrate adding a second index on AcctId, and constrain that field to have unique values.
+dbm.AddTable(iptab.Account{}).SetKeys(true, "Id").AddIndex("AcctIdIndex", "Btree", []string{"AcctId"}).SetUnique(true)
+
+err = dbm.CreateTablesIfNotExists()
+checkErr(err, "CreateTablesIfNotExists failed")
+
+err = dbm.CreateIndex()
+checkErr(err, "CreateIndex failed")
+
+```
+Check the effect of the CreateIndex() call in mysql:
+```
+$ mysql
+
+MariaDB [test]> show create table Account;
++---------+--------------------------+
+| Account | CREATE TABLE `Account` (
+ `Id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `AcctId` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`Id`),
+ UNIQUE KEY `AcctIdIndex` (`AcctId`) USING BTREE <<<--- yes! index added.
+) ENGINE=InnoDB DEFAULT CHARSET=utf8
++---------+--------------------------+
+
+```
+
+
+## Database Drivers
+
+gorp uses the Go 1 `database/sql` package. A full list of compliant
+drivers is available here:
+
+http://code.google.com/p/go-wiki/wiki/SQLDrivers
+
+Sadly, SQL databases differ on various issues. gorp provides a Dialect
+interface that should be implemented per database vendor. Dialects
+are provided for:
+
+* MySQL
+* PostgreSQL
+* sqlite3
+
+Each of these three databases pass the test suite. See `gorp_test.go`
+for example DSNs for these three databases.
+
+Support is also provided for:
+
+* Oracle (contributed by @klaidliadon)
+* SQL Server (contributed by @qrawl) - use driver:
+ github.com/denisenkom/go-mssqldb
+
+Note that these databases are not covered by CI and I (@coopernurse)
+have no good way to test them locally. So please try them and send
+patches as needed, but expect a bit more unpredicability.
+
+## Sqlite3 Extensions
+
+In order to use sqlite3 extensions you need to first register a custom driver:
+
+```go
+import (
+ "database/sql"
+
+ // use whatever database/sql driver you wish
+ sqlite "github.com/mattn/go-sqlite3"
+)
+
+func customDriver() (*sql.DB, error) {
+
+ // create custom driver with extensions defined
+ sql.Register("sqlite3-custom", &sqlite.SQLiteDriver{
+ Extensions: []string{
+ "mod_spatialite",
+ },
+ })
+
+ // now you can then connect using the 'sqlite3-custom' driver instead of 'sqlite3'
+ return sql.Open("sqlite3-custom", "/tmp/post_db.bin")
+}
+```
+
+## Known Issues
+
+### SQL placeholder portability
+
+Different databases use different strings to indicate variable
+placeholders in prepared SQL statements. Unlike some database
+abstraction layers (such as JDBC), Go's `database/sql` does not
+standardize this.
+
+SQL generated by gorp in the `Insert`, `Update`, `Delete`, and `Get`
+methods delegates to a Dialect implementation for each database, and
+will generate portable SQL.
+
+Raw SQL strings passed to `Exec`, `Select`, `SelectOne`, `SelectInt`,
+etc will not be parsed. Consequently you may have portability issues
+if you write a query like this:
+
+```go // works on MySQL and Sqlite3, but not with Postgresql err :=
+dbmap.SelectOne(&val, "select * from foo where id = ?", 30) ```
+
+In `Select` and `SelectOne` you can use named parameters to work
+around this. The following is portable:
+
+```go err := dbmap.SelectOne(&val, "select * from foo where id = :id",
+map[string]interface{} { "id": 30}) ```
+
+Additionally, when using Postgres as your database, you should utilize
+`$1` instead of `?` placeholders as utilizing `?` placeholders when
+querying Postgres will result in `pq: operator does not exist`
+errors. Alternatively, use `dbMap.Dialect.BindVar(varIdx)` to get the
+proper variable binding for your dialect.
+
+### time.Time and time zones
+
+gorp will pass `time.Time` fields through to the `database/sql`
+driver, but note that the behavior of this type varies across database
+drivers.
+
+MySQL users should be especially cautious. See:
+https://github.com/ziutek/mymysql/pull/77
+
+To avoid any potential issues with timezone/DST, consider:
+
+- Using an integer field for time data and storing UNIX time.
+- Using a custom time type that implements some SQL types:
+ - [`"database/sql".Scanner`](https://golang.org/pkg/database/sql/#Scanner)
+ - [`"database/sql/driver".Valuer`](https://golang.org/pkg/database/sql/driver/#Valuer)
+
+## Running the tests
+
+The included tests may be run against MySQL, Postgresql, or sqlite3.
+You must set two environment variables so the test code knows which
+driver to use, and how to connect to your database.
+
+```sh
+# MySQL example:
+export GORP_TEST_DSN=gomysql_test/gomysql_test/abc123
+export GORP_TEST_DIALECT=mysql
+
+# run the tests
+go test
+
+# run the tests and benchmarks
+go test -bench="Bench" -benchtime 10
+```
+
+Valid `GORP_TEST_DIALECT` values are: "mysql"(for mymysql),
+"gomysql"(for go-sql-driver), "postgres", "sqlite" See the
+`test_all.sh` script for examples of all 3 databases. This is the
+script I run locally to test the library.
+
+## Performance
+
+gorp uses reflection to construct SQL queries and bind parameters.
+See the BenchmarkNativeCrud vs BenchmarkGorpCrud in gorp_test.go for a
+simple perf test. On my MacBook Pro gorp is about 2-3% slower than
+hand written SQL.
+
+
+## Contributors
+
+* matthias-margush - column aliasing via tags
+* Rob Figueiredo - @robfig
+* Quinn Slack - @sqs
diff --git a/vendor/github.com/mattermost/gorp/column.go b/vendor/github.com/mattermost/gorp/column.go
new file mode 100644
index 000000000..99d4fd555
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/column.go
@@ -0,0 +1,83 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import "reflect"
+
+// ColumnMap represents a mapping between a Go struct field and a single
+// column in a table.
+// Unique and MaxSize only inform the
+// CreateTables() function and are not used by Insert/Update/Delete/Get.
+type ColumnMap struct {
+ // Column name in db table
+ ColumnName string
+
+ // If true, this column is skipped in generated SQL statements
+ Transient bool
+
+ // If true, " unique" is added to create table statements.
+ // Not used elsewhere
+ Unique bool
+
+ // Query used for getting generated id after insert
+ GeneratedIdQuery string
+
+ // Passed to Dialect.ToSqlType() to assist in informing the
+ // correct column type to map to in CreateTables()
+ MaxSize int
+
+ DefaultValue string
+
+ fieldName string
+ gotype reflect.Type
+ isPK bool
+ isAutoIncr bool
+ isNotNull bool
+}
+
+// Rename allows you to specify the column name in the table
+//
+// Example: table.ColMap("Updated").Rename("date_updated")
+//
+func (c *ColumnMap) Rename(colname string) *ColumnMap {
+ c.ColumnName = colname
+ return c
+}
+
+// SetTransient allows you to mark the column as transient. If true
+// this column will be skipped when SQL statements are generated
+func (c *ColumnMap) SetTransient(b bool) *ColumnMap {
+ c.Transient = b
+ return c
+}
+
+// SetUnique adds "unique" to the create table statements for this
+// column, if b is true.
+func (c *ColumnMap) SetUnique(b bool) *ColumnMap {
+ c.Unique = b
+ return c
+}
+
+// SetNotNull adds "not null" to the create table statements for this
+// column, if nn is true.
+func (c *ColumnMap) SetNotNull(nn bool) *ColumnMap {
+ c.isNotNull = nn
+ return c
+}
+
+// SetMaxSize specifies the max length of values of this column. This is
+// passed to the dialect.ToSqlType() function, which can use the value
+// to alter the generated type for "create table" statements
+func (c *ColumnMap) SetMaxSize(size int) *ColumnMap {
+ c.MaxSize = size
+ return c
+}
diff --git a/vendor/github.com/mattermost/gorp/db.go b/vendor/github.com/mattermost/gorp/db.go
new file mode 100644
index 000000000..8a65fbe49
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/db.go
@@ -0,0 +1,803 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "bytes"
+ "context"
+ "database/sql"
+ "database/sql/driver"
+ "errors"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// DbMap is the root gorp mapping object. Create one of these for each
+// database schema you wish to map. Each DbMap contains a list of
+// mapped tables.
+//
+// Example:
+//
+// dialect := gorp.MySQLDialect{"InnoDB", "UTF8"}
+// dbmap := &gorp.DbMap{Db: db, Dialect: dialect}
+//
+type DbMap struct {
+ // Db handle to use with this map
+ Db *sql.DB
+
+ // Dialect implementation to use with this map
+ Dialect Dialect
+
+ TypeConverter TypeConverter
+
+ QueryTimeout time.Duration
+
+ tables []*TableMap
+ tablesDynamic map[string]*TableMap // tables that use same go-struct and different db table names
+ logger GorpLogger
+ logPrefix string
+}
+
+func (m *DbMap) dynamicTableAdd(tableName string, tbl *TableMap) {
+ if m.tablesDynamic == nil {
+ m.tablesDynamic = make(map[string]*TableMap)
+ }
+ m.tablesDynamic[tableName] = tbl
+}
+
+func (m *DbMap) dynamicTableFind(tableName string) (*TableMap, bool) {
+ if m.tablesDynamic == nil {
+ return nil, false
+ }
+ tbl, found := m.tablesDynamic[tableName]
+ return tbl, found
+}
+
+func (m *DbMap) dynamicTableMap() map[string]*TableMap {
+ if m.tablesDynamic == nil {
+ m.tablesDynamic = make(map[string]*TableMap)
+ }
+ return m.tablesDynamic
+}
+
+func (m *DbMap) CreateIndex() error {
+
+ var err error
+ dialect := reflect.TypeOf(m.Dialect)
+ for _, table := range m.tables {
+ for _, index := range table.indexes {
+ err = m.createIndexImpl(dialect, table, index)
+ if err != nil {
+ break
+ }
+ }
+ }
+
+ for _, table := range m.dynamicTableMap() {
+ for _, index := range table.indexes {
+ err = m.createIndexImpl(dialect, table, index)
+ if err != nil {
+ break
+ }
+ }
+ }
+
+ return err
+}
+
+func (m *DbMap) createIndexImpl(dialect reflect.Type,
+ table *TableMap,
+ index *IndexMap) error {
+ s := bytes.Buffer{}
+ s.WriteString("create")
+ if index.Unique {
+ s.WriteString(" unique")
+ }
+ s.WriteString(" index")
+ s.WriteString(fmt.Sprintf(" %s on %s", index.IndexName, table.TableName))
+ if dname := dialect.Name(); dname == "PostgresDialect" && index.IndexType != "" {
+ s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType))
+ }
+ s.WriteString(" (")
+ for x, col := range index.columns {
+ if x > 0 {
+ s.WriteString(", ")
+ }
+ s.WriteString(m.Dialect.QuoteField(col))
+ }
+ s.WriteString(")")
+
+ if dname := dialect.Name(); dname == "MySQLDialect" && index.IndexType != "" {
+ s.WriteString(fmt.Sprintf(" %s %s", m.Dialect.CreateIndexSuffix(), index.IndexType))
+ }
+ s.WriteString(";")
+ _, err := m.ExecNoTimeout(s.String())
+ return err
+}
+
+func (t *TableMap) DropIndex(name string) error {
+
+ var err error
+ dialect := reflect.TypeOf(t.dbmap.Dialect)
+ for _, idx := range t.indexes {
+ if idx.IndexName == name {
+ s := bytes.Buffer{}
+ s.WriteString(fmt.Sprintf("DROP INDEX %s", idx.IndexName))
+
+ if dname := dialect.Name(); dname == "MySQLDialect" {
+ s.WriteString(fmt.Sprintf(" %s %s", t.dbmap.Dialect.DropIndexSuffix(), t.TableName))
+ }
+ s.WriteString(";")
+ _, e := t.dbmap.ExecNoTimeout(s.String())
+ if e != nil {
+ err = e
+ }
+ break
+ }
+ }
+ t.ResetSql()
+ return err
+}
+
+// AddTable registers the given interface type with gorp. The table name
+// will be given the name of the TypeOf(i). You must call this function,
+// or AddTableWithName, for any struct type you wish to persist with
+// the given DbMap.
+//
+// This operation is idempotent. If i's type is already mapped, the
+// existing *TableMap is returned
+func (m *DbMap) AddTable(i interface{}) *TableMap {
+ return m.AddTableWithName(i, "")
+}
+
+// AddTableWithName has the same behavior as AddTable, but sets
+// table.TableName to name.
+func (m *DbMap) AddTableWithName(i interface{}, name string) *TableMap {
+ return m.AddTableWithNameAndSchema(i, "", name)
+}
+
+// AddTableWithNameAndSchema has the same behavior as AddTable, but sets
+// table.TableName to name.
+func (m *DbMap) AddTableWithNameAndSchema(i interface{}, schema string, name string) *TableMap {
+ t := reflect.TypeOf(i)
+ if name == "" {
+ name = t.Name()
+ }
+
+ // check if we have a table for this type already
+ // if so, update the name and return the existing pointer
+ for i := range m.tables {
+ table := m.tables[i]
+ if table.gotype == t {
+ table.TableName = name
+ return table
+ }
+ }
+
+ tmap := &TableMap{gotype: t, TableName: name, SchemaName: schema, dbmap: m}
+ var primaryKey []*ColumnMap
+ tmap.Columns, primaryKey = m.readStructColumns(t)
+ m.tables = append(m.tables, tmap)
+ if len(primaryKey) > 0 {
+ tmap.keys = append(tmap.keys, primaryKey...)
+ }
+
+ return tmap
+}
+
+// AddTableDynamic registers the given interface type with gorp.
+// The table name will be dynamically determined at runtime by
+// using the GetTableName method on DynamicTable interface
+func (m *DbMap) AddTableDynamic(inp DynamicTable, schema string) *TableMap {
+
+ val := reflect.ValueOf(inp)
+ elm := val.Elem()
+ t := elm.Type()
+ name := inp.TableName()
+ if name == "" {
+ panic("Missing table name in DynamicTable instance")
+ }
+
+ // Check if there is another dynamic table with the same name
+ if _, found := m.dynamicTableFind(name); found {
+ panic(fmt.Sprintf("A table with the same name %v already exists", name))
+ }
+
+ tmap := &TableMap{gotype: t, TableName: name, SchemaName: schema, dbmap: m}
+ var primaryKey []*ColumnMap
+ tmap.Columns, primaryKey = m.readStructColumns(t)
+ if len(primaryKey) > 0 {
+ tmap.keys = append(tmap.keys, primaryKey...)
+ }
+
+ m.dynamicTableAdd(name, tmap)
+
+ return tmap
+}
+
+func (m *DbMap) readStructColumns(t reflect.Type) (cols []*ColumnMap, primaryKey []*ColumnMap) {
+ primaryKey = make([]*ColumnMap, 0)
+ n := t.NumField()
+ for i := 0; i < n; i++ {
+ f := t.Field(i)
+ if f.Anonymous && f.Type.Kind() == reflect.Struct {
+ // Recursively add nested fields in embedded structs.
+ subcols, subpk := m.readStructColumns(f.Type)
+ // Don't append nested fields that have the same field
+ // name as an already-mapped field.
+ for _, subcol := range subcols {
+ shouldAppend := true
+ for _, col := range cols {
+ if !subcol.Transient && subcol.fieldName == col.fieldName {
+ shouldAppend = false
+ break
+ }
+ }
+ if shouldAppend {
+ cols = append(cols, subcol)
+ }
+ }
+ if subpk != nil {
+ primaryKey = append(primaryKey, subpk...)
+ }
+ } else {
+ // Tag = Name { ',' Option }
+ // Option = OptionKey [ ':' OptionValue ]
+ cArguments := strings.Split(f.Tag.Get("db"), ",")
+ columnName := cArguments[0]
+ var maxSize int
+ var defaultValue string
+ var isAuto bool
+ var isPK bool
+ var isNotNull bool
+ for _, argString := range cArguments[1:] {
+ argString = strings.TrimSpace(argString)
+ arg := strings.SplitN(argString, ":", 2)
+
+ // check mandatory/unexpected option values
+ switch arg[0] {
+ case "size", "default":
+ // options requiring value
+ if len(arg) == 1 {
+ panic(fmt.Sprintf("missing option value for option %v on field %v", arg[0], f.Name))
+ }
+ default:
+ // options where value is invalid (currently all other options)
+ if len(arg) == 2 {
+ panic(fmt.Sprintf("unexpected option value for option %v on field %v", arg[0], f.Name))
+ }
+ }
+
+ switch arg[0] {
+ case "size":
+ maxSize, _ = strconv.Atoi(arg[1])
+ case "default":
+ defaultValue = arg[1]
+ case "primarykey":
+ isPK = true
+ case "autoincrement":
+ isAuto = true
+ case "notnull":
+ isNotNull = true
+ default:
+ panic(fmt.Sprintf("Unrecognized tag option for field %v: %v", f.Name, arg))
+ }
+ }
+ if columnName == "" {
+ columnName = f.Name
+ }
+
+ gotype := f.Type
+ valueType := gotype
+ if valueType.Kind() == reflect.Ptr {
+ valueType = valueType.Elem()
+ }
+ value := reflect.New(valueType).Interface()
+ if m.TypeConverter != nil {
+ // Make a new pointer to a value of type gotype and
+ // pass it to the TypeConverter's FromDb method to see
+ // if a different type should be used for the column
+ // type during table creation.
+ scanner, useHolder := m.TypeConverter.FromDb(value)
+ if useHolder {
+ value = scanner.Holder
+ gotype = reflect.TypeOf(value)
+ }
+ }
+ if typer, ok := value.(SqlTyper); ok {
+ gotype = reflect.TypeOf(typer.SqlType())
+ } else if valuer, ok := value.(driver.Valuer); ok {
+ // Only check for driver.Valuer if SqlTyper wasn't
+ // found.
+ v, err := valuer.Value()
+ if err == nil && v != nil {
+ gotype = reflect.TypeOf(v)
+ }
+ }
+ cm := &ColumnMap{
+ ColumnName: columnName,
+ DefaultValue: defaultValue,
+ Transient: columnName == "-",
+ fieldName: f.Name,
+ gotype: gotype,
+ isPK: isPK,
+ isAutoIncr: isAuto,
+ isNotNull: isNotNull,
+ MaxSize: maxSize,
+ }
+ if isPK {
+ primaryKey = append(primaryKey, cm)
+ }
+ // Check for nested fields of the same field name and
+ // override them.
+ shouldAppend := true
+ for index, col := range cols {
+ if !col.Transient && col.fieldName == cm.fieldName {
+ cols[index] = cm
+ shouldAppend = false
+ break
+ }
+ }
+ if shouldAppend {
+ cols = append(cols, cm)
+ }
+ }
+
+ }
+ return
+}
+
+// CreateTables iterates through TableMaps registered to this DbMap and
+// executes "create table" statements against the database for each.
+//
+// This is particularly useful in unit tests where you want to create
+// and destroy the schema automatically.
+func (m *DbMap) CreateTables() error {
+ return m.createTables(false)
+}
+
+// CreateTablesIfNotExists is similar to CreateTables, but starts
+// each statement with "create table if not exists" so that existing
+// tables do not raise errors
+func (m *DbMap) CreateTablesIfNotExists() error {
+ return m.createTables(true)
+}
+
+func (m *DbMap) createTables(ifNotExists bool) error {
+ var err error
+ for i := range m.tables {
+ table := m.tables[i]
+ sql := table.SqlForCreate(ifNotExists)
+ _, err = m.ExecNoTimeout(sql)
+ if err != nil {
+ return err
+ }
+ }
+
+ for _, tbl := range m.dynamicTableMap() {
+ sql := tbl.SqlForCreate(ifNotExists)
+ _, err = m.ExecNoTimeout(sql)
+ if err != nil {
+ return err
+ }
+ }
+
+ return err
+}
+
+// DropTable drops an individual table.
+// Returns an error when the table does not exist.
+func (m *DbMap) DropTable(table interface{}) error {
+ t := reflect.TypeOf(table)
+
+ tableName := ""
+ if dyn, ok := table.(DynamicTable); ok {
+ tableName = dyn.TableName()
+ }
+
+ return m.dropTable(t, tableName, false)
+}
+
+// DropTableIfExists drops an individual table when the table exists.
+func (m *DbMap) DropTableIfExists(table interface{}) error {
+ t := reflect.TypeOf(table)
+
+ tableName := ""
+ if dyn, ok := table.(DynamicTable); ok {
+ tableName = dyn.TableName()
+ }
+
+ return m.dropTable(t, tableName, true)
+}
+
+// DropTables iterates through TableMaps registered to this DbMap and
+// executes "drop table" statements against the database for each.
+func (m *DbMap) DropTables() error {
+ return m.dropTables(false)
+}
+
+// DropTablesIfExists is the same as DropTables, but uses the "if exists" clause to
+// avoid errors for tables that do not exist.
+func (m *DbMap) DropTablesIfExists() error {
+ return m.dropTables(true)
+}
+
+// Goes through all the registered tables, dropping them one by one.
+// If an error is encountered, then it is returned and the rest of
+// the tables are not dropped.
+func (m *DbMap) dropTables(addIfExists bool) (err error) {
+ for _, table := range m.tables {
+ err = m.dropTableImpl(table, addIfExists)
+ if err != nil {
+ return err
+ }
+ }
+
+ for _, table := range m.dynamicTableMap() {
+ err = m.dropTableImpl(table, addIfExists)
+ if err != nil {
+ return err
+ }
+ }
+
+ return err
+}
+
+// Implementation of dropping a single table.
+func (m *DbMap) dropTable(t reflect.Type, name string, addIfExists bool) error {
+ table := tableOrNil(m, t, name)
+ if table == nil {
+ return fmt.Errorf("table %s was not registered", table.TableName)
+ }
+
+ return m.dropTableImpl(table, addIfExists)
+}
+
+func (m *DbMap) dropTableImpl(table *TableMap, ifExists bool) (err error) {
+ tableDrop := "drop table"
+ if ifExists {
+ tableDrop = m.Dialect.IfTableExists(tableDrop, table.SchemaName, table.TableName)
+ }
+ _, err = m.ExecNoTimeout(fmt.Sprintf("%s %s;", tableDrop, m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
+ return err
+}
+
+// TruncateTables iterates through TableMaps registered to this DbMap and
+// executes "truncate table" statements against the database for each, or in the case of
+// sqlite, a "delete from" with no "where" clause, which uses the truncate optimization
+// (http://www.sqlite.org/lang_delete.html)
+func (m *DbMap) TruncateTables() error {
+ var err error
+ for i := range m.tables {
+ table := m.tables[i]
+ _, e := m.ExecNoTimeout(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
+ if e != nil {
+ err = e
+ }
+ }
+
+ for _, table := range m.dynamicTableMap() {
+ _, e := m.ExecNoTimeout(fmt.Sprintf("%s %s;", m.Dialect.TruncateClause(), m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
+ if e != nil {
+ err = e
+ }
+ }
+
+ return err
+}
+
+// Insert runs a SQL INSERT statement for each element in list. List
+// items must be pointers.
+//
+// Any interface whose TableMap has an auto-increment primary key will
+// have its last insert id bound to the PK field on the struct.
+//
+// The hook functions PreInsert() and/or PostInsert() will be executed
+// before/after the INSERT statement if the interface defines them.
+//
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) Insert(list ...interface{}) error {
+ return insert(m, m, list...)
+}
+
+// Update runs a SQL UPDATE statement for each element in list. List
+// items must be pointers.
+//
+// The hook functions PreUpdate() and/or PostUpdate() will be executed
+// before/after the UPDATE statement if the interface defines them.
+//
+// Returns the number of rows updated.
+//
+// Returns an error if SetKeys has not been called on the TableMap
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) Update(list ...interface{}) (int64, error) {
+ return update(m, m, nil, list...)
+}
+
+// UpdateColumns runs a SQL UPDATE statement for each element in list. List
+// items must be pointers.
+//
+// Only the columns accepted by filter are included in the UPDATE.
+//
+// The hook functions PreUpdate() and/or PostUpdate() will be executed
+// before/after the UPDATE statement if the interface defines them.
+//
+// Returns the number of rows updated.
+//
+// Returns an error if SetKeys has not been called on the TableMap
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) {
+ return update(m, m, filter, list...)
+}
+
+// Delete runs a SQL DELETE statement for each element in list. List
+// items must be pointers.
+//
+// The hook functions PreDelete() and/or PostDelete() will be executed
+// before/after the DELETE statement if the interface defines them.
+//
+// Returns the number of rows deleted.
+//
+// Returns an error if SetKeys has not been called on the TableMap
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) Delete(list ...interface{}) (int64, error) {
+ return delete(m, m, list...)
+}
+
+// Get runs a SQL SELECT to fetch a single row from the table based on the
+// primary key(s)
+//
+// i should be an empty value for the struct to load. keys should be
+// the primary key value(s) for the row to load. If multiple keys
+// exist on the table, the order should match the column order
+// specified in SetKeys() when the table mapping was defined.
+//
+// The hook function PostGet() will be executed after the SELECT
+// statement if the interface defines them.
+//
+// Returns a pointer to a struct that matches or nil if no row is found.
+//
+// Returns an error if SetKeys has not been called on the TableMap
+// Panics if any interface in the list has not been registered with AddTable
+func (m *DbMap) Get(i interface{}, keys ...interface{}) (interface{}, error) {
+ return get(m, m, i, keys...)
+}
+
+// Select runs an arbitrary SQL query, binding the columns in the result
+// to fields on the struct specified by i. args represent the bind
+// parameters for the SQL statement.
+//
+// Column names on the SELECT statement should be aliased to the field names
+// on the struct i. Returns an error if one or more columns in the result
+// do not match. It is OK if fields on i are not part of the SQL
+// statement.
+//
+// The hook function PostGet() will be executed after the SELECT
+// statement if the interface defines them.
+//
+// Values are returned in one of two ways:
+// 1. If i is a struct or a pointer to a struct, returns a slice of pointers to
+// matching rows of type i.
+// 2. If i is a pointer to a slice, the results will be appended to that slice
+// and nil returned.
+//
+// i does NOT need to be registered with AddTable()
+func (m *DbMap) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
+ return hookedselect(m, m, i, query, args...)
+}
+
+// Exec runs an arbitrary SQL statement. args represent the bind parameters.
+// This is equivalent to running: Exec() using database/sql
+// Times out based on the DbMap.QueryTimeout field
+func (m *DbMap) Exec(query string, args ...interface{}) (sql.Result, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+ return exec(m, query, true, args...)
+}
+
+// ExecNoTimeout is the same as Exec except it will not time out
+func (m *DbMap) ExecNoTimeout(query string, args ...interface{}) (sql.Result, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+ return exec(m, query, false, args...)
+}
+
+// SelectInt is a convenience wrapper around the gorp.SelectInt function
+func (m *DbMap) SelectInt(query string, args ...interface{}) (int64, error) {
+ return SelectInt(m, query, args...)
+}
+
+// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function
+func (m *DbMap) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) {
+ return SelectNullInt(m, query, args...)
+}
+
+// SelectFloat is a convenience wrapper around the gorp.SelectFloat function
+func (m *DbMap) SelectFloat(query string, args ...interface{}) (float64, error) {
+ return SelectFloat(m, query, args...)
+}
+
+// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function
+func (m *DbMap) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) {
+ return SelectNullFloat(m, query, args...)
+}
+
+// SelectStr is a convenience wrapper around the gorp.SelectStr function
+func (m *DbMap) SelectStr(query string, args ...interface{}) (string, error) {
+ return SelectStr(m, query, args...)
+}
+
+// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function
+func (m *DbMap) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) {
+ return SelectNullStr(m, query, args...)
+}
+
+// SelectOne is a convenience wrapper around the gorp.SelectOne function
+func (m *DbMap) SelectOne(holder interface{}, query string, args ...interface{}) error {
+ return SelectOne(m, m, holder, query, args...)
+}
+
+// Begin starts a gorp Transaction
+func (m *DbMap) Begin() (*Transaction, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, "begin;")
+ }
+ tx, err := m.Db.Begin()
+ if err != nil {
+ return nil, err
+ }
+ return &Transaction{m, tx, false}, nil
+}
+
+// TableFor returns the *TableMap corresponding to the given Go Type
+// If no table is mapped to that type an error is returned.
+// If checkPK is true and the mapped table has no registered PKs, an error is returned.
+func (m *DbMap) TableFor(t reflect.Type, checkPK bool) (*TableMap, error) {
+ table := tableOrNil(m, t, "")
+ if table == nil {
+ return nil, fmt.Errorf("no table found for type: %v", t.Name())
+ }
+
+ if checkPK && len(table.keys) < 1 {
+ e := fmt.Sprintf("gorp: no keys defined for table: %s",
+ table.TableName)
+ return nil, errors.New(e)
+ }
+
+ return table, nil
+}
+
+// DynamicTableFor returns the *TableMap for the dynamic table corresponding
+// to the input tablename
+// If no table is mapped to that tablename an error is returned.
+// If checkPK is true and the mapped table has no registered PKs, an error is returned.
+func (m *DbMap) DynamicTableFor(tableName string, checkPK bool) (*TableMap, error) {
+ table, found := m.dynamicTableFind(tableName)
+ if !found {
+ return nil, fmt.Errorf("gorp: no table found for name: %v", tableName)
+ }
+
+ if checkPK && len(table.keys) < 1 {
+ e := fmt.Sprintf("gorp: no keys defined for table: %s",
+ table.TableName)
+ return nil, errors.New(e)
+ }
+
+ return table, nil
+}
+
+// Prepare creates a prepared statement for later queries or executions.
+// Multiple queries or executions may be run concurrently from the returned statement.
+// This is equivalent to running: Prepare() using database/sql
+func (m *DbMap) Prepare(query string) (*sql.Stmt, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, nil)
+ }
+ return m.Db.Prepare(query)
+}
+
+func tableOrNil(m *DbMap, t reflect.Type, name string) *TableMap {
+ if name != "" {
+ // Search by table name (dynamic tables)
+ if table, found := m.dynamicTableFind(name); found {
+ return table
+ }
+ return nil
+ }
+
+ for i := range m.tables {
+ table := m.tables[i]
+ if table.gotype == t {
+ return table
+ }
+ }
+ return nil
+}
+
+func (m *DbMap) tableForPointer(ptr interface{}, checkPK bool) (*TableMap, reflect.Value, error) {
+ ptrv := reflect.ValueOf(ptr)
+ if ptrv.Kind() != reflect.Ptr {
+ e := fmt.Sprintf("gorp: passed non-pointer: %v (kind=%v)", ptr,
+ ptrv.Kind())
+ return nil, reflect.Value{}, errors.New(e)
+ }
+ elem := ptrv.Elem()
+ ifc := elem.Interface()
+ var t *TableMap
+ var err error
+ tableName := ""
+ if dyn, isDyn := ptr.(DynamicTable); isDyn {
+ tableName = dyn.TableName()
+ t, err = m.DynamicTableFor(tableName, checkPK)
+ } else {
+ etype := reflect.TypeOf(ifc)
+ t, err = m.TableFor(etype, checkPK)
+ }
+
+ if err != nil {
+ return nil, reflect.Value{}, err
+ }
+
+ return t, elem, nil
+}
+
+func (m *DbMap) QueryRow(query string, args ...interface{}) *sql.Row {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+
+ return m.Db.QueryRow(query, args...)
+}
+
+func (m *DbMap) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+
+ return m.Db.QueryRowContext(ctx, query, args...)
+}
+
+func (m *DbMap) Query(query string, args ...interface{}) (*sql.Rows, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+
+ return m.Db.Query(query, args...)
+}
+
+func (m *DbMap) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
+ if m.logger != nil {
+ now := time.Now()
+ defer m.trace(now, query, args...)
+ }
+
+ return m.Db.QueryContext(ctx, query, args...)
+}
+
+func (m *DbMap) trace(started time.Time, query string, args ...interface{}) {
+ if m.logger != nil {
+ var margs = argsString(args...)
+ m.logger.Printf("%s%s [%s] (%v)", m.logPrefix, query, margs, (time.Now().Sub(started)))
+ }
+}
diff --git a/vendor/github.com/mattermost/gorp/dialect.go b/vendor/github.com/mattermost/gorp/dialect.go
new file mode 100644
index 000000000..922212640
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/dialect.go
@@ -0,0 +1,114 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import "reflect"
+
+// The Dialect interface encapsulates behaviors that differ across
+// SQL databases. At present the Dialect is only used by CreateTables()
+// but this could change in the future
+type Dialect interface {
+
+ // dialect name
+ Name() string
+
+ // adds a suffix to any query, usually ";"
+ QuerySuffix() string
+
+ // ToSqlType returns the SQL column type to use when creating a
+ // table of the given Go Type. maxsize can be used to switch based on
+ // size. For example, in MySQL []byte could map to BLOB, MEDIUMBLOB,
+ // or LONGBLOB depending on the maxsize
+ ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string
+
+ // string to append to primary key column definitions
+ AutoIncrStr() string
+
+ // string to bind autoincrement columns to. Empty string will
+ // remove reference to those columns in the INSERT statement.
+ AutoIncrBindValue() string
+
+ AutoIncrInsertSuffix(col *ColumnMap) string
+
+ // string to append to "create table" statement for vendor specific
+ // table attributes
+ CreateTableSuffix() string
+
+ // string to append to "create index" statement
+ CreateIndexSuffix() string
+
+ // string to append to "drop index" statement
+ DropIndexSuffix() string
+
+ // string to truncate tables
+ TruncateClause() string
+
+ // bind variable string to use when forming SQL statements
+ // in many dbs it is "?", but Postgres appears to use $1
+ //
+ // i is a zero based index of the bind variable in this statement
+ //
+ BindVar(i int) string
+
+ // Handles quoting of a field name to ensure that it doesn't raise any
+ // SQL parsing exceptions by using a reserved word as a field name.
+ QuoteField(field string) string
+
+ // Handles building up of a schema.database string that is compatible with
+ // the given dialect
+ //
+ // schema - The schema that <table> lives in
+ // table - The table name
+ QuotedTableForQuery(schema string, table string) string
+
+ // Existance clause for table creation / deletion
+ IfSchemaNotExists(command, schema string) string
+ IfTableExists(command, schema, table string) string
+ IfTableNotExists(command, schema, table string) string
+}
+
+// IntegerAutoIncrInserter is implemented by dialects that can perform
+// inserts with automatically incremented integer primary keys. If
+// the dialect can handle automatic assignment of more than just
+// integers, see TargetedAutoIncrInserter.
+type IntegerAutoIncrInserter interface {
+ InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error)
+}
+
+// TargetedAutoIncrInserter is implemented by dialects that can
+// perform automatic assignment of any primary key type (i.e. strings
+// for uuids, integers for serials, etc).
+type TargetedAutoIncrInserter interface {
+ // InsertAutoIncrToTarget runs an insert operation and assigns the
+ // automatically generated primary key directly to the passed in
+ // target. The target should be a pointer to the primary key
+ // field of the value being inserted.
+ InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error
+}
+
+// TargetQueryInserter is implemented by dialects that can perform
+// assignment of integer primary key type by executing a query
+// like "select sequence.currval from dual".
+type TargetQueryInserter interface {
+ // TargetQueryInserter runs an insert operation and assigns the
+ // automatically generated primary key retrived by the query
+ // extracted from the GeneratedIdQuery field of the id column.
+ InsertQueryToTarget(exec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error
+}
+
+func standardInsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
+ res, err := exec.Exec(insertSql, params...)
+ if err != nil {
+ return 0, err
+ }
+ return res.LastInsertId()
+}
diff --git a/vendor/github.com/mattermost/gorp/dialect_mysql.go b/vendor/github.com/mattermost/gorp/dialect_mysql.go
new file mode 100644
index 000000000..b263c3308
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/dialect_mysql.go
@@ -0,0 +1,173 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Implementation of Dialect for MySQL databases.
+type MySQLDialect struct {
+
+ // Engine is the storage engine to use "InnoDB" vs "MyISAM" for example
+ Engine string
+
+ // Encoding is the character encoding to use for created tables
+ Encoding string
+}
+
+func (d MySQLDialect) Name() string { return "MySQLDialect" }
+
+func (d MySQLDialect) QuerySuffix() string { return ";" }
+
+func (d MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "boolean"
+ case reflect.Int8:
+ return "tinyint"
+ case reflect.Uint8:
+ return "tinyint unsigned"
+ case reflect.Int16:
+ return "smallint"
+ case reflect.Uint16:
+ return "smallint unsigned"
+ case reflect.Int, reflect.Int32:
+ return "int"
+ case reflect.Uint, reflect.Uint32:
+ return "int unsigned"
+ case reflect.Int64:
+ return "bigint"
+ case reflect.Uint64:
+ return "bigint unsigned"
+ case reflect.Float64, reflect.Float32:
+ return "double"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "mediumblob"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "bigint"
+ case "NullFloat64":
+ return "double"
+ case "NullBool":
+ return "tinyint"
+ case "Time":
+ return "datetime"
+ }
+
+ if maxsize < 1 {
+ maxsize = 255
+ }
+
+ /* == About varchar(N) ==
+ * N is number of characters.
+ * A varchar column can store up to 65535 bytes.
+ * Remember that 1 character is 3 bytes in utf-8 charset.
+ * Also remember that each row can store up to 65535 bytes,
+ * and you have some overheads, so it's not possible for a
+ * varchar column to have 65535/3 characters really.
+ * So it would be better to use 'text' type in stead of
+ * large varchar type.
+ */
+ if maxsize < 256 {
+ return fmt.Sprintf("varchar(%d)", maxsize)
+ } else {
+ return "text"
+ }
+}
+
+// Returns auto_increment
+func (d MySQLDialect) AutoIncrStr() string {
+ return "auto_increment"
+}
+
+func (d MySQLDialect) AutoIncrBindValue() string {
+ return "null"
+}
+
+func (d MySQLDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return ""
+}
+
+// Returns engine=%s charset=%s based on values stored on struct
+func (d MySQLDialect) CreateTableSuffix() string {
+ if d.Engine == "" || d.Encoding == "" {
+ msg := "gorp - undefined"
+
+ if d.Engine == "" {
+ msg += " MySQLDialect.Engine"
+ }
+ if d.Engine == "" && d.Encoding == "" {
+ msg += ","
+ }
+ if d.Encoding == "" {
+ msg += " MySQLDialect.Encoding"
+ }
+ msg += ". Check that your MySQLDialect was correctly initialized when declared."
+ panic(msg)
+ }
+
+ return fmt.Sprintf(" engine=%s charset=%s", d.Engine, d.Encoding)
+}
+
+func (m MySQLDialect) CreateIndexSuffix() string {
+ return "using"
+}
+
+func (m MySQLDialect) DropIndexSuffix() string {
+ return "on"
+}
+
+func (m MySQLDialect) TruncateClause() string {
+ return "truncate"
+}
+
+// Returns "?"
+func (d MySQLDialect) BindVar(i int) string {
+ return "?"
+}
+
+func (d MySQLDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
+ return standardInsertAutoIncr(exec, insertSql, params...)
+}
+
+func (d MySQLDialect) QuoteField(f string) string {
+ return "`" + f + "`"
+}
+
+func (d MySQLDialect) QuotedTableForQuery(schema string, table string) string {
+ if strings.TrimSpace(schema) == "" {
+ return d.QuoteField(table)
+ }
+
+ return schema + "." + d.QuoteField(table)
+}
+
+func (d MySQLDialect) IfSchemaNotExists(command, schema string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
+
+func (d MySQLDialect) IfTableExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if exists", command)
+}
+
+func (d MySQLDialect) IfTableNotExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
diff --git a/vendor/github.com/mattermost/gorp/dialect_mysql_test.go b/vendor/github.com/mattermost/gorp/dialect_mysql_test.go
new file mode 100644
index 000000000..d1018cf69
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/dialect_mysql_test.go
@@ -0,0 +1,204 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp_test
+
+import (
+ "database/sql"
+ "reflect"
+ "time"
+
+ // ginkgo/gomega functions read better as dot-imports.
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/ginkgo/extensions/table"
+ . "github.com/onsi/gomega"
+
+ "github.com/go-gorp/gorp"
+)
+
+var _ = Describe("MySQLDialect", func() {
+ var (
+ engine, encoding string
+ dialect gorp.MySQLDialect
+ )
+
+ JustBeforeEach(func() {
+ dialect = gorp.MySQLDialect{
+ Engine: engine,
+ Encoding: encoding,
+ }
+ })
+
+ DescribeTable("ToSqlType",
+ func(value interface{}, maxsize int, autoIncr bool, expected string) {
+ typ := reflect.TypeOf(value)
+ sqlType := dialect.ToSqlType(typ, maxsize, autoIncr)
+ Expect(sqlType).To(Equal(expected))
+ },
+ Entry("bool", true, 0, false, "boolean"),
+ Entry("int8", int8(1), 0, false, "tinyint"),
+ Entry("uint8", uint8(1), 0, false, "tinyint unsigned"),
+ Entry("int16", int16(1), 0, false, "smallint"),
+ Entry("uint16", uint16(1), 0, false, "smallint unsigned"),
+ Entry("int32", int32(1), 0, false, "int"),
+ Entry("int (treated as int32)", int(1), 0, false, "int"),
+ Entry("uint32", uint32(1), 0, false, "int unsigned"),
+ Entry("uint (treated as uint32)", uint(1), 0, false, "int unsigned"),
+ Entry("int64", int64(1), 0, false, "bigint"),
+ Entry("uint64", uint64(1), 0, false, "bigint unsigned"),
+ Entry("float32", float32(1), 0, false, "double"),
+ Entry("float64", float64(1), 0, false, "double"),
+ Entry("[]uint8", []uint8{1}, 0, false, "mediumblob"),
+ Entry("NullInt64", sql.NullInt64{}, 0, false, "bigint"),
+ Entry("NullFloat64", sql.NullFloat64{}, 0, false, "double"),
+ Entry("NullBool", sql.NullBool{}, 0, false, "tinyint"),
+ Entry("Time", time.Time{}, 0, false, "datetime"),
+ Entry("default-size string", "", 0, false, "varchar(255)"),
+ Entry("sized string", "", 50, false, "varchar(50)"),
+ Entry("large string", "", 1024, false, "text"),
+ )
+
+ Describe("AutoIncrStr", func() {
+ It("returns the auto increment string", func() {
+ Expect(dialect.AutoIncrStr()).To(Equal("auto_increment"))
+ })
+ })
+
+ Describe("AutoIncrBindValue", func() {
+ It("returns the value used to bind the auto-increment value", func() {
+ Expect(dialect.AutoIncrBindValue()).To(Equal("null"))
+ })
+ })
+
+ Describe("AutoIncrInsertSuffix", func() {
+ It("returns the suffix needed for auto-incrementing", func() {
+ Expect(dialect.AutoIncrInsertSuffix(nil)).To(BeEmpty())
+ })
+ })
+
+ Describe("CreateTableSuffix", func() {
+ Context("with an empty engine", func() {
+ BeforeEach(func() {
+ engine = ""
+ encoding = "foo"
+ })
+ It("panics", func() {
+ Expect(func() {
+ dialect.CreateTableSuffix()
+ }).To(Panic())
+ })
+ })
+
+ Context("with an empty encoding", func() {
+ BeforeEach(func() {
+ engine = "foo"
+ encoding = ""
+ })
+ It("panics", func() {
+ Expect(func() {
+ dialect.CreateTableSuffix()
+ }).To(Panic())
+ })
+ })
+
+ Context("with an engine and an encoding", func() {
+ BeforeEach(func() {
+ engine = "foo"
+ encoding = "bar"
+ })
+ It("returns a valid suffix", func() {
+ Expect(dialect.CreateTableSuffix()).To(Equal(" engine=foo charset=bar"))
+ })
+ })
+ })
+
+ Describe("CreateIndexSuffix", func() {
+ It("returns the suffix for creating indexes", func() {
+ Expect(dialect.CreateIndexSuffix()).To(Equal("using"))
+ })
+ })
+
+ Describe("DropIndexSuffix", func() {
+ It("returns the suffix for deleting indexes", func() {
+ Expect(dialect.DropIndexSuffix()).To(Equal("on"))
+ })
+ })
+
+ Describe("TruncateClause", func() {
+ It("returns the clause for truncating a table", func() {
+ Expect(dialect.TruncateClause()).To(Equal("truncate"))
+ })
+ })
+
+ Describe("BindVar", func() {
+ It("returns the variable binding sequence", func() {
+ Expect(dialect.BindVar(0)).To(Equal("?"))
+ })
+ })
+
+ PDescribe("InsertAutoIncr", func() {})
+
+ Describe("QuoteField", func() {
+ It("returns the argument quoted as a field", func() {
+ Expect(dialect.QuoteField("foo")).To(Equal("`foo`"))
+ })
+ })
+
+ Describe("QuotedTableForQuery", func() {
+ var (
+ schema, table string
+
+ quotedTable string
+ )
+
+ JustBeforeEach(func() {
+ quotedTable = dialect.QuotedTableForQuery(schema, table)
+ })
+
+ Context("using the default schema", func() {
+ BeforeEach(func() {
+ schema = ""
+ table = "foo"
+ })
+ It("returns just the table", func() {
+ Expect(quotedTable).To(Equal("`foo`"))
+ })
+ })
+
+ Context("with a supplied schema", func() {
+ BeforeEach(func() {
+ schema = "foo"
+ table = "bar"
+ })
+ It("returns the schema and table", func() {
+ Expect(quotedTable).To(Equal("foo.`bar`"))
+ })
+ })
+ })
+
+ Describe("IfSchemaNotExists", func() {
+ It("appends 'if not exists' to the command", func() {
+ Expect(dialect.IfSchemaNotExists("foo", "bar")).To(Equal("foo if not exists"))
+ })
+ })
+
+ Describe("IfTableExists", func() {
+ It("appends 'if exists' to the command", func() {
+ Expect(dialect.IfTableExists("foo", "bar", "baz")).To(Equal("foo if exists"))
+ })
+ })
+
+ Describe("IfTableNotExists", func() {
+ It("appends 'if not exists' to the command", func() {
+ Expect(dialect.IfTableNotExists("foo", "bar", "baz")).To(Equal("foo if not exists"))
+ })
+ })
+})
diff --git a/vendor/github.com/mattermost/gorp/dialect_oracle.go b/vendor/github.com/mattermost/gorp/dialect_oracle.go
new file mode 100644
index 000000000..b1cee0a0a
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/dialect_oracle.go
@@ -0,0 +1,148 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Implementation of Dialect for Oracle databases.
+type OracleDialect struct{}
+
+func (d OracleDialect) Name() string { return "OracleDialect" }
+
+func (d OracleDialect) QuerySuffix() string { return "" }
+
+func (d OracleDialect) CreateIndexSuffix() string { return "" }
+
+func (d OracleDialect) DropIndexSuffix() string { return "" }
+
+func (d OracleDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "boolean"
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
+ if isAutoIncr {
+ return "serial"
+ }
+ return "integer"
+ case reflect.Int64, reflect.Uint64:
+ if isAutoIncr {
+ return "bigserial"
+ }
+ return "bigint"
+ case reflect.Float64:
+ return "double precision"
+ case reflect.Float32:
+ return "real"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "bytea"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "bigint"
+ case "NullFloat64":
+ return "double precision"
+ case "NullBool":
+ return "boolean"
+ case "NullTime", "Time":
+ return "timestamp with time zone"
+ }
+
+ if maxsize > 0 {
+ return fmt.Sprintf("varchar(%d)", maxsize)
+ } else {
+ return "text"
+ }
+
+}
+
+// Returns empty string
+func (d OracleDialect) AutoIncrStr() string {
+ return ""
+}
+
+func (d OracleDialect) AutoIncrBindValue() string {
+ return "NULL"
+}
+
+func (d OracleDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return ""
+}
+
+// Returns suffix
+func (d OracleDialect) CreateTableSuffix() string {
+ return ""
+}
+
+func (d OracleDialect) TruncateClause() string {
+ return "truncate"
+}
+
+// Returns "$(i+1)"
+func (d OracleDialect) BindVar(i int) string {
+ return fmt.Sprintf(":%d", i+1)
+}
+
+// After executing the insert uses the ColMap IdQuery to get the generated id
+func (d OracleDialect) InsertQueryToTarget(exec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error {
+ _, err := exec.Exec(insertSql, params...)
+ if err != nil {
+ return err
+ }
+ id, err := exec.SelectInt(idSql)
+ if err != nil {
+ return err
+ }
+ switch target.(type) {
+ case *int64:
+ *(target.(*int64)) = id
+ case *int32:
+ *(target.(*int32)) = int32(id)
+ case int:
+ *(target.(*int)) = int(id)
+ default:
+ return fmt.Errorf("Id field can be int, int32 or int64")
+ }
+ return nil
+}
+
+func (d OracleDialect) QuoteField(f string) string {
+ return `"` + strings.ToUpper(f) + `"`
+}
+
+func (d OracleDialect) QuotedTableForQuery(schema string, table string) string {
+ if strings.TrimSpace(schema) == "" {
+ return d.QuoteField(table)
+ }
+
+ return schema + "." + d.QuoteField(table)
+}
+
+func (d OracleDialect) IfSchemaNotExists(command, schema string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
+
+func (d OracleDialect) IfTableExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if exists", command)
+}
+
+func (d OracleDialect) IfTableNotExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
diff --git a/vendor/github.com/mattermost/gorp/dialect_postgres.go b/vendor/github.com/mattermost/gorp/dialect_postgres.go
new file mode 100644
index 000000000..aa7f34cba
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/dialect_postgres.go
@@ -0,0 +1,149 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+type PostgresDialect struct {
+ suffix string
+}
+
+func (d PostgresDialect) Name() string { return "PostgresDialect" }
+
+func (d PostgresDialect) QuerySuffix() string { return ";" }
+
+func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "boolean"
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
+ if isAutoIncr {
+ return "serial"
+ }
+ return "integer"
+ case reflect.Int64, reflect.Uint64:
+ if isAutoIncr {
+ return "bigserial"
+ }
+ return "bigint"
+ case reflect.Float64:
+ return "double precision"
+ case reflect.Float32:
+ return "real"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "bytea"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "bigint"
+ case "NullFloat64":
+ return "double precision"
+ case "NullBool":
+ return "boolean"
+ case "Time", "NullTime":
+ return "timestamp with time zone"
+ }
+
+ if maxsize > 0 {
+ return fmt.Sprintf("varchar(%d)", maxsize)
+ } else {
+ return "text"
+ }
+
+}
+
+// Returns empty string
+func (d PostgresDialect) AutoIncrStr() string {
+ return ""
+}
+
+func (d PostgresDialect) AutoIncrBindValue() string {
+ return "default"
+}
+
+func (d PostgresDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return " returning " + d.QuoteField(col.ColumnName)
+}
+
+// Returns suffix
+func (d PostgresDialect) CreateTableSuffix() string {
+ return d.suffix
+}
+
+func (d PostgresDialect) CreateIndexSuffix() string {
+ return "using"
+}
+
+func (d PostgresDialect) DropIndexSuffix() string {
+ return ""
+}
+
+func (d PostgresDialect) TruncateClause() string {
+ return "truncate"
+}
+
+// Returns "$(i+1)"
+func (d PostgresDialect) BindVar(i int) string {
+ return fmt.Sprintf("$%d", i+1)
+}
+
+func (d PostgresDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error {
+ rows, err := exec.Query(insertSql, params...)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ return fmt.Errorf("No serial value returned for insert: %s Encountered error: %s", insertSql, rows.Err())
+ }
+ if err := rows.Scan(target); err != nil {
+ return err
+ }
+ if rows.Next() {
+ return fmt.Errorf("more than two serial value returned for insert: %s", insertSql)
+ }
+ return rows.Err()
+}
+
+func (d PostgresDialect) QuoteField(f string) string {
+ return `"` + strings.ToLower(f) + `"`
+}
+
+func (d PostgresDialect) QuotedTableForQuery(schema string, table string) string {
+ if strings.TrimSpace(schema) == "" {
+ return d.QuoteField(table)
+ }
+
+ return schema + "." + d.QuoteField(table)
+}
+
+func (d PostgresDialect) IfSchemaNotExists(command, schema string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
+
+func (d PostgresDialect) IfTableExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if exists", command)
+}
+
+func (d PostgresDialect) IfTableNotExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
diff --git a/vendor/github.com/mattermost/gorp/dialect_sqlite.go b/vendor/github.com/mattermost/gorp/dialect_sqlite.go
new file mode 100644
index 000000000..e78605983
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/dialect_sqlite.go
@@ -0,0 +1,121 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type SqliteDialect struct {
+ suffix string
+}
+
+func (d SqliteDialect) Name() string { return "SQLiteDialect" }
+
+func (d SqliteDialect) QuerySuffix() string { return ";" }
+
+func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "integer"
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return "integer"
+ case reflect.Float64, reflect.Float32:
+ return "real"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "blob"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "integer"
+ case "NullFloat64":
+ return "real"
+ case "NullBool":
+ return "integer"
+ case "Time":
+ return "datetime"
+ }
+
+ if maxsize < 1 {
+ maxsize = 255
+ }
+ return fmt.Sprintf("varchar(%d)", maxsize)
+}
+
+// Returns autoincrement
+func (d SqliteDialect) AutoIncrStr() string {
+ return "autoincrement"
+}
+
+func (d SqliteDialect) AutoIncrBindValue() string {
+ return "null"
+}
+
+func (d SqliteDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return ""
+}
+
+// Returns suffix
+func (d SqliteDialect) CreateTableSuffix() string {
+ return d.suffix
+}
+
+func (d SqliteDialect) CreateIndexSuffix() string {
+ return ""
+}
+
+func (d SqliteDialect) DropIndexSuffix() string {
+ return ""
+}
+
+// With sqlite, there technically isn't a TRUNCATE statement,
+// but a DELETE FROM uses a truncate optimization:
+// http://www.sqlite.org/lang_delete.html
+func (d SqliteDialect) TruncateClause() string {
+ return "delete from"
+}
+
+// Returns "?"
+func (d SqliteDialect) BindVar(i int) string {
+ return "?"
+}
+
+func (d SqliteDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
+ return standardInsertAutoIncr(exec, insertSql, params...)
+}
+
+func (d SqliteDialect) QuoteField(f string) string {
+ return `"` + f + `"`
+}
+
+// sqlite does not have schemas like PostgreSQL does, so just escape it like normal
+func (d SqliteDialect) QuotedTableForQuery(schema string, table string) string {
+ return d.QuoteField(table)
+}
+
+func (d SqliteDialect) IfSchemaNotExists(command, schema string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
+
+func (d SqliteDialect) IfTableExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if exists", command)
+}
+
+func (d SqliteDialect) IfTableNotExists(command, schema, table string) string {
+ return fmt.Sprintf("%s if not exists", command)
+}
diff --git a/vendor/github.com/mattermost/gorp/dialect_sqlserver.go b/vendor/github.com/mattermost/gorp/dialect_sqlserver.go
new file mode 100644
index 000000000..904110e35
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/dialect_sqlserver.go
@@ -0,0 +1,154 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Implementation of Dialect for Microsoft SQL Server databases.
+// Use gorp.SqlServerDialect{"2005"} for legacy datatypes.
+// Tested with driver: github.com/denisenkom/go-mssqldb
+
+type SqlServerDialect struct {
+
+ // If set to "2005" legacy datatypes will be used
+ Version string
+}
+
+func (d SqlServerDialect) Name() string { return "SQLServerDialect" }
+
+func (d SqlServerDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string {
+ switch val.Kind() {
+ case reflect.Ptr:
+ return d.ToSqlType(val.Elem(), maxsize, isAutoIncr)
+ case reflect.Bool:
+ return "bit"
+ case reflect.Int8:
+ return "tinyint"
+ case reflect.Uint8:
+ return "smallint"
+ case reflect.Int16:
+ return "smallint"
+ case reflect.Uint16:
+ return "int"
+ case reflect.Int, reflect.Int32:
+ return "int"
+ case reflect.Uint, reflect.Uint32:
+ return "bigint"
+ case reflect.Int64:
+ return "bigint"
+ case reflect.Uint64:
+ return "numeric(20,0)"
+ case reflect.Float32:
+ return "float(24)"
+ case reflect.Float64:
+ return "float(53)"
+ case reflect.Slice:
+ if val.Elem().Kind() == reflect.Uint8 {
+ return "varbinary"
+ }
+ }
+
+ switch val.Name() {
+ case "NullInt64":
+ return "bigint"
+ case "NullFloat64":
+ return "float(53)"
+ case "NullBool":
+ return "bit"
+ case "NullTime", "Time":
+ if d.Version == "2005" {
+ return "datetime"
+ }
+ return "datetime2"
+ }
+
+ if maxsize < 1 {
+ if d.Version == "2005" {
+ maxsize = 255
+ } else {
+ return fmt.Sprintf("nvarchar(max)")
+ }
+ }
+ return fmt.Sprintf("nvarchar(%d)", maxsize)
+}
+
+// Returns auto_increment
+func (d SqlServerDialect) AutoIncrStr() string {
+ return "identity(0,1)"
+}
+
+// Empty string removes autoincrement columns from the INSERT statements.
+func (d SqlServerDialect) AutoIncrBindValue() string {
+ return ""
+}
+
+func (d SqlServerDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
+ return ""
+}
+
+func (d SqlServerDialect) CreateTableSuffix() string { return ";" }
+
+func (d SqlServerDialect) TruncateClause() string {
+ return "truncate table"
+}
+
+// Returns "?"
+func (d SqlServerDialect) BindVar(i int) string {
+ return "?"
+}
+
+func (d SqlServerDialect) InsertAutoIncr(exec SqlExecutor, insertSql string, params ...interface{}) (int64, error) {
+ return standardInsertAutoIncr(exec, insertSql, params...)
+}
+
+func (d SqlServerDialect) QuoteField(f string) string {
+ return "[" + strings.Replace(f, "]", "]]", -1) + "]"
+}
+
+func (d SqlServerDialect) QuotedTableForQuery(schema string, table string) string {
+ if strings.TrimSpace(schema) == "" {
+ return d.QuoteField(table)
+ }
+ return d.QuoteField(schema) + "." + d.QuoteField(table)
+}
+
+func (d SqlServerDialect) QuerySuffix() string { return ";" }
+
+func (d SqlServerDialect) IfSchemaNotExists(command, schema string) string {
+ s := fmt.Sprintf("if schema_id(N'%s') is null %s", schema, command)
+ return s
+}
+
+func (d SqlServerDialect) IfTableExists(command, schema, table string) string {
+ var schema_clause string
+ if strings.TrimSpace(schema) != "" {
+ schema_clause = fmt.Sprintf("%s.", d.QuoteField(schema))
+ }
+ s := fmt.Sprintf("if object_id('%s%s') is not null %s", schema_clause, d.QuoteField(table), command)
+ return s
+}
+
+func (d SqlServerDialect) IfTableNotExists(command, schema, table string) string {
+ var schema_clause string
+ if strings.TrimSpace(schema) != "" {
+ schema_clause = fmt.Sprintf("%s.", schema)
+ }
+ s := fmt.Sprintf("if object_id('%s%s') is null %s", schema_clause, table, command)
+ return s
+}
+
+func (d SqlServerDialect) CreateIndexSuffix() string { return "" }
+func (d SqlServerDialect) DropIndexSuffix() string { return "" }
diff --git a/vendor/github.com/mattermost/gorp/errors.go b/vendor/github.com/mattermost/gorp/errors.go
new file mode 100644
index 000000000..d13f03fc3
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/errors.go
@@ -0,0 +1,38 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+)
+
+// A non-fatal error, when a select query returns columns that do not exist
+// as fields in the struct it is being mapped to
+// TODO: discuss wether this needs an error. encoding/json silently ignores missing fields
+type NoFieldInTypeError struct {
+ TypeName string
+ MissingColNames []string
+}
+
+func (err *NoFieldInTypeError) Error() string {
+ return fmt.Sprintf("gorp: no fields %+v in type %s", err.MissingColNames, err.TypeName)
+}
+
+// returns true if the error is non-fatal (ie, we shouldn't immediately return)
+func NonFatalError(err error) bool {
+ switch err.(type) {
+ case *NoFieldInTypeError:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/vendor/github.com/mattermost/gorp/gorp.go b/vendor/github.com/mattermost/gorp/gorp.go
new file mode 100644
index 000000000..728971e48
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/gorp.go
@@ -0,0 +1,615 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+//
+package gorp
+
+import (
+ "context"
+ "database/sql"
+ "database/sql/driver"
+ "fmt"
+ "reflect"
+ "regexp"
+ "strings"
+ "time"
+)
+
+// OracleString (empty string is null)
+// TODO: move to dialect/oracle?, rename to String?
+type OracleString struct {
+ sql.NullString
+}
+
+// Scan implements the Scanner interface.
+func (os *OracleString) Scan(value interface{}) error {
+ if value == nil {
+ os.String, os.Valid = "", false
+ return nil
+ }
+ os.Valid = true
+ return os.NullString.Scan(value)
+}
+
+// Value implements the driver Valuer interface.
+func (os OracleString) Value() (driver.Value, error) {
+ if !os.Valid || os.String == "" {
+ return nil, nil
+ }
+ return os.String, nil
+}
+
+// SqlTyper is a type that returns its database type. Most of the
+// time, the type can just use "database/sql/driver".Valuer; but when
+// it returns nil for its empty value, it needs to implement SqlTyper
+// to have its column type detected properly during table creation.
+type SqlTyper interface {
+ SqlType() driver.Valuer
+}
+
+// for fields that exists in DB table, but not exists in struct
+type dummyField struct{}
+
+// Scan implements the Scanner interface.
+func (nt *dummyField) Scan(value interface{}) error {
+ return nil
+}
+
+var zeroVal reflect.Value
+var versFieldConst = "[gorp_ver_field]"
+
+// The TypeConverter interface provides a way to map a value of one
+// type to another type when persisting to, or loading from, a database.
+//
+// Example use cases: Implement type converter to convert bool types to "y"/"n" strings,
+// or serialize a struct member as a JSON blob.
+type TypeConverter interface {
+ // ToDb converts val to another type. Called before INSERT/UPDATE operations
+ ToDb(val interface{}) (interface{}, error)
+
+ // FromDb returns a CustomScanner appropriate for this type. This will be used
+ // to hold values returned from SELECT queries.
+ //
+ // In particular the CustomScanner returned should implement a Binder
+ // function appropriate for the Go type you wish to convert the db value to
+ //
+ // If bool==false, then no custom scanner will be used for this field.
+ FromDb(target interface{}) (CustomScanner, bool)
+}
+
+// Executor exposes the sql.DB and sql.Tx Exec function so that it can be used
+// on internal functions that convert named parameters for the Exec function.
+type executor interface {
+ Exec(query string, args ...interface{}) (sql.Result, error)
+ ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
+}
+
+// SqlExecutor exposes gorp operations that can be run from Pre/Post
+// hooks. This hides whether the current operation that triggered the
+// hook is in a transaction.
+//
+// See the DbMap function docs for each of the functions below for more
+// information.
+type SqlExecutor interface {
+ Get(i interface{}, keys ...interface{}) (interface{}, error)
+ Insert(list ...interface{}) error
+ Update(list ...interface{}) (int64, error)
+ Delete(list ...interface{}) (int64, error)
+ Exec(query string, args ...interface{}) (sql.Result, error)
+ ExecNoTimeout(query string, args ...interface{}) (sql.Result, error)
+ Select(i interface{}, query string,
+ args ...interface{}) ([]interface{}, error)
+ SelectInt(query string, args ...interface{}) (int64, error)
+ SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error)
+ SelectFloat(query string, args ...interface{}) (float64, error)
+ SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error)
+ SelectStr(query string, args ...interface{}) (string, error)
+ SelectNullStr(query string, args ...interface{}) (sql.NullString, error)
+ SelectOne(holder interface{}, query string, args ...interface{}) error
+ Query(query string, args ...interface{}) (*sql.Rows, error)
+ QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
+ QueryRow(query string, args ...interface{}) *sql.Row
+ QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
+}
+
+// DynamicTable allows the users of gorp to dynamically
+// use different database table names during runtime
+// while sharing the same golang struct for in-memory data
+type DynamicTable interface {
+ TableName() string
+ SetTableName(string)
+}
+
+// Compile-time check that DbMap and Transaction implement the SqlExecutor
+// interface.
+var _, _ SqlExecutor = &DbMap{}, &Transaction{}
+
+func argsString(args ...interface{}) string {
+ var margs string
+ for i, a := range args {
+ var v interface{} = a
+ if x, ok := v.(driver.Valuer); ok {
+ y, err := x.Value()
+ if err == nil {
+ v = y
+ }
+ }
+ switch v.(type) {
+ case string:
+ v = fmt.Sprintf("%q", v)
+ default:
+ v = fmt.Sprintf("%v", v)
+ }
+ margs += fmt.Sprintf("%d:%s", i+1, v)
+ if i+1 < len(args) {
+ margs += " "
+ }
+ }
+ return margs
+}
+
+// Calls the Exec function on the executor, but attempts to expand any eligible named
+// query arguments first.
+func exec(e SqlExecutor, query string, doTimeout bool, args ...interface{}) (sql.Result, error) {
+ var dbMap *DbMap
+ var executor executor
+ switch m := e.(type) {
+ case *DbMap:
+ executor = m.Db
+ dbMap = m
+ case *Transaction:
+ executor = m.tx
+ dbMap = m.dbmap
+ }
+
+ if len(args) == 1 {
+ query, args = maybeExpandNamedQuery(dbMap, query, args)
+ }
+
+ if doTimeout && dbMap.Dialect.Name() != "PostgresDialect" {
+ ctx, cancel := context.WithTimeout(context.Background(), dbMap.QueryTimeout)
+ defer cancel()
+ return executor.ExecContext(ctx, query, args...)
+ } else {
+ return executor.Exec(query, args...)
+ }
+}
+
+// maybeExpandNamedQuery checks the given arg to see if it's eligible to be used
+// as input to a named query. If so, it rewrites the query to use
+// dialect-dependent bindvars and instantiates the corresponding slice of
+// parameters by extracting data from the map / struct.
+// If not, returns the input values unchanged.
+func maybeExpandNamedQuery(m *DbMap, query string, args []interface{}) (string, []interface{}) {
+ var (
+ arg = args[0]
+ argval = reflect.ValueOf(arg)
+ )
+ if argval.Kind() == reflect.Ptr {
+ argval = argval.Elem()
+ }
+
+ if argval.Kind() == reflect.Map && argval.Type().Key().Kind() == reflect.String {
+ return expandNamedQuery(m, query, func(key string) reflect.Value {
+ return argval.MapIndex(reflect.ValueOf(key))
+ })
+ }
+ if argval.Kind() != reflect.Struct {
+ return query, args
+ }
+ if _, ok := arg.(time.Time); ok {
+ // time.Time is driver.Value
+ return query, args
+ }
+ if _, ok := arg.(driver.Valuer); ok {
+ // driver.Valuer will be converted to driver.Value.
+ return query, args
+ }
+
+ return expandNamedQuery(m, query, argval.FieldByName)
+}
+
+var keyRegexp = regexp.MustCompile(`:[[:word:]]+`)
+
+// expandNamedQuery accepts a query with placeholders of the form ":key", and a
+// single arg of Kind Struct or Map[string]. It returns the query with the
+// dialect's placeholders, and a slice of args ready for positional insertion
+// into the query.
+func expandNamedQuery(m *DbMap, query string, keyGetter func(key string) reflect.Value) (string, []interface{}) {
+ var (
+ n int
+ args []interface{}
+ )
+ return keyRegexp.ReplaceAllStringFunc(query, func(key string) string {
+ val := keyGetter(key[1:])
+ if !val.IsValid() {
+ return key
+ }
+ args = append(args, val.Interface())
+ newVar := m.Dialect.BindVar(n)
+ n++
+ return newVar
+ }), args
+}
+
+func columnToFieldIndex(m *DbMap, t reflect.Type, name string, cols []string) ([][]int, error) {
+ colToFieldIndex := make([][]int, len(cols))
+
+ // check if type t is a mapped table - if so we'll
+ // check the table for column aliasing below
+ tableMapped := false
+ table := tableOrNil(m, t, name)
+ if table != nil {
+ tableMapped = true
+ }
+
+ // Loop over column names and find field in i to bind to
+ // based on column name. all returned columns must match
+ // a field in the i struct
+ missingColNames := []string{}
+ for x := range cols {
+ colName := strings.ToLower(cols[x])
+ field, found := t.FieldByNameFunc(func(fieldName string) bool {
+ field, _ := t.FieldByName(fieldName)
+ cArguments := strings.Split(field.Tag.Get("db"), ",")
+ fieldName = cArguments[0]
+
+ if fieldName == "-" {
+ return false
+ } else if fieldName == "" {
+ fieldName = field.Name
+ }
+ if tableMapped {
+ colMap := colMapOrNil(table, fieldName)
+ if colMap != nil {
+ fieldName = colMap.ColumnName
+ }
+ }
+ return colName == strings.ToLower(fieldName)
+ })
+ if found {
+ colToFieldIndex[x] = field.Index
+ }
+ if colToFieldIndex[x] == nil {
+ missingColNames = append(missingColNames, colName)
+ }
+ }
+ if len(missingColNames) > 0 {
+ return colToFieldIndex, &NoFieldInTypeError{
+ TypeName: t.Name(),
+ MissingColNames: missingColNames,
+ }
+ }
+ return colToFieldIndex, nil
+}
+
+func fieldByName(val reflect.Value, fieldName string) *reflect.Value {
+ // try to find field by exact match
+ f := val.FieldByName(fieldName)
+
+ if f != zeroVal {
+ return &f
+ }
+
+ // try to find by case insensitive match - only the Postgres driver
+ // seems to require this - in the case where columns are aliased in the sql
+ fieldNameL := strings.ToLower(fieldName)
+ fieldCount := val.NumField()
+ t := val.Type()
+ for i := 0; i < fieldCount; i++ {
+ sf := t.Field(i)
+ if strings.ToLower(sf.Name) == fieldNameL {
+ f := val.Field(i)
+ return &f
+ }
+ }
+
+ return nil
+}
+
+// toSliceType returns the element type of the given object, if the object is a
+// "*[]*Element" or "*[]Element". If not, returns nil.
+// err is returned if the user was trying to pass a pointer-to-slice but failed.
+func toSliceType(i interface{}) (reflect.Type, error) {
+ t := reflect.TypeOf(i)
+ if t.Kind() != reflect.Ptr {
+ // If it's a slice, return a more helpful error message
+ if t.Kind() == reflect.Slice {
+ return nil, fmt.Errorf("gorp: cannot SELECT into a non-pointer slice: %v", t)
+ }
+ return nil, nil
+ }
+ if t = t.Elem(); t.Kind() != reflect.Slice {
+ return nil, nil
+ }
+ return t.Elem(), nil
+}
+
+func toType(i interface{}) (reflect.Type, error) {
+ t := reflect.TypeOf(i)
+
+ // If a Pointer to a type, follow
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Struct {
+ return nil, fmt.Errorf("gorp: cannot SELECT into this type: %v", reflect.TypeOf(i))
+ }
+ return t, nil
+}
+
+type foundTable struct {
+ table *TableMap
+ dynName *string
+}
+
+func tableFor(m *DbMap, t reflect.Type, i interface{}) (*foundTable, error) {
+ if dyn, isDynamic := i.(DynamicTable); isDynamic {
+ tableName := dyn.TableName()
+ table, err := m.DynamicTableFor(tableName, true)
+ if err != nil {
+ return nil, err
+ }
+ return &foundTable{
+ table: table,
+ dynName: &tableName,
+ }, nil
+ }
+ table, err := m.TableFor(t, true)
+ if err != nil {
+ return nil, err
+ }
+ return &foundTable{table: table}, nil
+}
+
+func get(m *DbMap, exec SqlExecutor, i interface{},
+ keys ...interface{}) (interface{}, error) {
+
+ t, err := toType(i)
+ if err != nil {
+ return nil, err
+ }
+
+ foundTable, err := tableFor(m, t, i)
+ if err != nil {
+ return nil, err
+ }
+ table := foundTable.table
+
+ plan := table.bindGet()
+
+ v := reflect.New(t)
+ if foundTable.dynName != nil {
+ retDyn := v.Interface().(DynamicTable)
+ retDyn.SetTableName(*foundTable.dynName)
+ }
+
+ dest := make([]interface{}, len(plan.argFields))
+
+ conv := m.TypeConverter
+ custScan := make([]CustomScanner, 0)
+
+ for x, fieldName := range plan.argFields {
+ f := v.Elem().FieldByName(fieldName)
+ target := f.Addr().Interface()
+ if conv != nil {
+ scanner, ok := conv.FromDb(target)
+ if ok {
+ target = scanner.Holder
+ custScan = append(custScan, scanner)
+ }
+ }
+ dest[x] = target
+ }
+
+ var row *sql.Row
+ if m.Dialect.Name() != "PostgresDialect" {
+ ctx, cancel := context.WithTimeout(context.Background(), m.QueryTimeout)
+ defer cancel()
+ row = exec.QueryRowContext(ctx, plan.query, keys...)
+ } else {
+ row = exec.QueryRow(plan.query, keys...)
+ }
+
+ err = row.Scan(dest...)
+ if err != nil {
+ if err == sql.ErrNoRows {
+ err = nil
+ }
+ return nil, err
+ }
+
+ for _, c := range custScan {
+ err = c.Bind()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if v, ok := v.Interface().(HasPostGet); ok {
+ err := v.PostGet(exec)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return v.Interface(), nil
+}
+
+func delete(m *DbMap, exec SqlExecutor, list ...interface{}) (int64, error) {
+ count := int64(0)
+ for _, ptr := range list {
+ table, elem, err := m.tableForPointer(ptr, true)
+ if err != nil {
+ return -1, err
+ }
+
+ eval := elem.Addr().Interface()
+ if v, ok := eval.(HasPreDelete); ok {
+ err = v.PreDelete(exec)
+ if err != nil {
+ return -1, err
+ }
+ }
+
+ bi, err := table.bindDelete(elem)
+ if err != nil {
+ return -1, err
+ }
+
+ res, err := exec.Exec(bi.query, bi.args...)
+ if err != nil {
+ return -1, err
+ }
+ rows, err := res.RowsAffected()
+ if err != nil {
+ return -1, err
+ }
+
+ if rows == 0 && bi.existingVersion > 0 {
+ return lockError(m, exec, table.TableName,
+ bi.existingVersion, elem, bi.keys...)
+ }
+
+ count += rows
+
+ if v, ok := eval.(HasPostDelete); ok {
+ err := v.PostDelete(exec)
+ if err != nil {
+ return -1, err
+ }
+ }
+ }
+
+ return count, nil
+}
+
+func update(m *DbMap, exec SqlExecutor, colFilter ColumnFilter, list ...interface{}) (int64, error) {
+ count := int64(0)
+ for _, ptr := range list {
+ table, elem, err := m.tableForPointer(ptr, true)
+ if err != nil {
+ return -1, err
+ }
+
+ eval := elem.Addr().Interface()
+ if v, ok := eval.(HasPreUpdate); ok {
+ err = v.PreUpdate(exec)
+ if err != nil {
+ return -1, err
+ }
+ }
+
+ bi, err := table.bindUpdate(elem, colFilter)
+ if err != nil {
+ return -1, err
+ }
+
+ res, err := exec.Exec(bi.query, bi.args...)
+ if err != nil {
+ return -1, err
+ }
+
+ rows, err := res.RowsAffected()
+ if err != nil {
+ return -1, err
+ }
+
+ if rows == 0 && bi.existingVersion > 0 {
+ return lockError(m, exec, table.TableName,
+ bi.existingVersion, elem, bi.keys...)
+ }
+
+ if bi.versField != "" {
+ elem.FieldByName(bi.versField).SetInt(bi.existingVersion + 1)
+ }
+
+ count += rows
+
+ if v, ok := eval.(HasPostUpdate); ok {
+ err = v.PostUpdate(exec)
+ if err != nil {
+ return -1, err
+ }
+ }
+ }
+ return count, nil
+}
+
+func insert(m *DbMap, exec SqlExecutor, list ...interface{}) error {
+ for _, ptr := range list {
+ table, elem, err := m.tableForPointer(ptr, false)
+ if err != nil {
+ return err
+ }
+
+ eval := elem.Addr().Interface()
+ if v, ok := eval.(HasPreInsert); ok {
+ err := v.PreInsert(exec)
+ if err != nil {
+ return err
+ }
+ }
+
+ bi, err := table.bindInsert(elem)
+ if err != nil {
+ return err
+ }
+
+ if bi.autoIncrIdx > -1 {
+ f := elem.FieldByName(bi.autoIncrFieldName)
+ switch inserter := m.Dialect.(type) {
+ case IntegerAutoIncrInserter:
+ id, err := inserter.InsertAutoIncr(exec, bi.query, bi.args...)
+ if err != nil {
+ return err
+ }
+ k := f.Kind()
+ if (k == reflect.Int) || (k == reflect.Int16) || (k == reflect.Int32) || (k == reflect.Int64) {
+ f.SetInt(id)
+ } else if (k == reflect.Uint) || (k == reflect.Uint16) || (k == reflect.Uint32) || (k == reflect.Uint64) {
+ f.SetUint(uint64(id))
+ } else {
+ return fmt.Errorf("gorp: cannot set autoincrement value on non-Int field. SQL=%s autoIncrIdx=%d autoIncrFieldName=%s", bi.query, bi.autoIncrIdx, bi.autoIncrFieldName)
+ }
+ case TargetedAutoIncrInserter:
+ err := inserter.InsertAutoIncrToTarget(exec, bi.query, f.Addr().Interface(), bi.args...)
+ if err != nil {
+ return err
+ }
+ case TargetQueryInserter:
+ var idQuery = table.ColMap(bi.autoIncrFieldName).GeneratedIdQuery
+ if idQuery == "" {
+ return fmt.Errorf("gorp: cannot set %s value if its ColumnMap.GeneratedIdQuery is empty", bi.autoIncrFieldName)
+ }
+ err := inserter.InsertQueryToTarget(exec, bi.query, idQuery, f.Addr().Interface(), bi.args...)
+ if err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("gorp: cannot use autoincrement fields on dialects that do not implement an autoincrementing interface")
+ }
+ } else {
+ _, err := exec.Exec(bi.query, bi.args...)
+ if err != nil {
+ return err
+ }
+ }
+
+ if v, ok := eval.(HasPostInsert); ok {
+ err := v.PostInsert(exec)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/mattermost/gorp/gorp_suite_test.go b/vendor/github.com/mattermost/gorp/gorp_suite_test.go
new file mode 100644
index 000000000..2abcaaf71
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/gorp_suite_test.go
@@ -0,0 +1,13 @@
+package gorp_test
+
+import (
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+
+ "testing"
+)
+
+func TestGorp(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Gorp Suite")
+}
diff --git a/vendor/github.com/mattermost/gorp/gorp_test.go b/vendor/github.com/mattermost/gorp/gorp_test.go
new file mode 100644
index 000000000..98ea4d3e4
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/gorp_test.go
@@ -0,0 +1,2696 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp_test
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "encoding/json"
+ "errors"
+ "flag"
+ "fmt"
+ "log"
+ "math/rand"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/go-gorp/gorp"
+
+ _ "github.com/go-sql-driver/mysql"
+ _ "github.com/lib/pq"
+ _ "github.com/mattn/go-sqlite3"
+ _ "github.com/ziutek/mymysql/godrv"
+)
+
+var (
+ // verify interface compliance
+ _ = []gorp.Dialect{
+ gorp.SqliteDialect{},
+ gorp.PostgresDialect{},
+ gorp.MySQLDialect{},
+ gorp.SqlServerDialect{},
+ gorp.OracleDialect{},
+ }
+
+ debug bool
+)
+
+func init() {
+ flag.BoolVar(&debug, "trace", true, "Turn on or off database tracing (DbMap.TraceOn)")
+ flag.Parse()
+}
+
+type testable interface {
+ GetId() int64
+ Rand()
+}
+
+type Invoice struct {
+ Id int64
+ Created int64
+ Updated int64
+ Memo string
+ PersonId int64
+ IsPaid bool
+}
+
+type InvoiceWithValuer struct {
+ Id int64
+ Created int64
+ Updated int64
+ Memo string
+ Person PersonValuerScanner `db:"personid"`
+ IsPaid bool
+}
+
+func (me *Invoice) GetId() int64 { return me.Id }
+func (me *Invoice) Rand() {
+ me.Memo = fmt.Sprintf("random %d", rand.Int63())
+ me.Created = rand.Int63()
+ me.Updated = rand.Int63()
+}
+
+type InvoiceTag struct {
+ Id int64 `db:"myid, primarykey, autoincrement"`
+ Created int64 `db:"myCreated"`
+ Updated int64 `db:"date_updated"`
+ Memo string
+ PersonId int64 `db:"person_id"`
+ IsPaid bool `db:"is_Paid"`
+}
+
+func (me *InvoiceTag) GetId() int64 { return me.Id }
+func (me *InvoiceTag) Rand() {
+ me.Memo = fmt.Sprintf("random %d", rand.Int63())
+ me.Created = rand.Int63()
+ me.Updated = rand.Int63()
+}
+
+// See: https://github.com/go-gorp/gorp/issues/175
+type AliasTransientField struct {
+ Id int64 `db:"id"`
+ Bar int64 `db:"-"`
+ BarStr string `db:"bar"`
+}
+
+func (me *AliasTransientField) GetId() int64 { return me.Id }
+func (me *AliasTransientField) Rand() {
+ me.BarStr = fmt.Sprintf("random %d", rand.Int63())
+}
+
+type OverriddenInvoice struct {
+ Invoice
+ Id string
+}
+
+type Person struct {
+ Id int64
+ Created int64
+ Updated int64
+ FName string
+ LName string
+ Version int64
+}
+
+// PersonValuerScanner is used as a field in test types to ensure that we
+// make use of "database/sql/driver".Valuer for choosing column types when
+// creating tables and that we don't get in the way of the underlying
+// database libraries when they make use of either Valuer or
+// "database/sql".Scanner.
+type PersonValuerScanner struct {
+ Person
+}
+
+// Value implements "database/sql/driver".Valuer. It will be automatically
+// run by the "database/sql" package when inserting/updating data.
+func (p PersonValuerScanner) Value() (driver.Value, error) {
+ return p.Id, nil
+}
+
+// Scan implements "database/sql".Scanner. It will be automatically run
+// by the "database/sql" package when reading column data into a field
+// of type PersonValuerScanner.
+func (p *PersonValuerScanner) Scan(value interface{}) (err error) {
+ switch src := value.(type) {
+ case []byte:
+ // TODO: this case is here for mysql only. For some reason,
+ // one (both?) of the mysql libraries opt to pass us a []byte
+ // instead of an int64 for the bigint column. We should add
+ // table tests around valuers/scanners and try to solve these
+ // types of odd discrepencies to make it easier for users of
+ // gorp to migrate to other database engines.
+ p.Id, err = strconv.ParseInt(string(src), 10, 64)
+ case int64:
+ // Most libraries pass in the type we'd expect.
+ p.Id = src
+ default:
+ typ := reflect.TypeOf(value)
+ return fmt.Errorf("Expected person value to be convertible to int64, got %v (type %s)", value, typ)
+ }
+ return
+}
+
+type FNameOnly struct {
+ FName string
+}
+
+type InvoicePersonView struct {
+ InvoiceId int64
+ PersonId int64
+ Memo string
+ FName string
+ LegacyVersion int64
+}
+
+type TableWithNull struct {
+ Id int64
+ Str sql.NullString
+ Int64 sql.NullInt64
+ Float64 sql.NullFloat64
+ Bool sql.NullBool
+ Bytes []byte
+}
+
+type WithIgnoredColumn struct {
+ internal int64 `db:"-"`
+ Id int64
+ Created int64
+}
+
+type IdCreated struct {
+ Id int64
+ Created int64
+}
+
+type IdCreatedExternal struct {
+ IdCreated
+ External int64
+}
+
+type WithStringPk struct {
+ Id string
+ Name string
+}
+
+type CustomStringType string
+
+type TypeConversionExample struct {
+ Id int64
+ PersonJSON Person
+ Name CustomStringType
+}
+
+type PersonUInt32 struct {
+ Id uint32
+ Name string
+}
+
+type PersonUInt64 struct {
+ Id uint64
+ Name string
+}
+
+type PersonUInt16 struct {
+ Id uint16
+ Name string
+}
+
+type WithEmbeddedStruct struct {
+ Id int64
+ Names
+}
+
+type WithEmbeddedStructConflictingEmbeddedMemberNames struct {
+ Id int64
+ Names
+ NamesConflict
+}
+
+type WithEmbeddedStructSameMemberName struct {
+ Id int64
+ SameName
+}
+
+type WithEmbeddedStructBeforeAutoincrField struct {
+ Names
+ Id int64
+}
+
+type WithEmbeddedAutoincr struct {
+ WithEmbeddedStruct
+ MiddleName string
+}
+
+type Names struct {
+ FirstName string
+ LastName string
+}
+
+type NamesConflict struct {
+ FirstName string
+ Surname string
+}
+
+type SameName struct {
+ SameName string
+}
+
+type UniqueColumns struct {
+ FirstName string
+ LastName string
+ City string
+ ZipCode int64
+}
+
+type SingleColumnTable struct {
+ SomeId string
+}
+
+type CustomDate struct {
+ time.Time
+}
+
+type WithCustomDate struct {
+ Id int64
+ Added CustomDate
+}
+
+type WithNullTime struct {
+ Id int64
+ Time gorp.NullTime
+}
+
+type testTypeConverter struct{}
+
+func (me testTypeConverter) ToDb(val interface{}) (interface{}, error) {
+
+ switch t := val.(type) {
+ case Person:
+ b, err := json.Marshal(t)
+ if err != nil {
+ return "", err
+ }
+ return string(b), nil
+ case CustomStringType:
+ return string(t), nil
+ case CustomDate:
+ return t.Time, nil
+ }
+
+ return val, nil
+}
+
+func (me testTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
+ switch target.(type) {
+ case *Person:
+ binder := func(holder, target interface{}) error {
+ s, ok := holder.(*string)
+ if !ok {
+ return errors.New("FromDb: Unable to convert Person to *string")
+ }
+ b := []byte(*s)
+ return json.Unmarshal(b, target)
+ }
+ return gorp.CustomScanner{new(string), target, binder}, true
+ case *CustomStringType:
+ binder := func(holder, target interface{}) error {
+ s, ok := holder.(*string)
+ if !ok {
+ return errors.New("FromDb: Unable to convert CustomStringType to *string")
+ }
+ st, ok := target.(*CustomStringType)
+ if !ok {
+ return errors.New(fmt.Sprint("FromDb: Unable to convert target to *CustomStringType: ", reflect.TypeOf(target)))
+ }
+ *st = CustomStringType(*s)
+ return nil
+ }
+ return gorp.CustomScanner{new(string), target, binder}, true
+ case *CustomDate:
+ binder := func(holder, target interface{}) error {
+ t, ok := holder.(*time.Time)
+ if !ok {
+ return errors.New("FromDb: Unable to convert CustomDate to *time.Time")
+ }
+ dateTarget, ok := target.(*CustomDate)
+ if !ok {
+ return errors.New(fmt.Sprint("FromDb: Unable to convert target to *CustomDate: ", reflect.TypeOf(target)))
+ }
+ dateTarget.Time = *t
+ return nil
+ }
+ return gorp.CustomScanner{new(time.Time), target, binder}, true
+ }
+
+ return gorp.CustomScanner{}, false
+}
+
+func (p *Person) PreInsert(s gorp.SqlExecutor) error {
+ p.Created = time.Now().UnixNano()
+ p.Updated = p.Created
+ if p.FName == "badname" {
+ return fmt.Errorf("Invalid name: %s", p.FName)
+ }
+ return nil
+}
+
+func (p *Person) PostInsert(s gorp.SqlExecutor) error {
+ p.LName = "postinsert"
+ return nil
+}
+
+func (p *Person) PreUpdate(s gorp.SqlExecutor) error {
+ p.FName = "preupdate"
+ return nil
+}
+
+func (p *Person) PostUpdate(s gorp.SqlExecutor) error {
+ p.LName = "postupdate"
+ return nil
+}
+
+func (p *Person) PreDelete(s gorp.SqlExecutor) error {
+ p.FName = "predelete"
+ return nil
+}
+
+func (p *Person) PostDelete(s gorp.SqlExecutor) error {
+ p.LName = "postdelete"
+ return nil
+}
+
+func (p *Person) PostGet(s gorp.SqlExecutor) error {
+ p.LName = "postget"
+ return nil
+}
+
+type PersistentUser struct {
+ Key int32
+ Id string
+ PassedTraining bool
+}
+
+type TenantDynamic struct {
+ Id int64 `db:"id"`
+ Name string
+ Address string
+ curTable string `db:"-"`
+}
+
+func (curObj *TenantDynamic) TableName() string {
+ return curObj.curTable
+}
+func (curObj *TenantDynamic) SetTableName(tblName string) {
+ curObj.curTable = tblName
+}
+
+var dynTableInst1 = TenantDynamic{curTable: "t_1_tenant_dynamic"}
+var dynTableInst2 = TenantDynamic{curTable: "t_2_tenant_dynamic"}
+
+func dynamicTablesTest(t *testing.T, dbmap *gorp.DbMap) {
+
+ dynamicTablesTestTableMap(t, dbmap, &dynTableInst1)
+ dynamicTablesTestTableMap(t, dbmap, &dynTableInst2)
+
+ // TEST - dbmap.Insert using dynTableInst1
+ dynTableInst1.Name = "Test Name 1"
+ dynTableInst1.Address = "Test Address 1"
+ err := dbmap.Insert(&dynTableInst1)
+ if err != nil {
+ t.Errorf("Errow while saving dynTableInst1. Details: %v", err)
+ }
+
+ // TEST - dbmap.Insert using dynTableInst2
+ dynTableInst2.Name = "Test Name 2"
+ dynTableInst2.Address = "Test Address 2"
+ err = dbmap.Insert(&dynTableInst2)
+ if err != nil {
+ t.Errorf("Errow while saving dynTableInst2. Details: %v", err)
+ }
+
+ dynamicTablesTestSelect(t, dbmap, &dynTableInst1)
+ dynamicTablesTestSelect(t, dbmap, &dynTableInst2)
+ dynamicTablesTestSelectOne(t, dbmap, &dynTableInst1)
+ dynamicTablesTestSelectOne(t, dbmap, &dynTableInst2)
+ dynamicTablesTestGetUpdateGet(t, dbmap, &dynTableInst1)
+ dynamicTablesTestGetUpdateGet(t, dbmap, &dynTableInst2)
+ dynamicTablesTestDelete(t, dbmap, &dynTableInst1)
+ dynamicTablesTestDelete(t, dbmap, &dynTableInst2)
+
+}
+
+func dynamicTablesTestTableMap(t *testing.T,
+ dbmap *gorp.DbMap,
+ inpInst *TenantDynamic) {
+
+ tableName := inpInst.TableName()
+
+ tblMap, err := dbmap.DynamicTableFor(tableName, true)
+ if err != nil {
+ t.Errorf("Error while searching for tablemap for tableName: %v, Error:%v", tableName, err)
+ }
+ if tblMap == nil {
+ t.Errorf("Unable to find tablemap for tableName:%v", tableName)
+ }
+}
+
+func dynamicTablesTestSelect(t *testing.T,
+ dbmap *gorp.DbMap,
+ inpInst *TenantDynamic) {
+
+ // TEST - dbmap.Select using inpInst
+
+ // read the data back from dynInst to see if the
+ // table mapping is correct
+ var dbTenantInst1 = TenantDynamic{curTable: inpInst.curTable}
+ selectSQL1 := "select * from " + inpInst.curTable
+ dbObjs, err := dbmap.Select(&dbTenantInst1, selectSQL1)
+ if err != nil {
+ t.Errorf("Errow in dbmap.Select. SQL: %v, Details: %v", selectSQL1, err)
+ }
+ if dbObjs == nil {
+ t.Fatalf("Nil return from dbmap.Select")
+ }
+ rwCnt := len(dbObjs)
+ if rwCnt != 1 {
+ t.Errorf("Unexpected row count for tenantInst:%v", rwCnt)
+ }
+
+ dbInst := dbObjs[0].(*TenantDynamic)
+
+ inpTableName := inpInst.TableName()
+ resTableName := dbInst.TableName()
+ if inpTableName != resTableName {
+ t.Errorf("Mismatched table names %v != %v ",
+ inpTableName, resTableName)
+ }
+
+ if inpInst.Id != dbInst.Id {
+ t.Errorf("Mismatched Id values %v != %v ",
+ inpInst.Id, dbInst.Id)
+ }
+
+ if inpInst.Name != dbInst.Name {
+ t.Errorf("Mismatched Name values %v != %v ",
+ inpInst.Name, dbInst.Name)
+ }
+
+ if inpInst.Address != dbInst.Address {
+ t.Errorf("Mismatched Address values %v != %v ",
+ inpInst.Address, dbInst.Address)
+ }
+}
+
+func dynamicTablesTestGetUpdateGet(t *testing.T,
+ dbmap *gorp.DbMap,
+ inpInst *TenantDynamic) {
+
+ // TEST - dbmap.Get, dbmap.Update, dbmap.Get sequence
+
+ // read and update one of the instances to make sure
+ // that the common gorp APIs are working well with dynamic table
+ var inpIface2 = TenantDynamic{curTable: inpInst.curTable}
+ dbObj, err := dbmap.Get(&inpIface2, inpInst.Id)
+ if err != nil {
+ t.Errorf("Errow in dbmap.Get. id: %v, Details: %v", inpInst.Id, err)
+ }
+ if dbObj == nil {
+ t.Errorf("Nil return from dbmap.Get")
+ }
+
+ dbInst := dbObj.(*TenantDynamic)
+
+ {
+ inpTableName := inpInst.TableName()
+ resTableName := dbInst.TableName()
+ if inpTableName != resTableName {
+ t.Errorf("Mismatched table names %v != %v ",
+ inpTableName, resTableName)
+ }
+
+ if inpInst.Id != dbInst.Id {
+ t.Errorf("Mismatched Id values %v != %v ",
+ inpInst.Id, dbInst.Id)
+ }
+
+ if inpInst.Name != dbInst.Name {
+ t.Errorf("Mismatched Name values %v != %v ",
+ inpInst.Name, dbInst.Name)
+ }
+
+ if inpInst.Address != dbInst.Address {
+ t.Errorf("Mismatched Address values %v != %v ",
+ inpInst.Address, dbInst.Address)
+ }
+ }
+
+ {
+ updatedName := "Testing Updated Name2"
+ dbInst.Name = updatedName
+ cnt, err := dbmap.Update(dbInst)
+ if err != nil {
+ t.Errorf("Error from dbmap.Update: %v", err.Error())
+ }
+ if cnt != 1 {
+ t.Errorf("Update count must be 1, got %v", cnt)
+ }
+
+ // Read the object again to make sure that the
+ // data was updated in db
+ dbObj2, err := dbmap.Get(&inpIface2, inpInst.Id)
+ if err != nil {
+ t.Errorf("Errow in dbmap.Get. id: %v, Details: %v", inpInst.Id, err)
+ }
+ if dbObj2 == nil {
+ t.Errorf("Nil return from dbmap.Get")
+ }
+
+ dbInst2 := dbObj2.(*TenantDynamic)
+
+ inpTableName := inpInst.TableName()
+ resTableName := dbInst2.TableName()
+ if inpTableName != resTableName {
+ t.Errorf("Mismatched table names %v != %v ",
+ inpTableName, resTableName)
+ }
+
+ if inpInst.Id != dbInst2.Id {
+ t.Errorf("Mismatched Id values %v != %v ",
+ inpInst.Id, dbInst2.Id)
+ }
+
+ if updatedName != dbInst2.Name {
+ t.Errorf("Mismatched Name values %v != %v ",
+ updatedName, dbInst2.Name)
+ }
+
+ if inpInst.Address != dbInst.Address {
+ t.Errorf("Mismatched Address values %v != %v ",
+ inpInst.Address, dbInst.Address)
+ }
+
+ }
+}
+
+func dynamicTablesTestSelectOne(t *testing.T,
+ dbmap *gorp.DbMap,
+ inpInst *TenantDynamic) {
+
+ // TEST - dbmap.SelectOne
+
+ // read the data back from inpInst to see if the
+ // table mapping is correct
+ var dbTenantInst1 = TenantDynamic{curTable: inpInst.curTable}
+ selectSQL1 := "select * from " + dbTenantInst1.curTable + " where id = :idKey"
+ params := map[string]interface{}{"idKey": inpInst.Id}
+ err := dbmap.SelectOne(&dbTenantInst1, selectSQL1, params)
+ if err != nil {
+ t.Errorf("Errow in dbmap.SelectOne. SQL: %v, Details: %v", selectSQL1, err)
+ }
+
+ inpTableName := inpInst.curTable
+ resTableName := dbTenantInst1.TableName()
+ if inpTableName != resTableName {
+ t.Errorf("Mismatched table names %v != %v ",
+ inpTableName, resTableName)
+ }
+
+ if inpInst.Id != dbTenantInst1.Id {
+ t.Errorf("Mismatched Id values %v != %v ",
+ inpInst.Id, dbTenantInst1.Id)
+ }
+
+ if inpInst.Name != dbTenantInst1.Name {
+ t.Errorf("Mismatched Name values %v != %v ",
+ inpInst.Name, dbTenantInst1.Name)
+ }
+
+ if inpInst.Address != dbTenantInst1.Address {
+ t.Errorf("Mismatched Address values %v != %v ",
+ inpInst.Address, dbTenantInst1.Address)
+ }
+}
+
+func dynamicTablesTestDelete(t *testing.T,
+ dbmap *gorp.DbMap,
+ inpInst *TenantDynamic) {
+
+ // TEST - dbmap.Delete
+ cnt, err := dbmap.Delete(inpInst)
+ if err != nil {
+ t.Errorf("Errow in dbmap.Delete. Details: %v", err)
+ }
+ if cnt != 1 {
+ t.Errorf("Expected delete count for %v : 1, found count:%v",
+ inpInst.TableName(), cnt)
+ }
+
+ // Try reading again to make sure instance is gone from db
+ getInst := TenantDynamic{curTable: inpInst.TableName()}
+ dbInst, err := dbmap.Get(&getInst, inpInst.Id)
+ if err != nil {
+ t.Errorf("Error while trying to read deleted %v object using id: %v",
+ inpInst.TableName(), inpInst.Id)
+ }
+
+ if dbInst != nil {
+ t.Errorf("Found deleted %v instance using id: %v",
+ inpInst.TableName(), inpInst.Id)
+ }
+
+ if getInst.Name != "" {
+ t.Errorf("Found data from deleted %v instance using id: %v",
+ inpInst.TableName(), inpInst.Id)
+ }
+
+}
+
+func TestCreateTablesIfNotExists(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ err := dbmap.CreateTablesIfNotExists()
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestTruncateTables(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+ err := dbmap.CreateTablesIfNotExists()
+ if err != nil {
+ t.Error(err)
+ }
+
+ // Insert some data
+ p1 := &Person{0, 0, 0, "Bob", "Smith", 0}
+ dbmap.Insert(p1)
+ inv := &Invoice{0, 0, 1, "my invoice", 0, true}
+ dbmap.Insert(inv)
+
+ err = dbmap.TruncateTables()
+ if err != nil {
+ t.Error(err)
+ }
+
+ // Make sure all rows are deleted
+ rows, _ := dbmap.Select(Person{}, "SELECT * FROM person_test")
+ if len(rows) != 0 {
+ t.Errorf("Expected 0 person rows, got %d", len(rows))
+ }
+ rows, _ = dbmap.Select(Invoice{}, "SELECT * FROM invoice_test")
+ if len(rows) != 0 {
+ t.Errorf("Expected 0 invoice rows, got %d", len(rows))
+ }
+}
+
+func TestCustomDateType(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.TypeConverter = testTypeConverter{}
+ dbmap.AddTable(WithCustomDate{}).SetKeys(true, "Id")
+ err := dbmap.CreateTables()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+
+ test1 := &WithCustomDate{Added: CustomDate{Time: time.Now().Truncate(time.Second)}}
+ err = dbmap.Insert(test1)
+ if err != nil {
+ t.Errorf("Could not insert struct with custom date field: %s", err)
+ t.FailNow()
+ }
+ // Unfortunately, the mysql driver doesn't handle time.Time
+ // values properly during Get(). I can't find a way to work
+ // around that problem - every other type that I've tried is just
+ // silently converted. time.Time is the only type that causes
+ // the issue that this test checks for. As such, if the driver is
+ // mysql, we'll just skip the rest of this test.
+ if _, driver := dialectAndDriver(); driver == "mysql" {
+ t.Skip("TestCustomDateType can't run Get() with the mysql driver; skipping the rest of this test...")
+ }
+ result, err := dbmap.Get(new(WithCustomDate), test1.Id)
+ if err != nil {
+ t.Errorf("Could not get struct with custom date field: %s", err)
+ t.FailNow()
+ }
+ test2 := result.(*WithCustomDate)
+ if test2.Added.UTC() != test1.Added.UTC() {
+ t.Errorf("Custom dates do not match: %v != %v", test2.Added.UTC(), test1.Added.UTC())
+ }
+}
+
+func TestUIntPrimaryKey(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.AddTable(PersonUInt64{}).SetKeys(true, "Id")
+ dbmap.AddTable(PersonUInt32{}).SetKeys(true, "Id")
+ dbmap.AddTable(PersonUInt16{}).SetKeys(true, "Id")
+ err := dbmap.CreateTablesIfNotExists()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+
+ p1 := &PersonUInt64{0, "name1"}
+ p2 := &PersonUInt32{0, "name2"}
+ p3 := &PersonUInt16{0, "name3"}
+ err = dbmap.Insert(p1, p2, p3)
+ if err != nil {
+ t.Error(err)
+ }
+ if p1.Id != 1 {
+ t.Errorf("%d != 1", p1.Id)
+ }
+ if p2.Id != 1 {
+ t.Errorf("%d != 1", p2.Id)
+ }
+ if p3.Id != 1 {
+ t.Errorf("%d != 1", p3.Id)
+ }
+}
+
+func TestSetUniqueTogether(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.AddTable(UniqueColumns{}).SetUniqueTogether("FirstName", "LastName").SetUniqueTogether("City", "ZipCode")
+ err := dbmap.CreateTablesIfNotExists()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+
+ n1 := &UniqueColumns{"Steve", "Jobs", "Cupertino", 95014}
+ err = dbmap.Insert(n1)
+ if err != nil {
+ t.Error(err)
+ }
+
+ // Should fail because of the first constraint
+ n2 := &UniqueColumns{"Steve", "Jobs", "Sunnyvale", 94085}
+ err = dbmap.Insert(n2)
+ if err == nil {
+ t.Error(err)
+ }
+ // "unique" for Postgres/SQLite, "Duplicate entry" for MySQL
+ errLower := strings.ToLower(err.Error())
+ if !strings.Contains(errLower, "unique") && !strings.Contains(errLower, "duplicate entry") {
+ t.Error(err)
+ }
+
+ // Should also fail because of the second unique-together
+ n3 := &UniqueColumns{"Steve", "Wozniak", "Cupertino", 95014}
+ err = dbmap.Insert(n3)
+ if err == nil {
+ t.Error(err)
+ }
+ // "unique" for Postgres/SQLite, "Duplicate entry" for MySQL
+ errLower = strings.ToLower(err.Error())
+ if !strings.Contains(errLower, "unique") && !strings.Contains(errLower, "duplicate entry") {
+ t.Error(err)
+ }
+
+ // This one should finally succeed
+ n4 := &UniqueColumns{"Steve", "Wozniak", "Sunnyvale", 94085}
+ err = dbmap.Insert(n4)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestPersistentUser(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.Exec("drop table if exists PersistentUser")
+ table := dbmap.AddTable(PersistentUser{}).SetKeys(false, "Key")
+ table.ColMap("Key").Rename("mykey")
+ err := dbmap.CreateTablesIfNotExists()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+ pu := &PersistentUser{43, "33r", false}
+ err = dbmap.Insert(pu)
+ if err != nil {
+ panic(err)
+ }
+
+ // prove we can pass a pointer into Get
+ pu2, err := dbmap.Get(pu, pu.Key)
+ if err != nil {
+ panic(err)
+ }
+ if !reflect.DeepEqual(pu, pu2) {
+ t.Errorf("%v!=%v", pu, pu2)
+ }
+
+ arr, err := dbmap.Select(pu, "select * from "+tableName(dbmap, PersistentUser{}))
+ if err != nil {
+ panic(err)
+ }
+ if !reflect.DeepEqual(pu, arr[0]) {
+ t.Errorf("%v!=%v", pu, arr[0])
+ }
+
+ // prove we can get the results back in a slice
+ var puArr []*PersistentUser
+ _, err = dbmap.Select(&puArr, "select * from "+tableName(dbmap, PersistentUser{}))
+ if err != nil {
+ panic(err)
+ }
+ if len(puArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+ if !reflect.DeepEqual(pu, puArr[0]) {
+ t.Errorf("%v!=%v", pu, puArr[0])
+ }
+
+ // prove we can get the results back in a non-pointer slice
+ var puValues []PersistentUser
+ _, err = dbmap.Select(&puValues, "select * from "+tableName(dbmap, PersistentUser{}))
+ if err != nil {
+ panic(err)
+ }
+ if len(puValues) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+ if !reflect.DeepEqual(*pu, puValues[0]) {
+ t.Errorf("%v!=%v", *pu, puValues[0])
+ }
+
+ // prove we can get the results back in a string slice
+ var idArr []*string
+ _, err = dbmap.Select(&idArr, "select "+columnName(dbmap, PersistentUser{}, "Id")+" from "+tableName(dbmap, PersistentUser{}))
+ if err != nil {
+ panic(err)
+ }
+ if len(idArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+ if !reflect.DeepEqual(pu.Id, *idArr[0]) {
+ t.Errorf("%v!=%v", pu.Id, *idArr[0])
+ }
+
+ // prove we can get the results back in an int slice
+ var keyArr []*int32
+ _, err = dbmap.Select(&keyArr, "select mykey from "+tableName(dbmap, PersistentUser{}))
+ if err != nil {
+ panic(err)
+ }
+ if len(keyArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+ if !reflect.DeepEqual(pu.Key, *keyArr[0]) {
+ t.Errorf("%v!=%v", pu.Key, *keyArr[0])
+ }
+
+ // prove we can get the results back in a bool slice
+ var passedArr []*bool
+ _, err = dbmap.Select(&passedArr, "select "+columnName(dbmap, PersistentUser{}, "PassedTraining")+" from "+tableName(dbmap, PersistentUser{}))
+ if err != nil {
+ panic(err)
+ }
+ if len(passedArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+ if !reflect.DeepEqual(pu.PassedTraining, *passedArr[0]) {
+ t.Errorf("%v!=%v", pu.PassedTraining, *passedArr[0])
+ }
+
+ // prove we can get the results back in a non-pointer slice
+ var stringArr []string
+ _, err = dbmap.Select(&stringArr, "select "+columnName(dbmap, PersistentUser{}, "Id")+" from "+tableName(dbmap, PersistentUser{}))
+ if err != nil {
+ panic(err)
+ }
+ if len(stringArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+ if !reflect.DeepEqual(pu.Id, stringArr[0]) {
+ t.Errorf("%v!=%v", pu.Id, stringArr[0])
+ }
+}
+
+func TestNamedQueryMap(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.Exec("drop table if exists PersistentUser")
+ table := dbmap.AddTable(PersistentUser{}).SetKeys(false, "Key")
+ table.ColMap("Key").Rename("mykey")
+ err := dbmap.CreateTablesIfNotExists()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+ pu := &PersistentUser{43, "33r", false}
+ pu2 := &PersistentUser{500, "abc", false}
+ err = dbmap.Insert(pu, pu2)
+ if err != nil {
+ panic(err)
+ }
+
+ // Test simple case
+ var puArr []*PersistentUser
+ _, err = dbmap.Select(&puArr, "select * from "+tableName(dbmap, PersistentUser{})+" where mykey = :Key", map[string]interface{}{
+ "Key": 43,
+ })
+ if err != nil {
+ t.Errorf("Failed to select: %s", err)
+ t.FailNow()
+ }
+ if len(puArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+ if !reflect.DeepEqual(pu, puArr[0]) {
+ t.Errorf("%v!=%v", pu, puArr[0])
+ }
+
+ // Test more specific map value type is ok
+ puArr = nil
+ _, err = dbmap.Select(&puArr, "select * from "+tableName(dbmap, PersistentUser{})+" where mykey = :Key", map[string]int{
+ "Key": 43,
+ })
+ if err != nil {
+ t.Errorf("Failed to select: %s", err)
+ t.FailNow()
+ }
+ if len(puArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+
+ // Test multiple parameters set.
+ puArr = nil
+ _, err = dbmap.Select(&puArr, `
+select * from `+tableName(dbmap, PersistentUser{})+`
+ where mykey = :Key
+ and `+columnName(dbmap, PersistentUser{}, "PassedTraining")+` = :PassedTraining
+ and `+columnName(dbmap, PersistentUser{}, "Id")+` = :Id`, map[string]interface{}{
+ "Key": 43,
+ "PassedTraining": false,
+ "Id": "33r",
+ })
+ if err != nil {
+ t.Errorf("Failed to select: %s", err)
+ t.FailNow()
+ }
+ if len(puArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+
+ // Test colon within a non-key string
+ // Test having extra, unused properties in the map.
+ puArr = nil
+ _, err = dbmap.Select(&puArr, `
+select * from `+tableName(dbmap, PersistentUser{})+`
+ where mykey = :Key
+ and `+columnName(dbmap, PersistentUser{}, "Id")+` != 'abc:def'`, map[string]interface{}{
+ "Key": 43,
+ "PassedTraining": false,
+ })
+ if err != nil {
+ t.Errorf("Failed to select: %s", err)
+ t.FailNow()
+ }
+ if len(puArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+
+ // Test to delete with Exec and named params.
+ result, err := dbmap.Exec("delete from "+tableName(dbmap, PersistentUser{})+" where mykey = :Key", map[string]interface{}{
+ "Key": 43,
+ })
+ count, err := result.RowsAffected()
+ if err != nil {
+ t.Errorf("Failed to exec: %s", err)
+ t.FailNow()
+ }
+ if count != 1 {
+ t.Errorf("Expected 1 persistentuser to be deleted, but %d deleted", count)
+ }
+}
+
+func TestNamedQueryStruct(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.Exec("drop table if exists PersistentUser")
+ table := dbmap.AddTable(PersistentUser{}).SetKeys(false, "Key")
+ table.ColMap("Key").Rename("mykey")
+ err := dbmap.CreateTablesIfNotExists()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+ pu := &PersistentUser{43, "33r", false}
+ pu2 := &PersistentUser{500, "abc", false}
+ err = dbmap.Insert(pu, pu2)
+ if err != nil {
+ panic(err)
+ }
+
+ // Test select self
+ var puArr []*PersistentUser
+ _, err = dbmap.Select(&puArr, `
+select * from `+tableName(dbmap, PersistentUser{})+`
+ where mykey = :Key
+ and `+columnName(dbmap, PersistentUser{}, "PassedTraining")+` = :PassedTraining
+ and `+columnName(dbmap, PersistentUser{}, "Id")+` = :Id`, pu)
+ if err != nil {
+ t.Errorf("Failed to select: %s", err)
+ t.FailNow()
+ }
+ if len(puArr) != 1 {
+ t.Errorf("Expected one persistentuser, found none")
+ }
+ if !reflect.DeepEqual(pu, puArr[0]) {
+ t.Errorf("%v!=%v", pu, puArr[0])
+ }
+
+ // Test delete self.
+ result, err := dbmap.Exec(`
+delete from `+tableName(dbmap, PersistentUser{})+`
+ where mykey = :Key
+ and `+columnName(dbmap, PersistentUser{}, "PassedTraining")+` = :PassedTraining
+ and `+columnName(dbmap, PersistentUser{}, "Id")+` = :Id`, pu)
+ count, err := result.RowsAffected()
+ if err != nil {
+ t.Errorf("Failed to exec: %s", err)
+ t.FailNow()
+ }
+ if count != 1 {
+ t.Errorf("Expected 1 persistentuser to be deleted, but %d deleted", count)
+ }
+}
+
+// Ensure that the slices containing SQL results are non-nil when the result set is empty.
+func TestReturnsNonNilSlice(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+ noResultsSQL := "select * from invoice_test where " + columnName(dbmap, Invoice{}, "Id") + "=99999"
+ var r1 []*Invoice
+ rawSelect(dbmap, &r1, noResultsSQL)
+ if r1 == nil {
+ t.Errorf("r1==nil")
+ }
+
+ r2 := rawSelect(dbmap, Invoice{}, noResultsSQL)
+ if r2 == nil {
+ t.Errorf("r2==nil")
+ }
+}
+
+func TestOverrideVersionCol(t *testing.T) {
+ dbmap := newDbMap()
+ t1 := dbmap.AddTable(InvoicePersonView{}).SetKeys(false, "InvoiceId", "PersonId")
+ err := dbmap.CreateTables()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+ c1 := t1.SetVersionCol("LegacyVersion")
+ if c1.ColumnName != "LegacyVersion" {
+ t.Errorf("Wrong col returned: %v", c1)
+ }
+
+ ipv := &InvoicePersonView{1, 2, "memo", "fname", 0}
+ _update(dbmap, ipv)
+ if ipv.LegacyVersion != 1 {
+ t.Errorf("LegacyVersion not updated: %d", ipv.LegacyVersion)
+ }
+}
+
+func TestOptimisticLocking(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ p1 := &Person{0, 0, 0, "Bob", "Smith", 0}
+ dbmap.Insert(p1) // Version is now 1
+ if p1.Version != 1 {
+ t.Errorf("Insert didn't incr Version: %d != %d", 1, p1.Version)
+ return
+ }
+ if p1.Id == 0 {
+ t.Errorf("Insert didn't return a generated PK")
+ return
+ }
+
+ obj, err := dbmap.Get(Person{}, p1.Id)
+ if err != nil {
+ panic(err)
+ }
+ p2 := obj.(*Person)
+ p2.LName = "Edwards"
+ dbmap.Update(p2) // Version is now 2
+ if p2.Version != 2 {
+ t.Errorf("Update didn't incr Version: %d != %d", 2, p2.Version)
+ }
+
+ p1.LName = "Howard"
+ count, err := dbmap.Update(p1)
+ if _, ok := err.(gorp.OptimisticLockError); !ok {
+ t.Errorf("update - Expected gorp.OptimisticLockError, got: %v", err)
+ }
+ if count != -1 {
+ t.Errorf("update - Expected -1 count, got: %d", count)
+ }
+
+ count, err = dbmap.Delete(p1)
+ if _, ok := err.(gorp.OptimisticLockError); !ok {
+ t.Errorf("delete - Expected gorp.OptimisticLockError, got: %v", err)
+ }
+ if count != -1 {
+ t.Errorf("delete - Expected -1 count, got: %d", count)
+ }
+}
+
+// what happens if a legacy table has a null value?
+func TestDoubleAddTable(t *testing.T) {
+ dbmap := newDbMap()
+ t1 := dbmap.AddTable(TableWithNull{}).SetKeys(false, "Id")
+ t2 := dbmap.AddTable(TableWithNull{})
+ if t1 != t2 {
+ t.Errorf("%v != %v", t1, t2)
+ }
+}
+
+// what happens if a legacy table has a null value?
+func TestNullValues(t *testing.T) {
+ dbmap := initDbMapNulls()
+ defer dropAndClose(dbmap)
+
+ // insert a row directly
+ rawExec(dbmap, "insert into "+tableName(dbmap, TableWithNull{})+" values (10, null, "+
+ "null, null, null, null)")
+
+ // try to load it
+ expected := &TableWithNull{Id: 10}
+ obj := _get(dbmap, TableWithNull{}, 10)
+ t1 := obj.(*TableWithNull)
+ if !reflect.DeepEqual(expected, t1) {
+ t.Errorf("%v != %v", expected, t1)
+ }
+
+ // update it
+ t1.Str = sql.NullString{"hi", true}
+ expected.Str = t1.Str
+ t1.Int64 = sql.NullInt64{999, true}
+ expected.Int64 = t1.Int64
+ t1.Float64 = sql.NullFloat64{53.33, true}
+ expected.Float64 = t1.Float64
+ t1.Bool = sql.NullBool{true, true}
+ expected.Bool = t1.Bool
+ t1.Bytes = []byte{1, 30, 31, 33}
+ expected.Bytes = t1.Bytes
+ _update(dbmap, t1)
+
+ obj = _get(dbmap, TableWithNull{}, 10)
+ t1 = obj.(*TableWithNull)
+ if t1.Str.String != "hi" {
+ t.Errorf("%s != hi", t1.Str.String)
+ }
+ if !reflect.DeepEqual(expected, t1) {
+ t.Errorf("%v != %v", expected, t1)
+ }
+}
+
+func TestScannerValuer(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.AddTableWithName(PersonValuerScanner{}, "person_test").SetKeys(true, "Id")
+ dbmap.AddTableWithName(InvoiceWithValuer{}, "invoice_test").SetKeys(true, "Id")
+ err := dbmap.CreateTables()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+
+ pv := PersonValuerScanner{}
+ pv.FName = "foo"
+ pv.LName = "bar"
+ err = dbmap.Insert(&pv)
+ if err != nil {
+ t.Errorf("Could not insert PersonValuerScanner using Person table: %v", err)
+ t.FailNow()
+ }
+
+ inv := InvoiceWithValuer{}
+ inv.Memo = "foo"
+ inv.Person = pv
+ err = dbmap.Insert(&inv)
+ if err != nil {
+ t.Errorf("Could not insert InvoiceWithValuer using Invoice table: %v", err)
+ t.FailNow()
+ }
+
+ res, err := dbmap.Get(InvoiceWithValuer{}, inv.Id)
+ if err != nil {
+ t.Errorf("Could not get InvoiceWithValuer: %v", err)
+ t.FailNow()
+ }
+ dbInv := res.(*InvoiceWithValuer)
+
+ if dbInv.Person.Id != pv.Id {
+ t.Errorf("InvoiceWithValuer got wrong person ID: %d (expected) != %d (actual)", pv.Id, dbInv.Person.Id)
+ }
+}
+
+func TestColumnProps(t *testing.T) {
+ dbmap := newDbMap()
+ t1 := dbmap.AddTable(Invoice{}).SetKeys(true, "Id")
+ t1.ColMap("Created").Rename("date_created")
+ t1.ColMap("Updated").SetTransient(true)
+ t1.ColMap("Memo").SetMaxSize(10)
+ t1.ColMap("PersonId").SetUnique(true)
+
+ err := dbmap.CreateTables()
+ if err != nil {
+ panic(err)
+ }
+ defer dropAndClose(dbmap)
+
+ // test transient
+ inv := &Invoice{0, 0, 1, "my invoice", 0, true}
+ _insert(dbmap, inv)
+ obj := _get(dbmap, Invoice{}, inv.Id)
+ inv = obj.(*Invoice)
+ if inv.Updated != 0 {
+ t.Errorf("Saved transient column 'Updated'")
+ }
+
+ // test max size
+ inv.Memo = "this memo is too long"
+ err = dbmap.Insert(inv)
+ if err == nil {
+ t.Errorf("max size exceeded, but Insert did not fail.")
+ }
+
+ // test unique - same person id
+ inv = &Invoice{0, 0, 1, "my invoice2", 0, false}
+ err = dbmap.Insert(inv)
+ if err == nil {
+ t.Errorf("same PersonId inserted, but Insert did not fail.")
+ }
+}
+
+func TestRawSelect(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ p1 := &Person{0, 0, 0, "bob", "smith", 0}
+ _insert(dbmap, p1)
+
+ inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id, true}
+ _insert(dbmap, inv1)
+
+ expected := &InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName, 0}
+
+ query := "select i." + columnName(dbmap, Invoice{}, "Id") + " InvoiceId, p." + columnName(dbmap, Person{}, "Id") + " PersonId, i." + columnName(dbmap, Invoice{}, "Memo") + ", p." + columnName(dbmap, Person{}, "FName") + " " +
+ "from invoice_test i, person_test p " +
+ "where i." + columnName(dbmap, Invoice{}, "PersonId") + " = p." + columnName(dbmap, Person{}, "Id")
+ list := rawSelect(dbmap, InvoicePersonView{}, query)
+ if len(list) != 1 {
+ t.Errorf("len(list) != 1: %d", len(list))
+ } else if !reflect.DeepEqual(expected, list[0]) {
+ t.Errorf("%v != %v", expected, list[0])
+ }
+}
+
+func TestHooks(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ p1 := &Person{0, 0, 0, "bob", "smith", 0}
+ _insert(dbmap, p1)
+ if p1.Created == 0 || p1.Updated == 0 {
+ t.Errorf("p1.PreInsert() didn't run: %v", p1)
+ } else if p1.LName != "postinsert" {
+ t.Errorf("p1.PostInsert() didn't run: %v", p1)
+ }
+
+ obj := _get(dbmap, Person{}, p1.Id)
+ p1 = obj.(*Person)
+ if p1.LName != "postget" {
+ t.Errorf("p1.PostGet() didn't run: %v", p1)
+ }
+
+ _update(dbmap, p1)
+ if p1.FName != "preupdate" {
+ t.Errorf("p1.PreUpdate() didn't run: %v", p1)
+ } else if p1.LName != "postupdate" {
+ t.Errorf("p1.PostUpdate() didn't run: %v", p1)
+ }
+
+ var persons []*Person
+ bindVar := dbmap.Dialect.BindVar(0)
+ rawSelect(dbmap, &persons, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+" = "+bindVar, p1.Id)
+ if persons[0].LName != "postget" {
+ t.Errorf("p1.PostGet() didn't run after select: %v", p1)
+ }
+
+ _del(dbmap, p1)
+ if p1.FName != "predelete" {
+ t.Errorf("p1.PreDelete() didn't run: %v", p1)
+ } else if p1.LName != "postdelete" {
+ t.Errorf("p1.PostDelete() didn't run: %v", p1)
+ }
+
+ // Test error case
+ p2 := &Person{0, 0, 0, "badname", "", 0}
+ err := dbmap.Insert(p2)
+ if err == nil {
+ t.Errorf("p2.PreInsert() didn't return an error")
+ }
+}
+
+func TestTransaction(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ inv1 := &Invoice{0, 100, 200, "t1", 0, true}
+ inv2 := &Invoice{0, 100, 200, "t2", 0, false}
+
+ trans, err := dbmap.Begin()
+ if err != nil {
+ panic(err)
+ }
+ trans.Insert(inv1, inv2)
+ err = trans.Commit()
+ if err != nil {
+ panic(err)
+ }
+
+ obj, err := dbmap.Get(Invoice{}, inv1.Id)
+ if err != nil {
+ panic(err)
+ }
+ if !reflect.DeepEqual(inv1, obj) {
+ t.Errorf("%v != %v", inv1, obj)
+ }
+ obj, err = dbmap.Get(Invoice{}, inv2.Id)
+ if err != nil {
+ panic(err)
+ }
+ if !reflect.DeepEqual(inv2, obj) {
+ t.Errorf("%v != %v", inv2, obj)
+ }
+}
+
+func TestSavepoint(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ inv1 := &Invoice{0, 100, 200, "unpaid", 0, false}
+
+ trans, err := dbmap.Begin()
+ if err != nil {
+ panic(err)
+ }
+ trans.Insert(inv1)
+
+ var checkMemo = func(want string) {
+ memo, err := trans.SelectStr("select " + columnName(dbmap, Invoice{}, "Memo") + " from invoice_test")
+ if err != nil {
+ panic(err)
+ }
+ if memo != want {
+ t.Errorf("%q != %q", want, memo)
+ }
+ }
+ checkMemo("unpaid")
+
+ err = trans.Savepoint("foo")
+ if err != nil {
+ panic(err)
+ }
+ checkMemo("unpaid")
+
+ inv1.Memo = "paid"
+ _, err = trans.Update(inv1)
+ if err != nil {
+ panic(err)
+ }
+ checkMemo("paid")
+
+ err = trans.RollbackToSavepoint("foo")
+ if err != nil {
+ panic(err)
+ }
+ checkMemo("unpaid")
+
+ err = trans.Rollback()
+ if err != nil {
+ panic(err)
+ }
+}
+
+func TestMultiple(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ inv1 := &Invoice{0, 100, 200, "a", 0, false}
+ inv2 := &Invoice{0, 100, 200, "b", 0, true}
+ _insert(dbmap, inv1, inv2)
+
+ inv1.Memo = "c"
+ inv2.Memo = "d"
+ _update(dbmap, inv1, inv2)
+
+ count := _del(dbmap, inv1, inv2)
+ if count != 2 {
+ t.Errorf("%d != 2", count)
+ }
+}
+
+func TestCrud(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ inv := &Invoice{0, 100, 200, "first order", 0, true}
+ testCrudInternal(t, dbmap, inv)
+
+ invtag := &InvoiceTag{0, 300, 400, "some order", 33, false}
+ testCrudInternal(t, dbmap, invtag)
+
+ foo := &AliasTransientField{BarStr: "some bar"}
+ testCrudInternal(t, dbmap, foo)
+
+ dynamicTablesTest(t, dbmap)
+}
+
+func testCrudInternal(t *testing.T, dbmap *gorp.DbMap, val testable) {
+ table, err := dbmap.TableFor(reflect.TypeOf(val).Elem(), false)
+ if err != nil {
+ t.Errorf("couldn't call TableFor: val=%v err=%v", val, err)
+ }
+
+ _, err = dbmap.Exec("delete from " + table.TableName)
+ if err != nil {
+ t.Errorf("couldn't delete rows from: val=%v err=%v", val, err)
+ }
+
+ // INSERT row
+ _insert(dbmap, val)
+ if val.GetId() == 0 {
+ t.Errorf("val.GetId() was not set on INSERT")
+ return
+ }
+
+ // SELECT row
+ val2 := _get(dbmap, val, val.GetId())
+ if !reflect.DeepEqual(val, val2) {
+ t.Errorf("%v != %v", val, val2)
+ }
+
+ // UPDATE row and SELECT
+ val.Rand()
+ count := _update(dbmap, val)
+ if count != 1 {
+ t.Errorf("update 1 != %d", count)
+ }
+ val2 = _get(dbmap, val, val.GetId())
+ if !reflect.DeepEqual(val, val2) {
+ t.Errorf("%v != %v", val, val2)
+ }
+
+ // Select *
+ rows, err := dbmap.Select(val, "select * from "+dbmap.Dialect.QuoteField(table.TableName))
+ if err != nil {
+ t.Errorf("couldn't select * from %s err=%v", dbmap.Dialect.QuoteField(table.TableName), err)
+ } else if len(rows) != 1 {
+ t.Errorf("unexpected row count in %s: %d", dbmap.Dialect.QuoteField(table.TableName), len(rows))
+ } else if !reflect.DeepEqual(val, rows[0]) {
+ t.Errorf("select * result: %v != %v", val, rows[0])
+ }
+
+ // DELETE row
+ deleted := _del(dbmap, val)
+ if deleted != 1 {
+ t.Errorf("Did not delete row with Id: %d", val.GetId())
+ return
+ }
+
+ // VERIFY deleted
+ val2 = _get(dbmap, val, val.GetId())
+ if val2 != nil {
+ t.Errorf("Found invoice with id: %d after Delete()", val.GetId())
+ }
+}
+
+func TestWithIgnoredColumn(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ ic := &WithIgnoredColumn{-1, 0, 1}
+ _insert(dbmap, ic)
+ expected := &WithIgnoredColumn{0, 1, 1}
+ ic2 := _get(dbmap, WithIgnoredColumn{}, ic.Id).(*WithIgnoredColumn)
+
+ if !reflect.DeepEqual(expected, ic2) {
+ t.Errorf("%v != %v", expected, ic2)
+ }
+ if _del(dbmap, ic) != 1 {
+ t.Errorf("Did not delete row with Id: %d", ic.Id)
+ return
+ }
+ if _get(dbmap, WithIgnoredColumn{}, ic.Id) != nil {
+ t.Errorf("Found id: %d after Delete()", ic.Id)
+ }
+}
+
+func TestColumnFilter(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ inv1 := &Invoice{0, 100, 200, "a", 0, false}
+ _insert(dbmap, inv1)
+
+ inv1.Memo = "c"
+ inv1.IsPaid = true
+ _updateColumns(dbmap, func(col *gorp.ColumnMap) bool {
+ return col.ColumnName == "Memo"
+ }, inv1)
+
+ inv2 := &Invoice{}
+ inv2 = _get(dbmap, inv2, inv1.Id).(*Invoice)
+ if inv2.Memo != "c" {
+ t.Errorf("Expected column to be updated (%#v)", inv2)
+ }
+ if inv2.IsPaid {
+ t.Error("IsPaid shouldn't have been updated")
+ }
+}
+
+func TestTypeConversionExample(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ p := Person{FName: "Bob", LName: "Smith"}
+ tc := &TypeConversionExample{-1, p, CustomStringType("hi")}
+ _insert(dbmap, tc)
+
+ expected := &TypeConversionExample{1, p, CustomStringType("hi")}
+ tc2 := _get(dbmap, TypeConversionExample{}, tc.Id).(*TypeConversionExample)
+ if !reflect.DeepEqual(expected, tc2) {
+ t.Errorf("tc2 %v != %v", expected, tc2)
+ }
+
+ tc2.Name = CustomStringType("hi2")
+ tc2.PersonJSON = Person{FName: "Jane", LName: "Doe"}
+ _update(dbmap, tc2)
+
+ expected = &TypeConversionExample{1, tc2.PersonJSON, CustomStringType("hi2")}
+ tc3 := _get(dbmap, TypeConversionExample{}, tc.Id).(*TypeConversionExample)
+ if !reflect.DeepEqual(expected, tc3) {
+ t.Errorf("tc3 %v != %v", expected, tc3)
+ }
+
+ if _del(dbmap, tc) != 1 {
+ t.Errorf("Did not delete row with Id: %d", tc.Id)
+ }
+
+}
+
+func TestWithEmbeddedStruct(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ es := &WithEmbeddedStruct{-1, Names{FirstName: "Alice", LastName: "Smith"}}
+ _insert(dbmap, es)
+ expected := &WithEmbeddedStruct{1, Names{FirstName: "Alice", LastName: "Smith"}}
+ es2 := _get(dbmap, WithEmbeddedStruct{}, es.Id).(*WithEmbeddedStruct)
+ if !reflect.DeepEqual(expected, es2) {
+ t.Errorf("%v != %v", expected, es2)
+ }
+
+ es2.FirstName = "Bob"
+ expected.FirstName = "Bob"
+ _update(dbmap, es2)
+ es2 = _get(dbmap, WithEmbeddedStruct{}, es.Id).(*WithEmbeddedStruct)
+ if !reflect.DeepEqual(expected, es2) {
+ t.Errorf("%v != %v", expected, es2)
+ }
+
+ ess := rawSelect(dbmap, WithEmbeddedStruct{}, "select * from embedded_struct_test")
+ if !reflect.DeepEqual(es2, ess[0]) {
+ t.Errorf("%v != %v", es2, ess[0])
+ }
+}
+
+/*
+func TestWithEmbeddedStructConflictingEmbeddedMemberNames(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ es := &WithEmbeddedStructConflictingEmbeddedMemberNames{-1, Names{FirstName: "Alice", LastName: "Smith"}, NamesConflict{FirstName: "Andrew", Surname: "Wiggin"}}
+ _insert(dbmap, es)
+ expected := &WithEmbeddedStructConflictingEmbeddedMemberNames{-1, Names{FirstName: "Alice", LastName: "Smith"}, NamesConflict{FirstName: "Andrew", Surname: "Wiggin"}}
+ es2 := _get(dbmap, WithEmbeddedStructConflictingEmbeddedMemberNames{}, es.Id).(*WithEmbeddedStructConflictingEmbeddedMemberNames)
+ if !reflect.DeepEqual(expected, es2) {
+ t.Errorf("%v != %v", expected, es2)
+ }
+
+ es2.Names.FirstName = "Bob"
+ expected.Names.FirstName = "Bob"
+ _update(dbmap, es2)
+ es2 = _get(dbmap, WithEmbeddedStructConflictingEmbeddedMemberNames{}, es.Id).(*WithEmbeddedStructConflictingEmbeddedMemberNames)
+ if !reflect.DeepEqual(expected, es2) {
+ t.Errorf("%v != %v", expected, es2)
+ }
+
+ ess := rawSelect(dbmap, WithEmbeddedStructConflictingEmbeddedMemberNames{}, "select * from embedded_struct_conflict_name_test")
+ if !reflect.DeepEqual(es2, ess[0]) {
+ t.Errorf("%v != %v", es2, ess[0])
+ }
+}
+
+func TestWithEmbeddedStructSameMemberName(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ es := &WithEmbeddedStructSameMemberName{-1, SameName{SameName: "Alice"}}
+ _insert(dbmap, es)
+ expected := &WithEmbeddedStructSameMemberName{-1, SameName{SameName: "Alice"}}
+ es2 := _get(dbmap, WithEmbeddedStructSameMemberName{}, es.Id).(*WithEmbeddedStructSameMemberName)
+ if !reflect.DeepEqual(expected, es2) {
+ t.Errorf("%v != %v", expected, es2)
+ }
+
+ es2.SameName = SameName{"Bob"}
+ expected.SameName = SameName{"Bob"}
+ _update(dbmap, es2)
+ es2 = _get(dbmap, WithEmbeddedStructSameMemberName{}, es.Id).(*WithEmbeddedStructSameMemberName)
+ if !reflect.DeepEqual(expected, es2) {
+ t.Errorf("%v != %v", expected, es2)
+ }
+
+ ess := rawSelect(dbmap, WithEmbeddedStructSameMemberName{}, "select * from embedded_struct_same_member_name_test")
+ if !reflect.DeepEqual(es2, ess[0]) {
+ t.Errorf("%v != %v", es2, ess[0])
+ }
+}
+//*/
+
+func TestWithEmbeddedStructBeforeAutoincr(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ esba := &WithEmbeddedStructBeforeAutoincrField{Names: Names{FirstName: "Alice", LastName: "Smith"}}
+ _insert(dbmap, esba)
+ var expectedAutoincrId int64 = 1
+ if esba.Id != expectedAutoincrId {
+ t.Errorf("%d != %d", expectedAutoincrId, esba.Id)
+ }
+}
+
+func TestWithEmbeddedAutoincr(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ esa := &WithEmbeddedAutoincr{
+ WithEmbeddedStruct: WithEmbeddedStruct{Names: Names{FirstName: "Alice", LastName: "Smith"}},
+ MiddleName: "Rose",
+ }
+ _insert(dbmap, esa)
+ var expectedAutoincrId int64 = 1
+ if esa.Id != expectedAutoincrId {
+ t.Errorf("%d != %d", expectedAutoincrId, esa.Id)
+ }
+}
+
+func TestSelectVal(t *testing.T) {
+ dbmap := initDbMapNulls()
+ defer dropAndClose(dbmap)
+
+ bindVar := dbmap.Dialect.BindVar(0)
+
+ t1 := TableWithNull{Str: sql.NullString{"abc", true},
+ Int64: sql.NullInt64{78, true},
+ Float64: sql.NullFloat64{32.2, true},
+ Bool: sql.NullBool{true, true},
+ Bytes: []byte("hi")}
+ _insert(dbmap, &t1)
+
+ // SelectInt
+ i64 := selectInt(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Int64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='abc'")
+ if i64 != 78 {
+ t.Errorf("int64 %d != 78", i64)
+ }
+ i64 = selectInt(dbmap, "select count(*) from "+tableName(dbmap, TableWithNull{}))
+ if i64 != 1 {
+ t.Errorf("int64 count %d != 1", i64)
+ }
+ i64 = selectInt(dbmap, "select count(*) from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"="+bindVar, "asdfasdf")
+ if i64 != 0 {
+ t.Errorf("int64 no rows %d != 0", i64)
+ }
+
+ // SelectNullInt
+ n := selectNullInt(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Int64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='notfound'")
+ if !reflect.DeepEqual(n, sql.NullInt64{0, false}) {
+ t.Errorf("nullint %v != 0,false", n)
+ }
+
+ n = selectNullInt(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Int64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='abc'")
+ if !reflect.DeepEqual(n, sql.NullInt64{78, true}) {
+ t.Errorf("nullint %v != 78, true", n)
+ }
+
+ // SelectFloat
+ f64 := selectFloat(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Float64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='abc'")
+ if f64 != 32.2 {
+ t.Errorf("float64 %d != 32.2", f64)
+ }
+ f64 = selectFloat(dbmap, "select min("+columnName(dbmap, TableWithNull{}, "Float64")+") from "+tableName(dbmap, TableWithNull{}))
+ if f64 != 32.2 {
+ t.Errorf("float64 min %d != 32.2", f64)
+ }
+ f64 = selectFloat(dbmap, "select count(*) from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"="+bindVar, "asdfasdf")
+ if f64 != 0 {
+ t.Errorf("float64 no rows %d != 0", f64)
+ }
+
+ // SelectNullFloat
+ nf := selectNullFloat(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Float64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='notfound'")
+ if !reflect.DeepEqual(nf, sql.NullFloat64{0, false}) {
+ t.Errorf("nullfloat %v != 0,false", nf)
+ }
+
+ nf = selectNullFloat(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Float64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='abc'")
+ if !reflect.DeepEqual(nf, sql.NullFloat64{32.2, true}) {
+ t.Errorf("nullfloat %v != 32.2, true", nf)
+ }
+
+ // SelectStr
+ s := selectStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Int64")+"="+bindVar, 78)
+ if s != "abc" {
+ t.Errorf("s %s != abc", s)
+ }
+ s = selectStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='asdfasdf'")
+ if s != "" {
+ t.Errorf("s no rows %s != ''", s)
+ }
+
+ // SelectNullStr
+ ns := selectNullStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Int64")+"="+bindVar, 78)
+ if !reflect.DeepEqual(ns, sql.NullString{"abc", true}) {
+ t.Errorf("nullstr %v != abc,true", ns)
+ }
+ ns = selectNullStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"='asdfasdf'")
+ if !reflect.DeepEqual(ns, sql.NullString{"", false}) {
+ t.Errorf("nullstr no rows %v != '',false", ns)
+ }
+
+ // SelectInt/Str with named parameters
+ i64 = selectInt(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Int64")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Str")+"=:abc", map[string]string{"abc": "abc"})
+ if i64 != 78 {
+ t.Errorf("int64 %d != 78", i64)
+ }
+ ns = selectNullStr(dbmap, "select "+columnName(dbmap, TableWithNull{}, "Str")+" from "+tableName(dbmap, TableWithNull{})+" where "+columnName(dbmap, TableWithNull{}, "Int64")+"=:num", map[string]int{"num": 78})
+ if !reflect.DeepEqual(ns, sql.NullString{"abc", true}) {
+ t.Errorf("nullstr %v != abc,true", ns)
+ }
+}
+
+func TestVersionMultipleRows(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ persons := []*Person{
+ &Person{0, 0, 0, "Bob", "Smith", 0},
+ &Person{0, 0, 0, "Jane", "Smith", 0},
+ &Person{0, 0, 0, "Mike", "Smith", 0},
+ }
+
+ _insert(dbmap, persons[0], persons[1], persons[2])
+
+ for x, p := range persons {
+ if p.Version != 1 {
+ t.Errorf("person[%d].Version != 1: %d", x, p.Version)
+ }
+ }
+}
+
+func TestWithStringPk(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.AddTableWithName(WithStringPk{}, "string_pk_test").SetKeys(true, "Id")
+ _, err := dbmap.Exec("create table string_pk_test (Id varchar(255), Name varchar(255));")
+ if err != nil {
+ t.Errorf("couldn't create string_pk_test: %v", err)
+ }
+ defer dropAndClose(dbmap)
+
+ row := &WithStringPk{"1", "foo"}
+ err = dbmap.Insert(row)
+ if err == nil {
+ t.Errorf("Expected error when inserting into table w/non Int PK and autoincr set true")
+ }
+}
+
+// TestSqlExecutorInterfaceSelects ensures that all gorp.DbMap methods starting with Select...
+// are also exposed in the gorp.SqlExecutor interface. Select... functions can always
+// run on Pre/Post hooks.
+func TestSqlExecutorInterfaceSelects(t *testing.T) {
+ dbMapType := reflect.TypeOf(&gorp.DbMap{})
+ sqlExecutorType := reflect.TypeOf((*gorp.SqlExecutor)(nil)).Elem()
+ numDbMapMethods := dbMapType.NumMethod()
+ for i := 0; i < numDbMapMethods; i += 1 {
+ dbMapMethod := dbMapType.Method(i)
+ if !strings.HasPrefix(dbMapMethod.Name, "Select") {
+ continue
+ }
+ if _, found := sqlExecutorType.MethodByName(dbMapMethod.Name); !found {
+ t.Errorf("Method %s is defined on gorp.DbMap but not implemented in gorp.SqlExecutor",
+ dbMapMethod.Name)
+ }
+ }
+}
+
+func TestNullTime(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ // if time is null
+ ent := &WithNullTime{
+ Id: 0,
+ Time: gorp.NullTime{
+ Valid: false,
+ }}
+ err := dbmap.Insert(ent)
+ if err != nil {
+ t.Error("failed insert on %s", err.Error())
+ }
+ err = dbmap.SelectOne(ent, `select * from nulltime_test where `+columnName(dbmap, WithNullTime{}, "Id")+`=:Id`, map[string]interface{}{
+ "Id": ent.Id,
+ })
+ if err != nil {
+ t.Error("failed select on %s", err.Error())
+ }
+ if ent.Time.Valid {
+ t.Error("gorp.NullTime returns valid but expected null.")
+ }
+
+ // if time is not null
+ ts, err := time.Parse(time.Stamp, "Jan 2 15:04:05")
+ ent = &WithNullTime{
+ Id: 1,
+ Time: gorp.NullTime{
+ Valid: true,
+ Time: ts,
+ }}
+ err = dbmap.Insert(ent)
+ if err != nil {
+ t.Error("failed insert on %s", err.Error())
+ }
+ err = dbmap.SelectOne(ent, `select * from nulltime_test where `+columnName(dbmap, WithNullTime{}, "Id")+`=:Id`, map[string]interface{}{
+ "Id": ent.Id,
+ })
+ if err != nil {
+ t.Error("failed select on %s", err.Error())
+ }
+ if !ent.Time.Valid {
+ t.Error("gorp.NullTime returns invalid but expected valid.")
+ }
+ if ent.Time.Time.UTC() != ts.UTC() {
+ t.Errorf("expect %v but got %v.", ts, ent.Time.Time)
+ }
+
+ return
+}
+
+type WithTime struct {
+ Id int64
+ Time time.Time
+}
+
+type Times struct {
+ One time.Time
+ Two time.Time
+}
+
+type EmbeddedTime struct {
+ Id string
+ Times
+}
+
+func parseTimeOrPanic(format, date string) time.Time {
+ t1, err := time.Parse(format, date)
+ if err != nil {
+ panic(err)
+ }
+ return t1
+}
+
+// TODO: re-enable next two tests when this is merged:
+// https://github.com/ziutek/mymysql/pull/77
+//
+// This test currently fails w/MySQL b/c tz info is lost
+func testWithTime(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ t1 := parseTimeOrPanic("2006-01-02 15:04:05 -0700 MST",
+ "2013-08-09 21:30:43 +0800 CST")
+ w1 := WithTime{1, t1}
+ _insert(dbmap, &w1)
+
+ obj := _get(dbmap, WithTime{}, w1.Id)
+ w2 := obj.(*WithTime)
+ if w1.Time.UnixNano() != w2.Time.UnixNano() {
+ t.Errorf("%v != %v", w1, w2)
+ }
+}
+
+// See: https://github.com/go-gorp/gorp/issues/86
+func testEmbeddedTime(t *testing.T) {
+ dbmap := newDbMap()
+ dbmap.AddTable(EmbeddedTime{}).SetKeys(false, "Id")
+ defer dropAndClose(dbmap)
+ err := dbmap.CreateTables()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ time1 := parseTimeOrPanic("2006-01-02 15:04:05", "2013-08-09 21:30:43")
+
+ t1 := &EmbeddedTime{Id: "abc", Times: Times{One: time1, Two: time1.Add(10 * time.Second)}}
+ _insert(dbmap, t1)
+
+ x := _get(dbmap, EmbeddedTime{}, t1.Id)
+ t2, _ := x.(*EmbeddedTime)
+ if t1.One.UnixNano() != t2.One.UnixNano() || t1.Two.UnixNano() != t2.Two.UnixNano() {
+ t.Errorf("%v != %v", t1, t2)
+ }
+}
+
+func TestWithTimeSelect(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ halfhourago := time.Now().UTC().Add(-30 * time.Minute)
+
+ w1 := WithTime{1, halfhourago.Add(time.Minute * -1)}
+ w2 := WithTime{2, halfhourago.Add(time.Second)}
+ _insert(dbmap, &w1, &w2)
+
+ var caseIds []int64
+ _, err := dbmap.Select(&caseIds, "SELECT "+columnName(dbmap, WithTime{}, "Id")+" FROM time_test WHERE "+columnName(dbmap, WithTime{}, "Time")+" < "+dbmap.Dialect.BindVar(0), halfhourago)
+
+ if err != nil {
+ t.Error(err)
+ }
+ if len(caseIds) != 1 {
+ t.Errorf("%d != 1", len(caseIds))
+ }
+ if caseIds[0] != w1.Id {
+ t.Errorf("%d != %d", caseIds[0], w1.Id)
+ }
+}
+
+func TestInvoicePersonView(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ // Create some rows
+ p1 := &Person{0, 0, 0, "bob", "smith", 0}
+ dbmap.Insert(p1)
+
+ // notice how we can wire up p1.Id to the invoice easily
+ inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id, false}
+ dbmap.Insert(inv1)
+
+ // Run your query
+ query := "select i." + columnName(dbmap, Invoice{}, "Id") + " InvoiceId, p." + columnName(dbmap, Person{}, "Id") + " PersonId, i." + columnName(dbmap, Invoice{}, "Memo") + ", p." + columnName(dbmap, Person{}, "FName") + " " +
+ "from invoice_test i, person_test p " +
+ "where i." + columnName(dbmap, Invoice{}, "PersonId") + " = p." + columnName(dbmap, Person{}, "Id")
+
+ // pass a slice of pointers to Select()
+ // this avoids the need to type assert after the query is run
+ var list []*InvoicePersonView
+ _, err := dbmap.Select(&list, query)
+ if err != nil {
+ panic(err)
+ }
+
+ // this should test true
+ expected := &InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName, 0}
+ if !reflect.DeepEqual(list[0], expected) {
+ t.Errorf("%v != %v", list[0], expected)
+ }
+}
+
+func TestQuoteTableNames(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ quotedTableName := dbmap.Dialect.QuoteField("person_test")
+
+ // Use a buffer to hold the log to check generated queries
+ logBuffer := &bytes.Buffer{}
+ dbmap.TraceOn("", log.New(logBuffer, "gorptest:", log.Lmicroseconds))
+
+ // Create some rows
+ p1 := &Person{0, 0, 0, "bob", "smith", 0}
+ errorTemplate := "Expected quoted table name %v in query but didn't find it"
+
+ // Check if Insert quotes the table name
+ id := dbmap.Insert(p1)
+ if !bytes.Contains(logBuffer.Bytes(), []byte(quotedTableName)) {
+ t.Errorf(errorTemplate, quotedTableName)
+ }
+ logBuffer.Reset()
+
+ // Check if Get quotes the table name
+ dbmap.Get(Person{}, id)
+ if !bytes.Contains(logBuffer.Bytes(), []byte(quotedTableName)) {
+ t.Errorf(errorTemplate, quotedTableName)
+ }
+ logBuffer.Reset()
+}
+
+func TestSelectTooManyCols(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ p1 := &Person{0, 0, 0, "bob", "smith", 0}
+ p2 := &Person{0, 0, 0, "jane", "doe", 0}
+ _insert(dbmap, p1)
+ _insert(dbmap, p2)
+
+ obj := _get(dbmap, Person{}, p1.Id)
+ p1 = obj.(*Person)
+ obj = _get(dbmap, Person{}, p2.Id)
+ p2 = obj.(*Person)
+
+ params := map[string]interface{}{
+ "Id": p1.Id,
+ }
+
+ var p3 FNameOnly
+ err := dbmap.SelectOne(&p3, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params)
+ if err != nil {
+ if !gorp.NonFatalError(err) {
+ t.Error(err)
+ }
+ } else {
+ t.Errorf("Non-fatal error expected")
+ }
+
+ if p1.FName != p3.FName {
+ t.Errorf("%v != %v", p1.FName, p3.FName)
+ }
+
+ var pSlice []FNameOnly
+ _, err = dbmap.Select(&pSlice, "select * from person_test order by "+columnName(dbmap, Person{}, "FName")+" asc")
+ if err != nil {
+ if !gorp.NonFatalError(err) {
+ t.Error(err)
+ }
+ } else {
+ t.Errorf("Non-fatal error expected")
+ }
+
+ if p1.FName != pSlice[0].FName {
+ t.Errorf("%v != %v", p1.FName, pSlice[0].FName)
+ }
+ if p2.FName != pSlice[1].FName {
+ t.Errorf("%v != %v", p2.FName, pSlice[1].FName)
+ }
+}
+
+func TestSelectSingleVal(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ p1 := &Person{0, 0, 0, "bob", "smith", 0}
+ _insert(dbmap, p1)
+
+ obj := _get(dbmap, Person{}, p1.Id)
+ p1 = obj.(*Person)
+
+ params := map[string]interface{}{
+ "Id": p1.Id,
+ }
+
+ var p2 Person
+ err := dbmap.SelectOne(&p2, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if !reflect.DeepEqual(p1, &p2) {
+ t.Errorf("%v != %v", p1, &p2)
+ }
+
+ // verify SelectOne allows non-struct holders
+ var s string
+ err = dbmap.SelectOne(&s, "select "+columnName(dbmap, Person{}, "FName")+" from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params)
+ if err != nil {
+ t.Error(err)
+ }
+ if s != "bob" {
+ t.Error("Expected bob but got: " + s)
+ }
+
+ // verify SelectOne requires pointer receiver
+ err = dbmap.SelectOne(s, "select "+columnName(dbmap, Person{}, "FName")+" from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params)
+ if err == nil {
+ t.Error("SelectOne should have returned error for non-pointer holder")
+ }
+
+ // verify SelectOne works with uninitialized pointers
+ var p3 *Person
+ err = dbmap.SelectOne(&p3, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", params)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if !reflect.DeepEqual(p1, p3) {
+ t.Errorf("%v != %v", p1, p3)
+ }
+
+ // verify that the receiver is still nil if nothing was found
+ var p4 *Person
+ dbmap.SelectOne(&p3, "select * from person_test where 2<1 AND "+columnName(dbmap, Person{}, "Id")+"=:Id", params)
+ if p4 != nil {
+ t.Error("SelectOne should not have changed a nil receiver when no rows were found")
+ }
+
+ // verify that the error is set to sql.ErrNoRows if not found
+ err = dbmap.SelectOne(&p2, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=:Id", map[string]interface{}{
+ "Id": -2222,
+ })
+ if err == nil || err != sql.ErrNoRows {
+ t.Error("SelectOne should have returned an sql.ErrNoRows")
+ }
+
+ _insert(dbmap, &Person{0, 0, 0, "bob", "smith", 0})
+ err = dbmap.SelectOne(&p2, "select * from person_test where "+columnName(dbmap, Person{}, "FName")+"='bob'")
+ if err == nil {
+ t.Error("Expected error when two rows found")
+ }
+
+ // tests for #150
+ var tInt int64
+ var tStr string
+ var tBool bool
+ var tFloat float64
+ primVals := []interface{}{tInt, tStr, tBool, tFloat}
+ for _, prim := range primVals {
+ err = dbmap.SelectOne(&prim, "select * from person_test where "+columnName(dbmap, Person{}, "Id")+"=-123")
+ if err == nil || err != sql.ErrNoRows {
+ t.Error("primVals: SelectOne should have returned sql.ErrNoRows")
+ }
+ }
+}
+
+func TestSelectAlias(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ p1 := &IdCreatedExternal{IdCreated: IdCreated{Id: 1, Created: 3}, External: 2}
+
+ // Insert using embedded IdCreated, which reflects the structure of the table
+ _insert(dbmap, &p1.IdCreated)
+
+ // Select into IdCreatedExternal type, which includes some fields not present
+ // in id_created_test
+ var p2 IdCreatedExternal
+ err := dbmap.SelectOne(&p2, "select * from id_created_test where "+columnName(dbmap, IdCreatedExternal{}, "Id")+"=1")
+ if err != nil {
+ t.Error(err)
+ }
+ if p2.Id != 1 || p2.Created != 3 || p2.External != 0 {
+ t.Error("Expected ignored field defaults to not set")
+ }
+
+ // Prove that we can supply an aliased value in the select, and that it will
+ // automatically map to IdCreatedExternal.External
+ err = dbmap.SelectOne(&p2, "SELECT *, 1 AS external FROM id_created_test")
+ if err != nil {
+ t.Error(err)
+ }
+ if p2.External != 1 {
+ t.Error("Expected select as can map to exported field.")
+ }
+
+ var rows *sql.Rows
+ var cols []string
+ rows, err = dbmap.Db.Query("SELECT * FROM id_created_test")
+ cols, err = rows.Columns()
+ if err != nil || len(cols) != 2 {
+ t.Error("Expected ignored column not created")
+ }
+}
+
+func TestMysqlPanicIfDialectNotInitialized(t *testing.T) {
+ _, driver := dialectAndDriver()
+ // this test only applies to MySQL
+ if os.Getenv("GORP_TEST_DIALECT") != "mysql" {
+ return
+ }
+
+ // The expected behaviour is to catch a panic.
+ // Here is the deferred function which will check if a panic has indeed occurred :
+ defer func() {
+ r := recover()
+ if r == nil {
+ t.Error("db.CreateTables() should panic if db is initialized with an incorrect gorp.MySQLDialect")
+ }
+ }()
+
+ // invalid MySQLDialect : does not contain Engine or Encoding specification
+ dialect := gorp.MySQLDialect{}
+ db := &gorp.DbMap{Db: connect(driver), Dialect: dialect}
+ db.AddTableWithName(Invoice{}, "invoice")
+ // the following call should panic :
+ db.CreateTables()
+}
+
+func TestSingleColumnKeyDbReturnsZeroRowsUpdatedOnPKChange(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+ dbmap.AddTableWithName(SingleColumnTable{}, "single_column_table").SetKeys(false, "SomeId")
+ err := dbmap.DropTablesIfExists()
+ if err != nil {
+ t.Error("Drop tables failed")
+ }
+ err = dbmap.CreateTablesIfNotExists()
+ if err != nil {
+ t.Error("Create tables failed")
+ }
+ err = dbmap.TruncateTables()
+ if err != nil {
+ t.Error("Truncate tables failed")
+ }
+
+ sct := SingleColumnTable{
+ SomeId: "A Unique Id String",
+ }
+
+ count, err := dbmap.Update(&sct)
+ if err != nil {
+ t.Error(err)
+ }
+ if count != 0 {
+ t.Errorf("Expected 0 updated rows, got %d", count)
+ }
+
+}
+
+func TestPrepare(t *testing.T) {
+ dbmap := initDbMap()
+ defer dropAndClose(dbmap)
+
+ inv1 := &Invoice{0, 100, 200, "prepare-foo", 0, false}
+ inv2 := &Invoice{0, 100, 200, "prepare-bar", 0, false}
+ _insert(dbmap, inv1, inv2)
+
+ bindVar0 := dbmap.Dialect.BindVar(0)
+ bindVar1 := dbmap.Dialect.BindVar(1)
+ stmt, err := dbmap.Prepare(fmt.Sprintf("UPDATE invoice_test SET "+columnName(dbmap, Invoice{}, "Memo")+"=%s WHERE "+columnName(dbmap, Invoice{}, "Id")+"=%s", bindVar0, bindVar1))
+ if err != nil {
+ t.Error(err)
+ }
+ defer stmt.Close()
+ _, err = stmt.Exec("prepare-baz", inv1.Id)
+ if err != nil {
+ t.Error(err)
+ }
+ err = dbmap.SelectOne(inv1, "SELECT * from invoice_test WHERE "+columnName(dbmap, Invoice{}, "Memo")+"='prepare-baz'")
+ if err != nil {
+ t.Error(err)
+ }
+
+ trans, err := dbmap.Begin()
+ if err != nil {
+ t.Error(err)
+ }
+ transStmt, err := trans.Prepare(fmt.Sprintf("UPDATE invoice_test SET "+columnName(dbmap, Invoice{}, "IsPaid")+"=%s WHERE "+columnName(dbmap, Invoice{}, "Id")+"=%s", bindVar0, bindVar1))
+ if err != nil {
+ t.Error(err)
+ }
+ defer transStmt.Close()
+ _, err = transStmt.Exec(true, inv2.Id)
+ if err != nil {
+ t.Error(err)
+ }
+ err = dbmap.SelectOne(inv2, fmt.Sprintf("SELECT * from invoice_test WHERE "+columnName(dbmap, Invoice{}, "IsPaid")+"=%s", bindVar0), true)
+ if err == nil || err != sql.ErrNoRows {
+ t.Error("SelectOne should have returned an sql.ErrNoRows")
+ }
+ err = trans.SelectOne(inv2, fmt.Sprintf("SELECT * from invoice_test WHERE "+columnName(dbmap, Invoice{}, "IsPaid")+"=%s", bindVar0), true)
+ if err != nil {
+ t.Error(err)
+ }
+ err = trans.Commit()
+ if err != nil {
+ t.Error(err)
+ }
+ err = dbmap.SelectOne(inv2, fmt.Sprintf("SELECT * from invoice_test WHERE "+columnName(dbmap, Invoice{}, "IsPaid")+"=%s", bindVar0), true)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func BenchmarkNativeCrud(b *testing.B) {
+ b.StopTimer()
+ dbmap := initDbMapBench()
+ defer dropAndClose(dbmap)
+ columnId := columnName(dbmap, Invoice{}, "Id")
+ columnCreated := columnName(dbmap, Invoice{}, "Created")
+ columnUpdated := columnName(dbmap, Invoice{}, "Updated")
+ columnMemo := columnName(dbmap, Invoice{}, "Memo")
+ columnPersonId := columnName(dbmap, Invoice{}, "PersonId")
+ b.StartTimer()
+
+ var insert, sel, update, delete string
+ if os.Getenv("GORP_TEST_DIALECT") != "postgres" {
+ insert = "insert into invoice_test (" + columnCreated + ", " + columnUpdated + ", " + columnMemo + ", " + columnPersonId + ") values (?, ?, ?, ?)"
+ sel = "select " + columnId + ", " + columnCreated + ", " + columnUpdated + ", " + columnMemo + ", " + columnPersonId + " from invoice_test where " + columnId + "=?"
+ update = "update invoice_test set " + columnCreated + "=?, " + columnUpdated + "=?, " + columnMemo + "=?, " + columnPersonId + "=? where " + columnId + "=?"
+ delete = "delete from invoice_test where " + columnId + "=?"
+ } else {
+ insert = "insert into invoice_test (" + columnCreated + ", " + columnUpdated + ", " + columnMemo + ", " + columnPersonId + ") values ($1, $2, $3, $4)"
+ sel = "select " + columnId + ", " + columnCreated + ", " + columnUpdated + ", " + columnMemo + ", " + columnPersonId + " from invoice_test where " + columnId + "=$1"
+ update = "update invoice_test set " + columnCreated + "=$1, " + columnUpdated + "=$2, " + columnMemo + "=$3, " + columnPersonId + "=$4 where " + columnId + "=$5"
+ delete = "delete from invoice_test where " + columnId + "=$1"
+ }
+
+ inv := &Invoice{0, 100, 200, "my memo", 0, false}
+
+ for i := 0; i < b.N; i++ {
+ res, err := dbmap.Db.Exec(insert, inv.Created, inv.Updated,
+ inv.Memo, inv.PersonId)
+ if err != nil {
+ panic(err)
+ }
+
+ newid, err := res.LastInsertId()
+ if err != nil {
+ panic(err)
+ }
+ inv.Id = newid
+
+ row := dbmap.Db.QueryRow(sel, inv.Id)
+ err = row.Scan(&inv.Id, &inv.Created, &inv.Updated, &inv.Memo,
+ &inv.PersonId)
+ if err != nil {
+ panic(err)
+ }
+
+ inv.Created = 1000
+ inv.Updated = 2000
+ inv.Memo = "my memo 2"
+ inv.PersonId = 3000
+
+ _, err = dbmap.Db.Exec(update, inv.Created, inv.Updated, inv.Memo,
+ inv.PersonId, inv.Id)
+ if err != nil {
+ panic(err)
+ }
+
+ _, err = dbmap.Db.Exec(delete, inv.Id)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+}
+
+func BenchmarkGorpCrud(b *testing.B) {
+ b.StopTimer()
+ dbmap := initDbMapBench()
+ defer dropAndClose(dbmap)
+ b.StartTimer()
+
+ inv := &Invoice{0, 100, 200, "my memo", 0, true}
+ for i := 0; i < b.N; i++ {
+ err := dbmap.Insert(inv)
+ if err != nil {
+ panic(err)
+ }
+
+ obj, err := dbmap.Get(Invoice{}, inv.Id)
+ if err != nil {
+ panic(err)
+ }
+
+ inv2, ok := obj.(*Invoice)
+ if !ok {
+ panic(fmt.Sprintf("expected *Invoice, got: %v", obj))
+ }
+
+ inv2.Created = 1000
+ inv2.Updated = 2000
+ inv2.Memo = "my memo 2"
+ inv2.PersonId = 3000
+ _, err = dbmap.Update(inv2)
+ if err != nil {
+ panic(err)
+ }
+
+ _, err = dbmap.Delete(inv2)
+ if err != nil {
+ panic(err)
+ }
+
+ }
+}
+
+func initDbMapBench() *gorp.DbMap {
+ dbmap := newDbMap()
+ dbmap.Db.Exec("drop table if exists invoice_test")
+ dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id")
+ err := dbmap.CreateTables()
+ if err != nil {
+ panic(err)
+ }
+ return dbmap
+}
+
+func initDbMap() *gorp.DbMap {
+ dbmap := newDbMap()
+ dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id")
+ dbmap.AddTableWithName(InvoiceTag{}, "invoice_tag_test") //key is set via primarykey attribute
+ dbmap.AddTableWithName(AliasTransientField{}, "alias_trans_field_test").SetKeys(true, "id")
+ dbmap.AddTableWithName(OverriddenInvoice{}, "invoice_override_test").SetKeys(false, "Id")
+ dbmap.AddTableWithName(Person{}, "person_test").SetKeys(true, "Id").SetVersionCol("Version")
+ dbmap.AddTableWithName(WithIgnoredColumn{}, "ignored_column_test").SetKeys(true, "Id")
+ dbmap.AddTableWithName(IdCreated{}, "id_created_test").SetKeys(true, "Id")
+ dbmap.AddTableWithName(TypeConversionExample{}, "type_conv_test").SetKeys(true, "Id")
+ dbmap.AddTableWithName(WithEmbeddedStruct{}, "embedded_struct_test").SetKeys(true, "Id")
+ //dbmap.AddTableWithName(WithEmbeddedStructConflictingEmbeddedMemberNames{}, "embedded_struct_conflict_name_test").SetKeys(true, "Id")
+ //dbmap.AddTableWithName(WithEmbeddedStructSameMemberName{}, "embedded_struct_same_member_name_test").SetKeys(true, "Id")
+ dbmap.AddTableWithName(WithEmbeddedStructBeforeAutoincrField{}, "embedded_struct_before_autoincr_test").SetKeys(true, "Id")
+ dbmap.AddTableDynamic(&dynTableInst1, "").SetKeys(true, "Id").AddIndex("TenantInst1Index", "Btree", []string{"Name"}).SetUnique(true)
+ dbmap.AddTableDynamic(&dynTableInst2, "").SetKeys(true, "Id").AddIndex("TenantInst2Index", "Btree", []string{"Name"}).SetUnique(true)
+ dbmap.AddTableWithName(WithEmbeddedAutoincr{}, "embedded_autoincr_test").SetKeys(true, "Id")
+ dbmap.AddTableWithName(WithTime{}, "time_test").SetKeys(true, "Id")
+ dbmap.AddTableWithName(WithNullTime{}, "nulltime_test").SetKeys(false, "Id")
+ dbmap.TypeConverter = testTypeConverter{}
+ err := dbmap.DropTablesIfExists()
+ if err != nil {
+ panic(err)
+ }
+ err = dbmap.CreateTables()
+ if err != nil {
+ panic(err)
+ }
+
+ err = dbmap.CreateIndex()
+ if err != nil {
+ panic(err)
+ }
+
+ // See #146 and TestSelectAlias - this type is mapped to the same
+ // table as IdCreated, but includes an extra field that isn't in the table
+ dbmap.AddTableWithName(IdCreatedExternal{}, "id_created_test").SetKeys(true, "Id")
+
+ return dbmap
+}
+
+func initDbMapNulls() *gorp.DbMap {
+ dbmap := newDbMap()
+ dbmap.AddTable(TableWithNull{}).SetKeys(false, "Id")
+ err := dbmap.CreateTables()
+ if err != nil {
+ panic(err)
+ }
+ return dbmap
+}
+
+func newDbMap() *gorp.DbMap {
+ dialect, driver := dialectAndDriver()
+ dbmap := &gorp.DbMap{Db: connect(driver), Dialect: dialect}
+ if debug {
+ dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
+ }
+ return dbmap
+}
+
+func dropAndClose(dbmap *gorp.DbMap) {
+ dbmap.DropTablesIfExists()
+ dbmap.Db.Close()
+}
+
+func connect(driver string) *sql.DB {
+ dsn := os.Getenv("GORP_TEST_DSN")
+ if dsn == "" {
+ panic("GORP_TEST_DSN env variable is not set. Please see README.md")
+ }
+
+ db, err := sql.Open(driver, dsn)
+ if err != nil {
+ panic("Error connecting to db: " + err.Error())
+ }
+ return db
+}
+
+func dialectAndDriver() (gorp.Dialect, string) {
+ switch os.Getenv("GORP_TEST_DIALECT") {
+ case "mysql":
+ return gorp.MySQLDialect{"InnoDB", "UTF8"}, "mymysql"
+ case "gomysql":
+ return gorp.MySQLDialect{"InnoDB", "UTF8"}, "mysql"
+ case "postgres":
+ return gorp.PostgresDialect{}, "postgres"
+ case "sqlite":
+ return gorp.SqliteDialect{}, "sqlite3"
+ }
+ panic("GORP_TEST_DIALECT env variable is not set or is invalid. Please see README.md")
+}
+
+func _insert(dbmap *gorp.DbMap, list ...interface{}) {
+ err := dbmap.Insert(list...)
+ if err != nil {
+ panic(err)
+ }
+}
+
+func _update(dbmap *gorp.DbMap, list ...interface{}) int64 {
+ count, err := dbmap.Update(list...)
+ if err != nil {
+ panic(err)
+ }
+ return count
+}
+
+func _updateColumns(dbmap *gorp.DbMap, filter gorp.ColumnFilter, list ...interface{}) int64 {
+ count, err := dbmap.UpdateColumns(filter, list...)
+ if err != nil {
+ panic(err)
+ }
+ return count
+}
+
+func _del(dbmap *gorp.DbMap, list ...interface{}) int64 {
+ count, err := dbmap.Delete(list...)
+ if err != nil {
+ panic(err)
+ }
+
+ return count
+}
+
+func _get(dbmap *gorp.DbMap, i interface{}, keys ...interface{}) interface{} {
+ obj, err := dbmap.Get(i, keys...)
+ if err != nil {
+ panic(err)
+ }
+
+ return obj
+}
+
+func selectInt(dbmap *gorp.DbMap, query string, args ...interface{}) int64 {
+ i64, err := gorp.SelectInt(dbmap, query, args...)
+ if err != nil {
+ panic(err)
+ }
+
+ return i64
+}
+
+func selectNullInt(dbmap *gorp.DbMap, query string, args ...interface{}) sql.NullInt64 {
+ i64, err := gorp.SelectNullInt(dbmap, query, args...)
+ if err != nil {
+ panic(err)
+ }
+
+ return i64
+}
+
+func selectFloat(dbmap *gorp.DbMap, query string, args ...interface{}) float64 {
+ f64, err := gorp.SelectFloat(dbmap, query, args...)
+ if err != nil {
+ panic(err)
+ }
+
+ return f64
+}
+
+func selectNullFloat(dbmap *gorp.DbMap, query string, args ...interface{}) sql.NullFloat64 {
+ f64, err := gorp.SelectNullFloat(dbmap, query, args...)
+ if err != nil {
+ panic(err)
+ }
+
+ return f64
+}
+
+func selectStr(dbmap *gorp.DbMap, query string, args ...interface{}) string {
+ s, err := gorp.SelectStr(dbmap, query, args...)
+ if err != nil {
+ panic(err)
+ }
+
+ return s
+}
+
+func selectNullStr(dbmap *gorp.DbMap, query string, args ...interface{}) sql.NullString {
+ s, err := gorp.SelectNullStr(dbmap, query, args...)
+ if err != nil {
+ panic(err)
+ }
+
+ return s
+}
+
+func rawExec(dbmap *gorp.DbMap, query string, args ...interface{}) sql.Result {
+ res, err := dbmap.Exec(query, args...)
+ if err != nil {
+ panic(err)
+ }
+ return res
+}
+
+func rawSelect(dbmap *gorp.DbMap, i interface{}, query string, args ...interface{}) []interface{} {
+ list, err := dbmap.Select(i, query, args...)
+ if err != nil {
+ panic(err)
+ }
+ return list
+}
+
+func tableName(dbmap *gorp.DbMap, i interface{}) string {
+ t := reflect.TypeOf(i)
+ if table, err := dbmap.TableFor(t, false); table != nil && err == nil {
+ return dbmap.Dialect.QuoteField(table.TableName)
+ }
+ return t.Name()
+}
+
+func columnName(dbmap *gorp.DbMap, i interface{}, fieldName string) string {
+ t := reflect.TypeOf(i)
+ if table, err := dbmap.TableFor(t, false); table != nil && err == nil {
+ return dbmap.Dialect.QuoteField(table.ColMap(fieldName).ColumnName)
+ }
+ return fieldName
+}
diff --git a/vendor/github.com/mattermost/gorp/hooks.go b/vendor/github.com/mattermost/gorp/hooks.go
new file mode 100644
index 000000000..192b51f00
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/hooks.go
@@ -0,0 +1,49 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+//++ TODO v2-phase3: HasPostGet => PostGetter, HasPostDelete => PostDeleter, etc.
+
+// PostUpdate() will be executed after the GET statement.
+type HasPostGet interface {
+ PostGet(SqlExecutor) error
+}
+
+// PostUpdate() will be executed after the DELETE statement
+type HasPostDelete interface {
+ PostDelete(SqlExecutor) error
+}
+
+// PostUpdate() will be executed after the UPDATE statement
+type HasPostUpdate interface {
+ PostUpdate(SqlExecutor) error
+}
+
+// PostInsert() will be executed after the INSERT statement
+type HasPostInsert interface {
+ PostInsert(SqlExecutor) error
+}
+
+// PreDelete() will be executed before the DELETE statement.
+type HasPreDelete interface {
+ PreDelete(SqlExecutor) error
+}
+
+// PreUpdate() will be executed before UPDATE statement.
+type HasPreUpdate interface {
+ PreUpdate(SqlExecutor) error
+}
+
+// PreInsert() will be executed before INSERT statement.
+type HasPreInsert interface {
+ PreInsert(SqlExecutor) error
+}
diff --git a/vendor/github.com/mattermost/gorp/index.go b/vendor/github.com/mattermost/gorp/index.go
new file mode 100644
index 000000000..01ecd9eca
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/index.go
@@ -0,0 +1,56 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+// IndexMap represents a mapping between a Go struct field and a single
+// index in a table.
+// Unique and MaxSize only inform the
+// CreateTables() function and are not used by Insert/Update/Delete/Get.
+type IndexMap struct {
+ // Index name in db table
+ IndexName string
+
+ // If true, " unique" is added to create index statements.
+ // Not used elsewhere
+ Unique bool
+
+ // Index type supported by Dialect
+ // Postgres: B-tree, Hash, GiST and GIN.
+ // Mysql: Btree, Hash.
+ // Sqlite: nil.
+ IndexType string
+
+ // Columns name for single and multiple indexes
+ columns []string
+}
+
+// Rename allows you to specify the index name in the table
+//
+// Example: table.IndMap("customer_test_idx").Rename("customer_idx")
+//
+func (idx *IndexMap) Rename(indname string) *IndexMap {
+ idx.IndexName = indname
+ return idx
+}
+
+// SetUnique adds "unique" to the create index statements for this
+// index, if b is true.
+func (idx *IndexMap) SetUnique(b bool) *IndexMap {
+ idx.Unique = b
+ return idx
+}
+
+// SetIndexType specifies the index type supported by chousen SQL Dialect
+func (idx *IndexMap) SetIndexType(indtype string) *IndexMap {
+ idx.IndexType = indtype
+ return idx
+}
diff --git a/vendor/github.com/mattermost/gorp/lockerror.go b/vendor/github.com/mattermost/gorp/lockerror.go
new file mode 100644
index 000000000..07b3047ae
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/lockerror.go
@@ -0,0 +1,63 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// OptimisticLockError is returned by Update() or Delete() if the
+// struct being modified has a Version field and the value is not equal to
+// the current value in the database
+type OptimisticLockError struct {
+ // Table name where the lock error occurred
+ TableName string
+
+ // Primary key values of the row being updated/deleted
+ Keys []interface{}
+
+ // true if a row was found with those keys, indicating the
+ // LocalVersion is stale. false if no value was found with those
+ // keys, suggesting the row has been deleted since loaded, or
+ // was never inserted to begin with
+ RowExists bool
+
+ // Version value on the struct passed to Update/Delete. This value is
+ // out of sync with the database.
+ LocalVersion int64
+}
+
+// Error returns a description of the cause of the lock error
+func (e OptimisticLockError) Error() string {
+ if e.RowExists {
+ return fmt.Sprintf("gorp: OptimisticLockError table=%s keys=%v out of date version=%d", e.TableName, e.Keys, e.LocalVersion)
+ }
+
+ return fmt.Sprintf("gorp: OptimisticLockError no row found for table=%s keys=%v", e.TableName, e.Keys)
+}
+
+func lockError(m *DbMap, exec SqlExecutor, tableName string,
+ existingVer int64, elem reflect.Value,
+ keys ...interface{}) (int64, error) {
+
+ existing, err := get(m, exec, elem.Interface(), keys...)
+ if err != nil {
+ return -1, err
+ }
+
+ ole := OptimisticLockError{tableName, keys, true, existingVer}
+ if existing == nil {
+ ole.RowExists = false
+ }
+ return -1, ole
+}
diff --git a/vendor/github.com/mattermost/gorp/logging.go b/vendor/github.com/mattermost/gorp/logging.go
new file mode 100644
index 000000000..89d6c0e79
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/logging.go
@@ -0,0 +1,44 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import "fmt"
+
+type GorpLogger interface {
+ Printf(format string, v ...interface{})
+}
+
+// TraceOn turns on SQL statement logging for this DbMap. After this is
+// called, all SQL statements will be sent to the logger. If prefix is
+// a non-empty string, it will be written to the front of all logged
+// strings, which can aid in filtering log lines.
+//
+// Use TraceOn if you want to spy on the SQL statements that gorp
+// generates.
+//
+// Note that the base log.Logger type satisfies GorpLogger, but adapters can
+// easily be written for other logging packages (e.g., the golang-sanctioned
+// glog framework).
+func (m *DbMap) TraceOn(prefix string, logger GorpLogger) {
+ m.logger = logger
+ if prefix == "" {
+ m.logPrefix = prefix
+ } else {
+ m.logPrefix = fmt.Sprintf("%s ", prefix)
+ }
+}
+
+// TraceOff turns off tracing. It is idempotent.
+func (m *DbMap) TraceOff() {
+ m.logger = nil
+ m.logPrefix = ""
+}
diff --git a/vendor/github.com/mattermost/gorp/nulltypes.go b/vendor/github.com/mattermost/gorp/nulltypes.go
new file mode 100644
index 000000000..870770372
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/nulltypes.go
@@ -0,0 +1,58 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "database/sql/driver"
+ "time"
+)
+
+// A nullable Time value
+type NullTime struct {
+ Time time.Time
+ Valid bool // Valid is true if Time is not NULL
+}
+
+// Scan implements the Scanner interface.
+func (nt *NullTime) Scan(value interface{}) error {
+ switch t := value.(type) {
+ case time.Time:
+ nt.Time, nt.Valid = t, true
+ case []byte:
+ nt.Valid = false
+ for _, dtfmt := range []string{
+ "2006-01-02 15:04:05.999999999",
+ "2006-01-02T15:04:05.999999999",
+ "2006-01-02 15:04:05",
+ "2006-01-02T15:04:05",
+ "2006-01-02 15:04",
+ "2006-01-02T15:04",
+ "2006-01-02",
+ "2006-01-02 15:04:05-07:00",
+ } {
+ var err error
+ if nt.Time, err = time.Parse(dtfmt, string(t)); err == nil {
+ nt.Valid = true
+ break
+ }
+ }
+ }
+ return nil
+}
+
+// Value implements the driver Valuer interface.
+func (nt NullTime) Value() (driver.Value, error) {
+ if !nt.Valid {
+ return nil, nil
+ }
+ return nt.Time, nil
+}
diff --git a/vendor/github.com/mattermost/gorp/select.go b/vendor/github.com/mattermost/gorp/select.go
new file mode 100644
index 000000000..1967b0246
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/select.go
@@ -0,0 +1,383 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "reflect"
+)
+
+// SelectInt executes the given query, which should be a SELECT statement for a single
+// integer column, and returns the value of the first row returned. If no rows are
+// found, zero is returned.
+func SelectInt(e SqlExecutor, query string, args ...interface{}) (int64, error) {
+ var h int64
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return 0, err
+ }
+ return h, nil
+}
+
+// SelectNullInt executes the given query, which should be a SELECT statement for a single
+// integer column, and returns the value of the first row returned. If no rows are
+// found, the empty sql.NullInt64 value is returned.
+func SelectNullInt(e SqlExecutor, query string, args ...interface{}) (sql.NullInt64, error) {
+ var h sql.NullInt64
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return h, err
+ }
+ return h, nil
+}
+
+// SelectFloat executes the given query, which should be a SELECT statement for a single
+// float column, and returns the value of the first row returned. If no rows are
+// found, zero is returned.
+func SelectFloat(e SqlExecutor, query string, args ...interface{}) (float64, error) {
+ var h float64
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return 0, err
+ }
+ return h, nil
+}
+
+// SelectNullFloat executes the given query, which should be a SELECT statement for a single
+// float column, and returns the value of the first row returned. If no rows are
+// found, the empty sql.NullInt64 value is returned.
+func SelectNullFloat(e SqlExecutor, query string, args ...interface{}) (sql.NullFloat64, error) {
+ var h sql.NullFloat64
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return h, err
+ }
+ return h, nil
+}
+
+// SelectStr executes the given query, which should be a SELECT statement for a single
+// char/varchar column, and returns the value of the first row returned. If no rows are
+// found, an empty string is returned.
+func SelectStr(e SqlExecutor, query string, args ...interface{}) (string, error) {
+ var h string
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return "", err
+ }
+ return h, nil
+}
+
+// SelectNullStr executes the given query, which should be a SELECT
+// statement for a single char/varchar column, and returns the value
+// of the first row returned. If no rows are found, the empty
+// sql.NullString is returned.
+func SelectNullStr(e SqlExecutor, query string, args ...interface{}) (sql.NullString, error) {
+ var h sql.NullString
+ err := selectVal(e, &h, query, args...)
+ if err != nil && err != sql.ErrNoRows {
+ return h, err
+ }
+ return h, nil
+}
+
+// SelectOne executes the given query (which should be a SELECT statement)
+// and binds the result to holder, which must be a pointer.
+//
+// If no row is found, an error (sql.ErrNoRows specifically) will be returned
+//
+// If more than one row is found, an error will be returned.
+//
+func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
+ t := reflect.TypeOf(holder)
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ } else {
+ return fmt.Errorf("gorp: SelectOne holder must be a pointer, but got: %t", holder)
+ }
+
+ // Handle pointer to pointer
+ isptr := false
+ if t.Kind() == reflect.Ptr {
+ isptr = true
+ t = t.Elem()
+ }
+
+ if t.Kind() == reflect.Struct {
+ var nonFatalErr error
+
+ list, err := hookedselect(m, e, holder, query, args...)
+ if err != nil {
+ if !NonFatalError(err) { // FIXME: double negative, rename NonFatalError to FatalError
+ return err
+ }
+ nonFatalErr = err
+ }
+
+ dest := reflect.ValueOf(holder)
+ if isptr {
+ dest = dest.Elem()
+ }
+
+ if list != nil && len(list) > 0 { // FIXME: invert if/else
+ // check for multiple rows
+ if len(list) > 1 {
+ return fmt.Errorf("gorp: multiple rows returned for: %s - %v", query, args)
+ }
+
+ // Initialize if nil
+ if dest.IsNil() {
+ dest.Set(reflect.New(t))
+ }
+
+ // only one row found
+ src := reflect.ValueOf(list[0])
+ dest.Elem().Set(src.Elem())
+ } else {
+ // No rows found, return a proper error.
+ return sql.ErrNoRows
+ }
+
+ return nonFatalErr
+ }
+
+ return selectVal(e, holder, query, args...)
+}
+
+func selectVal(e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
+ var dbMap *DbMap
+ switch m := e.(type) {
+ case *DbMap:
+ dbMap = m
+ case *Transaction:
+ dbMap = m.dbmap
+ }
+
+ if len(args) == 1 {
+ query, args = maybeExpandNamedQuery(dbMap, query, args)
+ }
+
+ var rows *sql.Rows
+ var err error
+ if dbMap.Dialect.Name() != "PostgresDialect" {
+ ctx, cancel := context.WithTimeout(context.Background(), dbMap.QueryTimeout)
+ defer cancel()
+ rows, err = e.QueryContext(ctx, query, args...)
+ } else {
+ rows, err = e.Query(query, args...)
+ }
+
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ if !rows.Next() {
+ return sql.ErrNoRows
+ }
+
+ return rows.Scan(holder)
+}
+
+func hookedselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
+ args ...interface{}) ([]interface{}, error) {
+
+ list, err := rawselect(m, exec, i, query, args...)
+ if err != nil {
+ if !NonFatalError(err) {
+ return nil, err
+ }
+ }
+
+ // Determine where the results are: written to i, or returned in list
+ if t, _ := toSliceType(i); t == nil {
+ for _, v := range list {
+ if v, ok := v.(HasPostGet); ok {
+ err := v.PostGet(exec)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ } else {
+ resultsValue := reflect.Indirect(reflect.ValueOf(i))
+ for i := 0; i < resultsValue.Len(); i++ {
+ if v, ok := resultsValue.Index(i).Interface().(HasPostGet); ok {
+ err := v.PostGet(exec)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ }
+
+ return list, nil
+}
+
+func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
+ args ...interface{}) ([]interface{}, error) {
+ var (
+ appendToSlice = false // Write results to i directly?
+ intoStruct = true // Selecting into a struct?
+ pointerElements = true // Are the slice elements pointers (vs values)?
+ )
+
+ var nonFatalErr error
+
+ tableName := ""
+ var dynObj DynamicTable
+ isDynamic := false
+ if dynObj, isDynamic = i.(DynamicTable); isDynamic {
+ tableName = dynObj.TableName()
+ }
+
+ // get type for i, verifying it's a supported destination
+ t, err := toType(i)
+ if err != nil {
+ var err2 error
+ if t, err2 = toSliceType(i); t == nil {
+ if err2 != nil {
+ return nil, err2
+ }
+ return nil, err
+ }
+ pointerElements = t.Kind() == reflect.Ptr
+ if pointerElements {
+ t = t.Elem()
+ }
+ appendToSlice = true
+ intoStruct = t.Kind() == reflect.Struct
+ }
+
+ // If the caller supplied a single struct/map argument, assume a "named
+ // parameter" query. Extract the named arguments from the struct/map, create
+ // the flat arg slice, and rewrite the query to use the dialect's placeholder.
+ if len(args) == 1 {
+ query, args = maybeExpandNamedQuery(m, query, args)
+ }
+
+ // Run the query
+ var rows *sql.Rows
+ if m.Dialect.Name() != "PostgresDialect" {
+ ctx, cancel := context.WithTimeout(context.Background(), m.QueryTimeout)
+ defer cancel()
+ rows, err = exec.QueryContext(ctx, query, args...)
+ } else {
+ rows, err = exec.Query(query, args...)
+ }
+
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ // Fetch the column names as returned from db
+ cols, err := rows.Columns()
+ if err != nil {
+ return nil, err
+ }
+
+ if !intoStruct && len(cols) > 1 {
+ return nil, fmt.Errorf("gorp: select into non-struct slice requires 1 column, got %d", len(cols))
+ }
+
+ var colToFieldIndex [][]int
+ if intoStruct {
+ colToFieldIndex, err = columnToFieldIndex(m, t, tableName, cols)
+ if err != nil {
+ if !NonFatalError(err) {
+ return nil, err
+ }
+ nonFatalErr = err
+ }
+ }
+
+ conv := m.TypeConverter
+
+ // Add results to one of these two slices.
+ var (
+ list = make([]interface{}, 0)
+ sliceValue = reflect.Indirect(reflect.ValueOf(i))
+ )
+
+ for {
+ if !rows.Next() {
+ // if error occured return rawselect
+ if rows.Err() != nil {
+ return nil, rows.Err()
+ }
+ // time to exit from outer "for" loop
+ break
+ }
+ v := reflect.New(t)
+
+ if isDynamic {
+ v.Interface().(DynamicTable).SetTableName(tableName)
+ }
+
+ dest := make([]interface{}, len(cols))
+
+ custScan := make([]CustomScanner, 0)
+
+ for x := range cols {
+ f := v.Elem()
+ if intoStruct {
+ index := colToFieldIndex[x]
+ if index == nil {
+ // this field is not present in the struct, so create a dummy
+ // value for rows.Scan to scan into
+ var dummy dummyField
+ dest[x] = &dummy
+ continue
+ }
+ f = f.FieldByIndex(index)
+ }
+ target := f.Addr().Interface()
+ if conv != nil {
+ scanner, ok := conv.FromDb(target)
+ if ok {
+ target = scanner.Holder
+ custScan = append(custScan, scanner)
+ }
+ }
+ dest[x] = target
+ }
+
+ err = rows.Scan(dest...)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, c := range custScan {
+ err = c.Bind()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if appendToSlice {
+ if !pointerElements {
+ v = v.Elem()
+ }
+ sliceValue.Set(reflect.Append(sliceValue, v))
+ } else {
+ list = append(list, v.Interface())
+ }
+ }
+
+ if appendToSlice && sliceValue.IsNil() {
+ sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), 0, 0))
+ }
+
+ return list, nonFatalErr
+}
diff --git a/vendor/github.com/mattermost/gorp/table.go b/vendor/github.com/mattermost/gorp/table.go
new file mode 100644
index 000000000..5c513909a
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/table.go
@@ -0,0 +1,247 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// TableMap represents a mapping between a Go struct and a database table
+// Use dbmap.AddTable() or dbmap.AddTableWithName() to create these
+type TableMap struct {
+ // Name of database table.
+ TableName string
+ SchemaName string
+ gotype reflect.Type
+ Columns []*ColumnMap
+ keys []*ColumnMap
+ indexes []*IndexMap
+ uniqueTogether [][]string
+ version *ColumnMap
+ insertPlan bindPlan
+ updatePlan bindPlan
+ deletePlan bindPlan
+ getPlan bindPlan
+ dbmap *DbMap
+}
+
+// ResetSql removes cached insert/update/select/delete SQL strings
+// associated with this TableMap. Call this if you've modified
+// any column names or the table name itself.
+func (t *TableMap) ResetSql() {
+ t.insertPlan = bindPlan{}
+ t.updatePlan = bindPlan{}
+ t.deletePlan = bindPlan{}
+ t.getPlan = bindPlan{}
+}
+
+// SetKeys lets you specify the fields on a struct that map to primary
+// key columns on the table. If isAutoIncr is set, result.LastInsertId()
+// will be used after INSERT to bind the generated id to the Go struct.
+//
+// Automatically calls ResetSql() to ensure SQL statements are regenerated.
+//
+// Panics if isAutoIncr is true, and fieldNames length != 1
+//
+func (t *TableMap) SetKeys(isAutoIncr bool, fieldNames ...string) *TableMap {
+ if isAutoIncr && len(fieldNames) != 1 {
+ panic(fmt.Sprintf(
+ "gorp: SetKeys: fieldNames length must be 1 if key is auto-increment. (Saw %v fieldNames)",
+ len(fieldNames)))
+ }
+ t.keys = make([]*ColumnMap, 0)
+ for _, name := range fieldNames {
+ colmap := t.ColMap(name)
+ colmap.isPK = true
+ colmap.isAutoIncr = isAutoIncr
+ t.keys = append(t.keys, colmap)
+ }
+ t.ResetSql()
+
+ return t
+}
+
+// SetUniqueTogether lets you specify uniqueness constraints across multiple
+// columns on the table. Each call adds an additional constraint for the
+// specified columns.
+//
+// Automatically calls ResetSql() to ensure SQL statements are regenerated.
+//
+// Panics if fieldNames length < 2.
+//
+func (t *TableMap) SetUniqueTogether(fieldNames ...string) *TableMap {
+ if len(fieldNames) < 2 {
+ panic(fmt.Sprintf(
+ "gorp: SetUniqueTogether: must provide at least two fieldNames to set uniqueness constraint."))
+ }
+
+ columns := make([]string, 0)
+ for _, name := range fieldNames {
+ columns = append(columns, name)
+ }
+ t.uniqueTogether = append(t.uniqueTogether, columns)
+ t.ResetSql()
+
+ return t
+}
+
+// ColMap returns the ColumnMap pointer matching the given struct field
+// name. It panics if the struct does not contain a field matching this
+// name.
+func (t *TableMap) ColMap(field string) *ColumnMap {
+ col := colMapOrNil(t, field)
+ if col == nil {
+ e := fmt.Sprintf("No ColumnMap in table %s type %s with field %s",
+ t.TableName, t.gotype.Name(), field)
+
+ panic(e)
+ }
+ return col
+}
+
+func colMapOrNil(t *TableMap, field string) *ColumnMap {
+ for _, col := range t.Columns {
+ if col.fieldName == field || col.ColumnName == field {
+ return col
+ }
+ }
+ return nil
+}
+
+// IdxMap returns the IndexMap pointer matching the given index name.
+func (t *TableMap) IdxMap(field string) *IndexMap {
+ for _, idx := range t.indexes {
+ if idx.IndexName == field {
+ return idx
+ }
+ }
+ return nil
+}
+
+// AddIndex registers the index with gorp for specified table with given parameters.
+// This operation is idempotent. If index is already mapped, the
+// existing *IndexMap is returned
+// Function will panic if one of the given for index columns does not exists
+//
+// Automatically calls ResetSql() to ensure SQL statements are regenerated.
+//
+func (t *TableMap) AddIndex(name string, idxtype string, columns []string) *IndexMap {
+ // check if we have a index with this name already
+ for _, idx := range t.indexes {
+ if idx.IndexName == name {
+ return idx
+ }
+ }
+ for _, icol := range columns {
+ if res := t.ColMap(icol); res == nil {
+ e := fmt.Sprintf("No ColumnName in table %s to create index on", t.TableName)
+ panic(e)
+ }
+ }
+
+ idx := &IndexMap{IndexName: name, Unique: false, IndexType: idxtype, columns: columns}
+ t.indexes = append(t.indexes, idx)
+ t.ResetSql()
+ return idx
+}
+
+// SetVersionCol sets the column to use as the Version field. By default
+// the "Version" field is used. Returns the column found, or panics
+// if the struct does not contain a field matching this name.
+//
+// Automatically calls ResetSql() to ensure SQL statements are regenerated.
+func (t *TableMap) SetVersionCol(field string) *ColumnMap {
+ c := t.ColMap(field)
+ t.version = c
+ t.ResetSql()
+ return c
+}
+
+// SqlForCreateTable gets a sequence of SQL commands that will create
+// the specified table and any associated schema
+func (t *TableMap) SqlForCreate(ifNotExists bool) string {
+ s := bytes.Buffer{}
+ dialect := t.dbmap.Dialect
+
+ if strings.TrimSpace(t.SchemaName) != "" {
+ schemaCreate := "create schema"
+ if ifNotExists {
+ s.WriteString(dialect.IfSchemaNotExists(schemaCreate, t.SchemaName))
+ } else {
+ s.WriteString(schemaCreate)
+ }
+ s.WriteString(fmt.Sprintf(" %s;", t.SchemaName))
+ }
+
+ tableCreate := "create table"
+ if ifNotExists {
+ s.WriteString(dialect.IfTableNotExists(tableCreate, t.SchemaName, t.TableName))
+ } else {
+ s.WriteString(tableCreate)
+ }
+ s.WriteString(fmt.Sprintf(" %s (", dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
+
+ x := 0
+ for _, col := range t.Columns {
+ if !col.Transient {
+ if x > 0 {
+ s.WriteString(", ")
+ }
+ stype := dialect.ToSqlType(col.gotype, col.MaxSize, col.isAutoIncr)
+ s.WriteString(fmt.Sprintf("%s %s", dialect.QuoteField(col.ColumnName), stype))
+
+ if col.isPK || col.isNotNull {
+ s.WriteString(" not null")
+ }
+ if col.isPK && len(t.keys) == 1 {
+ s.WriteString(" primary key")
+ }
+ if col.Unique {
+ s.WriteString(" unique")
+ }
+ if col.isAutoIncr {
+ s.WriteString(fmt.Sprintf(" %s", dialect.AutoIncrStr()))
+ }
+
+ x++
+ }
+ }
+ if len(t.keys) > 1 {
+ s.WriteString(", primary key (")
+ for x := range t.keys {
+ if x > 0 {
+ s.WriteString(", ")
+ }
+ s.WriteString(dialect.QuoteField(t.keys[x].ColumnName))
+ }
+ s.WriteString(")")
+ }
+ if len(t.uniqueTogether) > 0 {
+ for _, columns := range t.uniqueTogether {
+ s.WriteString(", unique (")
+ for i, column := range columns {
+ if i > 0 {
+ s.WriteString(", ")
+ }
+ s.WriteString(dialect.QuoteField(column))
+ }
+ s.WriteString(")")
+ }
+ }
+ s.WriteString(") ")
+ s.WriteString(dialect.CreateTableSuffix())
+ s.WriteString(dialect.QuerySuffix())
+ return s.String()
+}
diff --git a/vendor/github.com/mattermost/gorp/table_bindings.go b/vendor/github.com/mattermost/gorp/table_bindings.go
new file mode 100644
index 000000000..5b049a360
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/table_bindings.go
@@ -0,0 +1,312 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "sync"
+)
+
+// CustomScanner binds a database column value to a Go type
+type CustomScanner struct {
+ // After a row is scanned, Holder will contain the value from the database column.
+ // Initialize the CustomScanner with the concrete Go type you wish the database
+ // driver to scan the raw column into.
+ Holder interface{}
+ // Target typically holds a pointer to the target struct field to bind the Holder
+ // value to.
+ Target interface{}
+ // Binder is a custom function that converts the holder value to the target type
+ // and sets target accordingly. This function should return error if a problem
+ // occurs converting the holder to the target.
+ Binder func(holder interface{}, target interface{}) error
+}
+
+// Used to filter columns when selectively updating
+type ColumnFilter func(*ColumnMap) bool
+
+func acceptAllFilter(col *ColumnMap) bool {
+ return true
+}
+
+// Bind is called automatically by gorp after Scan()
+func (me CustomScanner) Bind() error {
+ return me.Binder(me.Holder, me.Target)
+}
+
+type bindPlan struct {
+ query string
+ argFields []string
+ keyFields []string
+ versField string
+ autoIncrIdx int
+ autoIncrFieldName string
+ once sync.Once
+}
+
+func (plan *bindPlan) createBindInstance(elem reflect.Value, conv TypeConverter) (bindInstance, error) {
+ bi := bindInstance{query: plan.query, autoIncrIdx: plan.autoIncrIdx, autoIncrFieldName: plan.autoIncrFieldName, versField: plan.versField}
+ if plan.versField != "" {
+ bi.existingVersion = elem.FieldByName(plan.versField).Int()
+ }
+
+ var err error
+
+ for i := 0; i < len(plan.argFields); i++ {
+ k := plan.argFields[i]
+ if k == versFieldConst {
+ newVer := bi.existingVersion + 1
+ bi.args = append(bi.args, newVer)
+ if bi.existingVersion == 0 {
+ elem.FieldByName(plan.versField).SetInt(int64(newVer))
+ }
+ } else {
+ val := elem.FieldByName(k).Interface()
+ if conv != nil {
+ val, err = conv.ToDb(val)
+ if err != nil {
+ return bindInstance{}, err
+ }
+ }
+ bi.args = append(bi.args, val)
+ }
+ }
+
+ for i := 0; i < len(plan.keyFields); i++ {
+ k := plan.keyFields[i]
+ val := elem.FieldByName(k).Interface()
+ if conv != nil {
+ val, err = conv.ToDb(val)
+ if err != nil {
+ return bindInstance{}, err
+ }
+ }
+ bi.keys = append(bi.keys, val)
+ }
+
+ return bi, nil
+}
+
+type bindInstance struct {
+ query string
+ args []interface{}
+ keys []interface{}
+ existingVersion int64
+ versField string
+ autoIncrIdx int
+ autoIncrFieldName string
+}
+
+func (t *TableMap) bindInsert(elem reflect.Value) (bindInstance, error) {
+ plan := &t.insertPlan
+ plan.once.Do(func() {
+ plan.autoIncrIdx = -1
+
+ s := bytes.Buffer{}
+ s2 := bytes.Buffer{}
+ s.WriteString(fmt.Sprintf("insert into %s (", t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
+
+ x := 0
+ first := true
+ for y := range t.Columns {
+ col := t.Columns[y]
+ if !(col.isAutoIncr && t.dbmap.Dialect.AutoIncrBindValue() == "") {
+ if !col.Transient {
+ if !first {
+ s.WriteString(",")
+ s2.WriteString(",")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+
+ if col.isAutoIncr {
+ s2.WriteString(t.dbmap.Dialect.AutoIncrBindValue())
+ plan.autoIncrIdx = y
+ plan.autoIncrFieldName = col.fieldName
+ } else {
+ if col.DefaultValue == "" {
+ s2.WriteString(t.dbmap.Dialect.BindVar(x))
+ if col == t.version {
+ plan.versField = col.fieldName
+ plan.argFields = append(plan.argFields, versFieldConst)
+ } else {
+ plan.argFields = append(plan.argFields, col.fieldName)
+ }
+ x++
+ } else {
+ s2.WriteString(col.DefaultValue)
+ }
+ }
+ first = false
+ }
+ } else {
+ plan.autoIncrIdx = y
+ plan.autoIncrFieldName = col.fieldName
+ }
+ }
+ s.WriteString(") values (")
+ s.WriteString(s2.String())
+ s.WriteString(")")
+ if plan.autoIncrIdx > -1 {
+ s.WriteString(t.dbmap.Dialect.AutoIncrInsertSuffix(t.Columns[plan.autoIncrIdx]))
+ }
+ s.WriteString(t.dbmap.Dialect.QuerySuffix())
+
+ plan.query = s.String()
+ })
+
+ return plan.createBindInstance(elem, t.dbmap.TypeConverter)
+}
+
+func (t *TableMap) bindUpdate(elem reflect.Value, colFilter ColumnFilter) (bindInstance, error) {
+ if colFilter == nil {
+ colFilter = acceptAllFilter
+ }
+
+ plan := &t.updatePlan
+ plan.once.Do(func() {
+ s := bytes.Buffer{}
+ s.WriteString(fmt.Sprintf("update %s set ", t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
+ x := 0
+
+ for y := range t.Columns {
+ col := t.Columns[y]
+ if !col.isAutoIncr && !col.Transient && colFilter(col) {
+ if x > 0 {
+ s.WriteString(", ")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+
+ if col == t.version {
+ plan.versField = col.fieldName
+ plan.argFields = append(plan.argFields, versFieldConst)
+ } else {
+ plan.argFields = append(plan.argFields, col.fieldName)
+ }
+ x++
+ }
+ }
+
+ s.WriteString(" where ")
+ for y := range t.keys {
+ col := t.keys[y]
+ if y > 0 {
+ s.WriteString(" and ")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+
+ plan.argFields = append(plan.argFields, col.fieldName)
+ plan.keyFields = append(plan.keyFields, col.fieldName)
+ x++
+ }
+ if plan.versField != "" {
+ s.WriteString(" and ")
+ s.WriteString(t.dbmap.Dialect.QuoteField(t.version.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+ plan.argFields = append(plan.argFields, plan.versField)
+ }
+ s.WriteString(t.dbmap.Dialect.QuerySuffix())
+
+ plan.query = s.String()
+ })
+
+ return plan.createBindInstance(elem, t.dbmap.TypeConverter)
+}
+
+func (t *TableMap) bindDelete(elem reflect.Value) (bindInstance, error) {
+ plan := &t.deletePlan
+ plan.once.Do(func() {
+ s := bytes.Buffer{}
+ s.WriteString(fmt.Sprintf("delete from %s", t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
+
+ for y := range t.Columns {
+ col := t.Columns[y]
+ if !col.Transient {
+ if col == t.version {
+ plan.versField = col.fieldName
+ }
+ }
+ }
+
+ s.WriteString(" where ")
+ for x := range t.keys {
+ k := t.keys[x]
+ if x > 0 {
+ s.WriteString(" and ")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(k.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+
+ plan.keyFields = append(plan.keyFields, k.fieldName)
+ plan.argFields = append(plan.argFields, k.fieldName)
+ }
+ if plan.versField != "" {
+ s.WriteString(" and ")
+ s.WriteString(t.dbmap.Dialect.QuoteField(t.version.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(len(plan.argFields)))
+
+ plan.argFields = append(plan.argFields, plan.versField)
+ }
+ s.WriteString(t.dbmap.Dialect.QuerySuffix())
+
+ plan.query = s.String()
+ })
+
+ return plan.createBindInstance(elem, t.dbmap.TypeConverter)
+}
+
+func (t *TableMap) bindGet() *bindPlan {
+ plan := &t.getPlan
+ plan.once.Do(func() {
+ s := bytes.Buffer{}
+ s.WriteString("select ")
+
+ x := 0
+ for _, col := range t.Columns {
+ if !col.Transient {
+ if x > 0 {
+ s.WriteString(",")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+ plan.argFields = append(plan.argFields, col.fieldName)
+ x++
+ }
+ }
+ s.WriteString(" from ")
+ s.WriteString(t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName))
+ s.WriteString(" where ")
+ for x := range t.keys {
+ col := t.keys[x]
+ if x > 0 {
+ s.WriteString(" and ")
+ }
+ s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
+ s.WriteString("=")
+ s.WriteString(t.dbmap.Dialect.BindVar(x))
+
+ plan.keyFields = append(plan.keyFields, col.fieldName)
+ }
+ s.WriteString(t.dbmap.Dialect.QuerySuffix())
+
+ plan.query = s.String()
+ })
+
+ return plan
+}
diff --git a/vendor/github.com/mattermost/gorp/test_all.sh b/vendor/github.com/mattermost/gorp/test_all.sh
new file mode 100755
index 000000000..4c99584ef
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/test_all.sh
@@ -0,0 +1,41 @@
+#!/bin/bash -e
+
+# on macs, you may need to:
+# export GOBUILDFLAG=-ldflags -linkmode=external
+
+coveralls_testflags="-v -covermode=count -coverprofile=coverage.out"
+
+echo "Running unit tests"
+ginkgo -r -race -randomizeAllSpecs -keepGoing -- -test.run TestGorp
+
+echo "Testing against mysql"
+export GORP_TEST_DSN=gorptest/gorptest/gorptest
+export GORP_TEST_DIALECT=mysql
+go test $coveralls_testflags $GOBUILDFLAG $@ .
+
+echo "Testing against gomysql"
+export GORP_TEST_DSN=gorptest:gorptest@/gorptest
+export GORP_TEST_DIALECT=gomysql
+go test $coveralls_testflags $GOBUILDFLAG $@ .
+
+echo "Testing against postgres"
+export GORP_TEST_DSN="user=gorptest password=gorptest dbname=gorptest sslmode=disable"
+export GORP_TEST_DIALECT=postgres
+go test $coveralls_testflags $GOBUILDFLAG $@ .
+
+echo "Testing against sqlite"
+export GORP_TEST_DSN=/tmp/gorptest.bin
+export GORP_TEST_DIALECT=sqlite
+go test $coveralls_testflags $GOBUILDFLAG $@ .
+rm -f /tmp/gorptest.bin
+
+case $(go version) in
+ *go1.4*)
+ if [ "$(type -p goveralls)" != "" ]; then
+ goveralls -covermode=count -coverprofile=coverage.out -service=travis-ci
+ elif [ -x $HOME/gopath/bin/goveralls ]; then
+ $HOME/gopath/bin/goveralls -covermode=count -coverprofile=coverage.out -service=travis-ci
+ fi
+ ;;
+ *) ;;
+esac
diff --git a/vendor/github.com/mattermost/gorp/transaction.go b/vendor/github.com/mattermost/gorp/transaction.go
new file mode 100644
index 000000000..a9706ee65
--- /dev/null
+++ b/vendor/github.com/mattermost/gorp/transaction.go
@@ -0,0 +1,219 @@
+// Copyright 2012 James Cooper. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package gorp provides a simple way to marshal Go structs to and from
+// SQL databases. It uses the database/sql package, and should work with any
+// compliant database/sql driver.
+//
+// Source code and project home:
+// https://github.com/go-gorp/gorp
+
+package gorp
+
+import (
+ "context"
+ "database/sql"
+ "time"
+)
+
+// Transaction represents a database transaction.
+// Insert/Update/Delete/Get/Exec operations will be run in the context
+// of that transaction. Transactions should be terminated with
+// a call to Commit() or Rollback()
+type Transaction struct {
+ dbmap *DbMap
+ tx *sql.Tx
+ closed bool
+}
+
+// Insert has the same behavior as DbMap.Insert(), but runs in a transaction.
+func (t *Transaction) Insert(list ...interface{}) error {
+ return insert(t.dbmap, t, list...)
+}
+
+// Update had the same behavior as DbMap.Update(), but runs in a transaction.
+func (t *Transaction) Update(list ...interface{}) (int64, error) {
+ return update(t.dbmap, t, nil, list...)
+}
+
+// UpdateColumns had the same behavior as DbMap.UpdateColumns(), but runs in a transaction.
+func (t *Transaction) UpdateColumns(filter ColumnFilter, list ...interface{}) (int64, error) {
+ return update(t.dbmap, t, filter, list...)
+}
+
+// Delete has the same behavior as DbMap.Delete(), but runs in a transaction.
+func (t *Transaction) Delete(list ...interface{}) (int64, error) {
+ return delete(t.dbmap, t, list...)
+}
+
+// Get has the same behavior as DbMap.Get(), but runs in a transaction.
+func (t *Transaction) Get(i interface{}, keys ...interface{}) (interface{}, error) {
+ return get(t.dbmap, t, i, keys...)
+}
+
+// Select has the same behavior as DbMap.Select(), but runs in a transaction.
+func (t *Transaction) Select(i interface{}, query string, args ...interface{}) ([]interface{}, error) {
+ return hookedselect(t.dbmap, t, i, query, args...)
+}
+
+// Exec has the same behavior as DbMap.Exec(), but runs in a transaction.
+func (t *Transaction) Exec(query string, args ...interface{}) (sql.Result, error) {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return exec(t, query, true, args...)
+}
+
+// ExecNoTimeout has the same behavior as DbMap.ExecNoTimeout(), but runs in a transaction.
+func (t *Transaction) ExecNoTimeout(query string, args ...interface{}) (sql.Result, error) {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return exec(t, query, false, args...)
+}
+
+// SelectInt is a convenience wrapper around the gorp.SelectInt function.
+func (t *Transaction) SelectInt(query string, args ...interface{}) (int64, error) {
+ return SelectInt(t, query, args...)
+}
+
+// SelectNullInt is a convenience wrapper around the gorp.SelectNullInt function.
+func (t *Transaction) SelectNullInt(query string, args ...interface{}) (sql.NullInt64, error) {
+ return SelectNullInt(t, query, args...)
+}
+
+// SelectFloat is a convenience wrapper around the gorp.SelectFloat function.
+func (t *Transaction) SelectFloat(query string, args ...interface{}) (float64, error) {
+ return SelectFloat(t, query, args...)
+}
+
+// SelectNullFloat is a convenience wrapper around the gorp.SelectNullFloat function.
+func (t *Transaction) SelectNullFloat(query string, args ...interface{}) (sql.NullFloat64, error) {
+ return SelectNullFloat(t, query, args...)
+}
+
+// SelectStr is a convenience wrapper around the gorp.SelectStr function.
+func (t *Transaction) SelectStr(query string, args ...interface{}) (string, error) {
+ return SelectStr(t, query, args...)
+}
+
+// SelectNullStr is a convenience wrapper around the gorp.SelectNullStr function.
+func (t *Transaction) SelectNullStr(query string, args ...interface{}) (sql.NullString, error) {
+ return SelectNullStr(t, query, args...)
+}
+
+// SelectOne is a convenience wrapper around the gorp.SelectOne function.
+func (t *Transaction) SelectOne(holder interface{}, query string, args ...interface{}) error {
+ return SelectOne(t.dbmap, t, holder, query, args...)
+}
+
+// Commit commits the underlying database transaction.
+func (t *Transaction) Commit() error {
+ if !t.closed {
+ t.closed = true
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, "commit;")
+ }
+ return t.tx.Commit()
+ }
+
+ return sql.ErrTxDone
+}
+
+// Rollback rolls back the underlying database transaction.
+func (t *Transaction) Rollback() error {
+ if !t.closed {
+ t.closed = true
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, "rollback;")
+ }
+ return t.tx.Rollback()
+ }
+
+ return sql.ErrTxDone
+}
+
+// Savepoint creates a savepoint with the given name. The name is interpolated
+// directly into the SQL SAVEPOINT statement, so you must sanitize it if it is
+// derived from user input.
+func (t *Transaction) Savepoint(name string) error {
+ query := "savepoint " + t.dbmap.Dialect.QuoteField(name)
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, nil)
+ }
+ _, err := t.tx.Exec(query)
+ return err
+}
+
+// RollbackToSavepoint rolls back to the savepoint with the given name. The
+// name is interpolated directly into the SQL SAVEPOINT statement, so you must
+// sanitize it if it is derived from user input.
+func (t *Transaction) RollbackToSavepoint(savepoint string) error {
+ query := "rollback to savepoint " + t.dbmap.Dialect.QuoteField(savepoint)
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, nil)
+ }
+ _, err := t.tx.Exec(query)
+ return err
+}
+
+// ReleaseSavepint releases the savepoint with the given name. The name is
+// interpolated directly into the SQL SAVEPOINT statement, so you must sanitize
+// it if it is derived from user input.
+func (t *Transaction) ReleaseSavepoint(savepoint string) error {
+ query := "release savepoint " + t.dbmap.Dialect.QuoteField(savepoint)
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, nil)
+ }
+ _, err := t.tx.Exec(query)
+ return err
+}
+
+// Prepare has the same behavior as DbMap.Prepare(), but runs in a transaction.
+func (t *Transaction) Prepare(query string) (*sql.Stmt, error) {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, nil)
+ }
+ return t.tx.Prepare(query)
+}
+
+func (t *Transaction) QueryRow(query string, args ...interface{}) *sql.Row {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return t.tx.QueryRow(query, args...)
+}
+
+func (t *Transaction) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return t.tx.QueryRowContext(ctx, query, args...)
+}
+
+func (t *Transaction) Query(query string, args ...interface{}) (*sql.Rows, error) {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return t.tx.Query(query, args...)
+}
+
+func (t *Transaction) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
+ if t.dbmap.logger != nil {
+ now := time.Now()
+ defer t.dbmap.trace(now, query, args...)
+ }
+ return t.tx.QueryContext(ctx, query, args...)
+}