good morning!!!!

Skip to content
Snippets Groups Projects
Commit 131a7a46 authored by Peter Kieltyka's avatar Peter Kieltyka
Browse files

Merge pull request #66 from upper/feature/sqlx-tests

Fixing tests for postgresql adapter [feature/sqlx]
parents bf2ff21a d42c54d0
Branches
Tags
No related merge requests found
...@@ -27,16 +27,16 @@ import ( ...@@ -27,16 +27,16 @@ import (
"log" "log"
"os" "os"
"reflect" "reflect"
"strconv"
"testing" "testing"
"time" "time"
"github.com/jmoiron/sqlx"
"gopkg.in/mgo.v2" "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"upper.io/db" "upper.io/db"
_ "upper.io/db/mongo" _ "upper.io/db/mongo"
//_ "upper.io/db/mysql" // Disabled temporarily. //_ "upper.io/db/mysql" // Disabled temporarily.
_ "upper.io/db/postgresql" "upper.io/db/postgresql"
// Temporary removing QL. It includes a _solaris.go file that produces // Temporary removing QL. It includes a _solaris.go file that produces
// compile time errors on < go1.3. // compile time errors on < go1.3.
// _ "upper.io/db/ql" // _ "upper.io/db/ql"
...@@ -47,7 +47,7 @@ var wrappers = []string{ ...@@ -47,7 +47,7 @@ var wrappers = []string{
//`sqlite`, //`sqlite`,
//`mysql`, //`mysql`,
`postgresql`, `postgresql`,
`mongo`, //`mongo`,
// `ql`, // `ql`,
} }
...@@ -59,7 +59,7 @@ var ( ...@@ -59,7 +59,7 @@ var (
errDriverErr = errors.New(`Driver error`) errDriverErr = errors.New(`Driver error`)
) )
var settings map[string]*db.Settings var settings map[string]db.ConnectionURL
func init() { func init() {
...@@ -77,7 +77,7 @@ func init() { ...@@ -77,7 +77,7 @@ func init() {
log.Printf("Running tests against host %s.\n", host) log.Printf("Running tests against host %s.\n", host)
settings = map[string]*db.Settings{ settings = map[string]db.ConnectionURL{
`sqlite`: &db.Settings{ `sqlite`: &db.Settings{
Database: `upperio_tests.db`, Database: `upperio_tests.db`,
}, },
...@@ -93,11 +93,14 @@ func init() { ...@@ -93,11 +93,14 @@ func init() {
User: `upperio`, User: `upperio`,
Password: `upperio`, Password: `upperio`,
}, },
`postgresql`: &db.Settings{ `postgresql`: &postgresql.ConnectionURL{
Database: `upperio_tests`, Database: `upperio_tests`,
Host: host, Address: db.Host(host),
User: `upperio`, User: `upperio`,
Password: `upperio`, Password: `upperio`,
Options: map[string]string{
"timezone": "UTC",
},
}, },
`ql`: &db.Settings{ `ql`: &db.Settings{
Database: `file://upperio_test.ql`, Database: `file://upperio_test.ql`,
...@@ -131,7 +134,7 @@ var setupFn = map[string]func(driver interface{}) error{ ...@@ -131,7 +134,7 @@ var setupFn = map[string]func(driver interface{}) error{
return errDriverErr return errDriverErr
}, },
`postgresql`: func(driver interface{}) error { `postgresql`: func(driver interface{}) error {
if sqld, ok := driver.(*sql.DB); ok == true { if sqld, ok := driver.(*sqlx.DB); ok == true {
var err error var err error
_, err = sqld.Exec(`DROP TABLE IF EXISTS birthdays`) _, err = sqld.Exec(`DROP TABLE IF EXISTS birthdays`)
...@@ -141,7 +144,7 @@ var setupFn = map[string]func(driver interface{}) error{ ...@@ -141,7 +144,7 @@ var setupFn = map[string]func(driver interface{}) error{
_, err = sqld.Exec(`CREATE TABLE "birthdays" ( _, err = sqld.Exec(`CREATE TABLE "birthdays" (
"id" serial primary key, "id" serial primary key,
"name" CHARACTER VARYING(50), "name" CHARACTER VARYING(50),
"born" TIMESTAMP, "born" TIMESTAMP WITH TIME ZONE,
"born_ut" INT "born_ut" INT
)`) )`)
if err != nil { if err != nil {
...@@ -167,7 +170,7 @@ var setupFn = map[string]func(driver interface{}) error{ ...@@ -167,7 +170,7 @@ var setupFn = map[string]func(driver interface{}) error{
} }
_, err = sqld.Exec(`CREATE TABLE "is_even" ( _, err = sqld.Exec(`CREATE TABLE "is_even" (
"input" NUMERIC, "input" NUMERIC,
"is_even" INT "is_even" BOOL
)`) )`)
if err != nil { if err != nil {
return err return err
...@@ -178,8 +181,8 @@ var setupFn = map[string]func(driver interface{}) error{ ...@@ -178,8 +181,8 @@ var setupFn = map[string]func(driver interface{}) error{
return err return err
} }
_, err = sqld.Exec(`CREATE TABLE "CaSe_TesT" ( _, err = sqld.Exec(`CREATE TABLE "CaSe_TesT" (
"ID" SERIAL PRIMARY KEY, "id" SERIAL PRIMARY KEY,
"Case_Test" VARCHAR(60) "case_test" VARCHAR(60)
)`) )`)
if err != nil { if err != nil {
return err return err
...@@ -190,7 +193,7 @@ var setupFn = map[string]func(driver interface{}) error{ ...@@ -190,7 +193,7 @@ var setupFn = map[string]func(driver interface{}) error{
return errDriverErr return errDriverErr
}, },
`mysql`: func(driver interface{}) error { `mysql`: func(driver interface{}) error {
if sqld, ok := driver.(*sql.DB); ok == true { if sqld, ok := driver.(*sqlx.DB); ok == true {
var err error var err error
_, err = sqld.Exec(`DROP TABLE IF EXISTS birthdays`) _, err = sqld.Exec(`DROP TABLE IF EXISTS birthdays`)
...@@ -249,7 +252,7 @@ var setupFn = map[string]func(driver interface{}) error{ ...@@ -249,7 +252,7 @@ var setupFn = map[string]func(driver interface{}) error{
return errDriverErr return errDriverErr
}, },
`sqlite`: func(driver interface{}) error { `sqlite`: func(driver interface{}) error {
if sqld, ok := driver.(*sql.DB); ok == true { if sqld, ok := driver.(*sqlx.DB); ok == true {
var err error var err error
_, err = sqld.Exec(`DROP TABLE IF EXISTS "birthdays"`) _, err = sqld.Exec(`DROP TABLE IF EXISTS "birthdays"`)
...@@ -308,7 +311,7 @@ var setupFn = map[string]func(driver interface{}) error{ ...@@ -308,7 +311,7 @@ var setupFn = map[string]func(driver interface{}) error{
return errDriverErr return errDriverErr
}, },
`ql`: func(driver interface{}) error { `ql`: func(driver interface{}) error {
if sqld, ok := driver.(*sql.DB); ok == true { if sqld, ok := driver.(*sqlx.DB); ok == true {
var err error var err error
var tx *sql.Tx var tx *sql.Tx
...@@ -381,7 +384,7 @@ var setupFn = map[string]func(driver interface{}) error{ ...@@ -381,7 +384,7 @@ var setupFn = map[string]func(driver interface{}) error{
type birthday struct { type birthday struct {
Name string // `db:"name"` // Must match by name. Name string // `db:"name"` // Must match by name.
Born time.Time // `db:"born" // Must match by name. Born time.Time // `db:"born" // Must match by name.
BornUT *timeType `db:"born_ut"` BornUT timeType `db:"born_ut"`
OmitMe bool `json:"omit_me" db:"-" bson:"-"` OmitMe bool `json:"omit_me" db:"-" bson:"-"`
} }
...@@ -403,16 +406,16 @@ type oddEven struct { ...@@ -403,16 +406,16 @@ type oddEven struct {
// Struct that relies on explicit mapping. // Struct that relies on explicit mapping.
type mapE struct { type mapE struct {
ID uint `db:"ID,omitempty" bson:"-"` ID uint `db:"id,omitempty" bson:"-"`
MongoID bson.ObjectId `db:"-" bson:"_id,omitempty"` MongoID bson.ObjectId `db:"-" bson:"_id,omitempty"`
CaseTest string `db:"Case_Test" bson:"Case_Test"` CaseTest string `db:"case_test" bson:"Case_Test"`
} }
// Struct that will fallback to default mapping. // Struct that will fallback to default mapping.
type mapN struct { type mapN struct {
ID uint `db:",omitempty"` ID uint `db:"id,omitempty"`
MongoID bson.ObjectId `db:"-" bson:"_id,omitempty"` MongoID bson.ObjectId `db:"-" bson:"_id,omitempty"`
Casetest string Case_TEST string
} }
// Struct for testing marshalling. // Struct for testing marshalling.
...@@ -423,27 +426,30 @@ type timeType struct { ...@@ -423,27 +426,30 @@ type timeType struct {
} }
// time.Time -> unix timestamp // time.Time -> unix timestamp
func (u *timeType) MarshalDB() (interface{}, error) { func (u timeType) MarshalDB() (interface{}, error) {
return u.value.Unix(), nil return u.value.Unix(), nil
} }
// unix timestamp -> time.Time // unix timestamp -> time.Time
func (u *timeType) UnmarshalDB(v interface{}) error { func (u *timeType) UnmarshalDB(v interface{}) error {
var i int var unixTime int64
switch t := v.(type) { switch t := v.(type) {
case string: case int64:
i, _ = strconv.Atoi(t) unixTime = t
default: default:
return db.ErrUnsupportedValue return db.ErrUnsupportedValue
} }
t := time.Unix(int64(i), 0) t := time.Unix(unixTime, 0).In(time.UTC)
*u = timeType{t} *u = timeType{t}
return nil return nil
} }
var _ db.Marshaler = timeType{}
var _ db.Unmarshaler = &timeType{}
func even(i int) bool { func even(i int) bool {
if i%2 == 0 { if i%2 == 0 {
return true return true
...@@ -467,7 +473,7 @@ func TestOpen(t *testing.T) { ...@@ -467,7 +473,7 @@ func TestOpen(t *testing.T) {
t.Fatalf(`No such settings entry for wrapper %s.`, wrapper) t.Fatalf(`No such settings entry for wrapper %s.`, wrapper)
} else { } else {
var sess db.Database var sess db.Database
sess, err = db.Open(wrapper, *settings[wrapper]) sess, err = db.Open(wrapper, settings[wrapper])
if err != nil { if err != nil {
t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err) t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err)
} }
...@@ -487,7 +493,7 @@ func TestSetup(t *testing.T) { ...@@ -487,7 +493,7 @@ func TestSetup(t *testing.T) {
} else { } else {
var sess db.Database var sess db.Database
sess, err = db.Open(wrapper, *settings[wrapper]) sess, err = db.Open(wrapper, settings[wrapper])
if err != nil { if err != nil {
t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err) t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err)
} }
...@@ -521,19 +527,19 @@ func TestSimpleCRUD(t *testing.T) { ...@@ -521,19 +527,19 @@ func TestSimpleCRUD(t *testing.T) {
var sess db.Database var sess db.Database
sess, err = db.Open(wrapper, *settings[wrapper]) sess, err = db.Open(wrapper, settings[wrapper])
if err != nil { if err != nil {
t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err) t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err)
} }
defer sess.Close() defer sess.Close()
born := time.Date(1941, time.January, 5, 0, 0, 0, 0, time.Local) born := time.Date(1941, time.January, 5, 0, 0, 0, 0, time.UTC)
controlItem = birthday{ controlItem = birthday{
Name: "Hayao Miyazaki", Name: "Hayao Miyazaki",
Born: born, Born: born,
BornUT: &timeType{born}, BornUT: timeType{born},
} }
col, err := sess.Collection(`birthdays`) col, err := sess.Collection(`birthdays`)
...@@ -582,8 +588,8 @@ func TestSimpleCRUD(t *testing.T) { ...@@ -582,8 +588,8 @@ func TestSimpleCRUD(t *testing.T) {
} }
if reflect.DeepEqual(testItem, controlItem) == false { if reflect.DeepEqual(testItem, controlItem) == false {
t.Errorf("%s: testItem: %v (ts: %v)\n", wrapper, testItem, testItem.BornUT.value.Unix()) t.Errorf("%s: testItem (retrieved): %v (ts: %v)\n", wrapper, testItem, testItem.BornUT.value.Unix())
t.Errorf("%s: controlItem: %v (ts: %v)\n", wrapper, controlItem, controlItem.BornUT.value.Unix()) t.Errorf("%s: controlItem (inserted): %v (ts: %v)\n", wrapper, controlItem, controlItem.BornUT.value.Unix())
t.Fatalf("%s: Structs are different", wrapper) t.Fatalf("%s: Structs are different", wrapper)
} }
...@@ -655,7 +661,7 @@ func TestFibonacci(t *testing.T) { ...@@ -655,7 +661,7 @@ func TestFibonacci(t *testing.T) {
} else { } else {
var sess db.Database var sess db.Database
sess, err = db.Open(wrapper, *settings[wrapper]) sess, err = db.Open(wrapper, settings[wrapper])
if err != nil { if err != nil {
t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err) t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err)
} }
...@@ -867,7 +873,7 @@ func TestEven(t *testing.T) { ...@@ -867,7 +873,7 @@ func TestEven(t *testing.T) {
} else { } else {
var sess db.Database var sess db.Database
sess, err = db.Open(wrapper, *settings[wrapper]) sess, err = db.Open(wrapper, settings[wrapper])
if err != nil { if err != nil {
t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err) t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err)
} }
...@@ -971,26 +977,6 @@ func TestEven(t *testing.T) { ...@@ -971,26 +977,6 @@ func TestEven(t *testing.T) {
t.Fatalf("Expecting no data with wrapper %s. Got: %v\n", wrapper, item) t.Fatalf("Expecting no data with wrapper %s. Got: %v\n", wrapper, item)
} }
} }
// Testing (deprecated) "field" tag.
for {
// Testing named inputs (using tags).
var item struct {
Value uint `field:"input"`
}
err = res.Next(&item)
if err != nil {
if err == db.ErrNoMoreRows {
break
} else {
t.Fatalf(`%s: %v`, wrapper, err)
}
}
if item.Value%2 == 0 {
t.Fatalf("Expecting no data with wrapper %s. Got: %v\n", wrapper, item)
}
}
} }
} }
...@@ -1011,7 +997,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) { ...@@ -1011,7 +997,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) {
t.Fatalf(`No such settings entry for wrapper %s.`, wrapper) t.Fatalf(`No such settings entry for wrapper %s.`, wrapper)
} else { } else {
if sess, err = db.Open(wrapper, *settings[wrapper]); err != nil { if sess, err = db.Open(wrapper, settings[wrapper]); err != nil {
t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err) t.Fatalf(`Test for wrapper %s failed: %q`, wrapper, err)
} }
...@@ -1042,7 +1028,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) { ...@@ -1042,7 +1028,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
res = col.Find(db.Cond{"Case_Test": "Hello!"}) res = col.Find(db.Cond{"case_test": "Hello!"})
if wrapper == `ql` { if wrapper == `ql` {
res = res.Select(`id() as ID`, `Case_Test`) res = res.Select(`id() as ID`, `Case_Test`)
...@@ -1064,7 +1050,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) { ...@@ -1064,7 +1050,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) {
// Testing default mapping. // Testing default mapping.
testN = mapN{ testN = mapN{
Casetest: "World!", Case_TEST: "World!",
} }
if _, err = col.Append(testN); err != nil { if _, err = col.Append(testN); err != nil {
...@@ -1075,7 +1061,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) { ...@@ -1075,7 +1061,7 @@ func TestExplicitAndDefaultMapping(t *testing.T) {
// We don't have this kind of control with mongodb. // We don't have this kind of control with mongodb.
res = col.Find(db.Cond{"casetest": "World!"}) res = col.Find(db.Cond{"casetest": "World!"})
} else { } else {
res = col.Find(db.Cond{"Case_Test": "World!"}) res = col.Find(db.Cond{"case_test": "World!"})
} }
if wrapper == `ql` { if wrapper == `ql` {
......
...@@ -30,7 +30,6 @@ import ( ...@@ -30,7 +30,6 @@ import (
"time" "time"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/jmoiron/sqlx/reflectx"
_ "github.com/lib/pq" // PostgreSQL driver. _ "github.com/lib/pq" // PostgreSQL driver.
"upper.io/cache" "upper.io/cache"
"upper.io/db" "upper.io/db"
...@@ -111,7 +110,7 @@ func (s *source) Open() error { ...@@ -111,7 +110,7 @@ func (s *source) Open() error {
return err return err
} }
s.session.Mapper = s.mapper() s.session.Mapper = sqlutil.NewMapper()
if err = s.populateSchema(); err != nil { if err = s.populateSchema(); err != nil {
return err return err
...@@ -120,17 +119,6 @@ func (s *source) Open() error { ...@@ -120,17 +119,6 @@ func (s *source) Open() error {
return nil return nil
} }
// Return a struct tag mapper
func (s *source) mapper() *reflectx.Mapper {
m := reflectx.NewMapperTagFunc("db", strings.ToLower, func(value string) string {
if strings.Contains(value, ",") {
return strings.Split(value, ",")[0]
}
return value
})
return m
}
func (s *source) Clone() (db.Database, error) { func (s *source) Clone() (db.Database, error) {
return s.clone() return s.clone()
} }
......
...@@ -80,6 +80,8 @@ func FetchRow(rows *sqlx.Rows, dst interface{}) error { ...@@ -80,6 +80,8 @@ func FetchRow(rows *sqlx.Rows, dst interface{}) error {
func FetchRows(rows *sqlx.Rows, dst interface{}) error { func FetchRows(rows *sqlx.Rows, dst interface{}) error {
var err error var err error
defer rows.Close()
// Destination. // Destination.
dstv := reflect.ValueOf(dst) dstv := reflect.ValueOf(dst)
...@@ -117,8 +119,6 @@ func FetchRows(rows *sqlx.Rows, dst interface{}) error { ...@@ -117,8 +119,6 @@ func FetchRows(rows *sqlx.Rows, dst interface{}) error {
} }
} }
rows.Close() // TODO: should this be defer rows.Close higher up?
dstv.Elem().Set(slicev) dstv.Elem().Set(slicev)
return nil return nil
...@@ -191,21 +191,31 @@ func fetchResult(itemT reflect.Type, rows *sqlx.Rows, columns []string) (reflect ...@@ -191,21 +191,31 @@ func fetchResult(itemT reflect.Type, rows *sqlx.Rows, columns []string) (reflect
func fieldsByTraversal(v reflect.Value, traversals [][]int, values []interface{}, ptrs bool) error { func fieldsByTraversal(v reflect.Value, traversals [][]int, values []interface{}, ptrs bool) error {
v = reflect.Indirect(v) v = reflect.Indirect(v)
if v.Kind() != reflect.Struct { if v.Kind() != reflect.Struct {
return errors.New("argument not a struct") return errors.New("argument not a struct")
} }
for i, traversal := range traversals { for i, traversal := range traversals {
if len(traversal) == 0 { if len(traversal) == 0 {
values[i] = new(interface{}) values[i] = new(interface{})
continue continue
} }
f := reflectx.FieldByIndexes(v, traversal) f := reflectx.FieldByIndexes(v, traversal)
if ptrs { if ptrs {
values[i] = f.Addr().Interface() values[i] = f.Addr().Interface()
} else { } else {
values[i] = f.Interface() values[i] = f.Interface()
} }
// Provides compatibility with db.Unmarshaler
if u, ok := values[i].(db.Unmarshaler); ok {
values[i] = scanner{u}
}
} }
return nil return nil
} }
...@@ -25,6 +25,9 @@ import ( ...@@ -25,6 +25,9 @@ import (
"database/sql" "database/sql"
"reflect" "reflect"
"regexp" "regexp"
"strings"
"github.com/jmoiron/sqlx/reflectx"
"menteslibres.net/gosexy/to" "menteslibres.net/gosexy/to"
...@@ -59,9 +62,8 @@ func (t *T) columnLike(s string) string { ...@@ -59,9 +62,8 @@ func (t *T) columnLike(s string) string {
} }
func marshal(v interface{}) (interface{}, error) { func marshal(v interface{}) (interface{}, error) {
m, isM := v.(db.Marshaler)
if isM { if m, isMarshaler := v.(db.Marshaler); isMarshaler {
var err error var err error
if v, err = m.MarshalDB(); err != nil { if v, err = m.MarshalDB(); err != nil {
return nil, err return nil, err
...@@ -194,3 +196,17 @@ func reset(data interface{}) error { ...@@ -194,3 +196,17 @@ func reset(data interface{}) error {
v.Set(z) v.Set(z)
return nil return nil
} }
// NewMapper creates a reflectx.Mapper
func NewMapper() *reflectx.Mapper {
mapFunc := strings.ToLower
tagFunc := func(value string) string {
if strings.Contains(value, ",") {
return strings.Split(value, ",")[0]
}
return value
}
return reflectx.NewMapperTagFunc("db", mapFunc, tagFunc)
}
// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package sqlutil
import (
"database/sql"
"upper.io/db"
)
type scanner struct {
v db.Unmarshaler
}
func (u scanner) Scan(v interface{}) error {
return u.v.UnmarshalDB(v)
}
var _ sql.Scanner = scanner{}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment