mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
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
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
_test
|
||||
*.test
|
||||
_testmain.go
|
||||
_obj
|
||||
*~
|
||||
@@ -1,9 +1,13 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.6
|
||||
- 1.7
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgres
|
||||
@@ -24,5 +28,6 @@ before_script:
|
||||
- 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
|
||||
34
vendor/github.com/mattermost/gorp/CONTRIBUTING.md
generated
vendored
Normal file
34
vendor/github.com/mattermost/gorp/CONTRIBUTING.md
generated
vendored
Normal file
@@ -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.
|
||||
0
vendor/github.com/go-gorp/gorp/LICENSE → vendor/github.com/mattermost/gorp/LICENSE
generated
vendored
0
vendor/github.com/go-gorp/gorp/LICENSE → vendor/github.com/mattermost/gorp/LICENSE
generated
vendored
254
vendor/github.com/go-gorp/gorp/README.md → vendor/github.com/mattermost/gorp/README.md
generated
vendored
254
vendor/github.com/go-gorp/gorp/README.md → vendor/github.com/mattermost/gorp/README.md
generated
vendored
@@ -4,23 +4,40 @@
|
||||
[](https://coveralls.io/r/go-gorp/gorp)
|
||||
[](https://github.com/go-gorp/gorp/issues)
|
||||
[](https://godoc.org/gopkg.in/gorp.v1)
|
||||
[](https://godoc.org/gopkg.in/gorp.v2)
|
||||
[](https://godoc.org/github.com/go-gorp/gorp)
|
||||
|
||||
### Update 2015-07-01 Cleanup & feature freeze ([#270](https://github.com/go-gorp/gorp/issues/270))
|
||||
### Update 2016-11-13: Future versions
|
||||
|
||||
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`.
|
||||
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.
|
||||
|
||||
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).
|
||||
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).
|
||||
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.
|
||||
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.
|
||||
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
|
||||
@@ -35,42 +52,59 @@ I hope that gorp saves you time, minimizes the drudgery of getting data in and o
|
||||
* 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)
|
||||
* 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"
|
||||
)
|
||||
Use `go get` or your favorite vendoring tool, using whichever import
|
||||
path you'd like.
|
||||
|
||||
## Versioning
|
||||
|
||||
This project provides a stable release (v1.x tags) and a bleeding edge codebase (master).
|
||||
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.
|
||||
|
||||
`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.
|
||||
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.
|
||||
|
||||
If you want to use bleeding edge, use `github.com/go-gorp/gorp` as import path.
|
||||
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.
|
||||
|
||||
## 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
|
||||
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 compatible with the last 2 major versions of Go, at this time `1.3` and `1.4`.
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -530,10 +564,11 @@ Full list of hooks that you can implement:
|
||||
|
||||
#### 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.
|
||||
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
|
||||
@@ -582,13 +617,15 @@ if ok {
|
||||
```
|
||||
### 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.
|
||||
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
|
||||
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.
|
||||
In the example below we put an index both on the Id field, and on the
|
||||
AcctId field.
|
||||
|
||||
```
|
||||
type Account struct {
|
||||
@@ -626,76 +663,111 @@ MariaDB [test]> show create table Account;
|
||||
|
||||
## Database Drivers
|
||||
|
||||
gorp uses the Go 1 `database/sql` package. A full list of compliant drivers is available here:
|
||||
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:
|
||||
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.
|
||||
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
|
||||
* 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.
|
||||
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.
|
||||
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.
|
||||
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:
|
||||
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)
|
||||
```
|
||||
```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:
|
||||
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})
|
||||
```
|
||||
```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.
|
||||
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.
|
||||
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
|
||||
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.
|
||||
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.
|
||||
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:
|
||||
@@ -709,34 +781,18 @@ go test
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
268
vendor/github.com/go-gorp/gorp/db.go → vendor/github.com/mattermost/gorp/db.go
generated
vendored
268
vendor/github.com/go-gorp/gorp/db.go → vendor/github.com/mattermost/gorp/db.go
generated
vendored
@@ -13,6 +13,7 @@ package gorp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
@@ -41,9 +42,34 @@ type DbMap struct {
|
||||
|
||||
TypeConverter TypeConverter
|
||||
|
||||
tables []*TableMap
|
||||
logger GorpLogger
|
||||
logPrefix string
|
||||
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 {
|
||||
@@ -52,36 +78,52 @@ func (m *DbMap) CreateIndex() 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())
|
||||
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
|
||||
}
|
||||
|
||||
@@ -98,7 +140,7 @@ func (t *TableMap) DropIndex(name string) error {
|
||||
s.WriteString(fmt.Sprintf(" %s %s", t.dbmap.Dialect.DropIndexSuffix(), t.TableName))
|
||||
}
|
||||
s.WriteString(";")
|
||||
_, e := t.dbmap.Exec(s.String())
|
||||
_, e := t.dbmap.ExecNoTimeout(s.String())
|
||||
if e != nil {
|
||||
err = e
|
||||
}
|
||||
@@ -155,6 +197,36 @@ func (m *DbMap) AddTableWithNameAndSchema(i interface{}, schema string, name str
|
||||
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()
|
||||
@@ -189,6 +261,7 @@ func (m *DbMap) readStructColumns(t reflect.Type) (cols []*ColumnMap, primaryKey
|
||||
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)
|
||||
@@ -216,6 +289,8 @@ func (m *DbMap) readStructColumns(t reflect.Type) (cols []*ColumnMap, 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))
|
||||
}
|
||||
@@ -259,6 +334,7 @@ func (m *DbMap) readStructColumns(t reflect.Type) (cols []*ColumnMap, primaryKey
|
||||
gotype: gotype,
|
||||
isPK: isPK,
|
||||
isAutoIncr: isAuto,
|
||||
isNotNull: isNotNull,
|
||||
MaxSize: maxSize,
|
||||
}
|
||||
if isPK {
|
||||
@@ -304,11 +380,20 @@ func (m *DbMap) createTables(ifNotExists bool) error {
|
||||
for i := range m.tables {
|
||||
table := m.tables[i]
|
||||
sql := table.SqlForCreate(ifNotExists)
|
||||
_, err = m.Exec(sql)
|
||||
_, err = m.ExecNoTimeout(sql)
|
||||
if err != nil {
|
||||
break
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, tbl := range m.dynamicTableMap() {
|
||||
sql := tbl.SqlForCreate(ifNotExists)
|
||||
_, err = m.ExecNoTimeout(sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -316,13 +401,25 @@ func (m *DbMap) createTables(ifNotExists bool) error {
|
||||
// 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)
|
||||
|
||||
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)
|
||||
return m.dropTable(t, true)
|
||||
|
||||
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
|
||||
@@ -347,12 +444,20 @@ func (m *DbMap) dropTables(addIfExists bool) (err error) {
|
||||
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, addIfExists bool) error {
|
||||
table := tableOrNil(m, t)
|
||||
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)
|
||||
}
|
||||
@@ -365,7 +470,7 @@ func (m *DbMap) dropTableImpl(table *TableMap, ifExists bool) (err error) {
|
||||
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)))
|
||||
_, err = m.ExecNoTimeout(fmt.Sprintf("%s %s;", tableDrop, m.Dialect.QuotedTableForQuery(table.SchemaName, table.TableName)))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -377,11 +482,19 @@ 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)))
|
||||
_, 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
|
||||
}
|
||||
|
||||
@@ -487,12 +600,22 @@ func (m *DbMap) Select(i interface{}, query string, args ...interface{}) ([]inte
|
||||
|
||||
// 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, 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
|
||||
@@ -547,7 +670,7 @@ func (m *DbMap) Begin() (*Transaction, error) {
|
||||
// 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)
|
||||
table := tableOrNil(m, t, "")
|
||||
if table == nil {
|
||||
return nil, fmt.Errorf("no table found for type: %v", t.Name())
|
||||
}
|
||||
@@ -561,6 +684,25 @@ func (m *DbMap) TableFor(t reflect.Type, checkPK bool) (*TableMap, error) {
|
||||
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
|
||||
@@ -572,7 +714,15 @@ func (m *DbMap) Prepare(query string) (*sql.Stmt, error) {
|
||||
return m.Db.Prepare(query)
|
||||
}
|
||||
|
||||
func tableOrNil(m *DbMap, t reflect.Type) *TableMap {
|
||||
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 {
|
||||
@@ -590,8 +740,18 @@ func (m *DbMap) tableForPointer(ptr interface{}, checkPK bool) (*TableMap, refle
|
||||
return nil, reflect.Value{}, errors.New(e)
|
||||
}
|
||||
elem := ptrv.Elem()
|
||||
etype := reflect.TypeOf(elem.Interface())
|
||||
t, err := m.TableFor(etype, checkPK)
|
||||
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
|
||||
}
|
||||
@@ -599,22 +759,42 @@ func (m *DbMap) tableForPointer(ptr interface{}, checkPK bool) (*TableMap, refle
|
||||
return t, elem, nil
|
||||
}
|
||||
|
||||
func (m *DbMap) queryRow(query string, args ...interface{}) *sql.Row {
|
||||
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) {
|
||||
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...)
|
||||
@@ -18,6 +18,9 @@ import "reflect"
|
||||
// but this could change in the future
|
||||
type Dialect interface {
|
||||
|
||||
// dialect name
|
||||
Name() string
|
||||
|
||||
// adds a suffix to any query, usually ";"
|
||||
QuerySuffix() string
|
||||
|
||||
@@ -27,6 +27,8 @@ type MySQLDialect struct {
|
||||
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 {
|
||||
204
vendor/github.com/mattermost/gorp/dialect_mysql_test.go
generated
vendored
Normal file
204
vendor/github.com/mattermost/gorp/dialect_mysql_test.go
generated
vendored
Normal file
@@ -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"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
// 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 "" }
|
||||
@@ -21,6 +21,8 @@ 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 {
|
||||
@@ -78,7 +80,7 @@ func (d PostgresDialect) AutoIncrBindValue() string {
|
||||
}
|
||||
|
||||
func (d PostgresDialect) AutoIncrInsertSuffix(col *ColumnMap) string {
|
||||
return " returning " + col.ColumnName
|
||||
return " returning " + d.QuoteField(col.ColumnName)
|
||||
}
|
||||
|
||||
// Returns suffix
|
||||
@@ -104,7 +106,7 @@ func (d PostgresDialect) BindVar(i int) string {
|
||||
}
|
||||
|
||||
func (d PostgresDialect) InsertAutoIncrToTarget(exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error {
|
||||
rows, err := exec.query(insertSql, params...)
|
||||
rows, err := exec.Query(insertSql, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -20,6 +20,8 @@ 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 {
|
||||
@@ -27,6 +27,8 @@ type SqlServerDialect struct {
|
||||
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:
|
||||
73
vendor/github.com/go-gorp/gorp/gorp.go → vendor/github.com/mattermost/gorp/gorp.go
generated
vendored
73
vendor/github.com/go-gorp/gorp/gorp.go → vendor/github.com/mattermost/gorp/gorp.go
generated
vendored
@@ -12,6 +12,7 @@
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
@@ -87,6 +88,7 @@ type TypeConverter interface {
|
||||
// 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
|
||||
@@ -101,6 +103,7 @@ type SqlExecutor interface {
|
||||
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)
|
||||
@@ -110,8 +113,18 @@ type SqlExecutor interface {
|
||||
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
|
||||
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
|
||||
@@ -144,7 +157,7 @@ func argsString(args ...interface{}) string {
|
||||
|
||||
// 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) {
|
||||
func exec(e SqlExecutor, query string, doTimeout bool, args ...interface{}) (sql.Result, error) {
|
||||
var dbMap *DbMap
|
||||
var executor executor
|
||||
switch m := e.(type) {
|
||||
@@ -160,7 +173,13 @@ func exec(e SqlExecutor, query string, args ...interface{}) (sql.Result, error)
|
||||
query, args = maybeExpandNamedQuery(dbMap, query, args)
|
||||
}
|
||||
|
||||
return executor.Exec(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
|
||||
@@ -220,13 +239,13 @@ func expandNamedQuery(m *DbMap, query string, keyGetter func(key string) reflect
|
||||
}), args
|
||||
}
|
||||
|
||||
func columnToFieldIndex(m *DbMap, t reflect.Type, cols []string) ([][]int, error) {
|
||||
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)
|
||||
table := tableOrNil(m, t, name)
|
||||
if table != nil {
|
||||
tableMapped = true
|
||||
}
|
||||
@@ -327,6 +346,30 @@ func toType(i interface{}) (reflect.Type, error) {
|
||||
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) {
|
||||
|
||||
@@ -335,14 +378,20 @@ func get(m *DbMap, exec SqlExecutor, i interface{},
|
||||
return nil, err
|
||||
}
|
||||
|
||||
table, err := m.TableFor(t, true)
|
||||
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
|
||||
@@ -361,7 +410,15 @@ func get(m *DbMap, exec SqlExecutor, i interface{},
|
||||
dest[x] = target
|
||||
}
|
||||
|
||||
row := exec.queryRow(plan.query, keys...)
|
||||
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 {
|
||||
13
vendor/github.com/mattermost/gorp/gorp_suite_test.go
generated
vendored
Normal file
13
vendor/github.com/mattermost/gorp/gorp_suite_test.go
generated
vendored
Normal file
@@ -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")
|
||||
}
|
||||
637
vendor/github.com/go-gorp/gorp/gorp_test.go → vendor/github.com/mattermost/gorp/gorp_test.go
generated
vendored
637
vendor/github.com/go-gorp/gorp/gorp_test.go → vendor/github.com/mattermost/gorp/gorp_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
0
vendor/github.com/go-gorp/gorp/hooks.go → vendor/github.com/mattermost/gorp/hooks.go
generated
vendored
0
vendor/github.com/go-gorp/gorp/hooks.go → vendor/github.com/mattermost/gorp/hooks.go
generated
vendored
0
vendor/github.com/go-gorp/gorp/index.go → vendor/github.com/mattermost/gorp/index.go
generated
vendored
0
vendor/github.com/go-gorp/gorp/index.go → vendor/github.com/mattermost/gorp/index.go
generated
vendored
60
vendor/github.com/go-gorp/gorp/select.go → vendor/github.com/mattermost/gorp/select.go
generated
vendored
60
vendor/github.com/go-gorp/gorp/select.go → vendor/github.com/mattermost/gorp/select.go
generated
vendored
@@ -12,6 +12,7 @@
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@@ -154,15 +155,28 @@ func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, 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)
|
||||
}
|
||||
var dbMap *DbMap
|
||||
switch m := e.(type) {
|
||||
case *DbMap:
|
||||
dbMap = m
|
||||
case *Transaction:
|
||||
dbMap = m.dbmap
|
||||
}
|
||||
rows, err := e.query(query, args...)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -178,14 +192,11 @@ func selectVal(e SqlExecutor, holder interface{}, query string, args ...interfac
|
||||
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
|
||||
@@ -209,7 +220,8 @@ func hookedselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
}
|
||||
}
|
||||
}
|
||||
return list, nonFatalErr
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
@@ -222,6 +234,13 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
|
||||
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 {
|
||||
@@ -248,7 +267,15 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
}
|
||||
|
||||
// Run the query
|
||||
rows, err := exec.query(query, args...)
|
||||
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
|
||||
}
|
||||
@@ -266,7 +293,7 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
|
||||
var colToFieldIndex [][]int
|
||||
if intoStruct {
|
||||
colToFieldIndex, err = columnToFieldIndex(m, t, cols)
|
||||
colToFieldIndex, err = columnToFieldIndex(m, t, tableName, cols)
|
||||
if err != nil {
|
||||
if !NonFatalError(err) {
|
||||
return nil, err
|
||||
@@ -293,6 +320,11 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
|
||||
break
|
||||
}
|
||||
v := reflect.New(t)
|
||||
|
||||
if isDynamic {
|
||||
v.Interface().(DynamicTable).SetTableName(tableName)
|
||||
}
|
||||
|
||||
dest := make([]interface{}, len(cols))
|
||||
|
||||
custScan := make([]CustomScanner, 0)
|
||||
0
vendor/github.com/go-gorp/gorp/table.go → vendor/github.com/mattermost/gorp/table.go
generated
vendored
0
vendor/github.com/go-gorp/gorp/table.go → vendor/github.com/mattermost/gorp/table.go
generated
vendored
@@ -5,6 +5,9 @@
|
||||
|
||||
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
|
||||
@@ -12,6 +12,7 @@
|
||||
package gorp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
@@ -62,7 +63,16 @@ func (t *Transaction) Exec(query string, args ...interface{}) (sql.Result, error
|
||||
now := time.Now()
|
||||
defer t.dbmap.trace(now, query, args...)
|
||||
}
|
||||
return exec(t, 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.
|
||||
@@ -176,7 +186,7 @@ func (t *Transaction) Prepare(query string) (*sql.Stmt, error) {
|
||||
return t.tx.Prepare(query)
|
||||
}
|
||||
|
||||
func (t *Transaction) queryRow(query string, args ...interface{}) *sql.Row {
|
||||
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...)
|
||||
@@ -184,10 +194,26 @@ func (t *Transaction) queryRow(query string, args ...interface{}) *sql.Row {
|
||||
return t.tx.QueryRow(query, args...)
|
||||
}
|
||||
|
||||
func (t *Transaction) query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
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...)
|
||||
}
|
||||
Reference in New Issue
Block a user