@ -19,6 +19,7 @@ import (
"sync"
"time"
"github.com/go-xorm/builder"
"github.com/go-xorm/core"
)
@ -40,7 +41,7 @@ type Engine struct {
showExecTime bool
logger core . ILogger
TZLocation * time . Location
TZLocation * time . Location // The timezone of the application
DatabaseTZ * time . Location // The timezone of the database
disableGlobalCache bool
@ -143,7 +144,6 @@ func (engine *Engine) Quote(value string) string {
// QuoteTo quotes string and writes into the buffer
func ( engine * Engine ) QuoteTo ( buf * bytes . Buffer , value string ) {
if buf == nil {
return
}
@ -169,7 +169,7 @@ func (engine *Engine) quote(sql string) string {
return engine . dialect . QuoteStr ( ) + sql + engine . dialect . QuoteStr ( )
}
// SqlType will be depra cated, please use SQLType instead
// SqlType will be depre cated, please use SQLType instead
//
// Deprecated: use SQLType instead
func ( engine * Engine ) SqlType ( c * core . Column ) string {
@ -205,14 +205,14 @@ func (engine *Engine) SetDefaultCacher(cacher core.Cacher) {
// you can use NoCache()
func ( engine * Engine ) NoCache ( ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . NoCache ( )
}
// NoCascade If you do not want to auto cascade load object
func ( engine * Engine ) NoCascade ( ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . NoCascade ( )
}
@ -245,7 +245,7 @@ func (engine *Engine) Dialect() core.Dialect {
// NewSession New a session
func ( engine * Engine ) NewSession ( ) * Session {
session := & Session { E ngine: engine }
session := & Session { e ngine: engine }
session . Init ( )
return session
}
@ -259,7 +259,6 @@ func (engine *Engine) Close() error {
func ( engine * Engine ) Ping ( ) error {
session := engine . NewSession ( )
defer session . Close ( )
engine . logger . Infof ( "PING DATABASE %v" , engine . DriverName ( ) )
return session . Ping ( )
}
@ -267,7 +266,7 @@ func (engine *Engine) Ping() error {
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 )
engine . logger . Infof ( "[SQL] %v %# v" , sqlStr , sqlArgs )
} else {
engine . logger . Infof ( "[SQL] %v" , sqlStr )
}
@ -320,7 +319,7 @@ func (engine *Engine) Sql(querystring string, args ...interface{}) *Session {
// 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 . I sAutoClose = true
session . i sAutoClose = true
return session . SQL ( query , args ... )
}
@ -329,14 +328,14 @@ func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session {
// invoked. Call NoAutoTime if you dont' want to fill automatically.
func ( engine * Engine ) NoAutoTime ( ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = true
return session . NoAutoCondition ( no ... )
}
@ -570,56 +569,56 @@ func (engine *Engine) tbName(v reflect.Value) string {
// Cascade use cascade or not
func ( engine * Engine ) Cascade ( trueOrFalse ... bool ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Cascade ( trueOrFalse ... )
}
// Where method provide a condition query
func ( engine * Engine ) Where ( query interface { } , args ... interface { } ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Where ( query , args ... )
}
// Id will be depra cated, please use ID instead
// Id will be depre cated, please use ID instead
func ( engine * Engine ) Id ( id interface { } ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Id ( id )
}
// ID method provoide a condition as (id) = ?
func ( engine * Engine ) ID ( id interface { } ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = true
return session . StoreEngine ( storeEngine )
}
@ -628,35 +627,35 @@ func (engine *Engine) StoreEngine(storeEngine string) *Session {
// but distinct will not provide id
func ( engine * Engine ) Distinct ( columns ... string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Distinct ( columns ... )
}
// Select customerize your select columns or contents
func ( engine * Engine ) Select ( str string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Select ( str )
}
// Cols only use the parameters as select or update columns
func ( engine * Engine ) Cols ( columns ... string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Cols ( columns ... )
}
// AllCols indicates that all columns should be use
func ( engine * Engine ) AllCols ( ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = true
return session . MustCols ( columns ... )
}
@ -667,77 +666,84 @@ func (engine *Engine) MustCols(columns ...string) *Session {
// it will use parameters's columns
func ( engine * Engine ) UseBool ( columns ... string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . UseBool ( columns ... )
}
// Omit only not use the parameters as select or update columns
func ( engine * Engine ) Omit ( columns ... string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = true
return session . Nullable ( columns ... )
}
// In will generate "column IN (?, ?)"
func ( engine * Engine ) In ( column string , args ... interface { } ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . In ( column , args ... )
}
// NotIn will generate "column NOT IN (?, ?)"
func ( engine * Engine ) NotIn ( column string , args ... interface { } ) * Session {
session := engine . NewSession ( )
session . isAutoClose = true
return session . NotIn ( 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
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = true
return session . Table ( tableNameOrBean )
}
// Alias set the table alias
func ( engine * Engine ) Alias ( alias string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Alias ( alias )
}
// Limit will generate "LIMIT start, limit"
func ( engine * Engine ) Limit ( limit int , start ... int ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = true
return session . Desc ( colNames ... )
}
@ -749,38 +755,44 @@ func (engine *Engine) Desc(colNames ...string) *Session {
//
func ( engine * Engine ) Asc ( colNames ... string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Asc ( colNames ... )
}
// OrderBy will generate "ORDER BY order"
func ( engine * Engine ) OrderBy ( order string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = 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 . I sAutoClose = true
session . i sAutoClose = true
return session . Join ( joinOperator , tablename , condition , args ... )
}
// GroupBy generate group by statement
func ( engine * Engine ) GroupBy ( keys string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . GroupBy ( keys )
}
// Having generate having statement
func ( engine * Engine ) Having ( conditions string ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Having ( conditions )
}
func ( engine * Engine ) unMapType ( t reflect . Type ) {
engine . mutex . Lock ( )
defer engine . mutex . Unlock ( )
delete ( engine . Tables , t )
}
func ( engine * Engine ) autoMapType ( v reflect . Value ) ( * core . Table , error ) {
t := v . Type ( )
engine . mutex . Lock ( )
@ -1007,6 +1019,10 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
col = core . NewColumn ( engine . ColumnMapper . Obj2Table ( t . Field ( i ) . Name ) ,
t . Field ( i ) . Name , sqlType , sqlType . DefaultLength ,
sqlType . DefaultLength2 , true )
if fieldType . Kind ( ) == reflect . Int64 && ( strings . ToUpper ( col . FieldName ) == "ID" || strings . HasSuffix ( strings . ToUpper ( col . FieldName ) , ".ID" ) ) {
idFieldColName = col . Name
}
}
if col . IsAutoIncrement {
col . Nullable = false
@ -1014,9 +1030,6 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
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 {
@ -1097,19 +1110,39 @@ func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) {
pk := make ( [ ] interface { } , len ( table . PrimaryKeys ) )
for i , col := range table . PKColumns ( ) {
var err error
pkField := v . FieldByName ( col . FieldName )
switch pkField . Kind ( ) {
case reflect . String :
pk [ i ] = pkField . String ( )
pk [ i ] , err = engine . idTypeAssertion ( col , pkField . String ( ) )
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
pk [ i ] = pkField . Int ( )
pk [ i ] , err = engine . idTypeAssertion ( col , strconv . FormatInt ( pkField . Int ( ) , 10 ) )
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 :
pk [ i ] = pkField . Uint ( )
// id of uint will be converted to int64
pk [ i ] , err = engine . idTypeAssertion ( col , strconv . FormatUint ( pkField . Uint ( ) , 10 ) )
}
if err != nil {
return nil , err
}
}
return core . PK ( pk ) , nil
}
func ( engine * Engine ) idTypeAssertion ( col * core . Column , sid string ) ( interface { } , error ) {
if col . SQLType . IsNumeric ( ) {
n , err := strconv . ParseInt ( sid , 10 , 64 )
if err != nil {
return nil , err
}
return n , nil
} else if col . SQLType . IsText ( ) {
return sid , nil
} else {
return nil , errors . New ( "not supported" )
}
}
// CreateIndexes create indexes
func ( engine * Engine ) CreateIndexes ( bean interface { } ) error {
session := engine . NewSession ( )
@ -1181,6 +1214,9 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
// 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 {
session := engine . NewSession ( )
defer session . Close ( )
for _ , bean := range beans {
v := rValue ( bean )
tableName := engine . tbName ( v )
@ -1189,14 +1225,12 @@ func (engine *Engine) Sync(beans ...interface{}) error {
return err
}
s := engine . NewSession ( )
defer s . Close ( )
isExist , err := s . Table ( bean ) . isTableExist ( tableName )
isExist , err := session . Table ( bean ) . isTableExist ( tableName )
if err != nil {
return err
}
if ! isExist {
err = engine . CreateTables ( bean )
err = session . createTable ( bean )
if err != nil {
return err
}
@ -1207,11 +1241,11 @@ func (engine *Engine) Sync(beans ...interface{}) error {
} * /
var isEmpty bool
if isEmpty {
err = engine . DropTables ( bean )
err = session . dropTable ( bean )
if err != nil {
return err
}
err = engine . CreateTables ( bean )
err = session . createTable ( bean )
if err != nil {
return err
}
@ -1222,9 +1256,7 @@ func (engine *Engine) Sync(beans ...interface{}) error {
return err
}
if ! isExist {
session := engine . NewSession ( )
defer session . Close ( )
if err := session . Statement . setRefValue ( v ) ; err != nil {
if err := session . statement . setRefValue ( v ) ; err != nil {
return err
}
err = session . addColumn ( col . Name )
@ -1235,21 +1267,16 @@ func (engine *Engine) Sync(beans ...interface{}) error {
}
for name , index := range table . Indexes {
session := engine . NewSession ( )
defer session . Close ( )
if err := session . Statement . setRefValue ( v ) ; err != nil {
if err := session . statement . setRefValue ( v ) ; err != nil {
return err
}
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 ( )
defer session . Close ( )
if err := session . Statement . setRefValue ( v ) ; err != nil {
if err := session . statement . setRefValue ( v ) ; err != nil {
return err
}
@ -1264,9 +1291,7 @@ func (engine *Engine) Sync(beans ...interface{}) error {
return err
}
if ! isExist {
session := engine . NewSession ( )
defer session . Close ( )
if err := session . Statement . setRefValue ( v ) ; err != nil {
if err := session . statement . setRefValue ( v ) ; err != nil {
return err
}
@ -1291,23 +1316,6 @@ func (engine *Engine) Sync2(beans ...interface{}) error {
return s . Sync2 ( beans ... )
}
// 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 ( )
@ -1319,7 +1327,7 @@ func (engine *Engine) CreateTables(beans ...interface{}) error {
}
for _ , bean := range beans {
err = session . C reateTable( bean )
err = session . c reateTable( bean )
if err != nil {
session . Rollback ( )
return err
@ -1339,7 +1347,7 @@ func (engine *Engine) DropTables(beans ...interface{}) error {
}
for _ , bean := range beans {
err = session . D ropTable( bean )
err = session . d ropTable( bean )
if err != nil {
session . Rollback ( )
return err
@ -1348,10 +1356,11 @@ func (engine *Engine) DropTables(beans ...interface{}) error {
return session . Commit ( )
}
func ( engine * Engine ) createAll ( ) error {
// DropIndexes drop indexes of a table
func ( engine * Engine ) DropIndexes ( bean interface { } ) error {
session := engine . NewSession ( )
defer session . Close ( )
return session . createAll ( )
return session . DropIndexes ( bean )
}
// Exec raw sql
@ -1416,6 +1425,13 @@ func (engine *Engine) Get(bean interface{}) (bool, error) {
return session . Get ( bean )
}
// Exist returns true if the record exist otherwise return false
func ( engine * Engine ) Exist ( bean ... interface { } ) ( bool , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Exist ( bean ... )
}
// Find retrieve records from table, condiBeans's non-empty fields
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct
@ -1441,10 +1457,10 @@ func (engine *Engine) Rows(bean interface{}) (*Rows, error) {
}
// Count counts the records. bean's non-empty fields are conditions.
func ( engine * Engine ) Count ( bean interface { } ) ( int64 , error ) {
func ( engine * Engine ) Count ( bean ... interface { } ) ( int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . Count ( bean )
return session . Count ( bean ... )
}
// Sum sum the records by some column. bean's non-empty fields are conditions.
@ -1454,6 +1470,13 @@ func (engine *Engine) Sum(bean interface{}, colName string) (float64, error) {
return session . Sum ( bean , colName )
}
// SumInt sum the records by some column. bean's non-empty fields are conditions.
func ( engine * Engine ) SumInt ( bean interface { } , colName string ) ( int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . SumInt ( 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 ( )
@ -1509,7 +1532,6 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
results = append ( results , result )
if err != nil {
return nil , err
//lastError = err
}
}
}
@ -1517,49 +1539,28 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
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 )
return engine . formatTime ( sqlTypeName , t . In ( engine . DatabaseTZ ) ) , t . In ( engine . TZLocation )
}
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 )
if t . IsZero ( ) {
if col . Nullable {
return nil
}
return ""
}
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 {
t = t . In ( tz )
} else {
t = engine . TZTime ( t )
if col . TimeZone != nil {
return engine . formatTime ( col . SQLType . Name , t . In ( col . TimeZone ) )
}
return engine . formatTime ( col . SQLType . Name , t . In ( engine . DatabaseTZ ) )
}
// formatTime format time as column type
func ( engine * Engine ) formatTime ( sqlTypeName string , t time . Time ) ( v interface { } ) {
switch sqlTypeName {
case core . Time :
s := t . Format ( "2006-01-02 15:04:05" ) //time.RFC3339
@ -1567,18 +1568,10 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T
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" )
}
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 )
}
@ -1593,6 +1586,14 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T
// Unscoped always disable struct tag "deleted"
func ( engine * Engine ) Unscoped ( ) * Session {
session := engine . NewSession ( )
session . I sAutoClose = true
session . i sAutoClose = true
return session . Unscoped ( )
}
// CondDeleted returns the conditions whether a record is soft deleted.
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 } )
}