// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package orm

import (
	"bytes"
	"context"
	"database/sql"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"testing"
	"time"

	"github.com/beego/beego/v2/client/orm/internal/utils"

	"github.com/beego/beego/v2/client/orm/internal/models"

	"github.com/stretchr/testify/assert"

	"github.com/beego/beego/v2/client/orm/clauses/order_clause"
	"github.com/beego/beego/v2/client/orm/hints"
)

var _ = os.PathSeparator

var (
	testDate     = utils.FormatDate + " -0700"
	testDateTime = utils.FormatDateTime + " -0700"
	testTime     = utils.FormatTime + " -0700"
)

type argAny []interface{}

// Get interface by index from interface slice
func (a argAny) Get(i int, args ...interface{}) (r interface{}) {
	if i >= 0 && i < len(a) {
		r = a[i]
	}
	if len(args) > 0 {
		r = args[0]
	}
	return
}

func ValuesCompare(is bool, a interface{}, args ...interface{}) (ok bool, err error) {
	if len(args) == 0 {
		return false, fmt.Errorf("miss args")
	}
	b := args[0]
	arg := argAny(args)

	switch v := a.(type) {
	case reflect.Kind:
		ok = reflect.ValueOf(b).Kind() == v
	case time.Time:
		if v2, vo := b.(time.Time); vo {
			if arg.Get(1) != nil {
				format := utils.ToStr(arg.Get(1))
				a = v.Format(format)
				b = v2.Format(format)
				ok = a == b
			} else {
				err = fmt.Errorf("compare datetime miss format")
				goto wrongArg
			}
		}
	default:
		ok = utils.ToStr(a) == utils.ToStr(b)
	}
	ok = is && ok || !is && !ok
	if !ok {
		err = fmt.Errorf("expected: `%v`, Get `%v`", b, a)
	}

wrongArg:
	if err != nil {
		return false, err
	}

	return true, nil
}

func AssertIs(a interface{}, args ...interface{}) error {
	if ok, err := ValuesCompare(true, a, args...); !ok {
		return err
	}
	return nil
}

func AssertNot(a interface{}, args ...interface{}) error {
	if ok, err := ValuesCompare(false, a, args...); !ok {
		return err
	}
	return nil
}

func getCaller(skip int) string {
	pc, file, line, _ := runtime.Caller(skip)
	fun := runtime.FuncForPC(pc)
	_, fn := filepath.Split(file)
	data, err := os.ReadFile(file)
	var codes []string
	if err == nil {
		lines := bytes.Split(data, []byte{'\n'})
		n := 10
		for i := 0; i < n; i++ {
			o := line - n
			if o < 0 {
				continue
			}
			cur := o + i + 1
			flag := "  "
			if cur == line {
				flag = ">>"
			}
			ls := formatLines(string(lines[o+i]))
			code := fmt.Sprintf(" %s %5d:   %s", flag, cur, ls)
			if code != "" {
				codes = append(codes, code)
			}
		}
	}
	funName := fun.Name()
	if i := strings.LastIndex(funName, "."); i > -1 {
		funName = funName[i+1:]
	}
	return fmt.Sprintf("%s:%s:%d: \n%s", fn, funName, line, strings.Join(codes, "\n"))
}

func formatLines(s string) string {
	return strings.ReplaceAll(s, "\t", "    ")
}

// Deprecated: Using stretchr/testify/assert
func throwFail(t *testing.T, err error, args ...interface{}) {
	if err != nil {
		con := fmt.Sprintf("\t\nError: %s\n%s\n", err.Error(), getCaller(2))
		if len(args) > 0 {
			parts := make([]string, 0, len(args))
			for _, arg := range args {
				parts = append(parts, fmt.Sprintf("%v", arg))
			}
			con += " " + strings.Join(parts, ", ")
		}
		t.Error(con)
		t.Fail()
	}
}

// deprecated using assert.XXX
func throwFailNow(t *testing.T, err error, args ...interface{}) {
	if err != nil {
		con := fmt.Sprintf("\t\nError: %s\n%s\n", err.Error(), getCaller(2))
		if len(args) > 0 {
			parts := make([]string, 0, len(args))
			for _, arg := range args {
				parts = append(parts, fmt.Sprintf("%v", arg))
			}
			con += " " + strings.Join(parts, ", ")
		}
		t.Error(con)
		t.FailNow()
	}
}

func TestGetDB(t *testing.T) {
	if db, err := GetDB(); err != nil {
		throwFailNow(t, err)
	} else {
		err = db.Ping()
		throwFailNow(t, err)
	}
}

func TestSyncDb(t *testing.T) {
	RegisterModel(new(Data), new(DataNull), new(DataCustom))
	RegisterModel(new(User))
	RegisterModel(new(Profile))
	RegisterModel(new(Post))
	RegisterModel(new(NullValue))
	RegisterModel(new(Tag))
	RegisterModel(new(Comment))
	RegisterModel(new(UserBig))
	RegisterModel(new(PostTags))
	RegisterModel(new(Group))
	RegisterModel(new(Permission))
	RegisterModel(new(GroupPermissions))
	RegisterModel(new(InLine))
	RegisterModel(new(InLineOneToOne))
	RegisterModel(new(IntegerPk))
	RegisterModel(new(UintPk))
	RegisterModel(new(PtrPk))
	RegisterModel(new(Index))
	RegisterModel(new(StrPk))
	RegisterModel(new(TM))
	RegisterModel(new(DeptInfo))

	err := RunSyncdb("default", true, Debug)
	throwFail(t, err)

	defaultModelCache.Clean()
}

func TestRegisterModels(_ *testing.T) {
	RegisterModel(new(Data), new(DataNull), new(DataCustom))
	RegisterModel(new(User))
	RegisterModel(new(Profile))
	RegisterModel(new(Post))
	RegisterModel(new(NullValue))
	RegisterModel(new(Tag))
	RegisterModel(new(Comment))
	RegisterModel(new(UserBig))
	RegisterModel(new(PostTags))
	RegisterModel(new(Group))
	RegisterModel(new(Permission))
	RegisterModel(new(GroupPermissions))
	RegisterModel(new(InLine))
	RegisterModel(new(InLineOneToOne))
	RegisterModel(new(IntegerPk))
	RegisterModel(new(UintPk))
	RegisterModel(new(PtrPk))
	RegisterModel(new(Index))
	RegisterModel(new(StrPk))
	RegisterModel(new(TM))
	RegisterModel(new(DeptInfo))

	BootStrap()

	dORM = NewOrm()
	dDbBaser = getDbAlias("default").DbBaser
}

func TestModelSyntax(t *testing.T) {
	user := &User{}
	ind := reflect.ValueOf(user).Elem()
	fn := models.GetFullName(ind.Type())
	_, ok := defaultModelCache.GetByFullName(fn)
	throwFail(t, AssertIs(ok, true))

	mi, ok := defaultModelCache.Get("user")
	throwFail(t, AssertIs(ok, true))
	if ok {
		throwFail(t, AssertIs(mi.Fields.GetByName("ShouldSkip") == nil, true))
	}
}

var DataValues = map[string]interface{}{
	"Boolean":  true,
	"Char":     "char",
	"Text":     "text",
	"JSON":     `{"name":"json"}`,
	"Jsonb":    `{"name": "jsonb"}`,
	"Time":     time.Now(),
	"Date":     time.Now(),
	"DateTime": time.Now(),
	"Byte":     byte(1<<8 - 1),
	"Rune":     rune(1<<31 - 1),
	"Int":      int(1<<31 - 1),
	"Int8":     int8(1<<7 - 1),
	"Int16":    int16(1<<15 - 1),
	"Int32":    int32(1<<31 - 1),
	"Int64":    int64(1<<63 - 1),
	"Uint":     uint(1<<32 - 1),
	"Uint8":    uint8(1<<8 - 1),
	"Uint16":   uint16(1<<16 - 1),
	"Uint32":   uint32(1<<32 - 1),
	"Uint64":   uint64(1<<63 - 1), // uint64 values with high bit Set are not supported
	"Float32":  float32(100.1234),
	"Float64":  float64(100.1234),
	"Decimal":  float64(100.1234),
}

func TestDataTypes(t *testing.T) {
	d := Data{}
	ind := reflect.Indirect(reflect.ValueOf(&d))

	for name, value := range DataValues {
		if name == "JSON" {
			continue
		}
		e := ind.FieldByName(name)
		e.Set(reflect.ValueOf(value))
	}
	id, err := dORM.Insert(&d)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 1))

	d = Data{ID: 1}
	err = dORM.Read(&d)
	throwFail(t, err)

	ind = reflect.Indirect(reflect.ValueOf(&d))

	for name, value := range DataValues {
		e := ind.FieldByName(name)
		vu := e.Interface()
		switch name {
		case "Date":
		case "DateTime":
		case "Time":
			assert.True(t, vu.(time.Time).In(DefaultTimeLoc).Sub(value.(time.Time).In(DefaultTimeLoc)) <= time.Second)
		default:
			assert.Equal(t, value, vu)
		}
	}
}

func TestTM(t *testing.T) {
	// The precision of sqlite is not implemented
	if dORM.Driver().Type() == 2 {
		return
	}
	var recTM TM
	tm := NewTM()
	tm.TMPrecision1 = time.Unix(1596766024, 123456789)
	tm.TMPrecision2 = time.Unix(1596766024, 123456789)
	_, err := dORM.Insert(tm)
	throwFail(t, err)

	err = dORM.QueryTable("tm").One(&recTM)
	throwFail(t, err)
	throwFail(t, AssertIs(recTM.TMPrecision1.String(), "2020-08-07 02:07:04.123 +0000 UTC"))
	throwFail(t, AssertIs(recTM.TMPrecision2.String(), "2020-08-07 02:07:04.1235 +0000 UTC"))
}

func TestUnregisterModel(t *testing.T) {
	data := []*DeptInfo{
		{
			DeptName:     "A",
			EmployeeName: "A1",
			Salary:       1000,
		},
		{
			DeptName:     "A",
			EmployeeName: "A2",
			Salary:       2000,
		},
		{
			DeptName:     "B",
			EmployeeName: "B1",
			Salary:       2000,
		},
		{
			DeptName:     "B",
			EmployeeName: "B2",
			Salary:       4000,
		},
		{
			DeptName:     "B",
			EmployeeName: "B3",
			Salary:       3000,
		},
	}
	qs := dORM.QueryTable("dept_info")
	i, _ := qs.PrepareInsert()
	for _, d := range data {
		_, err := i.Insert(d)
		if err != nil {
			throwFail(t, err)
		}
	}

	f := func() {
		var res []UnregisterModel
		n, err := dORM.QueryTable("dept_info").All(&res)
		throwFail(t, err)
		throwFail(t, AssertIs(n, 5))
		throwFail(t, AssertIs(res[0].EmployeeName, "A1"))

		type Sum struct {
			DeptName string
			Total    int
		}
		var sun []Sum
		qs.Aggregate("dept_name,sum(salary) as total").GroupBy("dept_name").OrderBy("dept_name").All(&sun)
		throwFail(t, AssertIs(sun[0].DeptName, "A"))
		throwFail(t, AssertIs(sun[0].Total, 3000))

		type Max struct {
			DeptName string
			Max      float64
		}
		var max []Max
		qs.Aggregate("dept_name,max(salary) as max").GroupBy("dept_name").OrderBy("dept_name").All(&max)
		throwFail(t, AssertIs(max[1].DeptName, "B"))
		throwFail(t, AssertIs(max[1].Max, 4000))
	}
	for i := 0; i < 5; i++ {
		f()
	}
}

