func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Join(joinOperator, tablename, condition, args...)
}
Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
package xorm
import (
	"bufio"
	"bytes"
	"database/sql"
	"encoding/gob"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/go-xorm/builder"
	"github.com/go-xorm/core"
)
type Engine struct {
	db      *core.DB
	dialect core.Dialect
	ColumnMapper  core.IMapper
	TableMapper   core.IMapper
	TagIdentifier string
	Tables        map[reflect.Type]*core.Table
	mutex  *sync.RWMutex
	Cacher core.Cacher
	showSQL      bool
	showExecTime bool
	logger     core.ILogger
	TZLocation *time.Location  
	DatabaseTZ *time.Location  
	disableGlobalCache bool
	tagHandlers map[string]tagHandler
	engineGroup *EngineGroup
}
func (engine *Engine) BufferSize(size int) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.BufferSize(size)
}
func (engine *Engine) CondDeleted(colName string) builder.Cond {
	if engine.dialect.DBType() == core.MSSQL {
		return builder.IsNull{colName}
	}
	return builder.IsNull{colName}.Or(builder.Eq{colName: zeroTime1})
}
func (engine *Engine) ShowSQL(show ...bool) {
	engine.logger.ShowSQL(show...)
	if len(show) == 0 {
		engine.showSQL = true
	} else {
		engine.showSQL = show[0]
	}
}
func (engine *Engine) ShowExecTime(show ...bool) {
	if len(show) == 0 {
		engine.showExecTime = true
	} else {
		engine.showExecTime = show[0]
	}
}
func (engine *Engine) Logger() core.ILogger {
	return engine.logger
}
func (engine *Engine) SetLogger(logger core.ILogger) {
	engine.logger = logger
	engine.dialect.SetLogger(logger)
}
func (engine *Engine) SetLogLevel(level core.LogLevel) {
	engine.logger.SetLevel(level)
}
func (engine *Engine) SetDisableGlobalCache(disable bool) {
	if engine.disableGlobalCache != disable {
		engine.disableGlobalCache = disable
	}
}
func (engine *Engine) DriverName() string {
	return engine.dialect.DriverName()
}
func (engine *Engine) DataSourceName() string {
	return engine.dialect.DataSourceName()
}
func (engine *Engine) SetMapper(mapper core.IMapper) {
	engine.SetTableMapper(mapper)
	engine.SetColumnMapper(mapper)
}
func (engine *Engine) SetTableMapper(mapper core.IMapper) {
	engine.TableMapper = mapper
}
func (engine *Engine) SetColumnMapper(mapper core.IMapper) {
	engine.ColumnMapper = mapper
}
func (engine *Engine) SupportInsertMany() bool {
	return engine.dialect.SupportInsertMany()
}
func (engine *Engine) QuoteStr() string {
	return engine.dialect.QuoteStr()
}
func (engine *Engine) Quote(value string) string {
	value = strings.TrimSpace(value)
	if len(value) == 0 {
		return value
	}
	if string(value[0]) == engine.dialect.QuoteStr() || value[0] == '`' {
		return value
	}
	value = strings.Replace(value, ".", engine.dialect.QuoteStr()+"."+engine.dialect.QuoteStr(), -1)
	return engine.dialect.QuoteStr() + value + engine.dialect.QuoteStr()
}
func (engine *Engine) QuoteTo(buf *bytes.Buffer, value string) {
	if buf == nil {
		return
	}
	value = strings.TrimSpace(value)
	if value == "" {
		return
	}
	if string(value[0]) == engine.dialect.QuoteStr() || value[0] == '`' {
		buf.WriteString(value)
		return
	}
	value = strings.Replace(value, ".", engine.dialect.QuoteStr()+"."+engine.dialect.QuoteStr(), -1)
	buf.WriteString(engine.dialect.QuoteStr())
	buf.WriteString(value)
	buf.WriteString(engine.dialect.QuoteStr())
}
func (engine *Engine) quote(sql string) string {
	return engine.dialect.QuoteStr() + sql + engine.dialect.QuoteStr()
}
func (engine *Engine) SqlType(c *core.Column) string {
	return engine.SQLType(c)
}
func (engine *Engine) SQLType(c *core.Column) string {
	return engine.dialect.SqlType(c)
}
func (engine *Engine) AutoIncrStr() string {
	return engine.dialect.AutoIncrStr()
}
func (engine *Engine) SetMaxOpenConns(conns int) {
	engine.db.SetMaxOpenConns(conns)
}
func (engine *Engine) SetMaxIdleConns(conns int) {
	engine.db.SetMaxIdleConns(conns)
}
func (engine *Engine) SetDefaultCacher(cacher core.Cacher) {
	engine.Cacher = cacher
}
func (engine *Engine) GetDefaultCacher() core.Cacher {
	return engine.Cacher
}
func (engine *Engine) NoCache() *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.NoCache()
}
func (engine *Engine) NoCascade() *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.NoCascade()
}
func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) error {
	v := rValue(bean)
	tb, err := engine.autoMapType(v)
	if err != nil {
		return err
	}
	tb.Cacher = cacher
	return nil
}
func (engine *Engine) NewDB() (*core.DB, error) {
	return core.OpenDialect(engine.dialect)
}
func (engine *Engine) DB() *core.DB {
	return engine.db
}
func (engine *Engine) Dialect() core.Dialect {
	return engine.dialect
}
func (engine *Engine) NewSession() *Session {
	session := &Session{engine: engine}
	session.Init()
	return session
}
func (engine *Engine) Close() error {
	return engine.db.Close()
}
func (engine *Engine) Ping() error {
	session := engine.NewSession()
	defer session.Close()
	return session.Ping()
}
func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
	if engine.showSQL && !engine.showExecTime {
		if len(sqlArgs) > 0 {
			engine.logger.Infof("[SQL] %v %#v", sqlStr, sqlArgs)
		} else {
			engine.logger.Infof("[SQL] %v", sqlStr)
		}
	}
}
func (engine *Engine) Sql(querystring string, args ...interface{}) *Session {
	return engine.SQL(querystring, args...)
}
func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.SQL(query, args...)
}
func (engine *Engine) NoAutoTime() *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.NoAutoTime()
}
func (engine *Engine) NoAutoCondition(no ...bool) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.NoAutoCondition(no...)
}
func (engine *Engine) DBMetas() ([]*core.Table, error) {
	tables, err := engine.dialect.GetTables()
	if err != nil {
		return nil, err
	}
	for _, table := range tables {
		colSeq, cols, err := engine.dialect.GetColumns(table.Name)
		if err != nil {
			return nil, err
		}
		for _, name := range colSeq {
			table.AddColumn(cols[name])
		}
		indexes, err := engine.dialect.GetIndexes(table.Name)
		if err != nil {
			return nil, err
		}
		table.Indexes = indexes
		for _, index := range indexes {
			for _, name := range index.Cols {
				if col := table.GetColumn(name); col != nil {
					col.Indexes[index.Name] = index.Type
				} else {
					return nil, fmt.Errorf("Unknown col %s in index %v of table %v, columns %v", name, index.Name, table.Name, table.ColumnsSeq())
				}
			}
		}
	}
	return tables, nil
}
func (engine *Engine) DumpAllToFile(fp string, tp ...core.DbType) error {
	f, err := os.Create(fp)
	if err != nil {
		return err
	}
	defer f.Close()
	return engine.DumpAll(f, tp...)
}
func (engine *Engine) DumpAll(w io.Writer, tp ...core.DbType) error {
	tables, err := engine.DBMetas()
	if err != nil {
		return err
	}
	return engine.DumpTables(tables, w, tp...)
}
func (engine *Engine) DumpTablesToFile(tables []*core.Table, fp string, tp ...core.DbType) error {
	f, err := os.Create(fp)
	if err != nil {
		return err
	}
	defer f.Close()
	return engine.DumpTables(tables, f, tp...)
}
func (engine *Engine) DumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error {
	return engine.dumpTables(tables, w, tp...)
}
func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error {
	var dialect core.Dialect
	var distDBName string
	if len(tp) == 0 {
		dialect = engine.dialect
		distDBName = string(engine.dialect.DBType())
	} else {
		dialect = core.QueryDialect(tp[0])
		if dialect == nil {
			return errors.New("Unsupported database type")
		}
		dialect.Init(nil, engine.dialect.URI(), "", "")
		distDBName = string(tp[0])
	}
	_, err := io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s, from %s to %s*/\n\n",
		Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05"), engine.dialect.DBType(), strings.ToUpper(distDBName)))
	if err != nil {
		return err
	}
	for i, table := range tables {
		if i > 0 {
			_, err = io.WriteString(w, "\n")
			if err != nil {
				return err
			}
		}
		_, err = io.WriteString(w, dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n")
		if err != nil {
			return err
		}
		for _, index := range table.Indexes {
			_, err = io.WriteString(w, dialect.CreateIndexSql(table.Name, index)+";\n")
			if err != nil {
				return err
			}
		}
		cols := table.ColumnsSeq()
		colNames := dialect.Quote(strings.Join(cols, dialect.Quote(", ")))
		rows, err := engine.DB().Query("SELECT " + colNames + " FROM " + engine.Quote(table.Name))
		if err != nil {
			return err
		}
		defer rows.Close()
		for rows.Next() {
			dest := make([]interface{}, len(cols))
			err = rows.ScanSlice(&dest)
			if err != nil {
				return err
			}
			_, err = io.WriteString(w, "INSERT INTO "+dialect.Quote(table.Name)+" ("+colNames+") VALUES (")
			if err != nil {
				return err
			}
			var temp string
			for i, d := range dest {
				col := table.GetColumn(cols[i])
				if col == nil {
					return errors.New("unknow column error")
				}
				if d == nil {
					temp += ", NULL"
				} else if col.SQLType.IsText() || col.SQLType.IsTime() {
					var v = fmt.Sprintf("%s", d)
					if strings.HasSuffix(v, " +0000 UTC") {
						temp += fmt.Sprintf(", '%s'", v[0:len(v)-len(" +0000 UTC")])
					} else {
						temp += ", '" + strings.Replace(v, "'", "''", -1) + "'"
					}
				} else if col.SQLType.IsBlob() {
					if reflect.TypeOf(d).Kind() == reflect.Slice {
						temp += fmt.Sprintf(", %s", dialect.FormatBytes(d.([]byte)))
					} else if reflect.TypeOf(d).Kind() == reflect.String {
						temp += fmt.Sprintf(", '%s'", d.(string))
					}
				} else if col.SQLType.IsNumeric() {
					switch reflect.TypeOf(d).Kind() {
					case reflect.Slice:
						temp += fmt.Sprintf(", %s", string(d.([]byte)))
					case reflect.Int16, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int:
						if col.SQLType.Name == core.Bool {
							temp += fmt.Sprintf(", %v", strconv.FormatBool(reflect.ValueOf(d).Int() > 0))
						} else {
							temp += fmt.Sprintf(", %v", d)
						}
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						if col.SQLType.Name == core.Bool {
							temp += fmt.Sprintf(", %v", strconv.FormatBool(reflect.ValueOf(d).Uint() > 0))
						} else {
							temp += fmt.Sprintf(", %v", d)
						}
					default:
						temp += fmt.Sprintf(", %v", d)
					}
				} else {
					s := fmt.Sprintf("%v", d)
					if strings.Contains(s, ":") || strings.Contains(s, "-") {
						if strings.HasSuffix(s, " +0000 UTC") {
							temp += fmt.Sprintf(", '%s'", s[0:len(s)-len(" +0000 UTC")])
						} else {
							temp += fmt.Sprintf(", '%s'", s)
						}
					} else {
						temp += fmt.Sprintf(", %s", s)
					}
				}
			}
			_, err = io.WriteString(w, temp[2:]+");\n")
			if err != nil {
				return err
			}
		}
		if string(dialect.DBType()) == core.POSTGRES && table.AutoIncrColumn() != nil {
			_, err = io.WriteString(w, "SELECT setval('table_id_seq', COALESCE((SELECT MAX("+table.AutoIncrColumn().Name+") FROM "+dialect.Quote(table.Name)+"), 1), false);\n")
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func (engine *Engine) tableName(beanOrTableName interface{}) (string, error) {
	v := rValue(beanOrTableName)
	if v.Type().Kind() == reflect.String {
		return beanOrTableName.(string), nil
	} else if v.Type().Kind() == reflect.Struct {
		return engine.tbName(v), nil
	}
	return "", errors.New("bean should be a struct or struct's point")
}
func (engine *Engine) tbName(v reflect.Value) string {
	if tb, ok := v.Interface().(TableName); ok {
		return tb.TableName()
	}
	if v.Type().Kind() == reflect.Ptr {
		if tb, ok := reflect.Indirect(v).Interface().(TableName); ok {
			return tb.TableName()
		}
	} else if v.CanAddr() {
		if tb, ok := v.Addr().Interface().(TableName); ok {
			return tb.TableName()
		}
	}
	return engine.TableMapper.Obj2Table(reflect.Indirect(v).Type().Name())
}
func (engine *Engine) Cascade(trueOrFalse ...bool) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Cascade(trueOrFalse...)
}
func (engine *Engine) Where(query interface{}, args ...interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Where(query, args...)
}
func (engine *Engine) Id(id interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Id(id)
}
func (engine *Engine) ID(id interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.ID(id)
}
func (engine *Engine) Before(closures func(interface{})) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Before(closures)
}
func (engine *Engine) After(closures func(interface{})) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.After(closures)
}
func (engine *Engine) Charset(charset string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Charset(charset)
}
func (engine *Engine) StoreEngine(storeEngine string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.StoreEngine(storeEngine)
}
func (engine *Engine) Distinct(columns ...string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Distinct(columns...)
}
func (engine *Engine) Select(str string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Select(str)
}
func (engine *Engine) Cols(columns ...string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Cols(columns...)
}
func (engine *Engine) AllCols() *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.AllCols()
}
func (engine *Engine) MustCols(columns ...string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.MustCols(columns...)
}
func (engine *Engine) UseBool(columns ...string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.UseBool(columns...)
}
func (engine *Engine) Omit(columns ...string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Omit(columns...)
}
func (engine *Engine) Nullable(columns ...string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Nullable(columns...)
}
func (engine *Engine) In(column string, args ...interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.In(column, args...)
}
func (engine *Engine) NotIn(column string, args ...interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.NotIn(column, args...)
}
func (engine *Engine) Incr(column string, arg ...interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Incr(column, arg...)
}
func (engine *Engine) Decr(column string, arg ...interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Decr(column, arg...)
}
func (engine *Engine) SetExpr(column string, expression string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.SetExpr(column, expression)
}
func (engine *Engine) Table(tableNameOrBean interface{}) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Table(tableNameOrBean)
}
func (engine *Engine) Alias(alias string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Alias(alias)
}
func (engine *Engine) Limit(limit int, start ...int) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Limit(limit, start...)
}
func (engine *Engine) Desc(colNames ...string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Desc(colNames...)
}
func (engine *Engine) Asc(colNames ...string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Asc(colNames...)
}
func (engine *Engine) OrderBy(order string) *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.OrderBy(order)
}
func (engine *Engine) Prepare() *Session {
	session := engine.NewSession()
	session.isAutoClose = true
	return session.Prepare()
}