2016-11-04 01:16:01 +03:00
// Copyright 2015 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xorm
import (
"bufio"
"bytes"
"database/sql"
"encoding/gob"
"errors"
"fmt"
"io"
"os"
"reflect"
"strconv"
"strings"
"sync"
"time"
"github.com/go-xorm/core"
)
// Engine is the major struct of xorm, it means a database manager.
// Commonly, an application only need one engine
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 // The timezone of the database
disableGlobalCache bool
}
2017-01-23 12:11:18 +03:00
// ShowSQL show SQL statement or not on logger if log level is great than INFO
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) ShowSQL ( show ... bool ) {
engine . logger . ShowSQL ( show ... )
if len ( show ) == 0 {
engine . showSQL = true
} else {
engine . showSQL = show [ 0 ]
}
}
2017-01-23 12:11:18 +03:00
// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) ShowExecTime ( show ... bool ) {
if len ( show ) == 0 {
engine . showExecTime = true
} else {
engine . showExecTime = show [ 0 ]
}
}
// Logger return the logger interface
func ( engine * Engine ) Logger ( ) core . ILogger {
return engine . logger
}
// SetLogger set the new logger
func ( engine * Engine ) SetLogger ( logger core . ILogger ) {
engine . logger = logger
engine . dialect . SetLogger ( logger )
}
// SetDisableGlobalCache disable global cache or not
func ( engine * Engine ) SetDisableGlobalCache ( disable bool ) {
if engine . disableGlobalCache != disable {
engine . disableGlobalCache = disable
}
}
// DriverName return the current sql driver's name
func ( engine * Engine ) DriverName ( ) string {
return engine . dialect . DriverName ( )
}
// DataSourceName return the current connection string
func ( engine * Engine ) DataSourceName ( ) string {
return engine . dialect . DataSourceName ( )
}
// SetMapper set the name mapping rules
func ( engine * Engine ) SetMapper ( mapper core . IMapper ) {
engine . SetTableMapper ( mapper )
engine . SetColumnMapper ( mapper )
}
// SetTableMapper set the table name mapping rule
func ( engine * Engine ) SetTableMapper ( mapper core . IMapper ) {
engine . TableMapper = mapper
}
// SetColumnMapper set the column name mapping rule
func ( engine * Engine ) SetColumnMapper ( mapper core . IMapper ) {
engine . ColumnMapper = mapper
}
// SupportInsertMany If engine's database support batch insert records like
// "insert into user values (name, age), (name, age)".
// When the return is ture, then engine.Insert(&users) will
// generate batch sql and exeute.
func ( engine * Engine ) SupportInsertMany ( ) bool {
return engine . dialect . SupportInsertMany ( )
}
2017-01-23 12:11:18 +03:00
// QuoteStr Engine's database use which character as quote.
2016-11-04 01:16:01 +03:00
// mysql, sqlite use ` and postgres use "
func ( engine * Engine ) QuoteStr ( ) string {
return engine . dialect . QuoteStr ( )
}
// Quote Use QuoteStr quote the string sql
2016-11-11 19:40:21 +03:00
func ( engine * Engine ) Quote ( value string ) string {
value = strings . TrimSpace ( value )
if len ( value ) == 0 {
return value
}
2016-11-04 01:16:01 +03:00
2016-11-11 19:40:21 +03:00
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 ( )
2016-11-04 01:16:01 +03:00
}
2016-11-11 19:40:21 +03:00
// QuoteTo quotes string and writes into the buffer
func ( engine * Engine ) QuoteTo ( buf * bytes . Buffer , value string ) {
if buf == nil {
return
}
value = strings . TrimSpace ( value )
if value == "" {
return
2016-11-04 01:16:01 +03:00
}
2016-11-11 19:40:21 +03:00
if string ( value [ 0 ] ) == engine . dialect . QuoteStr ( ) || value [ 0 ] == '`' {
buf . WriteString ( value )
return
2016-11-04 01:16:01 +03:00
}
2016-11-11 19:40:21 +03:00
value = strings . Replace ( value , "." , engine . dialect . QuoteStr ( ) + "." + engine . dialect . QuoteStr ( ) , - 1 )
2016-11-04 01:16:01 +03:00
2016-11-11 19:40:21 +03:00
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 ( )
2016-11-04 01:16:01 +03:00
}
// SqlType will be depracated, please use SQLType instead
2017-01-03 11:20:28 +03:00
//
// Deprecated: use SQLType instead
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) SqlType ( c * core . Column ) string {
2017-01-03 11:20:28 +03:00
return engine . SQLType ( c )
2016-11-04 01:16:01 +03:00
}
// SQLType A simple wrapper to dialect's core.SqlType method
func ( engine * Engine ) SQLType ( c * core . Column ) string {
return engine . dialect . SqlType ( c )
}
// AutoIncrStr Database's autoincrement statement
func ( engine * Engine ) AutoIncrStr ( ) string {
return engine . dialect . AutoIncrStr ( )
}
// SetMaxOpenConns is only available for go 1.2+
func ( engine * Engine ) SetMaxOpenConns ( conns int ) {
engine . db . SetMaxOpenConns ( conns )
}
// SetMaxIdleConns set the max idle connections on pool, default is 2
func ( engine * Engine ) SetMaxIdleConns ( conns int ) {
engine . db . SetMaxIdleConns ( conns )
}
// SetDefaultCacher set the default cacher. Xorm's default not enable cacher.
func ( engine * Engine ) SetDefaultCacher ( cacher core . Cacher ) {
engine . Cacher = cacher
}
// NoCache If you has set default cacher, and you want temporilly stop use cache,
// you can use NoCache()
func ( engine * Engine ) NoCache ( ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . NoCache ( )
}
// NoCascade If you do not want to auto cascade load object
func ( engine * Engine ) NoCascade ( ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . NoCascade ( )
}
// MapCacher Set a table use a special cacher
func ( engine * Engine ) MapCacher ( bean interface { } , cacher core . Cacher ) {
v := rValue ( bean )
tb := engine . autoMapType ( v )
tb . Cacher = cacher
}
// NewDB provides an interface to operate database directly
func ( engine * Engine ) NewDB ( ) ( * core . DB , error ) {
return core . OpenDialect ( engine . dialect )
}
// DB return the wrapper of sql.DB
func ( engine * Engine ) DB ( ) * core . DB {
return engine . db
}
// Dialect return database dialect
func ( engine * Engine ) Dialect ( ) core . Dialect {
return engine . dialect
}
// NewSession New a session
func ( engine * Engine ) NewSession ( ) * Session {
session := & Session { Engine : engine }
session . Init ( )
return session
}
// Close the engine
func ( engine * Engine ) Close ( ) error {
return engine . db . Close ( )
}
// Ping tests if database is alive
func ( engine * Engine ) Ping ( ) error {
session := engine . NewSession ( )
defer session . Close ( )
engine . logger . Infof ( "PING DATABASE %v" , engine . DriverName ( ) )
return session . Ping ( )
}
// logging sql
func ( engine * Engine ) logSQL ( sqlStr string , sqlArgs ... interface { } ) {
if engine . showSQL && ! engine . showExecTime {
if len ( sqlArgs ) > 0 {
engine . logger . Infof ( "[sql] %v [args] %v" , sqlStr , sqlArgs )
} else {
engine . logger . Infof ( "[sql] %v" , sqlStr )
}
}
}
func ( engine * Engine ) logSQLQueryTime ( sqlStr string , args [ ] interface { } , executionBlock func ( ) ( * core . Stmt , * core . Rows , error ) ) ( * core . Stmt , * core . Rows , error ) {
if engine . showSQL && engine . showExecTime {
b4ExecTime := time . Now ( )
stmt , res , err := executionBlock ( )
execDuration := time . Since ( b4ExecTime )
if len ( args ) > 0 {
engine . logger . Infof ( "[sql] %s [args] %v - took: %v" , sqlStr , args , execDuration )
} else {
engine . logger . Infof ( "[sql] %s - took: %v" , sqlStr , execDuration )
}
return stmt , res , err
}
return executionBlock ( )
}
func ( engine * Engine ) logSQLExecutionTime ( sqlStr string , args [ ] interface { } , executionBlock func ( ) ( sql . Result , error ) ) ( sql . Result , error ) {
if engine . showSQL && engine . showExecTime {
b4ExecTime := time . Now ( )
res , err := executionBlock ( )
execDuration := time . Since ( b4ExecTime )
if len ( args ) > 0 {
engine . logger . Infof ( "[sql] %s [args] %v - took: %v" , sqlStr , args , execDuration )
} else {
engine . logger . Infof ( "[sql] %s - took: %v" , sqlStr , execDuration )
}
return res , err
}
return executionBlock ( )
}
2017-01-03 11:20:28 +03:00
// Sql provides raw sql input parameter. When you have a complex SQL statement
// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL.
//
// Deprecated: use SQL instead.
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) Sql ( querystring string , args ... interface { } ) * Session {
return engine . SQL ( querystring , args ... )
}
2017-01-23 12:11:18 +03:00
// SQL method let's you manually write raw SQL and operate
2016-11-04 01:16:01 +03:00
// For example:
//
// engine.SQL("select * from user").Find(&users)
//
// This code will execute "select * from user" and set the records to users
func ( engine * Engine ) SQL ( query interface { } , args ... interface { } ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . SQL ( query , args ... )
}
// NoAutoTime Default if your struct has "created" or "updated" filed tag, the fields
// will automatically be filled with current time when Insert or Update
// invoked. Call NoAutoTime if you dont' want to fill automatically.
func ( engine * Engine ) NoAutoTime ( ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . NoAutoTime ( )
}
// NoAutoCondition disable auto generate Where condition from bean or not
func ( engine * Engine ) NoAutoCondition ( no ... bool ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . NoAutoCondition ( no ... )
}
// DBMetas Retrieve all tables, columns, indexes' informations from database.
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 {
2017-01-23 14:11:57 +03:00
return nil , fmt . Errorf ( "Unknown col %s in index %v of table %v, columns %v" , name , index . Name , table . Name , table . ColumnsSeq ( ) )
2016-11-04 01:16:01 +03:00
}
}
}
}
return tables , nil
}
// DumpAllToFile dump database all table structs and data to a file
2017-01-23 12:11:18 +03:00
func ( engine * Engine ) DumpAllToFile ( fp string , tp ... core . DbType ) error {
2016-11-04 01:16:01 +03:00
f , err := os . Create ( fp )
if err != nil {
return err
}
defer f . Close ( )
2017-01-23 12:11:18 +03:00
return engine . DumpAll ( f , tp ... )
2016-11-04 01:16:01 +03:00
}
// DumpAll dump database all table structs and data to w
2017-01-23 12:11:18 +03:00
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 ... )
2016-11-04 01:16:01 +03:00
}
// DumpTablesToFile dump specified tables to SQL file.
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 ... )
}
// DumpTables dump specify tables to io.Writer
func ( engine * Engine ) DumpTables ( tables [ ] * core . Table , w io . Writer , tp ... core . DbType ) error {
return engine . dumpTables ( tables , w , tp ... )
}
2017-01-23 12:11:18 +03:00
// dumpTables dump database all table structs and data to w with specify db type
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) dumpTables ( tables [ ] * core . Table , w io . Writer , tp ... core . DbType ) error {
var dialect core . Dialect
2017-01-03 11:20:28 +03:00
var distDBName string
2016-11-04 01:16:01 +03:00
if len ( tp ) == 0 {
dialect = engine . dialect
2017-01-03 11:20:28 +03:00
distDBName = string ( engine . dialect . DBType ( ) )
2016-11-04 01:16:01 +03:00
} else {
dialect = core . QueryDialect ( tp [ 0 ] )
if dialect == nil {
2017-01-03 11:20:28 +03:00
return errors . New ( "Unsupported database type" )
2016-11-04 01:16:01 +03:00
}
dialect . Init ( nil , engine . dialect . URI ( ) , "" , "" )
2017-01-03 11:20:28 +03:00
distDBName = string ( tp [ 0 ] )
2016-11-04 01:16:01 +03:00
}
_ , err := io . WriteString ( w , fmt . Sprintf ( "/*Generated by xorm v%s %s, from %s to %s*/\n\n" ,
2017-01-03 11:20:28 +03:00
Version , time . Now ( ) . In ( engine . TZLocation ) . Format ( "2006-01-02 15:04:05" ) , engine . dialect . DBType ( ) , strings . ToUpper ( distDBName ) ) )
2016-11-04 01:16:01 +03:00
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
}
}
2017-01-23 12:11:18 +03:00
cols := table . ColumnsSeq ( )
colNames := dialect . Quote ( strings . Join ( cols , dialect . Quote ( ", " ) ) )
rows , err := engine . DB ( ) . Query ( "SELECT " + colNames + " FROM " + engine . Quote ( table . Name ) )
2016-11-04 01:16:01 +03:00
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
}
2017-01-23 12:11:18 +03:00
_ , err = io . WriteString ( w , "INSERT INTO " + dialect . Quote ( table . Name ) + " (" + colNames + ") VALUES (" )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
var temp string
for i , d := range dest {
col := table . GetColumn ( cols [ i ] )
2017-01-23 12:11:18 +03:00
if col == nil {
return errors . New ( "unknow column error" )
}
2016-11-04 01:16:01 +03:00
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 ) ) )
2017-01-23 12:11:18 +03:00
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 )
}
2016-11-04 01:16:01 +03:00
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
}
}
}
return nil
}
2017-01-23 12:11:18 +03:00
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 ( ) )
}
2016-11-04 01:16:01 +03:00
// Cascade use cascade or not
func ( engine * Engine ) Cascade ( trueOrFalse ... bool ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Cascade ( trueOrFalse ... )
}
// Where method provide a condition query
func ( engine * Engine ) Where ( query interface { } , args ... interface { } ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Where ( query , args ... )
}
// Id will be depracated, please use ID instead
func ( engine * Engine ) Id ( id interface { } ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Id ( id )
}
2017-01-25 17:54:52 +03:00
// ID method provoide a condition as (id) = ?
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) ID ( id interface { } ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . ID ( id )
}
// Before apply before Processor, affected bean is passed to closure arg
func ( engine * Engine ) Before ( closures func ( interface { } ) ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Before ( closures )
}
// After apply after insert Processor, affected bean is passed to closure arg
func ( engine * Engine ) After ( closures func ( interface { } ) ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . After ( closures )
}
// Charset set charset when create table, only support mysql now
func ( engine * Engine ) Charset ( charset string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Charset ( charset )
}
// StoreEngine set store engine when create table, only support mysql now
func ( engine * Engine ) StoreEngine ( storeEngine string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . StoreEngine ( storeEngine )
}
// Distinct use for distinct columns. Caution: when you are using cache,
// distinct will not be cached because cache system need id,
// but distinct will not provide id
func ( engine * Engine ) Distinct ( columns ... string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Distinct ( columns ... )
}
// Select customerize your select columns or contents
func ( engine * Engine ) Select ( str string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Select ( str )
}
2017-01-23 12:11:18 +03:00
// Cols only use the parameters as select or update columns
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) Cols ( columns ... string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Cols ( columns ... )
}
// AllCols indicates that all columns should be use
func ( engine * Engine ) AllCols ( ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . AllCols ( )
}
// MustCols specify some columns must use even if they are empty
func ( engine * Engine ) MustCols ( columns ... string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . MustCols ( columns ... )
}
// UseBool xorm automatically retrieve condition according struct, but
// if struct has bool field, it will ignore them. So use UseBool
// to tell system to do not ignore them.
2017-01-23 12:11:18 +03:00
// If no parameters, it will use all the bool field of struct, or
// it will use parameters's columns
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) UseBool ( columns ... string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . UseBool ( columns ... )
}
2017-01-23 12:11:18 +03:00
// Omit only not use the parameters as select or update columns
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) Omit ( columns ... string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Omit ( columns ... )
}
// Nullable set null when column is zero-value and nullable for update
func ( engine * Engine ) Nullable ( columns ... string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Nullable ( columns ... )
}
// In will generate "column IN (?, ?)"
func ( engine * Engine ) In ( column string , args ... interface { } ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . In ( column , args ... )
}
// Incr provides a update string like "column = column + ?"
func ( engine * Engine ) Incr ( column string , arg ... interface { } ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Incr ( column , arg ... )
}
// Decr provides a update string like "column = column - ?"
func ( engine * Engine ) Decr ( column string , arg ... interface { } ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Decr ( column , arg ... )
}
// SetExpr provides a update string like "column = {expression}"
func ( engine * Engine ) SetExpr ( column string , expression string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . SetExpr ( column , expression )
}
// Table temporarily change the Get, Find, Update's table
func ( engine * Engine ) Table ( tableNameOrBean interface { } ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Table ( tableNameOrBean )
}
// Alias set the table alias
func ( engine * Engine ) Alias ( alias string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Alias ( alias )
}
// Limit will generate "LIMIT start, limit"
func ( engine * Engine ) Limit ( limit int , start ... int ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Limit ( limit , start ... )
}
// Desc will generate "ORDER BY column1 DESC, column2 DESC"
func ( engine * Engine ) Desc ( colNames ... string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Desc ( colNames ... )
}
// Asc will generate "ORDER BY column1,column2 Asc"
// This method can chainable use.
//
// engine.Desc("name").Asc("age").Find(&users)
// // SELECT * FROM user ORDER BY name DESC, age ASC
//
func ( engine * Engine ) Asc ( colNames ... string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Asc ( colNames ... )
}
// OrderBy will generate "ORDER BY order"
func ( engine * Engine ) OrderBy ( order string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . OrderBy ( order )
}
// Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
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 ... )
}
// GroupBy generate group by statement
func ( engine * Engine ) GroupBy ( keys string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . GroupBy ( keys )
}
// Having generate having statement
func ( engine * Engine ) Having ( conditions string ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Having ( conditions )
}
func ( engine * Engine ) autoMapType ( v reflect . Value ) * core . Table {
t := v . Type ( )
engine . mutex . Lock ( )
defer engine . mutex . Unlock ( )
table , ok := engine . Tables [ t ]
if ! ok {
table = engine . mapType ( v )
engine . Tables [ t ] = table
if engine . Cacher != nil {
if v . CanAddr ( ) {
engine . GobRegister ( v . Addr ( ) . Interface ( ) )
} else {
engine . GobRegister ( v . Interface ( ) )
}
}
}
return table
}
// GobRegister register one struct to gob for cache use
func ( engine * Engine ) GobRegister ( v interface { } ) * Engine {
//fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v)
gob . Register ( v )
return engine
}
// Table table struct
type Table struct {
* core . Table
Name string
}
// TableInfo get table info according to bean's content
func ( engine * Engine ) TableInfo ( bean interface { } ) * Table {
v := rValue ( bean )
return & Table { engine . autoMapType ( v ) , engine . tbName ( v ) }
}
func addIndex ( indexName string , table * core . Table , col * core . Column , indexType int ) {
if index , ok := table . Indexes [ indexName ] ; ok {
index . AddColumn ( col . Name )
col . Indexes [ index . Name ] = indexType
} else {
index := core . NewIndex ( indexName , indexType )
index . AddColumn ( col . Name )
table . AddIndex ( index )
col . Indexes [ index . Name ] = indexType
}
}
func ( engine * Engine ) newTable ( ) * core . Table {
table := core . NewEmptyTable ( )
if ! engine . disableGlobalCache {
table . Cacher = engine . Cacher
}
return table
}
// TableName table name interface to define customerize table name
type TableName interface {
TableName ( ) string
}
var (
tpTableName = reflect . TypeOf ( ( * TableName ) ( nil ) ) . Elem ( )
)
func ( engine * Engine ) mapType ( v reflect . Value ) * core . Table {
t := v . Type ( )
table := engine . newTable ( )
if tb , ok := v . Interface ( ) . ( TableName ) ; ok {
table . Name = tb . TableName ( )
} else {
if v . CanAddr ( ) {
if tb , ok = v . Addr ( ) . Interface ( ) . ( TableName ) ; ok {
table . Name = tb . TableName ( )
}
}
if table . Name == "" {
table . Name = engine . TableMapper . Obj2Table ( t . Name ( ) )
}
}
table . Type = t
var idFieldColName string
var err error
var hasCacheTag , hasNoCacheTag bool
for i := 0 ; i < t . NumField ( ) ; i ++ {
tag := t . Field ( i ) . Tag
ormTagStr := tag . Get ( engine . TagIdentifier )
var col * core . Column
fieldValue := v . Field ( i )
fieldType := fieldValue . Type ( )
if ormTagStr != "" {
col = & core . Column { FieldName : t . Field ( i ) . Name , Nullable : true , IsPrimaryKey : false ,
IsAutoIncrement : false , MapType : core . TWOSIDES , Indexes : make ( map [ string ] int ) }
tags := splitTag ( ormTagStr )
if len ( tags ) > 0 {
if tags [ 0 ] == "-" {
continue
}
if strings . ToUpper ( tags [ 0 ] ) == "EXTENDS" {
switch fieldValue . Kind ( ) {
case reflect . Ptr :
f := fieldValue . Type ( ) . Elem ( )
if f . Kind ( ) == reflect . Struct {
fieldPtr := fieldValue
fieldValue = fieldValue . Elem ( )
if ! fieldValue . IsValid ( ) || fieldPtr . IsNil ( ) {
fieldValue = reflect . New ( f ) . Elem ( )
}
}
fallthrough
case reflect . Struct :
parentTable := engine . mapType ( fieldValue )
for _ , col := range parentTable . Columns ( ) {
col . FieldName = fmt . Sprintf ( "%v.%v" , t . Field ( i ) . Name , col . FieldName )
table . AddColumn ( col )
for indexName , indexType := range col . Indexes {
addIndex ( indexName , table , col , indexType )
}
}
continue
default :
//TODO: warning
}
}
indexNames := make ( map [ string ] int )
var isIndex , isUnique bool
var preKey string
for j , key := range tags {
k := strings . ToUpper ( key )
switch {
case k == "<-" :
col . MapType = core . ONLYFROMDB
case k == "->" :
col . MapType = core . ONLYTODB
case k == "PK" :
col . IsPrimaryKey = true
col . Nullable = false
case k == "NULL" :
if j == 0 {
col . Nullable = true
} else {
col . Nullable = ( strings . ToUpper ( tags [ j - 1 ] ) != "NOT" )
}
// TODO: for postgres how add autoincr?
/ * case strings . HasPrefix ( k , "AUTOINCR(" ) && strings . HasSuffix ( k , ")" ) :
col . IsAutoIncrement = true
autoStart := k [ len ( "AUTOINCR" ) + 1 : len ( k ) - 1 ]
autoStartInt , err := strconv . Atoi ( autoStart )
if err != nil {
engine . LogError ( err )
}
col . AutoIncrStart = autoStartInt * /
case k == "AUTOINCR" :
col . IsAutoIncrement = true
//col.AutoIncrStart = 1
case k == "DEFAULT" :
col . Default = tags [ j + 1 ]
case k == "CREATED" :
col . IsCreated = true
case k == "VERSION" :
col . IsVersion = true
col . Default = "1"
case k == "UTC" :
col . TimeZone = time . UTC
case k == "LOCAL" :
col . TimeZone = time . Local
case strings . HasPrefix ( k , "LOCALE(" ) && strings . HasSuffix ( k , ")" ) :
location := k [ len ( "LOCALE" ) + 1 : len ( k ) - 1 ]
col . TimeZone , err = time . LoadLocation ( location )
if err != nil {
engine . logger . Error ( err )
}
case k == "UPDATED" :
col . IsUpdated = true
case k == "DELETED" :
col . IsDeleted = true
case strings . HasPrefix ( k , "INDEX(" ) && strings . HasSuffix ( k , ")" ) :
indexName := k [ len ( "INDEX" ) + 1 : len ( k ) - 1 ]
indexNames [ indexName ] = core . IndexType
case k == "INDEX" :
isIndex = true
case strings . HasPrefix ( k , "UNIQUE(" ) && strings . HasSuffix ( k , ")" ) :
indexName := k [ len ( "UNIQUE" ) + 1 : len ( k ) - 1 ]
indexNames [ indexName ] = core . UniqueType
case k == "UNIQUE" :
isUnique = true
case k == "NOTNULL" :
col . Nullable = false
case k == "CACHE" :
if ! hasCacheTag {
hasCacheTag = true
}
case k == "NOCACHE" :
if ! hasNoCacheTag {
hasNoCacheTag = true
}
case k == "NOT" :
default :
if strings . HasPrefix ( k , "'" ) && strings . HasSuffix ( k , "'" ) {
if preKey != "DEFAULT" {
col . Name = key [ 1 : len ( key ) - 1 ]
}
} else if strings . Contains ( k , "(" ) && strings . HasSuffix ( k , ")" ) {
fs := strings . Split ( k , "(" )
if _ , ok := core . SqlTypes [ fs [ 0 ] ] ; ! ok {
preKey = k
continue
}
col . SQLType = core . SQLType { Name : fs [ 0 ] }
if fs [ 0 ] == core . Enum && fs [ 1 ] [ 0 ] == '\'' { //enum
options := strings . Split ( fs [ 1 ] [ 0 : len ( fs [ 1 ] ) - 1 ] , "," )
col . EnumOptions = make ( map [ string ] int )
for k , v := range options {
v = strings . TrimSpace ( v )
v = strings . Trim ( v , "'" )
col . EnumOptions [ v ] = k
}
} else if fs [ 0 ] == core . Set && fs [ 1 ] [ 0 ] == '\'' { //set
options := strings . Split ( fs [ 1 ] [ 0 : len ( fs [ 1 ] ) - 1 ] , "," )
col . SetOptions = make ( map [ string ] int )
for k , v := range options {
v = strings . TrimSpace ( v )
v = strings . Trim ( v , "'" )
col . SetOptions [ v ] = k
}
} else {
fs2 := strings . Split ( fs [ 1 ] [ 0 : len ( fs [ 1 ] ) - 1 ] , "," )
if len ( fs2 ) == 2 {
col . Length , err = strconv . Atoi ( fs2 [ 0 ] )
if err != nil {
engine . logger . Error ( err )
}
col . Length2 , err = strconv . Atoi ( fs2 [ 1 ] )
if err != nil {
engine . logger . Error ( err )
}
} else if len ( fs2 ) == 1 {
col . Length , err = strconv . Atoi ( fs2 [ 0 ] )
if err != nil {
engine . logger . Error ( err )
}
}
}
} else {
if _ , ok := core . SqlTypes [ k ] ; ok {
col . SQLType = core . SQLType { Name : k }
} else if key != col . Default {
col . Name = key
}
}
engine . dialect . SqlType ( col )
}
preKey = k
}
if col . SQLType . Name == "" {
col . SQLType = core . Type2SQLType ( fieldType )
}
if col . Length == 0 {
col . Length = col . SQLType . DefaultLength
}
if col . Length2 == 0 {
col . Length2 = col . SQLType . DefaultLength2
}
if col . Name == "" {
col . Name = engine . ColumnMapper . Obj2Table ( t . Field ( i ) . Name )
}
if isUnique {
indexNames [ col . Name ] = core . UniqueType
} else if isIndex {
indexNames [ col . Name ] = core . IndexType
}
for indexName , indexType := range indexNames {
addIndex ( indexName , table , col , indexType )
}
}
} else {
var sqlType core . SQLType
if fieldValue . CanAddr ( ) {
if _ , ok := fieldValue . Addr ( ) . Interface ( ) . ( core . Conversion ) ; ok {
sqlType = core . SQLType { Name : core . Text }
}
}
if _ , ok := fieldValue . Interface ( ) . ( core . Conversion ) ; ok {
sqlType = core . SQLType { Name : core . Text }
} else {
sqlType = core . Type2SQLType ( fieldType )
}
col = core . NewColumn ( engine . ColumnMapper . Obj2Table ( t . Field ( i ) . Name ) ,
t . Field ( i ) . Name , sqlType , sqlType . DefaultLength ,
sqlType . DefaultLength2 , true )
}
if col . IsAutoIncrement {
col . Nullable = false
}
table . AddColumn ( col )
if fieldType . Kind ( ) == reflect . Int64 && ( strings . ToUpper ( col . FieldName ) == "ID" || strings . HasSuffix ( strings . ToUpper ( col . FieldName ) , ".ID" ) ) {
idFieldColName = col . Name
}
} // end for
if idFieldColName != "" && len ( table . PrimaryKeys ) == 0 {
col := table . GetColumn ( idFieldColName )
col . IsPrimaryKey = true
col . IsAutoIncrement = true
col . Nullable = false
table . PrimaryKeys = append ( table . PrimaryKeys , col . Name )
table . AutoIncrement = col . Name
}
if hasCacheTag {
if engine . Cacher != nil { // !nash! use engine's cacher if provided
engine . logger . Info ( "enable cache on table:" , table . Name )
table . Cacher = engine . Cacher
} else {
engine . logger . Info ( "enable LRU cache on table:" , table . Name )
table . Cacher = NewLRUCacher2 ( NewMemoryStore ( ) , time . Hour , 10000 ) // !nashtsai! HACK use LRU cacher for now
}
}
if hasNoCacheTag {
engine . logger . Info ( "no cache on table:" , table . Name )
table . Cacher = nil
}
return table
}
// IsTableEmpty if a table has any reocrd
func ( engine * Engine ) IsTableEmpty ( bean interface { } ) ( bool , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . IsTableEmpty ( bean )
}
// IsTableExist if a table is exist
func ( engine * Engine ) IsTableExist ( beanOrTableName interface { } ) ( bool , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . IsTableExist ( beanOrTableName )
}
// IdOf get id from one struct
2017-01-03 11:20:28 +03:00
//
// Deprecated: use IDOf instead.
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) IdOf ( bean interface { } ) core . PK {
2017-01-03 11:20:28 +03:00
return engine . IDOf ( bean )
}
// IDOf get id from one struct
func ( engine * Engine ) IDOf ( bean interface { } ) core . PK {
2016-11-04 01:16:01 +03:00
return engine . IdOfV ( reflect . ValueOf ( bean ) )
}
// IdOfV get id from one value of struct
2017-01-03 11:20:28 +03:00
//
// Deprecated: use IDOfV instead.
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) IdOfV ( rv reflect . Value ) core . PK {
2017-01-03 11:20:28 +03:00
return engine . IDOfV ( rv )
}
// IDOfV get id from one value of struct
func ( engine * Engine ) IDOfV ( rv reflect . Value ) core . PK {
2016-11-04 01:16:01 +03:00
v := reflect . Indirect ( rv )
table := engine . autoMapType ( v )
pk := make ( [ ] interface { } , len ( table . PrimaryKeys ) )
for i , col := range table . PKColumns ( ) {
pkField := v . FieldByName ( col . FieldName )
switch pkField . Kind ( ) {
case reflect . String :
pk [ i ] = pkField . String ( )
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
pk [ i ] = pkField . Int ( )
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 :
pk [ i ] = pkField . Uint ( )
}
}
return core . PK ( pk )
}
// CreateIndexes create indexes
func ( engine * Engine ) CreateIndexes ( bean interface { } ) error {
session := engine . NewSession ( )
defer session . Close ( )
return session . CreateIndexes ( bean )
}
// CreateUniques create uniques
func ( engine * Engine ) CreateUniques ( bean interface { } ) error {
session := engine . NewSession ( )
defer session . Close ( )
return session . CreateUniques ( bean )
}
func ( engine * Engine ) getCacher2 ( table * core . Table ) core . Cacher {
return table . Cacher
}
func ( engine * Engine ) getCacher ( v reflect . Value ) core . Cacher {
if table := engine . autoMapType ( v ) ; table != nil {
return table . Cacher
}
return engine . Cacher
}
// ClearCacheBean if enabled cache, clear the cache bean
func ( engine * Engine ) ClearCacheBean ( bean interface { } , id string ) error {
v := rValue ( bean )
t := v . Type ( )
if t . Kind ( ) != reflect . Struct {
return errors . New ( "error params" )
}
tableName := engine . tbName ( v )
table := engine . autoMapType ( v )
cacher := table . Cacher
if cacher == nil {
cacher = engine . Cacher
}
if cacher != nil {
cacher . ClearIds ( tableName )
cacher . DelBean ( tableName , id )
}
return nil
}
// ClearCache if enabled cache, clear some tables' cache
func ( engine * Engine ) ClearCache ( beans ... interface { } ) error {
for _ , bean := range beans {
v := rValue ( bean )
t := v . Type ( )
if t . Kind ( ) != reflect . Struct {
return errors . New ( "error params" )
}
tableName := engine . tbName ( v )
table := engine . autoMapType ( v )
cacher := table . Cacher
if cacher == nil {
cacher = engine . Cacher
}
if cacher != nil {
cacher . ClearIds ( tableName )
cacher . ClearBeans ( tableName )
}
}
return nil
}
// Sync the new struct changes to database, this method will automatically add
// table, column, index, unique. but will not delete or change anything.
// If you change some field, you should change the database manually.
func ( engine * Engine ) Sync ( beans ... interface { } ) error {
for _ , bean := range beans {
v := rValue ( bean )
tableName := engine . tbName ( v )
table := engine . autoMapType ( v )
s := engine . NewSession ( )
defer s . Close ( )
isExist , err := s . Table ( bean ) . isTableExist ( tableName )
if err != nil {
return err
}
if ! isExist {
err = engine . CreateTables ( bean )
if err != nil {
return err
}
}
/ * isEmpty , err := engine . IsEmptyTable ( bean )
if err != nil {
return err
} * /
var isEmpty bool
if isEmpty {
err = engine . DropTables ( bean )
if err != nil {
return err
}
err = engine . CreateTables ( bean )
if err != nil {
return err
}
} else {
for _ , col := range table . Columns ( ) {
isExist , err := engine . dialect . IsColumnExist ( tableName , col . Name )
if err != nil {
return err
}
if ! isExist {
session := engine . NewSession ( )
session . Statement . setRefValue ( v )
defer session . Close ( )
err = session . addColumn ( col . Name )
if err != nil {
return err
}
}
}
for name , index := range table . Indexes {
session := engine . NewSession ( )
session . Statement . setRefValue ( v )
defer session . Close ( )
if index . Type == core . UniqueType {
//isExist, err := session.isIndexExist(table.Name, name, true)
isExist , err := session . isIndexExist2 ( tableName , index . Cols , true )
if err != nil {
return err
}
if ! isExist {
session := engine . NewSession ( )
session . Statement . setRefValue ( v )
defer session . Close ( )
err = session . addUnique ( tableName , name )
if err != nil {
return err
}
}
} else if index . Type == core . IndexType {
isExist , err := session . isIndexExist2 ( tableName , index . Cols , false )
if err != nil {
return err
}
if ! isExist {
session := engine . NewSession ( )
session . Statement . setRefValue ( v )
defer session . Close ( )
err = session . addIndex ( tableName , name )
if err != nil {
return err
}
}
} else {
return errors . New ( "unknow index type" )
}
}
}
}
return nil
}
// Sync2 synchronize structs to database tables
func ( engine * Engine ) Sync2 ( beans ... interface { } ) error {
s := engine . NewSession ( )
defer s . Close ( )
return s . Sync2 ( beans ... )
}
func ( engine * Engine ) unMap ( beans ... interface { } ) ( e error ) {
engine . mutex . Lock ( )
defer engine . mutex . Unlock ( )
for _ , bean := range beans {
t := rType ( bean )
if _ , ok := engine . Tables [ t ] ; ok {
delete ( engine . Tables , t )
}
}
return
}
// Drop all mapped table
func ( engine * Engine ) dropAll ( ) error {
session := engine . NewSession ( )
defer session . Close ( )
err := session . Begin ( )
if err != nil {
return err
}
err = session . dropAll ( )
if err != nil {
session . Rollback ( )
return err
}
return session . Commit ( )
}
// CreateTables create tabls according bean
func ( engine * Engine ) CreateTables ( beans ... interface { } ) error {
session := engine . NewSession ( )
defer session . Close ( )
err := session . Begin ( )
if err != nil {
return err
}
for _ , bean := range beans {
err = session . CreateTable ( bean )
if err != nil {
session . Rollback ( )
return err
}
}
return session . Commit ( )
}
// DropTables drop specify tables
func ( engine * Engine ) DropTables ( beans ... interface { } ) error {
session := engine . NewSession ( )
defer session . Close ( )
err := session . Begin ( )
if err != nil {
return err
}
for _ , bean := range beans {
err = session . DropTable ( bean )
if err != nil {
session . Rollback ( )
return err
}
}
return session . Commit ( )
}
func ( engine * Engine ) createAll ( ) error {
session := engine . NewSession ( )
defer session . Close ( )
return session . createAll ( )
}
// Exec raw sql
func ( engine * Engine ) Exec ( sql string , args ... interface { } ) ( sql . Result , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Exec ( sql , args ... )
}
// Query a raw sql and return records as []map[string][]byte
func ( engine * Engine ) Query ( sql string , paramStr ... interface { } ) ( resultsSlice [ ] map [ string ] [ ] byte , err error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Query ( sql , paramStr ... )
}
// Insert one or more records
func ( engine * Engine ) Insert ( beans ... interface { } ) ( int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Insert ( beans ... )
}
// InsertOne insert only one record
func ( engine * Engine ) InsertOne ( bean interface { } ) ( int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . InsertOne ( bean )
}
// Update records, bean's non-empty fields are updated contents,
// condiBean' non-empty filds are conditions
// CAUTION:
// 1.bool will defaultly be updated content nor conditions
// You should call UseBool if you have bool to use.
// 2.float32 & float64 may be not inexact as conditions
func ( engine * Engine ) Update ( bean interface { } , condiBeans ... interface { } ) ( int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Update ( bean , condiBeans ... )
}
// Delete records, bean's non-empty fields are conditions
func ( engine * Engine ) Delete ( bean interface { } ) ( int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Delete ( bean )
}
// Get retrieve one record from table, bean's non-empty fields
// are conditions
func ( engine * Engine ) Get ( bean interface { } ) ( bool , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Get ( bean )
}
// Find retrieve records from table, condiBeans's non-empty fields
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct
func ( engine * Engine ) Find ( beans interface { } , condiBeans ... interface { } ) error {
session := engine . NewSession ( )
defer session . Close ( )
return session . Find ( beans , condiBeans ... )
}
// Iterate record by record handle records from table, bean's non-empty fields
// are conditions.
func ( engine * Engine ) Iterate ( bean interface { } , fun IterFunc ) error {
session := engine . NewSession ( )
defer session . Close ( )
return session . Iterate ( bean , fun )
}
// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields
// are conditions.
func ( engine * Engine ) Rows ( bean interface { } ) ( * Rows , error ) {
session := engine . NewSession ( )
return session . Rows ( bean )
}
// Count counts the records. bean's non-empty fields are conditions.
func ( engine * Engine ) Count ( bean interface { } ) ( int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Count ( bean )
}
// Sum sum the records by some column. bean's non-empty fields are conditions.
func ( engine * Engine ) Sum ( bean interface { } , colName string ) ( float64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Sum ( bean , colName )
}
// Sums sum the records by some columns. bean's non-empty fields are conditions.
func ( engine * Engine ) Sums ( bean interface { } , colNames ... string ) ( [ ] float64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Sums ( bean , colNames ... )
}
// SumsInt like Sums but return slice of int64 instead of float64.
func ( engine * Engine ) SumsInt ( bean interface { } , colNames ... string ) ( [ ] int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . SumsInt ( bean , colNames ... )
}
// ImportFile SQL DDL file
func ( engine * Engine ) ImportFile ( ddlPath string ) ( [ ] sql . Result , error ) {
file , err := os . Open ( ddlPath )
if err != nil {
return nil , err
}
defer file . Close ( )
return engine . Import ( file )
}
// Import SQL DDL from io.Reader
func ( engine * Engine ) Import ( r io . Reader ) ( [ ] sql . Result , error ) {
var results [ ] sql . Result
var lastError error
scanner := bufio . NewScanner ( r )
semiColSpliter := func ( data [ ] byte , atEOF bool ) ( advance int , token [ ] byte , err error ) {
if atEOF && len ( data ) == 0 {
return 0 , nil , nil
}
if i := bytes . IndexByte ( data , ';' ) ; i >= 0 {
return i + 1 , data [ 0 : i ] , nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len ( data ) , data , nil
}
// Request more data.
return 0 , nil , nil
}
scanner . Split ( semiColSpliter )
for scanner . Scan ( ) {
query := strings . Trim ( scanner . Text ( ) , " \t\n\r" )
if len ( query ) > 0 {
engine . logSQL ( query )
result , err := engine . DB ( ) . Exec ( query )
results = append ( results , result )
if err != nil {
return nil , err
//lastError = err
}
}
}
return results , lastError
}
// TZTime change one time to xorm time location
func ( engine * Engine ) TZTime ( t time . Time ) time . Time {
if ! t . IsZero ( ) { // if time is not initialized it's not suitable for Time.In()
return t . In ( engine . TZLocation )
}
return t
}
// NowTime return current time
func ( engine * Engine ) NowTime ( sqlTypeName string ) interface { } {
t := time . Now ( )
return engine . FormatTime ( sqlTypeName , t )
}
// NowTime2 return current time
func ( engine * Engine ) NowTime2 ( sqlTypeName string ) ( interface { } , time . Time ) {
t := time . Now ( )
return engine . FormatTime ( sqlTypeName , t ) , t
}
// FormatTime format time
func ( engine * Engine ) FormatTime ( sqlTypeName string , t time . Time ) ( v interface { } ) {
return engine . formatTime ( engine . TZLocation , sqlTypeName , t )
}
func ( engine * Engine ) formatColTime ( col * core . Column , t time . Time ) ( v interface { } ) {
if col . DisableTimeZone {
return engine . formatTime ( nil , col . SQLType . Name , t )
} else if col . TimeZone != nil {
return engine . formatTime ( col . TimeZone , col . SQLType . Name , t )
}
return engine . formatTime ( engine . TZLocation , col . SQLType . Name , t )
}
func ( engine * Engine ) formatTime ( tz * time . Location , sqlTypeName string , t time . Time ) ( v interface { } ) {
if engine . dialect . DBType ( ) == core . ORACLE {
return t
}
if tz != nil {
2017-01-25 17:54:52 +03:00
t = t . In ( tz )
} else {
2016-11-04 01:16:01 +03:00
t = engine . TZTime ( t )
}
switch sqlTypeName {
case core . Time :
s := t . Format ( "2006-01-02 15:04:05" ) //time.RFC3339
v = s [ 11 : 19 ]
case core . Date :
v = t . Format ( "2006-01-02" )
case core . DateTime , core . TimeStamp :
if engine . dialect . DBType ( ) == "ql" {
v = t
} else if engine . dialect . DBType ( ) == "sqlite3" {
v = t . UTC ( ) . Format ( "2006-01-02 15:04:05" )
} else {
v = t . Format ( "2006-01-02 15:04:05" )
}
case core . TimeStampz :
if engine . dialect . DBType ( ) == core . MSSQL {
v = t . Format ( "2006-01-02T15:04:05.9999999Z07:00" )
} else if engine . DriverName ( ) == "mssql" {
v = t
} else {
v = t . Format ( time . RFC3339Nano )
}
case core . BigInt , core . Int :
v = t . Unix ( )
default :
v = t
}
return
}
// Unscoped always disable struct tag "deleted"
func ( engine * Engine ) Unscoped ( ) * Session {
session := engine . NewSession ( )
session . IsAutoClose = true
return session . Unscoped ( )
}