func TestNullDataTypes(t *testing.T) {
	d := DataNull{}

	if IsPostgres {
		// can removed when this fixed
		// https://github.com/lib/pq/pull/125
		d.DateTime = time.Now()
	}

	id, err := dORM.Insert(&d)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 1))

	data := `{"ok":1,"data":{"arr":[1,2],"msg":"gopher"}}`
	d = DataNull{ID: 1, JSON: data}
	num, err := dORM.Update(&d)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	d = DataNull{ID: 1}
	err = dORM.Read(&d)
	throwFail(t, err)

	throwFail(t, AssertIs(d.JSON, data))

	throwFail(t, AssertIs(d.NullBool.Valid, false))
	throwFail(t, AssertIs(d.NullString.Valid, false))
	throwFail(t, AssertIs(d.NullInt64.Valid, false))
	throwFail(t, AssertIs(d.NullFloat64.Valid, false))

	throwFail(t, AssertIs(d.BooleanPtr, nil))
	throwFail(t, AssertIs(d.CharPtr, nil))
	throwFail(t, AssertIs(d.TextPtr, nil))
	throwFail(t, AssertIs(d.BytePtr, nil))
	throwFail(t, AssertIs(d.RunePtr, nil))
	throwFail(t, AssertIs(d.IntPtr, nil))
	throwFail(t, AssertIs(d.Int8Ptr, nil))
	throwFail(t, AssertIs(d.Int16Ptr, nil))
	throwFail(t, AssertIs(d.Int32Ptr, nil))
	throwFail(t, AssertIs(d.Int64Ptr, nil))
	throwFail(t, AssertIs(d.UintPtr, nil))
	throwFail(t, AssertIs(d.Uint8Ptr, nil))
	throwFail(t, AssertIs(d.Uint16Ptr, nil))
	throwFail(t, AssertIs(d.Uint32Ptr, nil))
	throwFail(t, AssertIs(d.Uint64Ptr, nil))
	throwFail(t, AssertIs(d.Float32Ptr, nil))
	throwFail(t, AssertIs(d.Float64Ptr, nil))
	throwFail(t, AssertIs(d.DecimalPtr, nil))
	throwFail(t, AssertIs(d.TimePtr, nil))
	throwFail(t, AssertIs(d.DatePtr, nil))
	throwFail(t, AssertIs(d.DateTimePtr, nil))

	_, err = dORM.Raw(`INSERT INTO data_null (boolean) VALUES (?)`, nil).Exec()
	throwFail(t, err)

	d = DataNull{ID: 2}
	err = dORM.Read(&d)
	throwFail(t, err)

	booleanPtr := true
	charPtr := string("test")
	textPtr := string("test")
	bytePtr := byte('t')
	runePtr := rune('t')
	intPtr := int(42)
	int8Ptr := int8(42)
	int16Ptr := int16(42)
	int32Ptr := int32(42)
	int64Ptr := int64(42)
	uintPtr := uint(42)
	uint8Ptr := uint8(42)
	uint16Ptr := uint16(42)
	uint32Ptr := uint32(42)
	uint64Ptr := uint64(42)
	float32Ptr := float32(42.0)
	float64Ptr := float64(42.0)
	decimalPtr := float64(42.0)
	timePtr := time.Now()
	datePtr := time.Now()
	dateTimePtr := time.Now()

	d = DataNull{
		DateTime:    time.Now(),
		NullString:  sql.NullString{String: "test", Valid: true},
		NullBool:    sql.NullBool{Bool: true, Valid: true},
		NullInt64:   sql.NullInt64{Int64: 42, Valid: true},
		NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
		BooleanPtr:  &booleanPtr,
		CharPtr:     &charPtr,
		TextPtr:     &textPtr,
		BytePtr:     &bytePtr,
		RunePtr:     &runePtr,
		IntPtr:      &intPtr,
		Int8Ptr:     &int8Ptr,
		Int16Ptr:    &int16Ptr,
		Int32Ptr:    &int32Ptr,
		Int64Ptr:    &int64Ptr,
		UintPtr:     &uintPtr,
		Uint8Ptr:    &uint8Ptr,
		Uint16Ptr:   &uint16Ptr,
		Uint32Ptr:   &uint32Ptr,
		Uint64Ptr:   &uint64Ptr,
		Float32Ptr:  &float32Ptr,
		Float64Ptr:  &float64Ptr,
		DecimalPtr:  &decimalPtr,
		TimePtr:     &timePtr,
		DatePtr:     &datePtr,
		DateTimePtr: &dateTimePtr,
	}

	id, err = dORM.Insert(&d)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 3))

	d = DataNull{ID: 3}
	err = dORM.Read(&d)
	throwFail(t, err)

	throwFail(t, AssertIs(d.NullBool.Valid, true))
	throwFail(t, AssertIs(d.NullBool.Bool, true))

	throwFail(t, AssertIs(d.NullString.Valid, true))
	throwFail(t, AssertIs(d.NullString.String, "test"))

	throwFail(t, AssertIs(d.NullInt64.Valid, true))
	throwFail(t, AssertIs(d.NullInt64.Int64, 42))

	throwFail(t, AssertIs(d.NullFloat64.Valid, true))
	throwFail(t, AssertIs(d.NullFloat64.Float64, 42.42))

	throwFail(t, AssertIs(*d.BooleanPtr, booleanPtr))
	throwFail(t, AssertIs(*d.CharPtr, charPtr))
	throwFail(t, AssertIs(*d.TextPtr, textPtr))
	throwFail(t, AssertIs(*d.BytePtr, bytePtr))
	throwFail(t, AssertIs(*d.RunePtr, runePtr))
	throwFail(t, AssertIs(*d.IntPtr, intPtr))
	throwFail(t, AssertIs(*d.Int8Ptr, int8Ptr))
	throwFail(t, AssertIs(*d.Int16Ptr, int16Ptr))
	throwFail(t, AssertIs(*d.Int32Ptr, int32Ptr))
	throwFail(t, AssertIs(*d.Int64Ptr, int64Ptr))
	throwFail(t, AssertIs(*d.UintPtr, uintPtr))
	throwFail(t, AssertIs(*d.Uint8Ptr, uint8Ptr))
	throwFail(t, AssertIs(*d.Uint16Ptr, uint16Ptr))
	throwFail(t, AssertIs(*d.Uint32Ptr, uint32Ptr))
	throwFail(t, AssertIs(*d.Uint64Ptr, uint64Ptr))
	throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr))
	throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr))
	throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr))

	// in mysql, there are some precision problem, (*d.TimePtr).UTC() != timePtr.UTC()
	assert.True(t, (*d.TimePtr).UTC().Sub(timePtr.UTC()) <= time.Second)
	assert.True(t, (*d.DatePtr).UTC().Sub(datePtr.UTC()) <= time.Second)
	assert.True(t, (*d.DateTimePtr).UTC().Sub(dateTimePtr.UTC()) <= time.Second)

	// test support for pointer Fields using RawSeter.QueryRows()
	var dnList []*DataNull
	Q := dDbBaser.TableQuote()
	num, err = dORM.Raw(fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q), 3).QueryRows(&dnList)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	equal := reflect.DeepEqual(*dnList[0], d)
	throwFailNow(t, AssertIs(equal, true))
}

func TestDataCustomTypes(t *testing.T) {
	d := DataCustom{}
	ind := reflect.Indirect(reflect.ValueOf(&d))

	for name, value := range DataValues {
		e := ind.FieldByName(name)
		if !e.IsValid() {
			continue
		}
		e.Set(reflect.ValueOf(value).Convert(e.Type()))
	}

	id, err := dORM.Insert(&d)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 1))

	d = DataCustom{ID: 1}
	err = dORM.Read(&d)
	throwFail(t, err)

	ind = reflect.Indirect(reflect.ValueOf(&d))

	for name, value := range DataValues {
		e := ind.FieldByName(name)
		if !e.IsValid() {
			continue
		}
		vu := e.Interface()
		value = reflect.ValueOf(value).Convert(e.Type()).Interface()
		throwFail(t, AssertIs(vu == value, true), value, vu)
	}
}

func TestCRUD(t *testing.T) {
	profile := NewProfile()
	profile.Age = 30
	profile.Money = 1234.12
	id, err := dORM.Insert(profile)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 1))

	user := NewUser()
	user.UserName = "slene"
	user.Email = "vslene@gmail.com"
	user.Password = "pass"
	user.Status = 3
	user.IsStaff = true
	user.IsActive = true

	id, err = dORM.Insert(user)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 1))

	u := &User{ID: user.ID}
	err = dORM.Read(u)
	throwFail(t, err)

	throwFail(t, AssertIs(u.UserName, "slene"))
	throwFail(t, AssertIs(u.Email, "vslene@gmail.com"))
	throwFail(t, AssertIs(u.Password, "pass"))
	throwFail(t, AssertIs(u.Status, 3))
	throwFail(t, AssertIs(u.IsStaff, true))
	throwFail(t, AssertIs(u.IsActive, true))

	assert.True(t, u.Created.In(DefaultTimeLoc).Sub(user.Created.In(DefaultTimeLoc)) <= time.Second)
	assert.True(t, u.Updated.In(DefaultTimeLoc).Sub(user.Updated.In(DefaultTimeLoc)) <= time.Second)

	user.UserName = "astaxie"
	user.Profile = profile
	num, err := dORM.Update(user)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	u = &User{ID: user.ID}
	err = dORM.Read(u)
	throwFailNow(t, err)
	throwFail(t, AssertIs(u.UserName, "astaxie"))
	throwFail(t, AssertIs(u.Profile.ID, profile.ID))

	u = &User{UserName: "astaxie", Password: "pass"}
	err = dORM.Read(u, "UserName")
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(id, 1))

	u.UserName = "QQ"
	u.Password = "111"
	num, err = dORM.Update(u, "UserName")
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	u = &User{ID: user.ID}
	err = dORM.Read(u)
	throwFailNow(t, err)
	throwFail(t, AssertIs(u.UserName, "QQ"))
	throwFail(t, AssertIs(u.Password, "pass"))

	num, err = dORM.Delete(profile)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	u = &User{ID: user.ID}
	err = dORM.Read(u)
	throwFail(t, err)
	throwFail(t, AssertIs(true, u.Profile == nil))

	num, err = dORM.Delete(user)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	u = &User{ID: 100}
	err = dORM.Read(u)
	throwFail(t, AssertIs(err, ErrNoRows))

	ub := UserBig{}
	ub.Name = "name"
	id, err = dORM.Insert(&ub)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 1))

	ub = UserBig{ID: 1}
	err = dORM.Read(&ub)
	throwFail(t, err)
	throwFail(t, AssertIs(ub.Name, "name"))

	num, err = dORM.Delete(&ub, "name")
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
}

