func TestTypeCache(t *testing.T) {
	for _, driver := range drivers {
		db := setup(driver, t)
		defer db.Close()
		ti, err := AddType(reflect.TypeOf(sampleQuery{}))
		if err != nil {
			t.Errorf("error adding type sampleQuery: %v", err)
		}
		if ti == nil {
			t.Errorf("expected to return typeInfo, got nil")
		}
		if len(ti.FieldNames) != 3 {
			t.Errorf("expected typeInfo to have %d fields, got %d", 3, len(ti.FieldNames))
		}
		fi, found := ti.FieldInfos["Id"]
		if !found {
			t.Errorf("expected typeInfo to have an Id field")
		}
		if fi.FieldName != "Id" {
			t.Errorf("expected field Id to have name: Id")
		}
		if fi.ColumnName != "id" {
			t.Errorf("expected field Id to have column name: id (lower-case)")
		}
		if !fi.IsPrimaryKey {
			t.Errorf("expected field Id to be primary key")
		}
		if !fi.IsAutoIncrement {
			t.Errorf("expected field Id to be auto-increment")
		}
		if fi.IsTransient {
			t.Errorf("expected field Id to not be transient")
		}
		fi, found = ti.FieldInfos["UserId"]
		if !found {
			t.Errorf("expected typeInfo to have a UserId field")
		}
		if fi.FieldName != "UserId" {
			t.Errorf("expected field UserId to have name: UserId")
		}
		if fi.ColumnName != "UserId" {
			t.Errorf("expected field UserId to have column name: User")
		}
		if fi.IsPrimaryKey {
			t.Errorf("expected field UserId to not be primary key")
		}
		if fi.IsAutoIncrement {
			t.Errorf("expected field UserId to not be auto-increment")
		}
		if fi.IsTransient {
			t.Errorf("expected field UserId to not be transient")
		}
		fi, found = ti.FieldInfos["Ignore"]
		if !found {
			t.Errorf("expected typeInfo to have an Ignore field")
		}
		if fi.FieldName != "Ignore" {
			t.Errorf("expected field Ignore to have name: Ignore")
		}
		if fi.ColumnName != "" {
			t.Errorf("expected field Ignore to have an empty column name")
		}
		if fi.IsPrimaryKey {
			t.Errorf("expected field Ignore to not be primary key")
		}
		if fi.IsAutoIncrement {
			t.Errorf("expected field Ignore to not be auto-increment")
		}
		if !fi.IsTransient {
			t.Errorf("expected field Ignore to be transient")
		}
	}
}
---- Types ---------------------------------------------------------------
package dapper
import (
	"database/sql"
	"fmt"
	"os"
	"reflect"
	"testing"
	"time"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
	_ "github.com/ziutek/mymysql/godrv"
)
const (
	testDBName = "dapper_test"
	testDBUser = "travis"
	testDBPass = ""
)
var (
	drivers = []string{"mymysql", "mysql", "sqlite3", "postgres"}
)
type cruddy struct {
	Id          int64      `dapper:"id,primarykey,autoincrement,table=cruddy"`
	Int         int        `dapper:"c_int"`
	Int32       int32      `dapper:"c_int32"`
	Int64       int64      `dapper:"c_int64"`
	Uint        uint       `dapper:"c_uint"`
	Uint32      uint32     `dapper:"c_uint32"`
	Uint64      uint64     `dapper:"c_uint64"`
	Float32     float32    `dapper:"c_float32"`
	Float64     float64    `dapper:"c_float64"`
	Decimal     float64    `dapper:"c_decimal"`
	DateTime    time.Time  `dapper:"c_datetime"`
	DateTimePtr *time.Time `dapper:"c_datetime_ptr"`
	Timestamp   *time.Time `dapper:"c_timestamp"`
	Bool        bool       `dapper:"c_bool"`
	Char        string     `dapper:"c_char"`
	Varchar     string     `dapper:"c_varchar"`
	Text        string     `dapper:"c_text"`
}
type tweet struct {
	Id       int64  `dapper:"id,primarykey,autoincrement,table=tweets"`
	UserId   int64  `dapper:"user_id"`
	Message  string `dapper:"message"`
	Retweets int64  `dapper:"retweets"`
	CreatedStr string `dapper:"created"`
}
type tweetById struct {
	Id int64
}
type tweetByUserId struct {
	UserId int64
}
type tweetByUserAndMinRetweets struct {
	UserId      int64
	NumRetweets int64
}
type sampleQuery struct {
	Id     int64  `dapper:"id,primarykey,autoincrement"`
	Ignore string `dapper:"-"`
	UserId int64
}
func (t *tweet) String() string {
	return fmt.Sprintf("tweet[Id=%v,UserId=%v,Message=%v,Retweets=%v,Created=%v]",
		t.Id, t.UserId, t.Message, t.Retweets, t.Created())
}
func (t *tweet) Created() *time.Time {
	tm, err := time.Parse(time.RFC3339, t.CreatedStr)
	if err != nil {
		return nil
	}
	return &tm
}
type validater interface {
	Validate() bool
}
type user struct {
	validater
	Id        int64    `dapper:"id,primarykey,autoincrement,table=users"`
	Name      string   `dapper:"name"`
	Karma     *float64 `dapper:"karma"`
	Suspended bool     `dapper:"suspended"`
}
type userWithoutTableNameTag struct {
	Id        int64    `dapper:"id,primarykey,autoincrement"`
	Name      string   `dapper:"name"`
	Karma     *float64 `dapper:"karma"`
	Suspended bool     `dapper:"suspended"`
}
type userWithoutPrimaryKeyTag struct {
	Id        int64    `dapper:"id,autoincrement,table=users"`
	Name      string   `dapper:"name"`
	Karma     *float64 `dapper:"karma"`
	Suspended bool     `dapper:"suspended"`
}
type userWithMissingColumns struct {
	Id   int64  `dapper:"id,primarykey,autoincrement,table=users"`
	Name string `dapper:"name"`
}
func (u *user) String() string {
	return fmt.Sprintf("user[Id=%v,Name=%v,Karma=%v,Suspended=%v]",
		u.Id, u.Name, u.Karma, u.Suspended)
}
func (u *user) Validate() bool {
	return u.Name != ""
}
type Order struct {
	Id         int64             `dapper:"id,primarykey,autoincrement,table=orders"`
	RefId      string            `dapper:"ref_id"`
	User       *user             `dapper:"-"`
	Items      []*OrderItem      `dapper:"oneToMany=OrderId"`
	Extensions []*OrderExtension `dapper:"oneToMany=OrderId"`
}
func (o Order) String() string {
	return fmt.Sprintf("<Order{Id:%d,RefId:%s,len(Items):%d}>", o.Id, o.RefId, len(o.Items))
}
type OrderItem struct {
	Id      int64             `dapper:"id,primarykey,autoincrement,table=order_items"`
	OrderId int64             `dapper:"order_id"`
	Order   *Order            `dapper:"oneToOne=OrderId"`
	Name    string            `dapper:"name"`
	Price   float64           `dapper:"price"`
	Qty     float64           `dapper:"qty"`
	Images  []*OrderItemImage `dapper:"oneToMany=OrderItemId"`
}
func (item OrderItem) String() string {
	return fmt.Sprintf("<OrderItem{Id:%d,OrderId:%d,Name:%s,Order:%v}>",
		item.Id, item.OrderId, item.Name, item.Order)
}
type OrderItemImage struct {
	Id          int64      `dapper:"id,primarykey,autoincrement,table=order_item_images"`
	OrderItemId int64      `dapper:"order_item_id"`
	Item        *OrderItem `dapper:"oneToOne=OrderItemId"`
	Image       string     `dapper:"image"`
}
func (img OrderItemImage) String() string {
	return fmt.Sprintf("<OrderItemImage{Id:%d,OrderItemId:%d,Image:%s}>",
		img.Id, img.OrderItemId, img.Image)
}
type OrderExtension struct {
	Id      int64   `dapper:"id,primarykey,autoincrement,table=order_extensions"`
	OrderId *int64  `dapper:"order_id"`  
	Order   *Order  `dapper:"oneToOne=OrderId"`
	Field   string  `dapper:"field"`
	Value   *string `dapper:"value"`
}
func (ext OrderExtension) String() string {
	return fmt.Sprintf("<OrderExtension{Id:%d,OrderId:%v,Field:%s,Value:%v}>",
		ext.Id, ext.OrderId, ext.Field, ext.Value)
}
func setupWithSession(driver string, t *testing.T) (db *sql.DB, session *Session) {
	db = setup(driver, t)
	if db == nil {
		return nil, nil
	}
	switch driver {
	case "mymysql":
		session = New(db).Dialect(MySQL)
	case "mysql":
		session = New(db).Dialect(MySQL)
	case "sqlite3":
		session = New(db).Dialect(Sqlite3)
	case "postgres":
		session = New(db).Dialect(PostgreSQL)
	default:
		t.Fatalf("unknown driver: %s", driver)
		return nil, nil
	}
	return db, session
}
func setup(driver string, t *testing.T) (db *sql.DB) {
	var err error
	switch driver {
	case "mymysql":
		connectionString := fmt.Sprintf("%s/%s/%s", testDBName, testDBUser, testDBPass)
		db, err = sql.Open("mymysql", connectionString)
		if err != nil {
			t.Fatalf("error connection to database: %v", err)
		}
	case "mysql":
		connectionString := fmt.Sprintf("%s:%s@/%s?charset=utf8", testDBUser, testDBPass, testDBName)
		db, err = sql.Open("mysql", connectionString)
		if err != nil {
			t.Fatalf("error connection to database: %v", err)
		}
	case "sqlite3":
		os.Remove("./" + testDBName + ".db")
		connectionString := fmt.Sprintf("./%s.db", testDBName)
		db, err = sql.Open("sqlite3", connectionString)
		if err != nil {
			t.Fatalf("error connection to database: %v", err)
		}
	case "postgres":
		connectionString := fmt.Sprintf("user=%s password='%s' dbname=%s sslmode=disable", testDBUser, testDBPass, testDBName)
		db, err = sql.Open("postgres", connectionString)
		if err != nil {
			t.Fatalf("error connection to database: %v", err)
		}
	}
	return seed(driver, t, db)
}
func seed(driver string, t *testing.T, db *sql.DB) *sql.DB {
	suffix := ""
	switch driver {
	default:
		suffix = "CASCADE"
	case "sqlite3":
		suffix = ""
	}
	_, err := db.Exec("DROP TABLE IF EXISTS tweets " + suffix)
	if err != nil {
		t.Fatalf("%s: error dropping tweets table: %v", driver, err)
	}
	_, err = db.Exec("DROP TABLE IF EXISTS users " + suffix)
	if err != nil {
		t.Fatalf("%s: error dropping users table: %v", driver, err)
	}
	_, err = db.Exec("DROP TABLE IF EXISTS cruddy " + suffix)
	if err != nil {
		t.Fatalf("%s: error dropping cruddy table: %v", driver, err)
	}
	_, err = db.Exec("DROP TABLE IF EXISTS order_extensions " + suffix)
	if err != nil {
		t.Fatalf("%s: error dropping order_extensions table: %v", driver, err)
	}
	_, err = db.Exec("DROP TABLE IF EXISTS order_item_images " + suffix)
	if err != nil {
		t.Fatalf("%s: error dropping order_item_images table: %v", driver, err)
	}
	_, err = db.Exec("DROP TABLE IF EXISTS order_items " + suffix)
	if err != nil {
		t.Fatalf("%s: error dropping order_items table: %v", driver, err)
	}
	_, err = db.Exec("DROP TABLE IF EXISTS orders " + suffix)
	if err != nil {
		t.Fatalf("%s: error dropping orders table: %v", driver, err)
	}
	pkCol := ""
	tsCol := ""
	dateTimeType := ""
	switch driver {
	default:
		pkCol = "int(11) not null primary key AUTO_INCREMENT"
		tsCol = "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
		dateTimeType = "datetime"
	case "sqlite3":
		pkCol = "integer not null primary key AUTOINCREMENT"
		tsCol = "datetime NOT NULL DEFAULT CURRENT_TIMESTAMP"
		dateTimeType = "datetime"
	case "postgres":
		pkCol = "serial not null primary key"
		tsCol = "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP"
		dateTimeType = "timestamp"
	}
	_, err = db.Exec(`
CREATE TABLE cruddy (
    id ` + pkCol + `,
		c_int int,
		c_int32 int,
		c_int64 int,
		c_uint  int,
		c_uint32 int,
		c_uint64 int,
		c_float32 float,
		c_float64 float,
		c_decimal decimal(19,5),
		c_date date,
		c_date_ptr date,
		c_datetime ` + dateTimeType + `,
		c_datetime_ptr ` + dateTimeType + `,
		c_timestamp ` + tsCol + `,
		c_bool bool,
		c_char char(3),
		c_varchar varchar(20),
		c_text text
)`)
	if err != nil {
		t.Fatalf("error creating cruddy table: %v", err)
	}
	_, err = db.Exec(`
CREATE TABLE users (
        id ` + pkCol + `,
        name varchar(100) not null,
        karma decimal(19,5),
        suspended smallint default '0'
)`)
	if err != nil {
		t.Fatalf("error creating users table: %v", err)
	}
	_, err = db.Exec(`
CREATE TABLE tweets (
        id ` + pkCol + `,
        user_id int not null,
        message text,
        retweets int,
        created timestamp not null default current_timestamp,
        foreign key (user_id) references users (id) on delete cascade
)`)
	if err != nil {
		t.Fatalf("error creating tweets table: %v", err)
	}
	_, err = db.Exec(`
CREATE TABLE orders (
        id ` + pkCol + `,
        ref_id varchar(100) not null
)`)
	if err != nil {
		t.Fatalf("error creating orders table: %v", err)
	}
	_, err = db.Exec(`
CREATE TABLE order_items (
        id ` + pkCol + `,
        order_id int not null,
        name varchar(100) not null,
        price decimal(15,3) not null,
        qty decimal(10,3) not null,
        foreign key (order_id) references orders (id) on delete cascade
)`)
	if err != nil {
		t.Fatalf("error creating order_items table: %v", err)
	}
	_, err = db.Exec(`
CREATE TABLE order_item_images (
        id ` + pkCol + `,
        order_item_id int not null,
        image varchar(100) not null,
        foreign key (order_item_id) references order_items (id) on delete cascade
)`)
	if err != nil {
		t.Fatalf("error creating order_item_images table: %v", err)
	}
	_, err = db.Exec(`
CREATE TABLE order_extensions (
        id ` + pkCol + `,
        order_id int null,
        field varchar(100) not null,
        value text,
        foreign key (order_id) references orders (id) on delete cascade
)`)
	if err != nil {
		t.Fatalf("error creating order_extensions table: %v", err)
	}
	_, err = db.Exec("INSERT INTO users (name,karma,suspended) VALUES ('Oliver', 42.13, 0)")
	if err != nil {
		t.Fatalf("error inserting user: %v", err)
	}
	_, err = db.Exec("INSERT INTO users (name,karma,suspended) VALUES ('Sandra', 57.19, 1)")
	if err != nil {
		t.Fatalf("error inserting user: %v", err)
	}
	_, err = db.Exec("INSERT INTO tweets (id,user_id,message,retweets) VALUES (1, 1, 'Google Go rocks', 179)")
	if err != nil {
		t.Fatalf("error inserting tweet: %v", err)
	}
	_, err = db.Exec("INSERT INTO tweets (id,user_id,message,retweets) VALUES (2, 1, '... so does Google Maps', 19)")
	if err != nil {
		t.Fatalf("error inserting tweet: %v", err)
	}
	_, err = db.Exec("INSERT INTO tweets (id,user_id,message,retweets) VALUES (3, 2, 'Holidays! Yay!', 1)")
	if err != nil {
		t.Fatalf("error inserting tweet: %v", err)
	}
	_, err = db.Exec("INSERT INTO orders (id,ref_id) VALUES (1, 'APPLE1')")
	if err != nil {
		t.Fatalf("error inserting order: %v", err)
	}
	_, err = db.Exec("INSERT INTO orders (id,ref_id) VALUES (2, 'OFFICE1')")
	if err != nil {
		t.Fatalf("error inserting order: %v", err)
	}
	_, err = db.Exec("INSERT INTO orders (id,ref_id) VALUES (3, 'EMPTY1')")
	if err != nil {
		t.Fatalf("error inserting order: %v", err)
	}
	_, err = db.Exec("INSERT INTO order_items (id,order_id,name,price,qty) VALUES (1, 1, 'MacBook Air 11\"', 1199.90, 1)")
	if err != nil {
		t.Fatalf("error inserting order item: %v", err)
	}
	_, err = db.Exec("INSERT INTO order_items (id,order_id,name,price,qty) VALUES (2, 1, 'iPad 4th gen.', 499.90, 2)")
	if err != nil {
		t.Fatalf("error inserting order item: %v", err)
	}
	_, err = db.Exec("INSERT INTO order_items (id,order_id,name,price,qty) VALUES (3, 2, 'Lenovo T430s', 1499.90, 1)")
	if err != nil {
		t.Fatalf("error inserting order item: %v", err)
	}
	_, err = db.Exec("INSERT INTO order_items (id,order_id,name,price,qty) VALUES (4, 2, 'BlackBox', 199.90, 20)")
	if err != nil {
		t.Fatalf("error inserting order item: %v", err)
	}
	_, err = db.Exec("INSERT INTO order_item_images (id,order_item_id,image) VALUES (1, 1, 'macbook_11.png')")
	if err != nil {
		t.Fatalf("error inserting order item image: %v", err)
	}
	_, err = db.Exec("INSERT INTO order_item_images (id,order_item_id,image) VALUES (2, 1, 'macbook_11_big.jpg')")
	if err != nil {
		t.Fatalf("error inserting order item image: %v", err)
	}
	_, err = db.Exec("INSERT INTO order_extensions (order_id,field,value) VALUES (1, 'VIP', 'Yes')")
	if err != nil {
		t.Fatalf("error inserting order extension: %v", err)
	}
	return db
}
func TestSessionDefaults(t *testing.T) {
	db := setup("mysql", t)
	defer db.Close()
	session := New(db)
	if session.dialect != MySQL {
		t.Errorf("expected MySQL dialect as default, got: %v", session.dialect)
	}
	session = session.Dialect(nil)
	if session.dialect != MySQL {
		t.Errorf("expected MySQL dialect as fallback, got: %v", session.dialect)
	}
	if session.debug {
		t.Errorf("expected no debugging by default, got: %v", session.debug)
	}
}
func TestSessionDebuggingEnable(t *testing.T) {
	db := setup("mysql", t)
	defer db.Close()
	session := New(db)
	if session.debug {
		t.Errorf("expected no debugging by default, got: %v", session.debug)
	}
	session = session.Debug(true)
	if !session.debug {
		t.Errorf("expected debugging to be true, got: %v", session.debug)
	}
}