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"
2017-08-22 14:39:52 +03:00
"github.com/go-xorm/builder"
2016-11-04 01:16:01 +03:00
"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
2017-08-22 14:39:52 +03:00
TZLocation * time . Location // The timezone of the application
2016-11-04 01:16:01 +03:00
DatabaseTZ * time . Location // The timezone of the database
disableGlobalCache bool
2017-04-07 04:47:25 +03:00
tagHandlers map [ string ] tagHandler
2018-01-27 18:20:59 +03:00
engineGroup * EngineGroup
2018-07-20 05:10:17 +03:00
cachers map [ string ] core . Cacher
cacherLock sync . RWMutex
}
func ( engine * Engine ) setCacher ( tableName string , cacher core . Cacher ) {
engine . cacherLock . Lock ( )
engine . cachers [ tableName ] = cacher
engine . cacherLock . Unlock ( )
}
func ( engine * Engine ) SetCacher ( tableName string , cacher core . Cacher ) {
engine . setCacher ( tableName , cacher )
}
func ( engine * Engine ) getCacher ( tableName string ) core . Cacher {
var cacher core . Cacher
var ok bool
engine . cacherLock . RLock ( )
cacher , ok = engine . cachers [ tableName ]
engine . cacherLock . RUnlock ( )
if ! ok && ! engine . disableGlobalCache {
cacher = engine . Cacher
}
return cacher
}
func ( engine * Engine ) GetCacher ( tableName string ) core . Cacher {
return engine . getCacher ( tableName )
2018-01-27 18:20:59 +03:00
}
// BufferSize sets buffer size for iterate
func ( engine * Engine ) BufferSize ( size int ) * Session {
session := engine . NewSession ( )
session . isAutoClose = true
return session . BufferSize ( size )
}
// 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 } )
2016-11-04 01:16:01 +03:00
}
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 )
}
2018-01-27 18:20:59 +03:00
// SetLogLevel sets the logger level
func ( engine * Engine ) SetLogLevel ( level core . LogLevel ) {
engine . logger . SetLevel ( level )
}
2016-11-04 01:16:01 +03:00
// 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 ( )
}
2018-12-12 04:01:41 +03:00
func ( engine * Engine ) quoteColumns ( columnStr string ) string {
columns := strings . Split ( columnStr , "," )
for i := 0 ; i < len ( columns ) ; i ++ {
columns [ i ] = engine . Quote ( strings . TrimSpace ( columns [ i ] ) )
}
return strings . Join ( columns , "," )
}
2016-11-04 01:16:01 +03:00
// 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
2018-07-20 05:10:17 +03:00
func ( engine * Engine ) QuoteTo ( buf * builder . StringBuilder , value string ) {
2016-11-11 19:40:21 +03:00
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
}
2017-08-22 14:39:52 +03:00
// SqlType will be deprecated, 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 ( )
}
2018-12-12 04:01:41 +03:00
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
func ( engine * Engine ) SetConnMaxLifetime ( d time . Duration ) {
engine . db . SetConnMaxLifetime ( d )
}
2016-11-04 01:16:01 +03:00
// 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
}
2018-01-27 18:20:59 +03:00
// GetDefaultCacher returns the default cacher
func ( engine * Engine ) GetDefaultCacher ( ) core . Cacher {
return engine . Cacher
}
2016-11-04 01:16:01 +03:00
// 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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . NoCache ( )
}
// NoCascade If you do not want to auto cascade load object
func ( engine * Engine ) NoCascade ( ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . NoCascade ( )
}
// MapCacher Set a table use a special cacher
2017-04-07 04:47:25 +03:00
func ( engine * Engine ) MapCacher ( bean interface { } , cacher core . Cacher ) error {
2018-07-20 05:10:17 +03:00
engine . setCacher ( engine . TableName ( bean , true ) , cacher )
2017-04-07 04:47:25 +03:00
return nil
2016-11-04 01:16:01 +03:00
}
// 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 {
2017-08-22 14:39:52 +03:00
session := & Session { engine : engine }
2016-11-04 01:16:01 +03:00
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 ( )
return session . Ping ( )
}
// logging sql
func ( engine * Engine ) logSQL ( sqlStr string , sqlArgs ... interface { } ) {
if engine . showSQL && ! engine . showExecTime {
if len ( sqlArgs ) > 0 {
2017-08-22 14:39:52 +03:00
engine . logger . Infof ( "[SQL] %v %#v" , sqlStr , sqlArgs )
2016-11-04 01:16:01 +03:00
} else {
2017-04-07 04:47:25 +03:00
engine . logger . Infof ( "[SQL] %v" , sqlStr )
2016-11-04 01:16:01 +03:00
}
}
}
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . NoAutoTime ( )
}
// NoAutoCondition disable auto generate Where condition from bean or not
func ( engine * Engine ) NoAutoCondition ( no ... bool ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2019-01-10 01:18:06 +03:00
colNames := engine . dialect . Quote ( strings . Join ( cols , engine . dialect . Quote ( ", " ) ) )
destColNames := dialect . Quote ( strings . Join ( cols , dialect . Quote ( ", " ) ) )
2017-01-23 12:11:18 +03:00
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
}
2019-01-10 01:18:06 +03:00
_ , err = io . WriteString ( w , "INSERT INTO " + dialect . Quote ( table . Name ) + " (" + destColNames + ") 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 :
2019-01-17 00:24:09 +03:00
if col . SQLType . Name == core . Bool {
temp += fmt . Sprintf ( ", %v" , strconv . FormatBool ( d . ( [ ] byte ) [ 0 ] != byte ( '0' ) ) )
} else {
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
}
}
2017-02-10 18:02:26 +03:00
// FIXME: Hack for postgres
if string ( dialect . DBType ( ) ) == core . POSTGRES && table . AutoIncrColumn ( ) != nil {
2019-01-17 00:24:09 +03:00
_ , err = io . WriteString ( w , "SELECT setval('" + table . Name + "_id_seq', COALESCE((SELECT MAX(" + table . AutoIncrColumn ( ) . Name + ") + 1 FROM " + dialect . Quote ( table . Name ) + "), 1), false);\n" )
2017-02-10 18:02:26 +03:00
if err != nil {
return err
}
}
2016-11-04 01:16:01 +03:00
}
return nil
}
// Cascade use cascade or not
func ( engine * Engine ) Cascade ( trueOrFalse ... bool ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Cascade ( trueOrFalse ... )
}
// Where method provide a condition query
func ( engine * Engine ) Where ( query interface { } , args ... interface { } ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Where ( query , args ... )
}
2017-08-22 14:39:52 +03:00
// Id will be deprecated, please use ID instead
2016-11-04 01:16:01 +03:00
func ( engine * Engine ) Id ( id interface { } ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . After ( closures )
}
// Charset set charset when create table, only support mysql now
func ( engine * Engine ) Charset ( charset string ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Distinct ( columns ... )
}
// Select customerize your select columns or contents
func ( engine * Engine ) Select ( str string ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Cols ( columns ... )
}
// AllCols indicates that all columns should be use
func ( engine * Engine ) AllCols ( ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . AllCols ( )
}
// MustCols specify some columns must use even if they are empty
func ( engine * Engine ) MustCols ( columns ... string ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Nullable ( columns ... )
}
// In will generate "column IN (?, ?)"
func ( engine * Engine ) In ( column string , args ... interface { } ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . In ( column , args ... )
}
2017-08-22 14:39:52 +03:00
// 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 ... )
}
2016-11-04 01:16:01 +03:00
// Incr provides a update string like "column = column + ?"
func ( engine * Engine ) Incr ( column string , arg ... interface { } ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . SetExpr ( column , expression )
}
// Table temporarily change the Get, Find, Update's table
func ( engine * Engine ) Table ( tableNameOrBean interface { } ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Table ( tableNameOrBean )
}
// Alias set the table alias
func ( engine * Engine ) Alias ( alias string ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Alias ( alias )
}
// Limit will generate "LIMIT start, limit"
func ( engine * Engine ) Limit ( limit int , start ... int ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Limit ( limit , start ... )
}
// Desc will generate "ORDER BY column1 DESC, column2 DESC"
func ( engine * Engine ) Desc ( colNames ... string ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Asc ( colNames ... )
}
// OrderBy will generate "ORDER BY order"
func ( engine * Engine ) OrderBy ( order string ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . OrderBy ( order )
}
2018-01-27 18:20:59 +03:00
// Prepare enables prepare statement
func ( engine * Engine ) Prepare ( ) * Session {
session := engine . NewSession ( )
session . isAutoClose = true
return session . Prepare ( )
}
2016-11-04 01:16:01 +03:00
// 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 ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Join ( joinOperator , tablename , condition , args ... )
}
// GroupBy generate group by statement
func ( engine * Engine ) GroupBy ( keys string ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . GroupBy ( keys )
}
// Having generate having statement
func ( engine * Engine ) Having ( conditions string ) * Session {
session := engine . NewSession ( )
2017-08-22 14:39:52 +03:00
session . isAutoClose = true
2016-11-04 01:16:01 +03:00
return session . Having ( conditions )
}
2018-01-27 18:20:59 +03:00
// UnMapType removes the datbase mapper of a type
func ( engine * Engine ) UnMapType ( t reflect . Type ) {
2017-08-22 14:39:52 +03:00
engine . mutex . Lock ( )
defer engine . mutex . Unlock ( )
delete ( engine . Tables , t )
}
2017-04-07 04:47:25 +03:00
func ( engine * Engine ) autoMapType ( v reflect . Value ) ( * core . Table , error ) {
2016-11-04 01:16:01 +03:00
t := v . Type ( )
engine . mutex . Lock ( )
defer engine . mutex . Unlock ( )
table , ok := engine . Tables [ t ]
if ! ok {
2017-04-07 04:47:25 +03:00
var err error
table , err = engine . mapType ( v )
if err != nil {
return nil , err
}
2016-11-04 01:16:01 +03:00
engine . Tables [ t ] = table
if engine . Cacher != nil {
if v . CanAddr ( ) {
engine . GobRegister ( v . Addr ( ) . Interface ( ) )
} else {
engine . GobRegister ( v . Interface ( ) )
}
}
}
2017-04-07 04:47:25 +03:00
return table , nil
2016-11-04 01:16:01 +03:00
}
// GobRegister register one struct to gob for cache use
func ( engine * Engine ) GobRegister ( v interface { } ) * Engine {
gob . Register ( v )
return engine
}
// Table table struct
type Table struct {
* core . Table
Name string
}
2017-04-07 04:47:25 +03:00
// IsValid if table is valid
func ( t * Table ) IsValid ( ) bool {
return t . Table != nil && len ( t . Name ) > 0
}
2016-11-04 01:16:01 +03:00
// TableInfo get table info according to bean's content
func ( engine * Engine ) TableInfo ( bean interface { } ) * Table {
v := rValue ( bean )
2017-04-07 04:47:25 +03:00
tb , err := engine . autoMapType ( v )
if err != nil {
engine . logger . Error ( err )
}
2018-07-20 05:10:17 +03:00
return & Table { tb , engine . TableName ( bean ) }
2016-11-04 01:16:01 +03:00
}
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
}
}
// TableName table name interface to define customerize table name
type TableName interface {
TableName ( ) string
}
var (
tpTableName = reflect . TypeOf ( ( * TableName ) ( nil ) ) . Elem ( )
)
2017-04-07 04:47:25 +03:00
func ( engine * Engine ) mapType ( v reflect . Value ) ( * core . Table , error ) {
2016-11-04 01:16:01 +03:00
t := v . Type ( )
2018-07-20 05:10:17 +03:00
table := core . NewEmptyTable ( )
2016-11-04 01:16:01 +03:00
table . Type = t
2018-07-20 05:10:17 +03:00
table . Name = engine . tbNameForMap ( v )
2016-11-04 01:16:01 +03:00
var idFieldColName string
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
}
2017-04-07 04:47:25 +03:00
var ctx = tagContext {
table : table ,
col : col ,
fieldValue : fieldValue ,
indexNames : make ( map [ string ] int ) ,
engine : engine ,
}
2016-11-04 01:16:01 +03:00
if strings . ToUpper ( tags [ 0 ] ) == "EXTENDS" {
2017-04-07 04:47:25 +03:00
if err := ExtendsTagHandler ( & ctx ) ; err != nil {
return nil , err
2016-11-04 01:16:01 +03:00
}
2017-04-07 04:47:25 +03:00
continue
2016-11-04 01:16:01 +03:00
}
for j , key := range tags {
2017-04-07 04:47:25 +03:00
if ctx . ignoreNext {
ctx . ignoreNext = false
continue
}
2016-11-04 01:16:01 +03:00
k := strings . ToUpper ( key )
2017-04-07 04:47:25 +03:00
ctx . tagName = k
2017-05-02 03:50:33 +03:00
ctx . params = [ ] string { }
2016-11-04 01:16:01 +03:00
2017-04-07 04:47:25 +03:00
pStart := strings . Index ( k , "(" )
if pStart == 0 {
return nil , errors . New ( "( could not be the first charactor" )
2016-11-04 01:16:01 +03:00
}
2017-04-07 04:47:25 +03:00
if pStart > - 1 {
if ! strings . HasSuffix ( k , ")" ) {
2018-01-27 18:20:59 +03:00
return nil , fmt . Errorf ( "field %s tag %s cannot match ) charactor" , col . FieldName , key )
2016-11-04 01:16:01 +03:00
}
2017-04-07 04:47:25 +03:00
ctx . tagName = k [ : pStart ]
2017-05-02 03:50:33 +03:00
ctx . params = strings . Split ( key [ pStart + 1 : len ( k ) - 1 ] , "," )
2017-04-07 04:47:25 +03:00
}
if j > 0 {
ctx . preTag = strings . ToUpper ( tags [ j - 1 ] )
}
if j < len ( tags ) - 1 {
2017-05-02 03:50:33 +03:00
ctx . nextTag = tags [ j + 1 ]
2017-04-07 04:47:25 +03:00
} else {
ctx . nextTag = ""
}
if h , ok := engine . tagHandlers [ ctx . tagName ] ; ok {
if err := h ( & ctx ) ; err != nil {
return nil , err
2016-11-04 01:16:01 +03:00
}
2017-04-07 04:47:25 +03:00
} else {
if strings . HasPrefix ( key , "'" ) && strings . HasSuffix ( key , "'" ) {
col . Name = key [ 1 : len ( key ) - 1 ]
2016-11-04 01:16:01 +03:00
} else {
2017-04-07 04:47:25 +03:00
col . Name = key
2016-11-04 01:16:01 +03:00
}
}
2017-04-07 04:47:25 +03:00
if ctx . hasCacheTag {
hasCacheTag = true
}
if ctx . hasNoCacheTag {
hasNoCacheTag = true
}
2016-11-04 01:16:01 +03:00
}
2017-04-07 04:47:25 +03:00
2016-11-04 01:16:01 +03:00
if col . SQLType . Name == "" {
col . SQLType = core . Type2SQLType ( fieldType )
}
2017-04-07 04:47:25 +03:00
engine . dialect . SqlType ( col )
2016-11-04 01:16:01 +03:00
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 )
}
2017-04-07 04:47:25 +03:00
if ctx . isUnique {
ctx . indexNames [ col . Name ] = core . UniqueType
} else if ctx . isIndex {
ctx . indexNames [ col . Name ] = core . IndexType
2016-11-04 01:16:01 +03:00
}
2017-04-07 04:47:25 +03:00
for indexName , indexType := range ctx . indexNames {
2016-11-04 01:16:01 +03:00
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 )
2017-08-22 14:39:52 +03:00
if fieldType . Kind ( ) == reflect . Int64 && ( strings . ToUpper ( col . FieldName ) == "ID" || strings . HasSuffix ( strings . ToUpper ( col . FieldName ) , ".ID" ) ) {
idFieldColName = col . Name
}
2016-11-04 01:16:01 +03:00
}
if col . IsAutoIncrement {
col . Nullable = false
}
table . AddColumn ( col )
} // 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 )
2018-07-20 05:10:17 +03:00
engine . setCacher ( table . Name , engine . Cacher )
2016-11-04 01:16:01 +03:00
} else {
engine . logger . Info ( "enable LRU cache on table:" , table . Name )
2018-07-20 05:10:17 +03:00
engine . setCacher ( table . Name , NewLRUCacher2 ( NewMemoryStore ( ) , time . Hour , 10000 ) )
2016-11-04 01:16:01 +03:00
}
}
if hasNoCacheTag {
2018-07-20 05:10:17 +03:00
engine . logger . Info ( "disable cache on table:" , table . Name )
engine . setCacher ( table . Name , nil )
2016-11-04 01:16:01 +03:00
}
2017-04-07 04:47:25 +03:00
return table , nil
2016-11-04 01:16:01 +03:00
}
// 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 {
2017-04-07 04:47:25 +03:00
pk , err := engine . idOfV ( rv )
if err != nil {
engine . logger . Error ( err )
return nil
}
return pk
}
func ( engine * Engine ) idOfV ( rv reflect . Value ) ( core . PK , error ) {
2016-11-04 01:16:01 +03:00
v := reflect . Indirect ( rv )
2017-04-07 04:47:25 +03:00
table , err := engine . autoMapType ( v )
if err != nil {
return nil , err
}
2016-11-04 01:16:01 +03:00
pk := make ( [ ] interface { } , len ( table . PrimaryKeys ) )
for i , col := range table . PKColumns ( ) {
2017-08-22 14:39:52 +03:00
var err error
2018-07-20 05:10:17 +03:00
fieldName := col . FieldName
for {
parts := strings . SplitN ( fieldName , "." , 2 )
if len ( parts ) == 1 {
break
}
v = v . FieldByName ( parts [ 0 ] )
if v . Kind ( ) == reflect . Ptr {
v = v . Elem ( )
}
if v . Kind ( ) != reflect . Struct {
return nil , ErrUnSupportedType
}
fieldName = parts [ 1 ]
}
pkField := v . FieldByName ( fieldName )
2016-11-04 01:16:01 +03:00
switch pkField . Kind ( ) {
case reflect . String :
2017-08-22 14:39:52 +03:00
pk [ i ] , err = engine . idTypeAssertion ( col , pkField . String ( ) )
2016-11-04 01:16:01 +03:00
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
2017-08-22 14:39:52 +03:00
pk [ i ] , err = engine . idTypeAssertion ( col , strconv . FormatInt ( pkField . Int ( ) , 10 ) )
2016-11-04 01:16:01 +03:00
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 :
2017-08-22 14:39:52 +03:00
// 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
2016-11-04 01:16:01 +03:00
}
}
2017-04-07 04:47:25 +03:00
return core . PK ( pk ) , nil
2016-11-04 01:16:01 +03:00
}
2017-08-22 14:39:52 +03:00
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" )
}
}
2016-11-04 01:16:01 +03:00
// 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 )
}
// ClearCacheBean if enabled cache, clear the cache bean
func ( engine * Engine ) ClearCacheBean ( bean interface { } , id string ) error {
2018-07-20 05:10:17 +03:00
tableName := engine . TableName ( bean )
cacher := engine . getCacher ( tableName )
2016-11-04 01:16:01 +03:00
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 {
2018-07-20 05:10:17 +03:00
tableName := engine . TableName ( bean )
cacher := engine . getCacher ( tableName )
2016-11-04 01:16:01 +03:00
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 {
2017-08-22 14:39:52 +03:00
session := engine . NewSession ( )
defer session . Close ( )
2016-11-04 01:16:01 +03:00
for _ , bean := range beans {
v := rValue ( bean )
2018-07-20 05:10:17 +03:00
tableNameNoSchema := engine . TableName ( bean )
2017-04-07 04:47:25 +03:00
table , err := engine . autoMapType ( v )
if err != nil {
return err
}
2016-11-04 01:16:01 +03:00
2018-07-20 05:10:17 +03:00
isExist , err := session . Table ( bean ) . isTableExist ( tableNameNoSchema )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
if ! isExist {
2017-08-22 14:39:52 +03:00
err = session . createTable ( bean )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
}
/ * isEmpty , err := engine . IsEmptyTable ( bean )
if err != nil {
return err
} * /
var isEmpty bool
if isEmpty {
2017-08-22 14:39:52 +03:00
err = session . dropTable ( bean )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
2017-08-22 14:39:52 +03:00
err = session . createTable ( bean )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
} else {
for _ , col := range table . Columns ( ) {
2018-07-20 05:10:17 +03:00
isExist , err := engine . dialect . IsColumnExist ( tableNameNoSchema , col . Name )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
if ! isExist {
2018-07-20 05:10:17 +03:00
if err := session . statement . setRefBean ( bean ) ; err != nil {
2017-05-02 03:50:33 +03:00
return err
}
2016-11-04 01:16:01 +03:00
err = session . addColumn ( col . Name )
if err != nil {
return err
}
}
}
for name , index := range table . Indexes {
2018-07-20 05:10:17 +03:00
if err := session . statement . setRefBean ( bean ) ; err != nil {
2017-05-02 03:50:33 +03:00
return err
}
2016-11-04 01:16:01 +03:00
if index . Type == core . UniqueType {
2018-07-20 05:10:17 +03:00
isExist , err := session . isIndexExist2 ( tableNameNoSchema , index . Cols , true )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
if ! isExist {
2018-07-20 05:10:17 +03:00
if err := session . statement . setRefBean ( bean ) ; err != nil {
2017-05-02 03:50:33 +03:00
return err
}
2018-07-20 05:10:17 +03:00
err = session . addUnique ( tableNameNoSchema , name )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
}
} else if index . Type == core . IndexType {
2018-07-20 05:10:17 +03:00
isExist , err := session . isIndexExist2 ( tableNameNoSchema , index . Cols , false )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
if ! isExist {
2018-07-20 05:10:17 +03:00
if err := session . statement . setRefBean ( bean ) ; err != nil {
2017-05-02 03:50:33 +03:00
return err
}
2018-07-20 05:10:17 +03:00
err = session . addIndex ( tableNameNoSchema , name )
2016-11-04 01:16:01 +03:00
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 ... )
}
// 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 {
2017-08-22 14:39:52 +03:00
err = session . createTable ( bean )
2016-11-04 01:16:01 +03:00
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 {
2017-08-22 14:39:52 +03:00
err = session . dropTable ( bean )
2016-11-04 01:16:01 +03:00
if err != nil {
session . Rollback ( )
return err
}
}
return session . Commit ( )
}
2017-08-22 14:39:52 +03:00
// DropIndexes drop indexes of a table
func ( engine * Engine ) DropIndexes ( bean interface { } ) error {
2016-11-04 01:16:01 +03:00
session := engine . NewSession ( )
defer session . Close ( )
2017-08-22 14:39:52 +03:00
return session . DropIndexes ( bean )
2016-11-04 01:16:01 +03:00
}
// Exec raw sql
2018-12-12 04:01:41 +03:00
func ( engine * Engine ) Exec ( sqlorArgs ... interface { } ) ( sql . Result , error ) {
2016-11-04 01:16:01 +03:00
session := engine . NewSession ( )
defer session . Close ( )
2018-12-12 04:01:41 +03:00
return session . Exec ( sqlorArgs ... )
2016-11-04 01:16:01 +03:00
}
// Query a raw sql and return records as []map[string][]byte
2018-01-27 18:20:59 +03:00
func ( engine * Engine ) Query ( sqlorArgs ... interface { } ) ( resultsSlice [ ] map [ string ] [ ] byte , err error ) {
2016-11-04 01:16:01 +03:00
session := engine . NewSession ( )
defer session . Close ( )
2018-01-27 18:20:59 +03:00
return session . Query ( sqlorArgs ... )
2016-11-04 01:16:01 +03:00
}
2017-04-07 04:47:25 +03:00
// QueryString runs a raw sql and return records as []map[string]string
2018-01-27 18:20:59 +03:00
func ( engine * Engine ) QueryString ( sqlorArgs ... interface { } ) ( [ ] map [ string ] string , error ) {
2017-04-07 04:47:25 +03:00
session := engine . NewSession ( )
defer session . Close ( )
2018-01-27 18:20:59 +03:00
return session . QueryString ( sqlorArgs ... )
2017-04-07 04:47:25 +03:00
}
2017-09-13 08:18:22 +03:00
// QueryInterface runs a raw sql and return records as []map[string]interface{}
2018-01-27 18:20:59 +03:00
func ( engine * Engine ) QueryInterface ( sqlorArgs ... interface { } ) ( [ ] map [ string ] interface { } , error ) {
2017-09-13 08:18:22 +03:00
session := engine . NewSession ( )
defer session . Close ( )
2018-01-27 18:20:59 +03:00
return session . QueryInterface ( sqlorArgs ... )
2017-09-13 08:18:22 +03:00
}
2016-11-04 01:16:01 +03:00
// 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 )
}
2017-08-22 14:39:52 +03:00
// 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 ... )
}
2016-11-04 01:16:01 +03:00
// 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 ... )
}
2018-07-20 05:10:17 +03:00
// FindAndCount find the results and also return the counts
func ( engine * Engine ) FindAndCount ( rowsSlicePtr interface { } , condiBean ... interface { } ) ( int64 , error ) {
session := engine . NewSession ( )
defer session . Close ( )
return session . FindAndCount ( rowsSlicePtr , condiBean ... )
}
2016-11-04 01:16:01 +03:00
// 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.
2017-08-22 14:39:52 +03:00
func ( engine * Engine ) Count ( bean ... interface { } ) ( int64 , error ) {
2016-11-04 01:16:01 +03:00
session := engine . NewSession ( )
defer session . Close ( )
2017-08-22 14:39:52 +03:00
return session . Count ( bean ... )
2016-11-04 01:16:01 +03:00
}
// 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 )
}
2017-08-22 14:39:52 +03:00
// 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 )
}
2016-11-04 01:16:01 +03:00
// 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
}
}
}
return results , lastError
}
2017-10-01 19:52:35 +03:00
// nowTime return current time
func ( engine * Engine ) nowTime ( col * core . Column ) ( interface { } , time . Time ) {
2016-11-04 01:16:01 +03:00
t := time . Now ( )
2017-10-01 19:52:35 +03:00
var tz = engine . DatabaseTZ
if ! col . DisableTimeZone && col . TimeZone != nil {
tz = col . TimeZone
}
return engine . formatTime ( col . SQLType . Name , t . In ( tz ) ) , t . In ( engine . TZLocation )
2016-11-04 01:16:01 +03:00
}
func ( engine * Engine ) formatColTime ( col * core . Column , t time . Time ) ( v interface { } ) {
2017-08-22 14:39:52 +03:00
if t . IsZero ( ) {
if col . Nullable {
return nil
}
return ""
2016-11-04 01:16:01 +03:00
}
2017-08-22 14:39:52 +03:00
if col . TimeZone != nil {
return engine . formatTime ( col . SQLType . Name , t . In ( col . TimeZone ) )
2016-11-04 01:16:01 +03:00
}
2017-08-22 14:39:52 +03:00
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 { } ) {
2016-11-04 01:16:01 +03:00
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 :
2017-08-22 14:39:52 +03:00
v = t . Format ( "2006-01-02 15:04:05" )
2016-11-04 01:16:01 +03:00
case core . TimeStampz :
if engine . dialect . DBType ( ) == core . MSSQL {
v = t . Format ( "2006-01-02T15:04:05.9999999Z07:00" )
} else {
v = t . Format ( time . RFC3339Nano )
}
case core . BigInt , core . Int :
v = t . Unix ( )
default :
v = t
}
return
}
2018-01-27 18:20:59 +03:00
// GetColumnMapper returns the column name mapper
func ( engine * Engine ) GetColumnMapper ( ) core . IMapper {
return engine . ColumnMapper
2016-11-04 01:16:01 +03:00
}
2017-08-22 14:39:52 +03:00
2018-01-27 18:20:59 +03:00
// GetTableMapper returns the table name mapper
func ( engine * Engine ) GetTableMapper ( ) core . IMapper {
return engine . TableMapper
2017-08-22 14:39:52 +03:00
}
2017-10-01 19:52:35 +03:00
2018-01-27 18:20:59 +03:00
// GetTZLocation returns time zone of the application
func ( engine * Engine ) GetTZLocation ( ) * time . Location {
return engine . TZLocation
}
// SetTZLocation sets time zone of the application
func ( engine * Engine ) SetTZLocation ( tz * time . Location ) {
engine . TZLocation = tz
}
// GetTZDatabase returns time zone of the database
func ( engine * Engine ) GetTZDatabase ( ) * time . Location {
return engine . DatabaseTZ
}
// SetTZDatabase sets time zone of the database
func ( engine * Engine ) SetTZDatabase ( tz * time . Location ) {
engine . DatabaseTZ = tz
}
2018-07-20 05:10:17 +03:00
// SetSchema sets the schema of database
func ( engine * Engine ) SetSchema ( schema string ) {
engine . dialect . URI ( ) . Schema = schema
}
2018-01-27 18:20:59 +03:00
// Unscoped always disable struct tag "deleted"
func ( engine * Engine ) Unscoped ( ) * Session {
2017-10-01 19:52:35 +03:00
session := engine . NewSession ( )
session . isAutoClose = true
2018-01-27 18:20:59 +03:00
return session . Unscoped ( )
2017-10-01 19:52:35 +03:00
}