func TestInsertTestData(t *testing.T) {
	var users []*User

	profile := NewProfile()
	profile.Age = 28
	profile.Money = 1234.12

	id, err := dORM.Insert(profile)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 2))

	user := NewUser()
	user.UserName = "slene"
	user.Email = "vslene@gmail.com"
	user.Password = "pass"
	user.Status = 1
	user.IsStaff = false
	user.IsActive = true
	user.Profile = profile

	users = append(users, user)

	id, err = dORM.Insert(user)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 2))

	profile = NewProfile()
	profile.Age = 30
	profile.Money = 4321.09

	id, err = dORM.Insert(profile)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 3))

	user = NewUser()
	user.UserName = "astaxie"
	user.Email = "astaxie@gmail.com"
	user.Password = "password"
	user.Status = 2
	user.IsStaff = true
	user.IsActive = false
	user.Profile = profile

	users = append(users, user)

	id, err = dORM.Insert(user)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 3))

	user = NewUser()
	user.UserName = "nobody"
	user.Email = "nobody@gmail.com"
	user.Password = "nobody"
	user.Status = 3
	user.IsStaff = false
	user.IsActive = false

	users = append(users, user)

	id, err = dORM.Insert(user)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 4))

	tags := []*Tag{
		{Name: "golang", BestPost: &Post{ID: 2}},
		{Name: "example"},
		{Name: "format"},
		{Name: "c++"},
	}

	posts := []*Post{
		{User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand.
This document gives tips for writing clear, idiomatic Go code. It augments the language specification, the Tour of Go, and How to Write Go Code, All of which you should read first.`},
		{User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`},
		{User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide.
With Go we take an unusual approach and let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't seem right, rearrange your program (or file a bug about gofmt), don't work around it.`},
		{User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code.
The program—and web server—godoc processes Go source files to extract documentation about the contents of the package. Comments that appear before top-level declarations, with no intervening newlines, are extracted along with the declaration to serve as explanatory text for the item. The nature and style of these comments determines the quality of the documentation godoc produces.`},
	}

	comments := []*Comment{
		{Post: posts[0], Content: "a comment"},
		{Post: posts[1], Content: "yes"},
		{Post: posts[1]},
		{Post: posts[1]},
		{Post: posts[2]},
		{Post: posts[2]},
	}

	for _, tag := range tags {
		id, err := dORM.Insert(tag)
		throwFail(t, err)
		throwFail(t, AssertIs(id > 0, true))
	}

	for _, post := range posts {
		id, err := dORM.Insert(post)
		throwFail(t, err)
		throwFail(t, AssertIs(id > 0, true))

		num := len(post.Tags)
		if num > 0 {
			nums, err := dORM.QueryM2M(post, "tags").Add(post.Tags)
			throwFailNow(t, err)
			throwFailNow(t, AssertIs(nums, num))
		}
	}

	for _, comment := range comments {
		id, err := dORM.Insert(comment)
		throwFail(t, err)
		throwFail(t, AssertIs(id > 0, true))
	}

	permissions := []*Permission{
		{Name: "writePosts"},
		{Name: "readComments"},
		{Name: "readPosts"},
	}

	groups := []*Group{
		{
			Name:        "admins",
			Permissions: []*Permission{permissions[0], permissions[1], permissions[2]},
		},
		{
			Name:        "users",
			Permissions: []*Permission{permissions[1], permissions[2]},
		},
	}

	for _, permission := range permissions {
		id, err := dORM.Insert(permission)
		throwFail(t, err)
		throwFail(t, AssertIs(id > 0, true))
	}

	for _, group := range groups {
		_, err := dORM.Insert(group)
		throwFail(t, err)
		throwFail(t, AssertIs(id > 0, true))

		num := len(group.Permissions)
		if num > 0 {
			nums, err := dORM.QueryM2M(group, "permissions").Add(group.Permissions)
			throwFailNow(t, err)
			throwFailNow(t, AssertIs(nums, num))
		}
	}
}

func TestCustomField(t *testing.T) {
	user := User{ID: 2}
	err := dORM.Read(&user)
	throwFailNow(t, err)

	user.Langs = append(user.Langs, "zh-CN", "en-US")
	user.Extra.Name = "beego"
	user.Extra.Data = "orm"
	_, err = dORM.Update(&user, "Langs", "Extra")
	throwFailNow(t, err)

	user = User{ID: 2}
	err = dORM.Read(&user)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(len(user.Langs), 2))
	throwFailNow(t, AssertIs(user.Langs[0], "zh-CN"))
	throwFailNow(t, AssertIs(user.Langs[1], "en-US"))

	throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
	throwFailNow(t, AssertIs(user.Extra.Data, "orm"))

	var users []User
	Q := dDbBaser.TableQuote()
	n, err := dORM.Raw(fmt.Sprintf("SELECT * FROM %suser%s where id=?", Q, Q), 2).QueryRows(&users)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(n, 1))
	throwFailNow(t, AssertIs(users[0].Extra.Name, "beego"))
	throwFailNow(t, AssertIs(users[0].Extra.Data, "orm"))

	user = User{}
	err = dORM.Raw(fmt.Sprintf("SELECT * FROM %suser%s where id=?", Q, Q), 2).QueryRow(&user)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(user.Extra.Name, "beego"))
	throwFailNow(t, AssertIs(user.Extra.Data, "orm"))
}

func TestExpr(t *testing.T) {
	user := &User{}
	var qs QuerySeter
	assert.NotPanics(t, func() { qs = dORM.QueryTable(user) })
	assert.NotPanics(t, func() { qs = dORM.QueryTable((*User)(nil)) })
	assert.NotPanics(t, func() { qs = dORM.QueryTable("User") })
	assert.NotPanics(t, func() { qs = dORM.QueryTable("user") })
	num, err := qs.Filter("UserName", "slene").Filter("user_name", "slene").Filter("profile__Age", 28).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("created", time.Now()).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))

	// num, err = qs.Filter("created", time.Now().Format(format_Date)).Count()
	// throwFail(t, err)
	// throwFail(t, AssertIs(num, 3))
}

func TestSpecifyIndex(t *testing.T) {
	var index *Index
	index = &Index{
		F1: 1,
		F2: 2,
	}
	_, _ = dORM.Insert(index)
	throwFailNow(t, AssertIs(index.Id, 1))

	index = &Index{
		F1: 3,
		F2: 4,
	}
	_, _ = dORM.Insert(index)
	throwFailNow(t, AssertIs(index.Id, 2))

	_ = dORM.QueryTable(&Index{}).Filter(`f1`, `1`).ForceIndex(`index_f1`).One(index)
	throwFailNow(t, AssertIs(index.F2, 2))

	_ = dORM.QueryTable(&Index{}).Filter(`f2`, `4`).UseIndex(`index_f2`).One(index)
	throwFailNow(t, AssertIs(index.F1, 3))

	_ = dORM.QueryTable(&Index{}).Filter(`f1`, `1`).IgnoreIndex(`index_f1`, `index_f2`).One(index)
	throwFailNow(t, AssertIs(index.F2, 2))
}

func TestOperators(t *testing.T) {
	qs := dORM.QueryTable("user")
	num, err := qs.Filter("user_name", "slene").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name__exact", String("slene")).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name__exact", "slene").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name__iexact", "Slene").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	if IsMysql {
		// Now only mysql support `strictexact`
		num, err = qs.Filter("user_name__strictexact", "Slene").Count()
		throwFail(t, err)
		throwFail(t, AssertIs(num, 0))

		num, err = qs.Filter("user_name__strictexact", "slene").Count()
		throwFail(t, err)
		throwFail(t, AssertIs(num, 1))
	}

	num, err = qs.Filter("user_name__contains", "e").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	var shouldNum int

	if IsSqlite || IsTidb {
		shouldNum = 2
	} else {
		shouldNum = 0
	}

	num, err = qs.Filter("user_name__contains", "E").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, shouldNum))

	num, err = qs.Filter("user_name__icontains", "E").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Filter("user_name__icontains", "E").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Filter("status__gt", 1).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Filter("status__gte", 1).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))

	num, err = qs.Filter("status__lt", Uint(3)).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Filter("status__lte", Int(3)).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))

	num, err = qs.Filter("user_name__startswith", "s").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	if IsSqlite || IsTidb {
		shouldNum = 1
	} else {
		shouldNum = 0
	}

	num, err = qs.Filter("user_name__startswith", "S").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, shouldNum))

	num, err = qs.Filter("user_name__istartswith", "S").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name__endswith", "e").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	if IsSqlite || IsTidb {
		shouldNum = 2
	} else {
		shouldNum = 0
	}

	num, err = qs.Filter("user_name__endswith", "E").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, shouldNum))

	num, err = qs.Filter("user_name__iendswith", "E").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Filter("profile__isnull", true).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("status__in", 1, 2).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Filter("status__in", []int{1, 2}).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	n1, n2 := 1, 2
	num, err = qs.Filter("status__in", []*int{&n1}, &n2).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Filter("id__between", 2, 3).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Filter("id__between", []int{2, 3}).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.FilterRaw("user_name", "= 'slene'").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.FilterRaw("status", "IN (1, 2)").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.FilterRaw("profile_id", "IN (SELECT id FROM user_profile WHERE age=30)").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
}

func TestSetCond(t *testing.T) {
	cond := NewCondition()
	cond1 := cond.And("profile__isnull", false).AndNot("status__in", 1).Or("profile__age__gt", 2000)

	qs := dORM.QueryTable("user")
	num, err := qs.SetCond(cond1).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	cond2 := cond.AndCond(cond1).OrCond(cond.And("user_name", "slene"))
	num, err = qs.SetCond(cond2).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	cond3 := cond.AndNotCond(cond.And("status__in", 1))
	num, err = qs.SetCond(cond3).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	cond4 := cond.And("user_name", "slene").OrNotCond(cond.And("user_name", "slene"))
	num, err = qs.SetCond(cond4).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))

	cond5 := cond.Raw("user_name", "= 'slene'").OrNotCond(cond.And("user_name", "slene"))
	num, err = qs.SetCond(cond5).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))
}

func TestLimit(t *testing.T) {
	var posts []*Post
	qs := dORM.QueryTable("post")
	num, err := qs.Limit(1).All(&posts)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Limit(-1).All(&posts)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 4))

	num, err = qs.Limit(-1, 2).All(&posts)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	num, err = qs.Limit(0, 2).All(&posts)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))
}

func TestOffset(t *testing.T) {
	var posts []*Post
	qs := dORM.QueryTable("post")
	num, err := qs.Limit(1).Offset(2).All(&posts)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Offset(2).All(&posts)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))
}

func TestCountOrderBy(t *testing.T) {
	if IsPostgres {
		return
	}
	qs := dORM.QueryTable("user")
	num, err := qs.OrderBy("-status").Filter("user_name", "nobody").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.OrderBy("status").Filter("user_name", "slene").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.OrderBy("-profile__age").Filter("user_name", "astaxie").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.OrderClauses(
		order_clause.Clause(
			order_clause.Column(`profile__age`),
			order_clause.SortDescending(),
		),
	).Filter("user_name", "astaxie").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	if IsMysql {
		num, err = qs.OrderClauses(
			order_clause.Clause(
				order_clause.Column(`rand()`),
				order_clause.Raw(),
			),
		).Filter("user_name", "astaxie").Count()
		throwFail(t, err)
		throwFail(t, AssertIs(num, 1))
	}
}

