blob: 01902314226e529b8dc95b6f3b11477163945d16 [file] [log] [blame]
package gorp
import (
"bytes"
"database/sql"
"encoding/json"
"errors"
"fmt"
"log"
"math/rand"
"os"
"reflect"
"strings"
"testing"
"time"
_ "github.com/go-sql-driver/mysql"
)
// verify interface compliance
var _ Dialect = SqliteDialect{}
var _ Dialect = PostgresDialect{}
var _ Dialect = MySQLDialect{}
var _ Dialect = SqlServerDialect{}
var _ Dialect = OracleDialect{}
type testable interface {
GetId() int64
Rand()
}
type Invoice struct {
Id int64
Created int64
Updated int64
Memo string
PersonId int64
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"`
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 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 WithEmbeddedStructBeforeAutoincrField struct {
Names
Id int64
}
type WithEmbeddedAutoincr struct {
WithEmbeddedStruct
MiddleName string
}
type Names struct {
FirstName string
LastName 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.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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")
dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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")
dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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")
dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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 TestColumnProps(t *testing.T) {
dbmap := newDbMap()
dbmap.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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 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 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.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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").SetKeys(true, "myid")
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(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.TraceOn("", log.New(os.Stdout, "gorptest: ", log.Lmicroseconds))
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}
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 _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
}