From 860e5d483cd952ec833c40312a2141bb3e4ef579 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 29 May 2017 15:46:35 -0400 Subject: PLT-6341/PLT-6342 Update gorp to mattermost fork and add connection timeout setting (#6410) * Update gorp to mattermost fork and add connection timeout setting * Add go dependency * Rename from connection timeout to query timeout * Properly add gorp dependency --- vendor/github.com/go-gorp/gorp/.gitignore | 8 - vendor/github.com/go-gorp/gorp/.travis.yml | 28 - vendor/github.com/go-gorp/gorp/LICENSE | 22 - vendor/github.com/go-gorp/gorp/README.md | 745 ------ vendor/github.com/go-gorp/gorp/column.go | 83 - vendor/github.com/go-gorp/gorp/db.go | 623 ----- vendor/github.com/go-gorp/gorp/dialect.go | 111 - vendor/github.com/go-gorp/gorp/dialect_mysql.go | 171 -- vendor/github.com/go-gorp/gorp/dialect_oracle.go | 146 -- vendor/github.com/go-gorp/gorp/dialect_postgres.go | 147 -- vendor/github.com/go-gorp/gorp/dialect_sqlite.go | 119 - .../github.com/go-gorp/gorp/dialect_sqlserver.go | 152 -- vendor/github.com/go-gorp/gorp/errors.go | 38 - vendor/github.com/go-gorp/gorp/gorp.go | 558 ---- vendor/github.com/go-gorp/gorp/gorp_test.go | 2371 ----------------- vendor/github.com/go-gorp/gorp/hooks.go | 49 - vendor/github.com/go-gorp/gorp/index.go | 56 - vendor/github.com/go-gorp/gorp/lockerror.go | 63 - vendor/github.com/go-gorp/gorp/logging.go | 44 - vendor/github.com/go-gorp/gorp/nulltypes.go | 58 - vendor/github.com/go-gorp/gorp/select.go | 351 --- vendor/github.com/go-gorp/gorp/table.go | 247 -- vendor/github.com/go-gorp/gorp/table_bindings.go | 312 --- vendor/github.com/go-gorp/gorp/test_all.sh | 38 - vendor/github.com/go-gorp/gorp/transaction.go | 193 -- vendor/github.com/mattermost/gorp/.gitignore | 9 + vendor/github.com/mattermost/gorp/.travis.yml | 33 + vendor/github.com/mattermost/gorp/CONTRIBUTING.md | 34 + vendor/github.com/mattermost/gorp/LICENSE | 22 + vendor/github.com/mattermost/gorp/README.md | 801 ++++++ vendor/github.com/mattermost/gorp/column.go | 83 + vendor/github.com/mattermost/gorp/db.go | 803 ++++++ vendor/github.com/mattermost/gorp/dialect.go | 114 + vendor/github.com/mattermost/gorp/dialect_mysql.go | 173 ++ .../mattermost/gorp/dialect_mysql_test.go | 204 ++ .../github.com/mattermost/gorp/dialect_oracle.go | 148 ++ .../github.com/mattermost/gorp/dialect_postgres.go | 149 ++ .../github.com/mattermost/gorp/dialect_sqlite.go | 121 + .../mattermost/gorp/dialect_sqlserver.go | 154 ++ vendor/github.com/mattermost/gorp/errors.go | 38 + vendor/github.com/mattermost/gorp/gorp.go | 615 +++++ .../github.com/mattermost/gorp/gorp_suite_test.go | 13 + vendor/github.com/mattermost/gorp/gorp_test.go | 2696 ++++++++++++++++++++ vendor/github.com/mattermost/gorp/hooks.go | 49 + vendor/github.com/mattermost/gorp/index.go | 56 + vendor/github.com/mattermost/gorp/lockerror.go | 63 + vendor/github.com/mattermost/gorp/logging.go | 44 + vendor/github.com/mattermost/gorp/nulltypes.go | 58 + vendor/github.com/mattermost/gorp/select.go | 383 +++ vendor/github.com/mattermost/gorp/table.go | 247 ++ .../github.com/mattermost/gorp/table_bindings.go | 312 +++ vendor/github.com/mattermost/gorp/test_all.sh | 41 + vendor/github.com/mattermost/gorp/transaction.go | 219 ++ 53 files changed, 7682 insertions(+), 6733 deletions(-) delete mode 100644 vendor/github.com/go-gorp/gorp/.gitignore delete mode 100644 vendor/github.com/go-gorp/gorp/.travis.yml delete mode 100644 vendor/github.com/go-gorp/gorp/LICENSE delete mode 100644 vendor/github.com/go-gorp/gorp/README.md delete mode 100644 vendor/github.com/go-gorp/gorp/column.go delete mode 100644 vendor/github.com/go-gorp/gorp/db.go delete mode 100644 vendor/github.com/go-gorp/gorp/dialect.go delete mode 100644 vendor/github.com/go-gorp/gorp/dialect_mysql.go delete mode 100644 vendor/github.com/go-gorp/gorp/dialect_oracle.go delete mode 100644 vendor/github.com/go-gorp/gorp/dialect_postgres.go delete mode 100644 vendor/github.com/go-gorp/gorp/dialect_sqlite.go delete mode 100644 vendor/github.com/go-gorp/gorp/dialect_sqlserver.go delete mode 100644 vendor/github.com/go-gorp/gorp/errors.go delete mode 100644 vendor/github.com/go-gorp/gorp/gorp.go delete mode 100644 vendor/github.com/go-gorp/gorp/gorp_test.go delete mode 100644 vendor/github.com/go-gorp/gorp/hooks.go delete mode 100644 vendor/github.com/go-gorp/gorp/index.go delete mode 100644 vendor/github.com/go-gorp/gorp/lockerror.go delete mode 100644 vendor/github.com/go-gorp/gorp/logging.go delete mode 100644 vendor/github.com/go-gorp/gorp/nulltypes.go delete mode 100644 vendor/github.com/go-gorp/gorp/select.go delete mode 100644 vendor/github.com/go-gorp/gorp/table.go delete mode 100644 vendor/github.com/go-gorp/gorp/table_bindings.go delete mode 100755 vendor/github.com/go-gorp/gorp/test_all.sh delete mode 100644 vendor/github.com/go-gorp/gorp/transaction.go create mode 100644 vendor/github.com/mattermost/gorp/.gitignore create mode 100644 vendor/github.com/mattermost/gorp/.travis.yml create mode 100644 vendor/github.com/mattermost/gorp/CONTRIBUTING.md create mode 100644 vendor/github.com/mattermost/gorp/LICENSE create mode 100644 vendor/github.com/mattermost/gorp/README.md create mode 100644 vendor/github.com/mattermost/gorp/column.go create mode 100644 vendor/github.com/mattermost/gorp/db.go create mode 100644 vendor/github.com/mattermost/gorp/dialect.go create mode 100644 vendor/github.com/mattermost/gorp/dialect_mysql.go create mode 100644 vendor/github.com/mattermost/gorp/dialect_mysql_test.go create mode 100644 vendor/github.com/mattermost/gorp/dialect_oracle.go create mode 100644 vendor/github.com/mattermost/gorp/dialect_postgres.go create mode 100644 vendor/github.com/mattermost/gorp/dialect_sqlite.go create mode 100644 vendor/github.com/mattermost/gorp/dialect_sqlserver.go create mode 100644 vendor/github.com/mattermost/gorp/errors.go create mode 100644 vendor/github.com/mattermost/gorp/gorp.go create mode 100644 vendor/github.com/mattermost/gorp/gorp_suite_test.go create mode 100644 vendor/github.com/mattermost/gorp/gorp_test.go create mode 100644 vendor/github.com/mattermost/gorp/hooks.go create mode 100644 vendor/github.com/mattermost/gorp/index.go create mode 100644 vendor/github.com/mattermost/gorp/lockerror.go create mode 100644 vendor/github.com/mattermost/gorp/logging.go create mode 100644 vendor/github.com/mattermost/gorp/nulltypes.go create mode 100644 vendor/github.com/mattermost/gorp/select.go create mode 100644 vendor/github.com/mattermost/gorp/table.go create mode 100644 vendor/github.com/mattermost/gorp/table_bindings.go create mode 100755 vendor/github.com/mattermost/gorp/test_all.sh create mode 100644 vendor/github.com/mattermost/gorp/transaction.go (limited to 'vendor/github.com') diff --git a/vendor/github.com/go-gorp/gorp/.gitignore b/vendor/github.com/go-gorp/gorp/.gitignore deleted file mode 100644 index 8a06adea5..000000000 --- a/vendor/github.com/go-gorp/gorp/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -_test -_testmain.go -_obj -*~ -*.6 -6.out -gorptest.bin -tmp diff --git a/vendor/github.com/go-gorp/gorp/.travis.yml b/vendor/github.com/go-gorp/gorp/.travis.yml deleted file mode 100644 index ce4602884..000000000 --- a/vendor/github.com/go-gorp/gorp/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: go -go: -- 1.3 -- 1.4 -- 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 - -script: ./test_all.sh diff --git a/vendor/github.com/go-gorp/gorp/LICENSE b/vendor/github.com/go-gorp/gorp/LICENSE deleted file mode 100644 index b661111d0..000000000 --- a/vendor/github.com/go-gorp/gorp/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -(The MIT License) - -Copyright (c) 2012 James Cooper - -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/go-gorp/gorp/README.md b/vendor/github.com/go-gorp/gorp/README.md deleted file mode 100644 index 8b9277805..000000000 --- a/vendor/github.com/go-gorp/gorp/README.md +++ /dev/null @@ -1,745 +0,0 @@ -# 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 bleeding edge](https://img.shields.io/badge/godoc-bleeding--edge-375EAB.svg)](https://godoc.org/github.com/go-gorp/gorp) - -### Update 2015-07-01 Cleanup & feature freeze ([#270](https://github.com/go-gorp/gorp/issues/270)) - -We are currently cleaning up the backlog of issues and PR's. When this is done the codebase will be split into separate files and there will be breaking changes to the API's. We're also adding better tests and documentation. As a result of these changes the `master` branch will be unstable. Please use `gopkg.in/gorp.v1`. When the cleanup and changes are done, we will release `v2.0`. - -At this time we won't accept new feature-related pull-requests because of changes to the codebase. Please create an issue for your feature and wait until `v2.0` has been released. - -For more information, please read [#270](https://github.com/go-gorp/gorp/issues/270). - -## 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 - - # install the library: - go get gopkg.in/gorp.v1 - - // use in your .go code: - import ( - "gopkg.in/gorp.v1" - ) - -## Versioning - -This project provides a stable release (v1.x tags) and a bleeding edge codebase (master). - -`gopkg.in/gorp.v1` points to the latest v1.x tag. The API's for v1 are stable and shouldn't change. Development takes place at the master branch. Althought the code in master should always compile and test successfully, it might break API's. We aim to maintain backwards compatibility, but API's and behaviour might be changed to fix a bug. Also note that API's that are new in the master branch can change until released as v2. - -If you want to use bleeding edge, use `github.com/go-gorp/gorp` as import path. - -## API Documentation - -Full godoc output from the latest v1 release is available here: - -https://godoc.org/gopkg.in/gorp.v1 - -For the latest code in master: - -https://godoc.org/github.com/go-gorp/gorp - -## Supported Go versions - -This package is compatible with the last 2 major versions of Go, at this time `1.3` and `1.4`. - -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. - -## 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. - -## 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. - -## 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. - -## 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 - -IRC: #gorp -Mailing list: gorp-dev@googlegroups.com -Bugs/Enhancements: Create a github issue - -## Pull requests / Contributions - -Contributions are very welcome. Please follow these guidelines: - -* Fork the `master` branch and issue pull requests targeting the `master` branch -* If you are adding an enhancement, please open an issue first with your proposed change. -* Changes that break backwards compatibility in the public API are only accepted after we - discuss on a GitHub issue for a while. - -Thanks! - -## Contributors - -* matthias-margush - column aliasing via tags -* Rob Figueiredo - @robfig -* Quinn Slack - @sqs diff --git a/vendor/github.com/go-gorp/gorp/column.go b/vendor/github.com/go-gorp/gorp/column.go deleted file mode 100644 index 99d4fd555..000000000 --- a/vendor/github.com/go-gorp/gorp/column.go +++ /dev/null @@ -1,83 +0,0 @@ -// 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/go-gorp/gorp/db.go b/vendor/github.com/go-gorp/gorp/db.go deleted file mode 100644 index 5ce68fdfd..000000000 --- a/vendor/github.com/go-gorp/gorp/db.go +++ /dev/null @@ -1,623 +0,0 @@ -// 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" - "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 - - tables []*TableMap - logger GorpLogger - logPrefix string -} - -func (m *DbMap) CreateIndex() error { - - var err error - dialect := reflect.TypeOf(m.Dialect) - for _, table := range m.tables { - for _, index := range table.indexes { - - 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.Exec(s.String()) - if err != nil { - break - } - } - } - 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.Exec(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 -} - -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 - 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 - 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, - 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.Exec(sql) - if err != nil { - break - } - } - 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) - return m.dropTable(t, false) -} - -// DropTableIfExists drops an individual table when the table exists. -func (m *DbMap) DropTableIfExists(table interface{}) error { - t := reflect.TypeOf(table) - return m.dropTable(t, 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 - } - } - return err -} - -// Implementation of dropping a single table. -func (m *DbMap) dropTable(t reflect.Type, addIfExists bool) error { - table := tableOrNil(m, t) - 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.Exec(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.Exec(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 -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, 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 -} - -// 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) *TableMap { - 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() - etype := reflect.TypeOf(elem.Interface()) - 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) 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) 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/go-gorp/gorp/dialect.go b/vendor/github.com/go-gorp/gorp/dialect.go deleted file mode 100644 index 203bc62b5..000000000 --- a/vendor/github.com/go-gorp/gorp/dialect.go +++ /dev/null @@ -1,111 +0,0 @@ -// 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 { - - // 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 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/go-gorp/gorp/dialect_mysql.go b/vendor/github.com/go-gorp/gorp/dialect_mysql.go deleted file mode 100644 index 3d7d34027..000000000 --- a/vendor/github.com/go-gorp/gorp/dialect_mysql.go +++ /dev/null @@ -1,171 +0,0 @@ -// 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) 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/go-gorp/gorp/dialect_oracle.go b/vendor/github.com/go-gorp/gorp/dialect_oracle.go deleted file mode 100644 index c381380f9..000000000 --- a/vendor/github.com/go-gorp/gorp/dialect_oracle.go +++ /dev/null @@ -1,146 +0,0 @@ -// 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) 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/go-gorp/gorp/dialect_postgres.go b/vendor/github.com/go-gorp/gorp/dialect_postgres.go deleted file mode 100644 index a086381a8..000000000 --- a/vendor/github.com/go-gorp/gorp/dialect_postgres.go +++ /dev/null @@ -1,147 +0,0 @@ -// 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) 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 " + 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/go-gorp/gorp/dialect_sqlite.go b/vendor/github.com/go-gorp/gorp/dialect_sqlite.go deleted file mode 100644 index 7d9b29757..000000000 --- a/vendor/github.com/go-gorp/gorp/dialect_sqlite.go +++ /dev/null @@ -1,119 +0,0 @@ -// 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) 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/go-gorp/gorp/dialect_sqlserver.go b/vendor/github.com/go-gorp/gorp/dialect_sqlserver.go deleted file mode 100644 index 8808af598..000000000 --- a/vendor/github.com/go-gorp/gorp/dialect_sqlserver.go +++ /dev/null @@ -1,152 +0,0 @@ -// 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) 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/go-gorp/gorp/errors.go b/vendor/github.com/go-gorp/gorp/errors.go deleted file mode 100644 index d13f03fc3..000000000 --- a/vendor/github.com/go-gorp/gorp/errors.go +++ /dev/null @@ -1,38 +0,0 @@ -// 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/go-gorp/gorp/gorp.go b/vendor/github.com/go-gorp/gorp/gorp.go deleted file mode 100644 index 1f32283f5..000000000 --- a/vendor/github.com/go-gorp/gorp/gorp.go +++ /dev/null @@ -1,558 +0,0 @@ -// 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" - "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) -} - -// 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) - 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) - queryRow(query string, args ...interface{}) *sql.Row -} - -// 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, 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) - } - - 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, 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) - 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 -} - -func get(m *DbMap, exec SqlExecutor, i interface{}, - keys ...interface{}) (interface{}, error) { - - t, err := toType(i) - if err != nil { - return nil, err - } - - table, err := m.TableFor(t, true) - if err != nil { - return nil, err - } - - plan := table.bindGet() - - v := reflect.New(t) - 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 - } - - 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/go-gorp/gorp/gorp_test.go b/vendor/github.com/go-gorp/gorp/gorp_test.go deleted file mode 100644 index 895ab450e..000000000 --- a/vendor/github.com/go-gorp/gorp/gorp_test.go +++ /dev/null @@ -1,2371 +0,0 @@ -// 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" - "database/sql" - "database/sql/driver" - "encoding/json" - "errors" - "flag" - "fmt" - "log" - "math/rand" - "os" - "reflect" - "strconv" - "strings" - "testing" - "time" - - _ "github.com/go-sql-driver/mysql" - _ "github.com/lib/pq" - _ "github.com/mattn/go-sqlite3" - _ "github.com/ziutek/mymysql/godrv" -) - -var ( - // verify interface compliance - _ = []Dialect{ - SqliteDialect{}, - PostgresDialect{}, - MySQLDialect{}, - SqlServerDialect{}, - 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 -} - -type PersonValuerScanner struct { - Person -} - -func (p PersonValuerScanner) Value() (driver.Value, error) { - return p.Id, nil -} - -// FIXME: this Scan is never actually used in the tests? -// Also: if the comments below on the mysql driver are true, then that should be fixed by the dialect when scanning values into structs. -func (p *PersonValuerScanner) Scan(value interface{}) (err error) { - switch src := value.(type) { - case []byte: - // The mysql driver seems to return a []byte, even though the - // type in the database is bigint. Note that this case is - // *only* used by the mysql driver. - p.Id, err = strconv.ParseInt(string(src), 10, 64) - case int64: - // postgres, gomysql, and sqlite drivers all return an int64, - // as you'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 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{}) (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 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 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 CustomScanner{new(time.Time), target, binder}, true - } - - return CustomScanner{}, false -} - -func (p *Person) PreInsert(s 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 SqlExecutor) error { - p.LName = "postinsert" - return nil -} - -func (p *Person) PreUpdate(s SqlExecutor) error { - p.FName = "preupdate" - return nil -} - -func (p *Person) PostUpdate(s SqlExecutor) error { - p.LName = "postupdate" - return nil -} - -func (p *Person) PreDelete(s SqlExecutor) error { - p.FName = "predelete" - return nil -} - -func (p *Person) PostDelete(s SqlExecutor) error { - p.LName = "postdelete" - return nil -} - -func (p *Person) PostGet(s SqlExecutor) error { - p.LName = "postget" - return nil -} - -type PersistentUser struct { - Key int32 - Id string - PassedTraining bool -} - -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 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 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 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 Id from 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 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 PassedTraining from 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 Id from 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 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 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 PersistentUser - where mykey = :Key - and PassedTraining = :PassedTraining - and 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 PersistentUser - where mykey = :Key - and 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 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 PersistentUser - where mykey = :Key - and PassedTraining = :PassedTraining - and 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 PersistentUser - where mykey = :Key - and PassedTraining = :PassedTraining - and 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 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.(OptimisticLockError); !ok { - t.Errorf("update - Expected OptimisticLockError, got: %v", err) - } - if count != -1 { - t.Errorf("update - Expected -1 count, got: %d", count) - } - - count, err = dbmap.Delete(p1) - if _, ok := err.(OptimisticLockError); !ok { - t.Errorf("delete - Expected 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 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.Id InvoiceId, p.Id PersonId, i.Memo, p.FName " + - "from invoice_test i, person_test p " + - "where i.PersonId = p.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 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 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) -} - -func testCrudInternal(t *testing.T, dbmap *DbMap, val testable) { - table, _, err := dbmap.tableForPointer(val, 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 "+table.TableName) - if err != nil { - t.Errorf("couldn't select * from %s err=%v", table.TableName, err) - } else if len(rows) != 1 { - t.Errorf("unexpected row count in %s: %d", 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 *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 Int64 from TableWithNull where Str='abc'") - if i64 != 78 { - t.Errorf("int64 %d != 78", i64) - } - i64 = selectInt(dbmap, "select count(*) from TableWithNull") - if i64 != 1 { - t.Errorf("int64 count %d != 1", i64) - } - i64 = selectInt(dbmap, "select count(*) from TableWithNull where Str="+bindVar, "asdfasdf") - if i64 != 0 { - t.Errorf("int64 no rows %d != 0", i64) - } - - // SelectNullInt - n := selectNullInt(dbmap, "select Int64 from TableWithNull where Str='notfound'") - if !reflect.DeepEqual(n, sql.NullInt64{0, false}) { - t.Errorf("nullint %v != 0,false", n) - } - - n = selectNullInt(dbmap, "select Int64 from TableWithNull where Str='abc'") - if !reflect.DeepEqual(n, sql.NullInt64{78, true}) { - t.Errorf("nullint %v != 78, true", n) - } - - // SelectFloat - f64 := selectFloat(dbmap, "select Float64 from TableWithNull where Str='abc'") - if f64 != 32.2 { - t.Errorf("float64 %d != 32.2", f64) - } - f64 = selectFloat(dbmap, "select min(Float64) from TableWithNull") - if f64 != 32.2 { - t.Errorf("float64 min %d != 32.2", f64) - } - f64 = selectFloat(dbmap, "select count(*) from TableWithNull where Str="+bindVar, "asdfasdf") - if f64 != 0 { - t.Errorf("float64 no rows %d != 0", f64) - } - - // SelectNullFloat - nf := selectNullFloat(dbmap, "select Float64 from TableWithNull where Str='notfound'") - if !reflect.DeepEqual(nf, sql.NullFloat64{0, false}) { - t.Errorf("nullfloat %v != 0,false", nf) - } - - nf = selectNullFloat(dbmap, "select Float64 from TableWithNull where Str='abc'") - if !reflect.DeepEqual(nf, sql.NullFloat64{32.2, true}) { - t.Errorf("nullfloat %v != 32.2, true", nf) - } - - // SelectStr - s := selectStr(dbmap, "select Str from TableWithNull where Int64="+bindVar, 78) - if s != "abc" { - t.Errorf("s %s != abc", s) - } - s = selectStr(dbmap, "select Str from TableWithNull where Str='asdfasdf'") - if s != "" { - t.Errorf("s no rows %s != ''", s) - } - - // SelectNullStr - ns := selectNullStr(dbmap, "select Str from TableWithNull where Int64="+bindVar, 78) - if !reflect.DeepEqual(ns, sql.NullString{"abc", true}) { - t.Errorf("nullstr %v != abc,true", ns) - } - ns = selectNullStr(dbmap, "select Str from TableWithNull where 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 Int64 from TableWithNull where Str=:abc", map[string]string{"abc": "abc"}) - if i64 != 78 { - t.Errorf("int64 %d != 78", i64) - } - ns = selectNullStr(dbmap, "select Str from TableWithNull where 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 DbMap methods starting with Select... -// are also exposed in the SqlExecutor interface. Select... functions can always -// run on Pre/Post hooks. -func TestSqlExecutorInterfaceSelects(t *testing.T) { - dbMapType := reflect.TypeOf(&DbMap{}) - sqlExecutorType := reflect.TypeOf((*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 DbMap but not implemented in SqlExecutor", - dbMapMethod.Name) - } - } -} - -func TestNullTime(t *testing.T) { - dbmap := initDbMap() - defer dropAndClose(dbmap) - - // if time is null - ent := &WithNullTime{ - Id: 0, - Time: 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 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("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: 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 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("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 id FROM time_test WHERE 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.Id InvoiceId, p.Id PersonId, i.Memo, p.FName " + - "from invoice_test i, person_test p " + - "where i.PersonId = p.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 Id=:Id", params) - if err != nil { - if !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 fname asc") - if err != nil { - if !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 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 FName from person_test where 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 FName from person_test where 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 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 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 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 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 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 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 MySQLDialect") - } - }() - - // invalid MySQLDialect : does not contain Engine or Encoding specification - dialect := MySQLDialect{} - db := &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 Memo=%s WHERE 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 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 IsPaid=%s WHERE 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 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 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 IsPaid=%s", bindVar0), true) - if err != nil { - t.Error(err) - } -} - -func BenchmarkNativeCrud(b *testing.B) { - b.StopTimer() - dbmap := initDbMapBench() - defer dropAndClose(dbmap) - b.StartTimer() - - insert := "insert into invoice_test (Created, Updated, Memo, PersonId) values (?, ?, ?, ?)" - sel := "select Id, Created, Updated, Memo, PersonId from invoice_test where Id=?" - update := "update invoice_test set Created=?, Updated=?, Memo=?, PersonId=? where Id=?" - delete := "delete from invoice_test where Id=?" - - 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() *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() *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.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) - } - - // 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() *DbMap { - dbmap := newDbMap() - dbmap.AddTable(TableWithNull{}).SetKeys(false, "Id") - err := dbmap.CreateTables() - if err != nil { - panic(err) - } - return dbmap -} - -func newDbMap() *DbMap { - dialect, driver := dialectAndDriver() - dbmap := &DbMap{Db: connect(driver), Dialect: dialect} - if debug { - dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds)) - } - return dbmap -} - -func dropAndClose(dbmap *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() (Dialect, string) { - switch os.Getenv("GORP_TEST_DIALECT") { - case "mysql": - return MySQLDialect{"InnoDB", "UTF8"}, "mymysql" - case "gomysql": - return MySQLDialect{"InnoDB", "UTF8"}, "mysql" - case "postgres": - return PostgresDialect{}, "postgres" - case "sqlite": - return SqliteDialect{}, "sqlite3" - } - panic("GORP_TEST_DIALECT env variable is not set or is invalid. Please see README.md") -} - -func _insert(dbmap *DbMap, list ...interface{}) { - err := dbmap.Insert(list...) - if err != nil { - panic(err) - } -} - -func _update(dbmap *DbMap, list ...interface{}) int64 { - count, err := dbmap.Update(list...) - if err != nil { - panic(err) - } - return count -} - -func _updateColumns(dbmap *DbMap, filter ColumnFilter, list ...interface{}) int64 { - count, err := dbmap.UpdateColumns(filter, list...) - if err != nil { - panic(err) - } - return count -} - -func _del(dbmap *DbMap, list ...interface{}) int64 { - count, err := dbmap.Delete(list...) - if err != nil { - panic(err) - } - - return count -} - -func _get(dbmap *DbMap, i interface{}, keys ...interface{}) interface{} { - obj, err := dbmap.Get(i, keys...) - if err != nil { - panic(err) - } - - return obj -} - -func selectInt(dbmap *DbMap, query string, args ...interface{}) int64 { - i64, err := SelectInt(dbmap, query, args...) - if err != nil { - panic(err) - } - - return i64 -} - -func selectNullInt(dbmap *DbMap, query string, args ...interface{}) sql.NullInt64 { - i64, err := SelectNullInt(dbmap, query, args...) - if err != nil { - panic(err) - } - - return i64 -} - -func selectFloat(dbmap *DbMap, query string, args ...interface{}) float64 { - f64, err := SelectFloat(dbmap, query, args...) - if err != nil { - panic(err) - } - - return f64 -} - -func selectNullFloat(dbmap *DbMap, query string, args ...interface{}) sql.NullFloat64 { - f64, err := SelectNullFloat(dbmap, query, args...) - if err != nil { - panic(err) - } - - return f64 -} - -func selectStr(dbmap *DbMap, query string, args ...interface{}) string { - s, err := SelectStr(dbmap, query, args...) - if err != nil { - panic(err) - } - - return s -} - -func selectNullStr(dbmap *DbMap, query string, args ...interface{}) sql.NullString { - s, err := SelectNullStr(dbmap, query, args...) - if err != nil { - panic(err) - } - - return s -} - -func _rawexec(dbmap *DbMap, query string, args ...interface{}) sql.Result { - res, err := dbmap.Exec(query, args...) - if err != nil { - panic(err) - } - return res -} - -func _rawselect(dbmap *DbMap, i interface{}, query string, args ...interface{}) []interface{} { - list, err := dbmap.Select(i, query, args...) - if err != nil { - panic(err) - } - return list -} diff --git a/vendor/github.com/go-gorp/gorp/hooks.go b/vendor/github.com/go-gorp/gorp/hooks.go deleted file mode 100644 index 192b51f00..000000000 --- a/vendor/github.com/go-gorp/gorp/hooks.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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/go-gorp/gorp/index.go b/vendor/github.com/go-gorp/gorp/index.go deleted file mode 100644 index 01ecd9eca..000000000 --- a/vendor/github.com/go-gorp/gorp/index.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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/go-gorp/gorp/lockerror.go b/vendor/github.com/go-gorp/gorp/lockerror.go deleted file mode 100644 index 07b3047ae..000000000 --- a/vendor/github.com/go-gorp/gorp/lockerror.go +++ /dev/null @@ -1,63 +0,0 @@ -// 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/go-gorp/gorp/logging.go b/vendor/github.com/go-gorp/gorp/logging.go deleted file mode 100644 index 89d6c0e79..000000000 --- a/vendor/github.com/go-gorp/gorp/logging.go +++ /dev/null @@ -1,44 +0,0 @@ -// 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/go-gorp/gorp/nulltypes.go b/vendor/github.com/go-gorp/gorp/nulltypes.go deleted file mode 100644 index 870770372..000000000 --- a/vendor/github.com/go-gorp/gorp/nulltypes.go +++ /dev/null @@ -1,58 +0,0 @@ -// 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/go-gorp/gorp/select.go b/vendor/github.com/go-gorp/gorp/select.go deleted file mode 100644 index d6ff92ee3..000000000 --- a/vendor/github.com/go-gorp/gorp/select.go +++ /dev/null @@ -1,351 +0,0 @@ -// 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" - "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 { - if len(args) == 1 { - switch m := e.(type) { - case *DbMap: - query, args = maybeExpandNamedQuery(m, query, args) - case *Transaction: - query, args = maybeExpandNamedQuery(m.dbmap, query, args) - } - } - 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) { - - var nonFatalErr error - - list, err := rawselect(m, exec, i, query, args...) - if err != nil { - if !NonFatalError(err) { - return nil, err - } - nonFatalErr = 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, nonFatalErr -} - -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 - - // 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 - 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, 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) - 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/go-gorp/gorp/table.go b/vendor/github.com/go-gorp/gorp/table.go deleted file mode 100644 index 5c513909a..000000000 --- a/vendor/github.com/go-gorp/gorp/table.go +++ /dev/null @@ -1,247 +0,0 @@ -// 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/go-gorp/gorp/table_bindings.go b/vendor/github.com/go-gorp/gorp/table_bindings.go deleted file mode 100644 index 5b049a360..000000000 --- a/vendor/github.com/go-gorp/gorp/table_bindings.go +++ /dev/null @@ -1,312 +0,0 @@ -// 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/go-gorp/gorp/test_all.sh b/vendor/github.com/go-gorp/gorp/test_all.sh deleted file mode 100755 index f2d16961a..000000000 --- a/vendor/github.com/go-gorp/gorp/test_all.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -e - -# on macs, you may need to: -# export GOBUILDFLAG=-ldflags -linkmode=external - -coveralls_testflags="-v -covermode=count -coverprofile=coverage.out" - -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/go-gorp/gorp/transaction.go b/vendor/github.com/go-gorp/gorp/transaction.go deleted file mode 100644 index 6430f24f1..000000000 --- a/vendor/github.com/go-gorp/gorp/transaction.go +++ /dev/null @@ -1,193 +0,0 @@ -// 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" - "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, 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) 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...) -} 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 + +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
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...) +} -- cgit v1.2.3-1-g7c22