func TestOrderBy(t *testing.T) {
	var users []*User
	qs := dORM.QueryTable("user")
	num, err := qs.OrderBy("-status").Filter("user_name", "nobody").All(&users)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.OrderBy("status").Filter("user_name", "slene").All(&users)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.OrderBy("-profile__age").Filter("user_name", "astaxie").All(&users)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.OrderClauses(
		order_clause.Clause(
			order_clause.Column(`profile__age`),
			order_clause.SortDescending(),
		),
	).Filter("user_name", "astaxie").All(&users)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	if IsMysql {
		num, err = qs.OrderClauses(
			order_clause.Clause(
				order_clause.Column(`rand()`),
				order_clause.Raw(),
			),
		).Filter("user_name", "astaxie").All(&users)
		throwFail(t, err)
		throwFail(t, AssertIs(num, 1))
	}
}

func TestCount(t *testing.T) {
	qs := dORM.QueryTable("user")
	num, err := qs.Filter("user_name", "nobody").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name", "slene").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name", "astaxie").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name", "astaxie").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
}

func TestAll(t *testing.T) {
	var users []*User
	qs := dORM.QueryTable("user")
	num, err := qs.OrderBy("Id").All(&users)
	throwFail(t, err)
	throwFailNow(t, AssertIs(num, 3))

	throwFail(t, AssertIs(users[0].UserName, "slene"))
	throwFail(t, AssertIs(users[1].UserName, "astaxie"))
	throwFail(t, AssertIs(users[2].UserName, "nobody"))

	var users2 []User
	qs = dORM.QueryTable("user")
	num, err = qs.OrderBy("Id").All(&users2)
	throwFail(t, err)
	throwFailNow(t, AssertIs(num, 3))

	throwFailNow(t, AssertIs(users2[0].UserName, "slene"))
	throwFailNow(t, AssertIs(users2[1].UserName, "astaxie"))
	throwFailNow(t, AssertIs(users2[2].UserName, "nobody"))

	qs = dORM.QueryTable("user")
	num, err = qs.OrderBy("Id").RelatedSel().All(&users2, "UserName")
	throwFail(t, err)
	throwFailNow(t, AssertIs(num, 3))
	throwFailNow(t, AssertIs(len(users2), 3))
	throwFailNow(t, AssertIs(users2[0].UserName, "slene"))
	throwFailNow(t, AssertIs(users2[1].UserName, "astaxie"))
	throwFailNow(t, AssertIs(users2[2].UserName, "nobody"))
	throwFailNow(t, AssertIs(users2[0].ID, 0))
	throwFailNow(t, AssertIs(users2[1].ID, 0))
	throwFailNow(t, AssertIs(users2[2].ID, 0))
	throwFailNow(t, AssertIs(users2[0].Profile == nil, false))
	throwFailNow(t, AssertIs(users2[1].Profile == nil, false))
	throwFailNow(t, AssertIs(users2[2].Profile == nil, true))

	qs = dORM.QueryTable("user")
	num, err = qs.Filter("user_name", "nothing").All(&users)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 0))

	var users3 []*User
	qs = dORM.QueryTable("user")
	num, err = qs.Filter("user_name", "nothing").All(&users3)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 0))
	throwFailNow(t, AssertIs(users3 == nil, false))
}

func TestOne(t *testing.T) {
	var user User
	qs := dORM.QueryTable("user")
	err := qs.One(&user)
	throwFail(t, err)

	user = User{}
	err = qs.OrderBy("Id").Limit(1).One(&user)
	throwFailNow(t, err)
	throwFail(t, AssertIs(user.UserName, "slene"))
	throwFail(t, AssertNot(err, ErrMultiRows))

	user = User{}
	err = qs.OrderBy("-Id").Limit(100).One(&user)
	throwFailNow(t, err)
	throwFail(t, AssertIs(user.UserName, "nobody"))
	throwFail(t, AssertNot(err, ErrMultiRows))

	err = qs.Filter("user_name", "nothing").One(&user)
	throwFail(t, AssertIs(err, ErrNoRows))
}

func TestValues(t *testing.T) {
	var maps []Params
	qs := dORM.QueryTable("user")

	num, err := qs.OrderBy("Id").Values(&maps)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))
	if num == 3 {
		throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
		throwFail(t, AssertIs(maps[2]["Profile"], nil))
	}

	num, err = qs.OrderClauses(
		order_clause.Clause(
			order_clause.Column("Id"),
			order_clause.SortAscending(),
		),
	).Values(&maps)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))
	if num == 3 {
		throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
		throwFail(t, AssertIs(maps[2]["Profile"], nil))
	}

	num, err = qs.OrderBy("Id").Values(&maps, "UserName", "Profile__Age")
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))
	if num == 3 {
		throwFail(t, AssertIs(maps[0]["UserName"], "slene"))
		throwFail(t, AssertIs(maps[0]["Profile__Age"], 28))
		throwFail(t, AssertIs(maps[2]["Profile__Age"], nil))
	}

	num, err = qs.Filter("UserName", "slene").Values(&maps)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
}

func TestValuesList(t *testing.T) {
	var list []ParamsList
	qs := dORM.QueryTable("user")

	num, err := qs.OrderBy("Id").ValuesList(&list)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))
	if num == 3 {
		throwFail(t, AssertIs(list[0][1], "slene")) // username
		throwFail(t, AssertIs(list[2][10], nil))    // profile
	}

	num, err = qs.OrderBy("Id").ValuesList(&list, "UserName", "Profile__Age")
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))
	if num == 3 {
		throwFail(t, AssertIs(list[0][0], "slene"))
		throwFail(t, AssertIs(list[0][1], 28))
		throwFail(t, AssertIs(list[2][1], nil))
	}
}

func TestValuesFlat(t *testing.T) {
	var list ParamsList
	qs := dORM.QueryTable("user")

	num, err := qs.OrderBy("id").ValuesFlat(&list, "UserName")
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))
	if num == 3 {
		throwFail(t, AssertIs(list[0], "slene"))
		throwFail(t, AssertIs(list[1], "astaxie"))
		throwFail(t, AssertIs(list[2], "nobody"))
	}
}

func TestRelatedSel(t *testing.T) {
	if IsTidb {
		// Skip it. TiDB does not support relation now.
		return
	}
	qs := dORM.QueryTable("user")
	num, err := qs.Filter("profile__age", 28).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("profile__age__gt", 28).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("profile__user__profile__age__gt", 28).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	var user User
	err = qs.Filter("user_name", "slene").RelatedSel("profile").One(&user)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
	throwFail(t, AssertNot(user.Profile, nil))
	if user.Profile != nil {
		throwFail(t, AssertIs(user.Profile.Age, 28))
	}

	err = qs.Filter("user_name", "slene").RelatedSel().One(&user)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
	throwFail(t, AssertNot(user.Profile, nil))
	if user.Profile != nil {
		throwFail(t, AssertIs(user.Profile.Age, 28))
	}

	err = qs.Filter("user_name", "nobody").RelatedSel("profile").One(&user)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
	throwFail(t, AssertIs(user.Profile, nil))

	qs = dORM.QueryTable("user_profile")
	num, err = qs.Filter("user__username", "slene").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	var posts []*Post
	qs = dORM.QueryTable("post")
	num, err = qs.RelatedSel().All(&posts)
	throwFail(t, err)
	throwFailNow(t, AssertIs(num, 4))

	throwFailNow(t, AssertIs(posts[0].User.UserName, "slene"))
	throwFailNow(t, AssertIs(posts[1].User.UserName, "astaxie"))
	throwFailNow(t, AssertIs(posts[2].User.UserName, "astaxie"))
	throwFailNow(t, AssertIs(posts[3].User.UserName, "nobody"))
}

func TestReverseQuery(t *testing.T) {
	var profile Profile
	err := dORM.QueryTable("user_profile").Filter("User", 3).One(&profile)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(profile.Age, 30))

	profile = Profile{}
	err = dORM.QueryTable("user_profile").Filter("User__UserName", "astaxie").One(&profile)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(profile.Age, 30))

	var user User
	err = dORM.QueryTable("user").Filter("Posts__Title", "Examples").One(&user)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(user.UserName, "astaxie"))

	user = User{}
	err = dORM.QueryTable("user").Filter("Posts__User__UserName", "astaxie").Limit(1).One(&user)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(user.UserName, "astaxie"))

	user = User{}
	err = dORM.QueryTable("user").Filter("Posts__User__UserName", "astaxie").RelatedSel().Limit(1).One(&user)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(user.UserName, "astaxie"))
	throwFailNow(t, AssertIs(user.Profile == nil, false))
	throwFailNow(t, AssertIs(user.Profile.Age, 30))

	var posts []*Post
	num, err := dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").All(&posts)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 3))
	throwFailNow(t, AssertIs(posts[0].Title, "Introduction"))

	posts = []*Post{}
	num, err = dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").Filter("User__UserName", "slene").All(&posts)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(posts[0].Title, "Introduction"))

	posts = []*Post{}
	num, err = dORM.QueryTable("post").Filter("Tags__Tag__Name", "golang").
		Filter("User__UserName", "slene").RelatedSel().All(&posts)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(posts[0].User == nil, false))
	throwFailNow(t, AssertIs(posts[0].User.UserName, "slene"))

	var tags []*Tag
	num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").All(&tags)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(tags[0].Name, "golang"))

	tags = []*Tag{}
	num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").
		Filter("BestPost__User__UserName", "astaxie").All(&tags)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(tags[0].Name, "golang"))

	tags = []*Tag{}
	num, err = dORM.QueryTable("tag").Filter("Posts__Post__Title", "Introduction").
		Filter("BestPost__User__UserName", "astaxie").RelatedSel().All(&tags)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(tags[0].Name, "golang"))
	throwFailNow(t, AssertIs(tags[0].BestPost == nil, false))
	throwFailNow(t, AssertIs(tags[0].BestPost.Title, "Examples"))
	throwFailNow(t, AssertIs(tags[0].BestPost.User == nil, false))
	throwFailNow(t, AssertIs(tags[0].BestPost.User.UserName, "astaxie"))
}

func TestLoadRelated(t *testing.T) {
	// load reverse foreign key
	user := User{ID: 3}

	err := dORM.Read(&user)
	throwFailNow(t, err)

	num, err := dORM.LoadRelated(&user, "Posts")
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))
	throwFailNow(t, AssertIs(len(user.Posts), 2))
	throwFailNow(t, AssertIs(user.Posts[0].User.ID, 3))

	num, err = dORM.LoadRelated(&user, "Posts", hints.DefaultRelDepth())
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))
	throwFailNow(t, AssertIs(len(user.Posts), 2))
	throwFailNow(t, AssertIs(user.Posts[0].User.UserName, "astaxie"))

	num, err = dORM.LoadRelated(&user, "Posts",
		hints.DefaultRelDepth(),
		hints.Limit(1))
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(len(user.Posts), 1))

	num, err = dORM.LoadRelated(&user, "Posts",
		hints.DefaultRelDepth(),
		hints.OrderBy("-Id"))
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))
	throwFailNow(t, AssertIs(len(user.Posts), 2))
	throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))

	num, err = dORM.LoadRelated(&user, "Posts",
		hints.DefaultRelDepth(),
		hints.Limit(1),
		hints.Offset(1),
		hints.OrderBy("Id"))
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(len(user.Posts), 1))
	throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting"))

	// load reverse one to one
	profile := Profile{ID: 3}
	profile.BestPost = &Post{ID: 2}
	num, err = dORM.Update(&profile, "BestPost")
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	err = dORM.Read(&profile)
	throwFailNow(t, err)

	num, err = dORM.LoadRelated(&profile, "User")
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(profile.User == nil, false))
	throwFailNow(t, AssertIs(profile.User.UserName, "astaxie"))

	num, err = dORM.LoadRelated(&profile, "User", hints.DefaultRelDepth())
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(profile.User == nil, false))
	throwFailNow(t, AssertIs(profile.User.UserName, "astaxie"))
	throwFailNow(t, AssertIs(profile.User.Profile.Age, profile.Age))

	// load rel one to one
	err = dORM.Read(&user)
	throwFailNow(t, err)

	num, err = dORM.LoadRelated(&user, "Profile")
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(user.Profile == nil, false))
	throwFailNow(t, AssertIs(user.Profile.Age, 30))

	num, err = dORM.LoadRelated(&user, "Profile", hints.DefaultRelDepth())
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(user.Profile == nil, false))
	throwFailNow(t, AssertIs(user.Profile.Age, 30))
	throwFailNow(t, AssertIs(user.Profile.BestPost == nil, false))
	throwFailNow(t, AssertIs(user.Profile.BestPost.Title, "Examples"))

	post := Post{ID: 2}

	// load rel foreign key
	err = dORM.Read(&post)
	throwFailNow(t, err)

	num, err = dORM.LoadRelated(&post, "User")
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(post.User == nil, false))
	throwFailNow(t, AssertIs(post.User.UserName, "astaxie"))

	num, err = dORM.LoadRelated(&post, "User", hints.DefaultRelDepth())
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(post.User == nil, false))
	throwFailNow(t, AssertIs(post.User.UserName, "astaxie"))
	throwFailNow(t, AssertIs(post.User.Profile == nil, false))
	throwFailNow(t, AssertIs(post.User.Profile.Age, 30))

	// load rel m2m
	post = Post{ID: 2}

	err = dORM.Read(&post)
	throwFailNow(t, err)

	num, err = dORM.LoadRelated(&post, "Tags")
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))
	throwFailNow(t, AssertIs(len(post.Tags), 2))
	throwFailNow(t, AssertIs(post.Tags[0].Name, "golang"))

	num, err = dORM.LoadRelated(&post, "Tags", hints.DefaultRelDepth())
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))
	throwFailNow(t, AssertIs(len(post.Tags), 2))
	throwFailNow(t, AssertIs(post.Tags[0].Name, "golang"))
	throwFailNow(t, AssertIs(post.Tags[0].BestPost == nil, false))
	throwFailNow(t, AssertIs(post.Tags[0].BestPost.User.UserName, "astaxie"))

	// load reverse m2m
	tag := Tag{ID: 1}

	err = dORM.Read(&tag)
	throwFailNow(t, err)

	num, err = dORM.LoadRelated(&tag, "Posts")
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 3))
	throwFailNow(t, AssertIs(tag.Posts[0].Title, "Introduction"))
	throwFailNow(t, AssertIs(tag.Posts[0].User.ID, 2))
	throwFailNow(t, AssertIs(tag.Posts[0].User.Profile == nil, true))

	num, err = dORM.LoadRelated(&tag, "Posts", hints.DefaultRelDepth())
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 3))
	throwFailNow(t, AssertIs(tag.Posts[0].Title, "Introduction"))
	throwFailNow(t, AssertIs(tag.Posts[0].User.ID, 2))
	throwFailNow(t, AssertIs(tag.Posts[0].User.UserName, "slene"))
}

func TestQueryM2M(t *testing.T) {
	post := Post{ID: 4}
	m2m := dORM.QueryM2M(&post, "Tags")

	tag1 := []*Tag{{Name: "TestTag1"}, {Name: "TestTag2"}}
	tag2 := &Tag{Name: "TestTag3"}
	tag3 := []interface{}{&Tag{Name: "TestTag4"}}

	tags := []interface{}{tag1[0], tag1[1], tag2, tag3[0]}

	for _, tag := range tags {
		_, err := dORM.Insert(tag)
		throwFailNow(t, err)
	}

	num, err := m2m.Add(tag1)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))

	num, err = m2m.Add(tag2)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	num, err = m2m.Add(tag3)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	num, err = m2m.Count()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 5))

	num, err = m2m.Remove(tag3)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	num, err = m2m.Count()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 4))

	exist := m2m.Exist(tag2)
	throwFailNow(t, AssertIs(exist, true))

	num, err = m2m.Remove(tag2)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	exist = m2m.Exist(tag2)
	throwFailNow(t, AssertIs(exist, false))

	num, err = m2m.Count()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 3))

	num, err = m2m.Clear()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 3))

	num, err = m2m.Count()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 0))

	tag := Tag{Name: "test"}
	_, err = dORM.Insert(&tag)
	throwFailNow(t, err)

	m2m = dORM.QueryM2M(&tag, "Posts")

	post1 := []*Post{{Title: "TestPost1"}, {Title: "TestPost2"}}
	post2 := &Post{Title: "TestPost3"}
	post3 := []interface{}{&Post{Title: "TestPost4"}}

	posts := []interface{}{post1[0], post1[1], post2, post3[0]}

	for _, post := range posts {
		p := post.(*Post)
		p.User = &User{ID: 1}
		_, err := dORM.Insert(post)
		throwFailNow(t, err)
	}

	num, err = m2m.Add(post1)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))

	num, err = m2m.Add(post2)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	num, err = m2m.Add(post3)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	num, err = m2m.Count()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 4))

	num, err = m2m.Remove(post3)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	num, err = m2m.Count()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 3))

	exist = m2m.Exist(post2)
	throwFailNow(t, AssertIs(exist, true))

	num, err = m2m.Remove(post2)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	exist = m2m.Exist(post2)
	throwFailNow(t, AssertIs(exist, false))

	num, err = m2m.Count()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))

	num, err = m2m.Clear()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))

	num, err = m2m.Count()
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 0))

	num, err = dORM.Delete(&tag)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
}

func TestQueryRelate(_ *testing.T) {
	// post := &Post{Id: 2}

	// qs := dORM.QueryRelate(post, "Tags")
	// num, err := qs.Count()
	// throwFailNow(t, err)
	// throwFailNow(t, AssertIs(num, 2))

	// var tags []*Tag
	// num, err = qs.All(&tags)
	// throwFailNow(t, err)
	// throwFailNow(t, AssertIs(num, 2))
	// throwFailNow(t, AssertIs(tags[0].Name, "golang"))

	// num, err = dORM.QueryTable("Tag").Filter("Posts__Post", 2).Count()
	// throwFailNow(t, err)
	// throwFailNow(t, AssertIs(num, 2))
}

func TestPkManyRelated(t *testing.T) {
	permission := &Permission{Name: "readPosts"}
	err := dORM.Read(permission, "Name")
	throwFailNow(t, err)

	var groups []*Group
	qs := dORM.QueryTable("Group")
	num, err := qs.Filter("Permissions__Permission", permission.ID).All(&groups)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))
}

func TestPrepareInsert(t *testing.T) {
	qs := dORM.QueryTable("user")
	i, err := qs.PrepareInsert()
	throwFailNow(t, err)

	var user User
	user.UserName = "testing1"
	num, err := i.Insert(&user)
	throwFail(t, err)
	throwFail(t, AssertIs(num > 0, true))

	user.UserName = "testing2"
	num, err = i.Insert(&user)
	throwFail(t, err)
	throwFail(t, AssertIs(num > 0, true))

	num, err = qs.Filter("user_name__in", "testing1", "testing2").Delete()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 2))

	err = i.Close()
	throwFail(t, err)
	err = i.Close()
	throwFail(t, AssertIs(err, ErrStmtClosed))
}

func TestRawExec(t *testing.T) {
	Q := dDbBaser.TableQuote()

	query := fmt.Sprintf("UPDATE %suser%s SET %suser_name%s = ? WHERE %suser_name%s = ?", Q, Q, Q, Q, Q, Q)
	res, err := dORM.Raw(query, "testing", "slene").Exec()
	throwFail(t, err)
	num, err := res.RowsAffected()
	throwFail(t, AssertIs(num, 1), err)

	res, err = dORM.Raw(query, "slene", "testing").Exec()
	throwFail(t, err)
	num, err = res.RowsAffected()
	throwFail(t, AssertIs(num, 1), err)
}

func TestRawQueryRow(t *testing.T) {
	var (
		Boolean  bool
		Char     string
		Text     string
		Time     time.Time
		Date     time.Time
		DateTime time.Time
		Byte     byte
		Rune     rune
		Int      int
		Int8     int
		Int16    int16
		Int32    int32
		Int64    int64
		Uint     uint
		Uint8    uint8
		Uint16   uint16
		Uint32   uint32
		Uint64   uint64
		Float32  float32
		Float64  float64
		Decimal  float64
	)

	dataValues := make(map[string]interface{}, len(DataValues))

	for k, v := range DataValues {
		dataValues[strings.ToLower(k)] = v
	}

	Q := dDbBaser.TableQuote()

	cols := []string{
		"id", "boolean", "char", "text", "time", "date", "datetime", "byte", "rune", "int", "int8", "int16", "int32",
		"int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "decimal",
	}
	sep := fmt.Sprintf("%s, %s", Q, Q)
	query := fmt.Sprintf("SELECT %s%s%s FROM data WHERE id = ?", Q, strings.Join(cols, sep), Q)
	var id int
	values := []interface{}{
		&id, &Boolean, &Char, &Text, &Time, &Date, &DateTime, &Byte, &Rune, &Int, &Int8, &Int16, &Int32,
		&Int64, &Uint, &Uint8, &Uint16, &Uint32, &Uint64, &Float32, &Float64, &Decimal,
	}
	err := dORM.Raw(query, 1).QueryRow(values...)
	throwFailNow(t, err)
	for i, col := range cols {
		vu := values[i]
		v := reflect.ValueOf(vu).Elem().Interface()
		switch col {
		case "id":
			throwFail(t, AssertIs(id, 1))
		case "time", "datetime":
			v = v.(time.Time).In(DefaultTimeLoc)
			value := dataValues[col].(time.Time).In(DefaultTimeLoc)
			assert.True(t, v.(time.Time).Sub(value) <= time.Second)
		case "date":
		default:
			throwFail(t, AssertIs(v, dataValues[col]))
		}
	}

	var (
		uid    int
		status *int
		pid    *int
	)

	cols = []string{
		"id", "Status", "profile_id",
	}
	query = fmt.Sprintf("SELECT %s%s%s FROM %suser%s WHERE id = ?", Q, strings.Join(cols, sep), Q, Q, Q)
	err = dORM.Raw(query, 4).QueryRow(&uid, &status, &pid)
	throwFail(t, err)
	throwFail(t, AssertIs(uid, 4))
	throwFail(t, AssertIs(*status, 3))
	throwFail(t, AssertIs(pid, nil))

	type Embedded struct {
		Email string
	}
	type queryRowNoModelTest struct {
		Id         int
		EmbedField Embedded
	}

	cols = []string{
		"id", "email",
	}
	var row queryRowNoModelTest
	query = fmt.Sprintf("SELECT %s%s%s FROM %suser%s WHERE id = ?", Q, strings.Join(cols, sep), Q, Q, Q)
	err = dORM.Raw(query, 4).QueryRow(&row)
	throwFail(t, err)
	throwFail(t, AssertIs(row.Id, 4))
	throwFail(t, AssertIs(row.EmbedField.Email, "nobody@gmail.com"))

	// test for sql.Null* Fields
	nData := &DataNull{
		NullString:  sql.NullString{String: "test sql.null", Valid: true},
		NullBool:    sql.NullBool{Bool: true, Valid: true},
		NullInt64:   sql.NullInt64{Int64: 42, Valid: true},
		NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
	}
	newId, err := dORM.Insert(nData)
	throwFailNow(t, err)

	var nd *DataNull
	query = fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q)
	err = dORM.Raw(query, newId).QueryRow(&nd)
	throwFailNow(t, err)

	throwFailNow(t, AssertNot(nd, nil))
	throwFail(t, AssertIs(nd.NullBool.Valid, true))
	throwFail(t, AssertIs(nd.NullBool.Bool, true))
	throwFail(t, AssertIs(nd.NullString.Valid, true))
	throwFail(t, AssertIs(nd.NullString.String, "test sql.null"))
	throwFail(t, AssertIs(nd.NullInt64.Valid, true))
	throwFail(t, AssertIs(nd.NullInt64.Int64, 42))
	throwFail(t, AssertIs(nd.NullFloat64.Valid, true))
	throwFail(t, AssertIs(nd.NullFloat64.Float64, 42.42))
}

// user_profile table
type userProfile struct {
	User
	Age   int
	Money float64
}

func TestQueryRows(t *testing.T) {
	Q := dDbBaser.TableQuote()

	var datas []*Data

	query := fmt.Sprintf("SELECT * FROM %sdata%s", Q, Q)
	num, err := dORM.Raw(query).QueryRows(&datas)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(len(datas), 1))

	ind := reflect.Indirect(reflect.ValueOf(datas[0]))

	for name, value := range DataValues {
		e := ind.FieldByName(name)
		vu := e.Interface()
		switch name {
		case "Time":
		case "Date":
		case "DateTime":
			assert.True(t, vu.(time.Time).In(DefaultTimeLoc).Sub(value.(time.Time).In(DefaultTimeLoc)) <= time.Second)
		default:
			assert.Equal(t, value, vu)
		}
	}

	var datas2 []Data

	query = fmt.Sprintf("SELECT * FROM %sdata%s", Q, Q)
	num, err = dORM.Raw(query).QueryRows(&datas2)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(len(datas2), 1))

	ind = reflect.Indirect(reflect.ValueOf(datas2[0]))

	for name, value := range DataValues {
		e := ind.FieldByName(name)
		vu := e.Interface()
		switch name {
		case "Time":
		case "Date":
		case "DateTime":
			assert.True(t, vu.(time.Time).In(DefaultTimeLoc).Sub(value.(time.Time).In(DefaultTimeLoc)) <= time.Second)
		default:
			assert.Equal(t, value, vu)
		}

	}

	var ids []int
	var usernames []string
	query = fmt.Sprintf("SELECT %sid%s, %suser_name%s FROM %suser%s ORDER BY %sid%s ASC", Q, Q, Q, Q, Q, Q, Q, Q)
	num, err = dORM.Raw(query).QueryRows(&ids, &usernames)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 3))
	throwFailNow(t, AssertIs(len(ids), 3))
	throwFailNow(t, AssertIs(ids[0], 2))
	throwFailNow(t, AssertIs(usernames[0], "slene"))
	throwFailNow(t, AssertIs(ids[1], 3))
	throwFailNow(t, AssertIs(usernames[1], "astaxie"))
	throwFailNow(t, AssertIs(ids[2], 4))
	throwFailNow(t, AssertIs(usernames[2], "nobody"))

	// test query rows by nested struct
	var l []userProfile
	query = fmt.Sprintf("SELECT * FROM %suser_profile%s LEFT JOIN %suser%s ON %suser_profile%s.%sid%s = %suser%s.%sid%s", Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q)
	num, err = dORM.Raw(query).QueryRows(&l)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 2))
	throwFailNow(t, AssertIs(len(l), 2))
	throwFailNow(t, AssertIs(l[0].UserName, "slene"))
	throwFailNow(t, AssertIs(l[0].Age, 28))
	throwFailNow(t, AssertIs(l[1].UserName, "astaxie"))
	throwFailNow(t, AssertIs(l[1].Age, 30))

	// test for sql.Null* Fields
	nData := &DataNull{
		NullString:  sql.NullString{String: "test sql.null", Valid: true},
		NullBool:    sql.NullBool{Bool: true, Valid: true},
		NullInt64:   sql.NullInt64{Int64: 42, Valid: true},
		NullFloat64: sql.NullFloat64{Float64: 42.42, Valid: true},
	}
	newId, err := dORM.Insert(nData)
	throwFailNow(t, err)

	var nDataList []*DataNull
	query = fmt.Sprintf("SELECT * FROM %sdata_null%s where id=?", Q, Q)
	num, err = dORM.Raw(query, newId).QueryRows(&nDataList)
	throwFailNow(t, err)
	throwFailNow(t, AssertIs(num, 1))

	nd := nDataList[0]
	throwFailNow(t, AssertNot(nd, nil))
	throwFail(t, AssertIs(nd.NullBool.Valid, true))
	throwFail(t, AssertIs(nd.NullBool.Bool, true))
	throwFail(t, AssertIs(nd.NullString.Valid, true))
	throwFail(t, AssertIs(nd.NullString.String, "test sql.null"))
	throwFail(t, AssertIs(nd.NullInt64.Valid, true))
	throwFail(t, AssertIs(nd.NullInt64.Int64, 42))
	throwFail(t, AssertIs(nd.NullFloat64.Valid, true))
	throwFail(t, AssertIs(nd.NullFloat64.Float64, 42.42))
}

func TestRawValues(t *testing.T) {
	Q := dDbBaser.TableQuote()

	var maps []Params
	query := fmt.Sprintf("SELECT %suser_name%s FROM %suser%s WHERE %sStatus%s = ?", Q, Q, Q, Q, Q, Q)
	num, err := dORM.Raw(query, 1).Values(&maps)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
	if num == 1 {
		throwFail(t, AssertIs(maps[0]["user_name"], "slene"))
	}

	var lists []ParamsList
	num, err = dORM.Raw(query, 1).ValuesList(&lists)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
	if num == 1 {
		throwFail(t, AssertIs(lists[0][0], "slene"))
	}

	query = fmt.Sprintf("SELECT %sprofile_id%s FROM %suser%s ORDER BY %sid%s ASC", Q, Q, Q, Q, Q, Q)
	var list ParamsList
	num, err = dORM.Raw(query).ValuesFlat(&list)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))
	if num == 3 {
		throwFail(t, AssertIs(list[0], "2"))
		throwFail(t, AssertIs(list[1], "3"))
		throwFail(t, AssertIs(list[2], nil))
	}
}

func TestForIssue4709(t *testing.T) {
	pre, err := dORM.Raw("INSERT into null_value (value) VALUES (?)").Prepare()
	assert.Nil(t, err)
	_, err = pre.Exec(nil)
	assert.Nil(t, err)
}

func TestRawPrepare(t *testing.T) {
	var (
		result sql.Result
		err    error
		pre    RawPreparer
	)
	if IsMysql || IsSqlite {
		pre, err = dORM.Raw("INSERT INTO tag (name) VALUES (?)").Prepare()
		assert.Nil(t, err)
		if pre != nil {
			result, err = pre.Exec("name1")
			assert.Nil(t, err)

			tid, err := result.LastInsertId()
			assert.Nil(t, err)
			assert.True(t, tid > 0)

			result, err = pre.Exec("name2")
			assert.Nil(t, err)

			id, err := result.LastInsertId()
			assert.Nil(t, err)
			assert.Equal(t, id, tid+1)

			result, err = pre.Exec("name3")
			assert.Nil(t, err)

			id, err = result.LastInsertId()
			assert.Nil(t, err)
			assert.Equal(t, id, tid+2)

			err = pre.Close()
			assert.Nil(t, err)

			res, err := dORM.Raw("DELETE FROM tag WHERE name IN (?, ?, ?)", []string{"name1", "name2", "name3"}).Exec()
			assert.Nil(t, err)

			num, err := res.RowsAffected()
			assert.Nil(t, err)
			assert.Equal(t, num, int64(3))
		}
	} else if IsPostgres {
		pre, err = dORM.Raw(`INSERT INTO "tag" ("name") VALUES (?) RETURNING "id"`).Prepare()
		assert.Nil(t, err)
		if pre != nil {
			_, err = pre.Exec("name1")
			assert.Nil(t, err)

			_, err = pre.Exec("name2")
			assert.Nil(t, err)

			_, err = pre.Exec("name3")
			assert.Nil(t, err)

			err = pre.Close()
			assert.Nil(t, err)

			res, err := dORM.Raw(`DELETE FROM "tag" WHERE "name" IN (?, ?, ?)`, []string{"name1", "name2", "name3"}).Exec()
			assert.Nil(t, err)

			if err == nil {
				num, err := res.RowsAffected()
				assert.Nil(t, err)
				assert.Equal(t, num, int64(3))
			}
		}
	}
}

func TestUpdate(t *testing.T) {
	qs := dORM.QueryTable("user")
	num, err := qs.Filter("user_name", "slene").Filter("is_staff", false).Update(Params{
		"is_staff":  true,
		"is_active": true,
	})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	// with join
	num, err = qs.Filter("user_name", "slene").Filter("profile__age", 28).Filter("is_staff", true).Update(Params{
		"is_staff": false,
	})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name", "slene").Update(Params{
		"Nums": ColValue(ColAdd, 100),
	})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name", "slene").Update(Params{
		"Nums": ColValue(ColMinus, 50),
	})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name", "slene").Update(Params{
		"Nums": ColValue(ColMultiply, 3),
	})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = qs.Filter("user_name", "slene").Update(Params{
		"Nums": ColValue(ColExcept, 5),
	})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	user := User{UserName: "slene"}
	err = dORM.Read(&user, "UserName")
	throwFail(t, err)
	throwFail(t, AssertIs(user.Nums, 30))
}

func TestDelete(t *testing.T) {
	qs := dORM.QueryTable("user_profile")
	num, err := qs.Filter("user__user_name", "slene").Delete()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	qs = dORM.QueryTable("user")
	num, err = qs.Filter("user_name", "slene").Filter("profile__isnull", true).Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	qs = dORM.QueryTable("comment")
	num, err = qs.Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 6))

	qs = dORM.QueryTable("post")
	num, err = qs.Filter("Id", 3).Delete()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	qs = dORM.QueryTable("comment")
	num, err = qs.Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 4))

	qs = dORM.QueryTable("comment")
	num, err = qs.Filter("Post__User", 3).Delete()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))

	qs = dORM.QueryTable("comment")
	num, err = qs.Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
}

func TestTransaction(t *testing.T) {
	// this test worked when database support transaction

	o := NewOrm()
	to, err := o.Begin()
	throwFail(t, err)

	names := []string{"1", "2", "3"}

	var tag Tag
	tag.Name = names[0]
	id, err := to.Insert(&tag)
	throwFail(t, err)
	throwFail(t, AssertIs(id > 0, true))

	num, err := to.QueryTable("tag").Filter("name", "golang").Update(Params{"name": names[1]})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	if IsMysql || IsSqlite {
		res, err := to.Raw("INSERT INTO tag (name) VALUES (?)", names[2]).Exec()
		throwFail(t, err)
		if err == nil {
			id, err = res.LastInsertId()
			throwFail(t, err)
			throwFail(t, AssertIs(id > 0, true))
		}
	}

	err = to.Rollback()
	assert.Nil(t, err)
	num, err = o.QueryTable("tag").Filter("name__in", names).Count()
	assert.Nil(t, err)
	assert.Equal(t, int64(0), num)

	to, err = o.Begin()
	assert.Nil(t, err)

	tag.Name = "commit"
	id, err = to.Insert(&tag)
	assert.Nil(t, err)
	assert.True(t, id > 0)

	err = to.Commit()
	assert.Nil(t, err)

	num, err = o.QueryTable("tag").Filter("name", "commit").Delete()
	assert.Nil(t, err)
	assert.Equal(t, int64(1), num)
}

func TestTxOrmRollbackUnlessCommit(t *testing.T) {
	o := NewOrm()
	var tag Tag

	// test not committed and call RollbackUnlessCommit
	to, err := o.Begin()
	assert.Nil(t, err)
	tag.Name = "rollback unless commit"
	rows, err := to.Insert(&tag)
	assert.Nil(t, err)
	assert.True(t, rows > 0)
	err = to.RollbackUnlessCommit()
	assert.Nil(t, err)
	num, err := o.QueryTable("tag").Filter("name", tag.Name).Delete()
	assert.Nil(t, err)
	assert.Equal(t, int64(0), num)

	// test commit and call RollbackUnlessCommit

	to, err = o.Begin()
	assert.Nil(t, err)
	tag.Name = "rollback unless commit"
	rows, err = to.Insert(&tag)
	assert.Nil(t, err)
	assert.True(t, rows > 0)

	err = to.Commit()
	assert.Nil(t, err)

	err = to.RollbackUnlessCommit()
	assert.Nil(t, err)

	num, err = o.QueryTable("tag").Filter("name", tag.Name).Delete()
	assert.Nil(t, err)
	assert.Equal(t, int64(1), num)
}

func TestTransactionIsolationLevel(t *testing.T) {
	// this test worked when database support transaction isolation level
	if IsSqlite {
		return
	}

	o1 := NewOrm()
	o2 := NewOrm()

	// start two transaction with isolation level repeatable read
	to1, err := o1.BeginWithCtxAndOpts(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
	throwFail(t, err)
	to2, err := o2.BeginWithCtxAndOpts(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
	throwFail(t, err)

	// o1 insert tag
	var tag Tag
	tag.Name = "test-transaction"
	id, err := to1.Insert(&tag)
	throwFail(t, err)
	throwFail(t, AssertIs(id > 0, true))

	// o2 query tag table, no result
	num, err := to2.QueryTable("tag").Filter("name", "test-transaction").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 0))

	// o1 commit
	to1.Commit()

	// o2 query tag table, still no result
	num, err = to2.QueryTable("tag").Filter("name", "test-transaction").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 0))

	// o2 commit and query tag table, Get the result
	to2.Commit()
	num, err = o2.QueryTable("tag").Filter("name", "test-transaction").Count()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))

	num, err = o1.QueryTable("tag").Filter("name", "test-transaction").Delete()
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
}

func TestBeginTxWithContextCanceled(t *testing.T) {
	o := NewOrm()
	ctx, cancel := context.WithCancel(context.Background())
	to, _ := o.BeginWithCtx(ctx)
	id, err := to.Insert(&Tag{Name: "test-context"})
	throwFail(t, err)
	throwFail(t, AssertIs(id > 0, true))

	// cancel the context before commit to make it error
	cancel()
	err = to.Commit()
	throwFail(t, AssertIs(err, context.Canceled))
}

func TestReadOrCreate(t *testing.T) {
	u := &User{
		UserName: "Kyle",
		Email:    "kylemcc@gmail.com",
		Password: "other_pass",
		Status:   7,
		IsStaff:  false,
		IsActive: true,
	}

	created, pk, err := dORM.ReadOrCreate(u, "UserName")
	throwFail(t, err)
	throwFail(t, AssertIs(created, true))
	throwFail(t, AssertIs(u.ID, pk))
	throwFail(t, AssertIs(u.UserName, "Kyle"))
	throwFail(t, AssertIs(u.Email, "kylemcc@gmail.com"))
	throwFail(t, AssertIs(u.Password, "other_pass"))
	throwFail(t, AssertIs(u.Status, 7))
	throwFail(t, AssertIs(u.IsStaff, false))
	throwFail(t, AssertIs(u.IsActive, true))
	throwFail(t, AssertIs(u.Created.In(DefaultTimeLoc), u.Created.In(DefaultTimeLoc), testDate))
	throwFail(t, AssertIs(u.Updated.In(DefaultTimeLoc), u.Updated.In(DefaultTimeLoc), testDateTime))

	nu := &User{UserName: u.UserName, Email: "someotheremail@gmail.com"}
	created, pk, err = dORM.ReadOrCreate(nu, "UserName")
	throwFail(t, err)
	throwFail(t, AssertIs(created, false))
	throwFail(t, AssertIs(nu.ID, u.ID))
	throwFail(t, AssertIs(pk, u.ID))
	throwFail(t, AssertIs(nu.UserName, u.UserName))
	throwFail(t, AssertIs(nu.Email, u.Email)) // should contain the value in the table, not the one specified above
	throwFail(t, AssertIs(nu.Password, u.Password))
	throwFail(t, AssertIs(nu.Status, u.Status))
	throwFail(t, AssertIs(nu.IsStaff, u.IsStaff))
	throwFail(t, AssertIs(nu.IsActive, u.IsActive))
	assert.True(t, u.ID > 0)

	dORM.Delete(u)
	assert.True(t, u.ID > 0)
}

func TestInLine(t *testing.T) {
	name := "inline"
	email := "hello@go.com"
	inline := NewInLine()
	inline.Name = name
	inline.Email = email

	id, err := dORM.Insert(inline)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 1))

	il := NewInLine()
	il.ID = 1
	err = dORM.Read(il)
	throwFail(t, err)

	throwFail(t, AssertIs(il.Name, name))
	throwFail(t, AssertIs(il.Email, email))
	assert.True(t, il.Created.In(DefaultTimeLoc).Sub(inline.Created.In(DefaultTimeLoc)) <= time.Second)
	assert.True(t, il.Updated.In(DefaultTimeLoc).Sub(inline.Updated.In(DefaultTimeLoc)) <= time.Second)
}

func TestInLineOneToOne(t *testing.T) {
	name := "121"
	email := "121@go.com"
	inline := NewInLine()
	inline.Name = name
	inline.Email = email

	id, err := dORM.Insert(inline)
	throwFail(t, err)
	throwFail(t, AssertIs(id, 2))

	note := "one2one"
	il121 := NewInLineOneToOne()
	il121.Note = note
	il121.InLine = inline
	_, err = dORM.Insert(il121)
	throwFail(t, err)
	throwFail(t, AssertIs(il121.ID, 1))

	il := NewInLineOneToOne()
	err = dORM.QueryTable(il).Filter("Id", 1).RelatedSel().One(il)

	throwFail(t, err)
	throwFail(t, AssertIs(il.Note, note))
	throwFail(t, AssertIs(il.InLine.ID, id))
	throwFail(t, AssertIs(il.InLine.Name, name))
	throwFail(t, AssertIs(il.InLine.Email, email))

	rinline := NewInLine()
	err = dORM.QueryTable(rinline).Filter("InLineOneToOne__Id", 1).One(rinline)

	throwFail(t, err)
	throwFail(t, AssertIs(rinline.ID, id))
	throwFail(t, AssertIs(rinline.Name, name))
	throwFail(t, AssertIs(rinline.Email, email))
}

func TestIntegerPk(t *testing.T) {
	its := []IntegerPk{
		{ID: math.MinInt64, Value: "-"},
		{ID: 0, Value: "0"},
		{ID: math.MaxInt64, Value: "+"},
	}

	num, err := dORM.InsertMulti(len(its), its)
	throwFail(t, err)
	throwFail(t, AssertIs(num, len(its)))

	for _, intPk := range its {
		out := IntegerPk{ID: intPk.ID}
		err = dORM.Read(&out)
		throwFail(t, err)
		throwFail(t, AssertIs(out.Value, intPk.Value))
	}

	num, err = dORM.InsertMulti(1, []*IntegerPk{{
		ID: 1, Value: "ok",
	}})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
}

func TestInsertAuto(t *testing.T) {
	u := &User{
		UserName: "autoPre",
		Email:    "autoPre@gmail.com",
	}

	id, err := dORM.Insert(u)
	throwFail(t, err)

	id += 100
	su := &User{
		ID:       int(id),
		UserName: "auto",
		Email:    "auto@gmail.com",
	}

	nid, err := dORM.Insert(su)
	throwFail(t, err)
	throwFail(t, AssertIs(nid, id))

	users := []User{
		{ID: int(id + 100), UserName: "auto_100"},
		{ID: int(id + 110), UserName: "auto_110"},
		{ID: int(id + 120), UserName: "auto_120"},
	}
	num, err := dORM.InsertMulti(100, users)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 3))

	u = &User{
		UserName: "auto_121",
	}

	nid, err = dORM.Insert(u)
	throwFail(t, err)
	throwFail(t, AssertIs(nid, id+120+1))
}

func TestUintPk(t *testing.T) {
	name := "go"
	u := &UintPk{
		ID:   8,
		Name: name,
	}

	created, _, err := dORM.ReadOrCreate(u, "ID")
	throwFail(t, err)
	throwFail(t, AssertIs(created, true))
	throwFail(t, AssertIs(u.Name, name))

	nu := &UintPk{ID: 8}
	created, pk, err := dORM.ReadOrCreate(nu, "ID")
	throwFail(t, err)
	throwFail(t, AssertIs(created, false))
	throwFail(t, AssertIs(nu.ID, u.ID))
	throwFail(t, AssertIs(pk, u.ID))
	throwFail(t, AssertIs(nu.Name, name))

	dORM.Delete(u)
}

func TestPtrPk(t *testing.T) {
	parent := &IntegerPk{ID: 10, Value: "10"}

	id, _ := dORM.Insert(parent)
	if !IsMysql {
		// MySql does not support last_insert_id in this case: see #2382
		throwFail(t, AssertIs(id, 10))
	}

	ptr := PtrPk{ID: parent, Positive: true}
	num, err := dORM.InsertMulti(2, []PtrPk{ptr})
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
	throwFail(t, AssertIs(ptr.ID, parent))

	nptr := &PtrPk{ID: parent}
	created, pk, err := dORM.ReadOrCreate(nptr, "ID")
	throwFail(t, err)
	throwFail(t, AssertIs(created, false))
	throwFail(t, AssertIs(pk, 10))
	throwFail(t, AssertIs(nptr.ID, parent))
	throwFail(t, AssertIs(nptr.Positive, true))

	nptr = &PtrPk{Positive: true}
	created, pk, err = dORM.ReadOrCreate(nptr, "Positive")
	throwFail(t, err)
	throwFail(t, AssertIs(created, false))
	throwFail(t, AssertIs(pk, 10))
	throwFail(t, AssertIs(nptr.ID, parent))

	nptr.Positive = false
	num, err = dORM.Update(nptr)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
	throwFail(t, AssertIs(nptr.ID, parent))
	throwFail(t, AssertIs(nptr.Positive, false))

	num, err = dORM.Delete(nptr)
	throwFail(t, err)
	throwFail(t, AssertIs(num, 1))
}

func TestSnake(t *testing.T) {
	cases := map[string]string{
		"i":           "i",
		"I":           "i",
		"iD":          "i_d",
		"ID":          "i_d",
		"NO":          "n_o",
		"NOO":         "n_o_o",
		"NOOooOOoo":   "n_o_ooo_o_ooo",
		"OrderNO":     "order_n_o",
		"tagName":     "tag_name",
		"tag_Name":    "tag__name",
		"tag_name":    "tag_name",
		"_tag_name":   "_tag_name",
		"tag_666name": "tag_666name",
		"tag_666Name": "tag_666_name",
	}
	for name, want := range cases {
		got := models.SnakeString(name)
		throwFail(t, AssertIs(got, want))
	}
}

func TestIgnoreCaseTag(t *testing.T) {
	type testTagModel struct {
		ID     int    `orm:"pk"`
		NOO    string `orm:"column(n)"`
		Name01 string `orm:"NULL"`
		Name02 string `orm:"COLUMN(Name)"`
		Name03 string `orm:"Column(name)"`
	}
	defaultModelCache.Clean()
	RegisterModel(&testTagModel{})
	info, ok := defaultModelCache.Get("test_tag_model")
	throwFail(t, AssertIs(ok, true))
	throwFail(t, AssertNot(info, nil))
	if t == nil {
		return
	}
	throwFail(t, AssertIs(info.Fields.GetByName("NOO").Column, "n"))
	throwFail(t, AssertIs(info.Fields.GetByName("Name01").Null, true))
	throwFail(t, AssertIs(info.Fields.GetByName("Name02").Column, "Name"))
	throwFail(t, AssertIs(info.Fields.GetByName("Name03").Column, "name"))
}

func TestInsertOrUpdate(t *testing.T) {
	RegisterModel(new(User))
	userName := "unique_username133"
	column := "user_name"
	user := User{UserName: userName, Status: 1, Password: "o"}
	user1 := User{UserName: userName, Status: 2, Password: "o"}
	user2 := User{UserName: userName, Status: 3, Password: "oo"}
	dORM.Insert(&user)
	fmt.Println(dORM.Driver().Name())
	if dORM.Driver().Name() == "sqlite3" {
		fmt.Println("sqlite3 is nonsupport")
		return
	}

	specs := []struct {
		description          string
		user                 User
		colConflitAndArgs    []string
		assertion            func(expected User, actual User)
		isPostgresCompatible bool
	}{
		{
			description:       "test1",
			user:              user1,
			colConflitAndArgs: []string{column},
			assertion: func(expected, actual User) {
				throwFailNow(t, AssertIs(expected.Status, actual.Status))
			},
			isPostgresCompatible: true,
		},
		{
			description:       "test2",
			user:              user2,
			colConflitAndArgs: []string{column},
			assertion: func(expected, actual User) {
				throwFailNow(t, AssertIs(expected.Status, actual.Status))
				throwFailNow(t, AssertIs(expected.Password, strings.TrimSpace(actual.Password)))
			},
			isPostgresCompatible: true,
		},
		{
			description:       "test3 +",
			user:              user2,
			colConflitAndArgs: []string{column, "status=status+1"},
			assertion: func(expected, actual User) {
				throwFailNow(t, AssertIs(expected.Status+1, actual.Status))
			},
			isPostgresCompatible: false,
		},
		{
			description:       "test4 -",
			user:              user2,
			colConflitAndArgs: []string{column, "status=status-1"},
			assertion: func(expected, actual User) {
				throwFailNow(t, AssertIs((expected.Status+1)-1, actual.Status))
			},
			isPostgresCompatible: false,
		},
		{
			description:       "test5 *",
			user:              user2,
			colConflitAndArgs: []string{column, "status=status*3"},
			assertion: func(expected, actual User) {
				throwFailNow(t, AssertIs(((expected.Status+1)-1)*3, actual.Status))
			},
			isPostgresCompatible: false,
		},
		{
			description:       "test6 /",
			user:              user2,
			colConflitAndArgs: []string{column, "Status=Status/3"},
			assertion: func(expected, actual User) {
				throwFailNow(t, AssertIs((((expected.Status+1)-1)*3)/3, actual.Status))
			},
			isPostgresCompatible: false,
		},
	}

	for _, spec := range specs {
		// postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values
		if IsPostgres && !spec.isPostgresCompatible {
			continue
		}

		_, err := dORM.InsertOrUpdate(&spec.user, spec.colConflitAndArgs...)
		if err != nil {
			fmt.Println(err)
			if !(err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego") {
				throwFailNow(t, err)
			}
			continue
		}

		test := User{UserName: userName}
		err = dORM.Read(&test, column)
		throwFailNow(t, AssertIs(err, nil))
		spec.assertion(spec.user, test)
	}
}

func TestStrPkInsert(t *testing.T) {
	RegisterModel(new(StrPk))
	pk := `1`
	value := `StrPkValues(*56`
	strPk := &StrPk{
		Id:    pk,
		Value: value,
	}

	var err error
	_, err = dORM.Insert(strPk)
	if err != ErrLastInsertIdUnavailable {
		throwFailNow(t, AssertIs(err, nil))
	}

	var vForTesting StrPk
	err = dORM.QueryTable(new(StrPk)).Filter(`id`, pk).One(&vForTesting)
	throwFailNow(t, AssertIs(err, nil))
	throwFailNow(t, AssertIs(vForTesting.Value, value))

	value2 := `s8s5da7as`
	strPkForUpsert := &StrPk{
		Id:    pk,
		Value: value2,
	}

	_, err = dORM.InsertOrUpdate(strPkForUpsert, `id`)
	if err != nil {
		fmt.Println(err)
		if err.Error() == "postgres version must 9.5 or higher" || err.Error() == "`sqlite3` nonsupport InsertOrUpdate in beego" {
			return
		} else if err == ErrLastInsertIdUnavailable {
			return
		} else {
			throwFailNow(t, err)
		}
	} else {
		var vForTesting2 StrPk
		err = dORM.QueryTable(new(StrPk)).Filter(`id`, pk).One(&vForTesting2)
		throwFailNow(t, AssertIs(err, nil))
		throwFailNow(t, AssertIs(vForTesting2.Value, value2))
	}
}

func TestPSQueryBuilder(t *testing.T) {
	// only test postgres
	if dORM.Driver().Type() != 4 {
		return
	}

	var user User
	var l []userProfile
	o := NewOrm()

	qb, err := NewQueryBuilder("postgres")
	if err != nil {
		throwFailNow(t, err)
	}
	qb.Select("user.id", "user.user_name").
		From("user").Where("id = ?").OrderBy("user_name").
		Desc().Limit(1).Offset(0)
	sql := qb.String()
	err = o.Raw(sql, 2).QueryRow(&user)
	if err != nil {
		throwFailNow(t, err)
	}
	throwFail(t, AssertIs(user.UserName, "slene"))

	qb.Select("*").
		From("user_profile").InnerJoin("user").
		On("user_profile.id = user.id")
	sql = qb.String()
	num, err := o.Raw(sql).QueryRows(&l)
	if err != nil {
		throwFailNow(t, err)
	}
	throwFailNow(t, AssertIs(num, 1))
	throwFailNow(t, AssertIs(l[0].UserName, "astaxie"))
	throwFailNow(t, AssertIs(l[0].Age, 30))
}

func TestCondition(t *testing.T) {
	// test Condition whether to include yourself
	cond := NewCondition()
	cond = cond.AndCond(cond.Or("ID", 1))
	cond = cond.AndCond(cond.Or("ID", 2))
	cond = cond.AndCond(cond.Or("ID", 3))
	cond = cond.AndCond(cond.Or("ID", 4))

	cycleFlag := false
	var hasCycle func(*Condition)
	hasCycle = func(c *Condition) {
		if nil == c || cycleFlag {
			return
		}
		condPointMap := make(map[string]bool)
		condPointMap[fmt.Sprintf("%p", c)] = true
		for _, p := range c.params {
			if p.isCond {
				adr := fmt.Sprintf("%p", p.cond)
				if condPointMap[adr] {
					// self as sub cond was cycle
					cycleFlag = true
					break
				}
				condPointMap[adr] = true

			}
		}
		if cycleFlag {
			return
		}
		for _, p := range c.params {
			if p.isCond {
				// check next cond
				hasCycle(p.cond)
			}
		}
	}
	hasCycle(cond)
	// cycleFlag was true,meaning use self as sub cond
	throwFail(t, AssertIs(!cycleFlag, true))
}

func TestContextCanceled(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())

	user := User{UserName: "slene"}

	err := dORM.ReadWithCtx(ctx, &user, "UserName")
	throwFail(t, err)

	cancel()
	err = dORM.ReadWithCtx(ctx, &user, "UserName")
	throwFail(t, AssertIs(err, context.Canceled))

	ctx, cancel = context.WithCancel(context.Background())
	cancel()

	qs := dORM.QueryTable(user)
	_, err = qs.Filter("UserName", "slene").CountWithCtx(ctx)
	throwFail(t, AssertIs(err, context.Canceled))
}

func TestDebugLog(t *testing.T) {
	txCommitFn := func() {
		o := NewOrm()
		o.DoTx(func(ctx context.Context, txOrm TxOrmer) (txerr error) {
			_, txerr = txOrm.QueryTable(&User{}).Count()
			return
		})
	}

	txRollbackFn := func() {
		o := NewOrm()
		o.DoTx(func(ctx context.Context, txOrm TxOrmer) (txerr error) {
			user := NewUser()
			user.UserName = "slene"
			user.Email = "vslene@gmail.com"
			user.Password = "pass"
			user.Status = 3
			user.IsStaff = true
			user.IsActive = true

			txOrm.Insert(user)
			txerr = fmt.Errorf("mock error")
			return
		})
	}

	Debug = true
	output1 := captureDebugLogOutput(txCommitFn)

	assert.Contains(t, output1, "START TRANSACTION")
	assert.Contains(t, output1, "COMMIT")

	output2 := captureDebugLogOutput(txRollbackFn)

	assert.Contains(t, output2, "START TRANSACTION")
	assert.Contains(t, output2, "ROLLBACK")

	Debug = false
	output1 = captureDebugLogOutput(txCommitFn)
	assert.EqualValues(t, output1, "")

	output2 = captureDebugLogOutput(txRollbackFn)
	assert.EqualValues(t, output2, "")
}

func captureDebugLogOutput(f func()) string {
	var buf bytes.Buffer
	DebugLog.SetOutput(&buf)
	defer func() {
		DebugLog.SetOutput(os.Stderr)
	}()
	f()
	return buf.String()
}
