mirror of https://github.com/gogits/gogs.git
Unknwon
7 years ago
47 changed files with 2963 additions and 1586 deletions
@ -0,0 +1,26 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package xorm |
||||
|
||||
import "context" |
||||
|
||||
// PingContext tests if database is alive
|
||||
func (engine *Engine) PingContext(ctx context.Context) error { |
||||
session := engine.NewSession() |
||||
defer session.Close() |
||||
return session.PingContext(ctx) |
||||
} |
||||
|
||||
// PingContext test if database is ok
|
||||
func (session *Session) PingContext(ctx context.Context) error { |
||||
if session.isAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName()) |
||||
return session.DB().PingContext(ctx) |
||||
} |
@ -0,0 +1,230 @@
|
||||
// Copyright 2017 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 ( |
||||
"database/sql/driver" |
||||
"encoding/json" |
||||
"fmt" |
||||
"reflect" |
||||
"time" |
||||
|
||||
"github.com/go-xorm/builder" |
||||
"github.com/go-xorm/core" |
||||
) |
||||
|
||||
func (engine *Engine) buildConds(table *core.Table, bean interface{}, |
||||
includeVersion bool, includeUpdated bool, includeNil bool, |
||||
includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool, |
||||
mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) { |
||||
var conds []builder.Cond |
||||
for _, col := range table.Columns() { |
||||
if !includeVersion && col.IsVersion { |
||||
continue |
||||
} |
||||
if !includeUpdated && col.IsUpdated { |
||||
continue |
||||
} |
||||
if !includeAutoIncr && col.IsAutoIncrement { |
||||
continue |
||||
} |
||||
|
||||
if engine.dialect.DBType() == core.MSSQL && (col.SQLType.Name == core.Text || col.SQLType.IsBlob() || col.SQLType.Name == core.TimeStampz) { |
||||
continue |
||||
} |
||||
if col.SQLType.IsJson() { |
||||
continue |
||||
} |
||||
|
||||
var colName string |
||||
if addedTableName { |
||||
var nm = tableName |
||||
if len(aliasName) > 0 { |
||||
nm = aliasName |
||||
} |
||||
colName = engine.Quote(nm) + "." + engine.Quote(col.Name) |
||||
} else { |
||||
colName = engine.Quote(col.Name) |
||||
} |
||||
|
||||
fieldValuePtr, err := col.ValueOf(bean) |
||||
if err != nil { |
||||
engine.logger.Error(err) |
||||
continue |
||||
} |
||||
|
||||
if col.IsDeleted && !unscoped { // tag "deleted" is enabled
|
||||
conds = append(conds, engine.CondDeleted(colName)) |
||||
} |
||||
|
||||
fieldValue := *fieldValuePtr |
||||
if fieldValue.Interface() == nil { |
||||
continue |
||||
} |
||||
|
||||
fieldType := reflect.TypeOf(fieldValue.Interface()) |
||||
requiredField := useAllCols |
||||
|
||||
if b, ok := getFlagForColumn(mustColumnMap, col); ok { |
||||
if b { |
||||
requiredField = true |
||||
} else { |
||||
continue |
||||
} |
||||
} |
||||
|
||||
if fieldType.Kind() == reflect.Ptr { |
||||
if fieldValue.IsNil() { |
||||
if includeNil { |
||||
conds = append(conds, builder.Eq{colName: nil}) |
||||
} |
||||
continue |
||||
} else if !fieldValue.IsValid() { |
||||
continue |
||||
} else { |
||||
// dereference ptr type to instance type
|
||||
fieldValue = fieldValue.Elem() |
||||
fieldType = reflect.TypeOf(fieldValue.Interface()) |
||||
requiredField = true |
||||
} |
||||
} |
||||
|
||||
var val interface{} |
||||
switch fieldType.Kind() { |
||||
case reflect.Bool: |
||||
if allUseBool || requiredField { |
||||
val = fieldValue.Interface() |
||||
} else { |
||||
// if a bool in a struct, it will not be as a condition because it default is false,
|
||||
// please use Where() instead
|
||||
continue |
||||
} |
||||
case reflect.String: |
||||
if !requiredField && fieldValue.String() == "" { |
||||
continue |
||||
} |
||||
// for MyString, should convert to string or panic
|
||||
if fieldType.String() != reflect.String.String() { |
||||
val = fieldValue.String() |
||||
} else { |
||||
val = fieldValue.Interface() |
||||
} |
||||
case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64: |
||||
if !requiredField && fieldValue.Int() == 0 { |
||||
continue |
||||
} |
||||
val = fieldValue.Interface() |
||||
case reflect.Float32, reflect.Float64: |
||||
if !requiredField && fieldValue.Float() == 0.0 { |
||||
continue |
||||
} |
||||
val = fieldValue.Interface() |
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64: |
||||
if !requiredField && fieldValue.Uint() == 0 { |
||||
continue |
||||
} |
||||
t := int64(fieldValue.Uint()) |
||||
val = reflect.ValueOf(&t).Interface() |
||||
case reflect.Struct: |
||||
if fieldType.ConvertibleTo(core.TimeType) { |
||||
t := fieldValue.Convert(core.TimeType).Interface().(time.Time) |
||||
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) { |
||||
continue |
||||
} |
||||
val = engine.formatColTime(col, t) |
||||
} else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok { |
||||
continue |
||||
} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok { |
||||
val, _ = valNul.Value() |
||||
if val == nil { |
||||
continue |
||||
} |
||||
} else { |
||||
if col.SQLType.IsJson() { |
||||
if col.SQLType.IsText() { |
||||
bytes, err := json.Marshal(fieldValue.Interface()) |
||||
if err != nil { |
||||
engine.logger.Error(err) |
||||
continue |
||||
} |
||||
val = string(bytes) |
||||
} else if col.SQLType.IsBlob() { |
||||
var bytes []byte |
||||
var err error |
||||
bytes, err = json.Marshal(fieldValue.Interface()) |
||||
if err != nil { |
||||
engine.logger.Error(err) |
||||
continue |
||||
} |
||||
val = bytes |
||||
} |
||||
} else { |
||||
engine.autoMapType(fieldValue) |
||||
if table, ok := engine.Tables[fieldValue.Type()]; ok { |
||||
if len(table.PrimaryKeys) == 1 { |
||||
pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) |
||||
// fix non-int pk issues
|
||||
//if pkField.Int() != 0 {
|
||||
if pkField.IsValid() && !isZero(pkField.Interface()) { |
||||
val = pkField.Interface() |
||||
} else { |
||||
continue |
||||
} |
||||
} else { |
||||
//TODO: how to handler?
|
||||
return nil, fmt.Errorf("not supported %v as %v", fieldValue.Interface(), table.PrimaryKeys) |
||||
} |
||||
} else { |
||||
val = fieldValue.Interface() |
||||
} |
||||
} |
||||
} |
||||
case reflect.Array: |
||||
continue |
||||
case reflect.Slice, reflect.Map: |
||||
if fieldValue == reflect.Zero(fieldType) { |
||||
continue |
||||
} |
||||
if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { |
||||
continue |
||||
} |
||||
|
||||
if col.SQLType.IsText() { |
||||
bytes, err := json.Marshal(fieldValue.Interface()) |
||||
if err != nil { |
||||
engine.logger.Error(err) |
||||
continue |
||||
} |
||||
val = string(bytes) |
||||
} else if col.SQLType.IsBlob() { |
||||
var bytes []byte |
||||
var err error |
||||
if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) && |
||||
fieldType.Elem().Kind() == reflect.Uint8 { |
||||
if fieldValue.Len() > 0 { |
||||
val = fieldValue.Bytes() |
||||
} else { |
||||
continue |
||||
} |
||||
} else { |
||||
bytes, err = json.Marshal(fieldValue.Interface()) |
||||
if err != nil { |
||||
engine.logger.Error(err) |
||||
continue |
||||
} |
||||
val = bytes |
||||
} |
||||
} else { |
||||
continue |
||||
} |
||||
default: |
||||
val = fieldValue.Interface() |
||||
} |
||||
|
||||
conds = append(conds, builder.Eq{colName: val}) |
||||
} |
||||
|
||||
return builder.And(conds...), nil |
||||
} |
@ -0,0 +1,194 @@
|
||||
// Copyright 2017 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 ( |
||||
"github.com/go-xorm/core" |
||||
) |
||||
|
||||
// EngineGroup defines an engine group
|
||||
type EngineGroup struct { |
||||
*Engine |
||||
slaves []*Engine |
||||
policy GroupPolicy |
||||
} |
||||
|
||||
// NewEngineGroup creates a new engine group
|
||||
func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) { |
||||
var eg EngineGroup |
||||
if len(policies) > 0 { |
||||
eg.policy = policies[0] |
||||
} else { |
||||
eg.policy = RoundRobinPolicy() |
||||
} |
||||
|
||||
driverName, ok1 := args1.(string) |
||||
conns, ok2 := args2.([]string) |
||||
if ok1 && ok2 { |
||||
engines := make([]*Engine, len(conns)) |
||||
for i, conn := range conns { |
||||
engine, err := NewEngine(driverName, conn) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
engine.engineGroup = &eg |
||||
engines[i] = engine |
||||
} |
||||
|
||||
eg.Engine = engines[0] |
||||
eg.slaves = engines[1:] |
||||
return &eg, nil |
||||
} |
||||
|
||||
master, ok3 := args1.(*Engine) |
||||
slaves, ok4 := args2.([]*Engine) |
||||
if ok3 && ok4 { |
||||
master.engineGroup = &eg |
||||
for i := 0; i < len(slaves); i++ { |
||||
slaves[i].engineGroup = &eg |
||||
} |
||||
eg.Engine = master |
||||
eg.slaves = slaves |
||||
return &eg, nil |
||||
} |
||||
return nil, ErrParamsType |
||||
} |
||||
|
||||
// Close the engine
|
||||
func (eg *EngineGroup) Close() error { |
||||
err := eg.Engine.Close() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
err := eg.slaves[i].Close() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// Master returns the master engine
|
||||
func (eg *EngineGroup) Master() *Engine { |
||||
return eg.Engine |
||||
} |
||||
|
||||
// Ping tests if database is alive
|
||||
func (eg *EngineGroup) Ping() error { |
||||
if err := eg.Engine.Ping(); err != nil { |
||||
return err |
||||
} |
||||
|
||||
for _, slave := range eg.slaves { |
||||
if err := slave.Ping(); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// SetColumnMapper set the column name mapping rule
|
||||
func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { |
||||
eg.Engine.ColumnMapper = mapper |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].ColumnMapper = mapper |
||||
} |
||||
} |
||||
|
||||
// SetDefaultCacher set the default cacher
|
||||
func (eg *EngineGroup) SetDefaultCacher(cacher core.Cacher) { |
||||
eg.Engine.SetDefaultCacher(cacher) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].SetDefaultCacher(cacher) |
||||
} |
||||
} |
||||
|
||||
// SetLogger set the new logger
|
||||
func (eg *EngineGroup) SetLogger(logger core.ILogger) { |
||||
eg.Engine.SetLogger(logger) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].SetLogger(logger) |
||||
} |
||||
} |
||||
|
||||
// SetLogLevel sets the logger level
|
||||
func (eg *EngineGroup) SetLogLevel(level core.LogLevel) { |
||||
eg.Engine.SetLogLevel(level) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].SetLogLevel(level) |
||||
} |
||||
} |
||||
|
||||
// SetMapper set the name mapping rules
|
||||
func (eg *EngineGroup) SetMapper(mapper core.IMapper) { |
||||
eg.Engine.SetMapper(mapper) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].SetMapper(mapper) |
||||
} |
||||
} |
||||
|
||||
// SetMaxIdleConns set the max idle connections on pool, default is 2
|
||||
func (eg *EngineGroup) SetMaxIdleConns(conns int) { |
||||
eg.Engine.db.SetMaxIdleConns(conns) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].db.SetMaxIdleConns(conns) |
||||
} |
||||
} |
||||
|
||||
// SetMaxOpenConns is only available for go 1.2+
|
||||
func (eg *EngineGroup) SetMaxOpenConns(conns int) { |
||||
eg.Engine.db.SetMaxOpenConns(conns) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].db.SetMaxOpenConns(conns) |
||||
} |
||||
} |
||||
|
||||
// SetPolicy set the group policy
|
||||
func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup { |
||||
eg.policy = policy |
||||
return eg |
||||
} |
||||
|
||||
// SetTableMapper set the table name mapping rule
|
||||
func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) { |
||||
eg.Engine.TableMapper = mapper |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].TableMapper = mapper |
||||
} |
||||
} |
||||
|
||||
// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO
|
||||
func (eg *EngineGroup) ShowExecTime(show ...bool) { |
||||
eg.Engine.ShowExecTime(show...) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].ShowExecTime(show...) |
||||
} |
||||
} |
||||
|
||||
// ShowSQL show SQL statement or not on logger if log level is great than INFO
|
||||
func (eg *EngineGroup) ShowSQL(show ...bool) { |
||||
eg.Engine.ShowSQL(show...) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].ShowSQL(show...) |
||||
} |
||||
} |
||||
|
||||
// Slave returns one of the physical databases which is a slave according the policy
|
||||
func (eg *EngineGroup) Slave() *Engine { |
||||
switch len(eg.slaves) { |
||||
case 0: |
||||
return eg.Engine |
||||
case 1: |
||||
return eg.slaves[0] |
||||
} |
||||
return eg.policy.Slave(eg) |
||||
} |
||||
|
||||
// Slaves returns all the slaves
|
||||
func (eg *EngineGroup) Slaves() []*Engine { |
||||
return eg.slaves |
||||
} |
@ -0,0 +1,116 @@
|
||||
// Copyright 2017 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 ( |
||||
"math/rand" |
||||
"sync" |
||||
"time" |
||||
) |
||||
|
||||
// GroupPolicy is be used by chosing the current slave from slaves
|
||||
type GroupPolicy interface { |
||||
Slave(*EngineGroup) *Engine |
||||
} |
||||
|
||||
// GroupPolicyHandler should be used when a function is a GroupPolicy
|
||||
type GroupPolicyHandler func(*EngineGroup) *Engine |
||||
|
||||
// Slave implements the chosen of slaves
|
||||
func (h GroupPolicyHandler) Slave(eg *EngineGroup) *Engine { |
||||
return h(eg) |
||||
} |
||||
|
||||
// RandomPolicy implmentes randomly chose the slave of slaves
|
||||
func RandomPolicy() GroupPolicyHandler { |
||||
var r = rand.New(rand.NewSource(time.Now().UnixNano())) |
||||
return func(g *EngineGroup) *Engine { |
||||
return g.Slaves()[r.Intn(len(g.Slaves()))] |
||||
} |
||||
} |
||||
|
||||
// WeightRandomPolicy implmentes randomly chose the slave of slaves
|
||||
func WeightRandomPolicy(weights []int) GroupPolicyHandler { |
||||
var rands = make([]int, 0, len(weights)) |
||||
for i := 0; i < len(weights); i++ { |
||||
for n := 0; n < weights[i]; n++ { |
||||
rands = append(rands, i) |
||||
} |
||||
} |
||||
var r = rand.New(rand.NewSource(time.Now().UnixNano())) |
||||
|
||||
return func(g *EngineGroup) *Engine { |
||||
var slaves = g.Slaves() |
||||
idx := rands[r.Intn(len(rands))] |
||||
if idx >= len(slaves) { |
||||
idx = len(slaves) - 1 |
||||
} |
||||
return slaves[idx] |
||||
} |
||||
} |
||||
|
||||
func RoundRobinPolicy() GroupPolicyHandler { |
||||
var pos = -1 |
||||
var lock sync.Mutex |
||||
return func(g *EngineGroup) *Engine { |
||||
var slaves = g.Slaves() |
||||
|
||||
lock.Lock() |
||||
defer lock.Unlock() |
||||
pos++ |
||||
if pos >= len(slaves) { |
||||
pos = 0 |
||||
} |
||||
|
||||
return slaves[pos] |
||||
} |
||||
} |
||||
|
||||
func WeightRoundRobinPolicy(weights []int) GroupPolicyHandler { |
||||
var rands = make([]int, 0, len(weights)) |
||||
for i := 0; i < len(weights); i++ { |
||||
for n := 0; n < weights[i]; n++ { |
||||
rands = append(rands, i) |
||||
} |
||||
} |
||||
var pos = -1 |
||||
var lock sync.Mutex |
||||
|
||||
return func(g *EngineGroup) *Engine { |
||||
var slaves = g.Slaves() |
||||
lock.Lock() |
||||
defer lock.Unlock() |
||||
pos++ |
||||
if pos >= len(rands) { |
||||
pos = 0 |
||||
} |
||||
|
||||
idx := rands[pos] |
||||
if idx >= len(slaves) { |
||||
idx = len(slaves) - 1 |
||||
} |
||||
return slaves[idx] |
||||
} |
||||
} |
||||
|
||||
// LeastConnPolicy implements GroupPolicy, every time will get the least connections slave
|
||||
func LeastConnPolicy() GroupPolicyHandler { |
||||
return func(g *EngineGroup) *Engine { |
||||
var slaves = g.Slaves() |
||||
connections := 0 |
||||
idx := 0 |
||||
for i := 0; i < len(slaves); i++ { |
||||
openConnections := slaves[i].DB().Stats().OpenConnections |
||||
if i == 0 { |
||||
connections = openConnections |
||||
idx = i |
||||
} else if openConnections <= connections { |
||||
connections = openConnections |
||||
idx = i |
||||
} |
||||
} |
||||
return slaves[idx] |
||||
} |
||||
} |
@ -0,0 +1,22 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package xorm |
||||
|
||||
import "time" |
||||
|
||||
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
|
||||
func (engine *Engine) SetConnMaxLifetime(d time.Duration) { |
||||
engine.db.SetConnMaxLifetime(d) |
||||
} |
||||
|
||||
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
|
||||
func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { |
||||
eg.Engine.SetConnMaxLifetime(d) |
||||
for i := 0; i < len(eg.slaves); i++ { |
||||
eg.slaves[i].SetConnMaxLifetime(d) |
||||
} |
||||
} |
@ -0,0 +1,104 @@
|
||||
// Copyright 2017 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 ( |
||||
"database/sql" |
||||
"reflect" |
||||
"time" |
||||
|
||||
"github.com/go-xorm/core" |
||||
) |
||||
|
||||
// Interface defines the interface which Engine, EngineGroup and Session will implementate.
|
||||
type Interface interface { |
||||
AllCols() *Session |
||||
Alias(alias string) *Session |
||||
Asc(colNames ...string) *Session |
||||
BufferSize(size int) *Session |
||||
Cols(columns ...string) *Session |
||||
Count(...interface{}) (int64, error) |
||||
CreateIndexes(bean interface{}) error |
||||
CreateUniques(bean interface{}) error |
||||
Decr(column string, arg ...interface{}) *Session |
||||
Desc(...string) *Session |
||||
Delete(interface{}) (int64, error) |
||||
Distinct(columns ...string) *Session |
||||
DropIndexes(bean interface{}) error |
||||
Exec(string, ...interface{}) (sql.Result, error) |
||||
Exist(bean ...interface{}) (bool, error) |
||||
Find(interface{}, ...interface{}) error |
||||
FindAndCount(interface{}, ...interface{}) (int64, error) |
||||
Get(interface{}) (bool, error) |
||||
GroupBy(keys string) *Session |
||||
ID(interface{}) *Session |
||||
In(string, ...interface{}) *Session |
||||
Incr(column string, arg ...interface{}) *Session |
||||
Insert(...interface{}) (int64, error) |
||||
InsertOne(interface{}) (int64, error) |
||||
IsTableEmpty(bean interface{}) (bool, error) |
||||
IsTableExist(beanOrTableName interface{}) (bool, error) |
||||
Iterate(interface{}, IterFunc) error |
||||
Limit(int, ...int) *Session |
||||
NoAutoCondition(...bool) *Session |
||||
NotIn(string, ...interface{}) *Session |
||||
Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session |
||||
Omit(columns ...string) *Session |
||||
OrderBy(order string) *Session |
||||
Ping() error |
||||
Query(sqlOrAgrs ...interface{}) (resultsSlice []map[string][]byte, err error) |
||||
QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) |
||||
QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) |
||||
Rows(bean interface{}) (*Rows, error) |
||||
SetExpr(string, string) *Session |
||||
SQL(interface{}, ...interface{}) *Session |
||||
Sum(bean interface{}, colName string) (float64, error) |
||||
SumInt(bean interface{}, colName string) (int64, error) |
||||
Sums(bean interface{}, colNames ...string) ([]float64, error) |
||||
SumsInt(bean interface{}, colNames ...string) ([]int64, error) |
||||
Table(tableNameOrBean interface{}) *Session |
||||
Unscoped() *Session |
||||
Update(bean interface{}, condiBeans ...interface{}) (int64, error) |
||||
UseBool(...string) *Session |
||||
Where(interface{}, ...interface{}) *Session |
||||
} |
||||
|
||||
// EngineInterface defines the interface which Engine, EngineGroup will implementate.
|
||||
type EngineInterface interface { |
||||
Interface |
||||
|
||||
Before(func(interface{})) *Session |
||||
Charset(charset string) *Session |
||||
CreateTables(...interface{}) error |
||||
DBMetas() ([]*core.Table, error) |
||||
Dialect() core.Dialect |
||||
DropTables(...interface{}) error |
||||
DumpAllToFile(fp string, tp ...core.DbType) error |
||||
GetColumnMapper() core.IMapper |
||||
GetDefaultCacher() core.Cacher |
||||
GetTableMapper() core.IMapper |
||||
GetTZDatabase() *time.Location |
||||
GetTZLocation() *time.Location |
||||
NewSession() *Session |
||||
NoAutoTime() *Session |
||||
Quote(string) string |
||||
SetDefaultCacher(core.Cacher) |
||||
SetLogLevel(core.LogLevel) |
||||
SetMapper(core.IMapper) |
||||
SetTZDatabase(tz *time.Location) |
||||
SetTZLocation(tz *time.Location) |
||||
ShowSQL(show ...bool) |
||||
Sync(...interface{}) error |
||||
Sync2(...interface{}) error |
||||
StoreEngine(storeEngine string) *Session |
||||
TableInfo(bean interface{}) *Table |
||||
UnMapType(reflect.Type) |
||||
} |
||||
|
||||
var ( |
||||
_ Interface = &Session{} |
||||
_ EngineInterface = &Engine{} |
||||
_ EngineInterface = &EngineGroup{} |
||||
) |
@ -0,0 +1,86 @@
|
||||
// Copyright 2017 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 ( |
||||
"errors" |
||||
"fmt" |
||||
"reflect" |
||||
|
||||
"github.com/go-xorm/builder" |
||||
"github.com/go-xorm/core" |
||||
) |
||||
|
||||
// Exist returns true if the record exist otherwise return false
|
||||
func (session *Session) Exist(bean ...interface{}) (bool, error) { |
||||
if session.isAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
var sqlStr string |
||||
var args []interface{} |
||||
var err error |
||||
|
||||
if session.statement.RawSQL == "" { |
||||
if len(bean) == 0 { |
||||
tableName := session.statement.TableName() |
||||
if len(tableName) <= 0 { |
||||
return false, ErrTableNotFound |
||||
} |
||||
|
||||
if session.statement.cond.IsValid() { |
||||
condSQL, condArgs, err := builder.ToSQL(session.statement.cond) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
|
||||
if session.engine.dialect.DBType() == core.MSSQL { |
||||
sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s WHERE %s", tableName, condSQL) |
||||
} else { |
||||
sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) |
||||
} |
||||
args = condArgs |
||||
} else { |
||||
if session.engine.dialect.DBType() == core.MSSQL { |
||||
sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s", tableName) |
||||
} else { |
||||
sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) |
||||
} |
||||
args = []interface{}{} |
||||
} |
||||
} else { |
||||
beanValue := reflect.ValueOf(bean[0]) |
||||
if beanValue.Kind() != reflect.Ptr { |
||||
return false, errors.New("needs a pointer") |
||||
} |
||||
|
||||
if beanValue.Elem().Kind() == reflect.Struct { |
||||
if err := session.statement.setRefValue(beanValue.Elem()); err != nil { |
||||
return false, err |
||||
} |
||||
} |
||||
|
||||
if len(session.statement.TableName()) <= 0 { |
||||
return false, ErrTableNotFound |
||||
} |
||||
session.statement.Limit(1) |
||||
sqlStr, args, err = session.statement.genGetSQL(bean[0]) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
} |
||||
} else { |
||||
sqlStr = session.statement.RawSQL |
||||
args = session.statement.RawParams |
||||
} |
||||
|
||||
rows, err := session.queryRows(sqlStr, args...) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
defer rows.Close() |
||||
|
||||
return rows.Next(), nil |
||||
} |
@ -0,0 +1,262 @@
|
||||
// Copyright 2017 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 ( |
||||
"fmt" |
||||
"reflect" |
||||
"strconv" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/go-xorm/builder" |
||||
"github.com/go-xorm/core" |
||||
) |
||||
|
||||
func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interface{}, error) { |
||||
if len(sqlorArgs) > 0 { |
||||
switch sqlorArgs[0].(type) { |
||||
case string: |
||||
return sqlorArgs[0].(string), sqlorArgs[1:], nil |
||||
case *builder.Builder: |
||||
return sqlorArgs[0].(*builder.Builder).ToSQL() |
||||
case builder.Builder: |
||||
bd := sqlorArgs[0].(builder.Builder) |
||||
return bd.ToSQL() |
||||
default: |
||||
return "", nil, ErrUnSupportedType |
||||
} |
||||
} |
||||
|
||||
if session.statement.RawSQL != "" { |
||||
return session.statement.RawSQL, session.statement.RawParams, nil |
||||
} |
||||
|
||||
if len(session.statement.TableName()) <= 0 { |
||||
return "", nil, ErrTableNotFound |
||||
} |
||||
|
||||
var columnStr = session.statement.ColumnStr |
||||
if len(session.statement.selectStr) > 0 { |
||||
columnStr = session.statement.selectStr |
||||
} else { |
||||
if session.statement.JoinStr == "" { |
||||
if columnStr == "" { |
||||
if session.statement.GroupByStr != "" { |
||||
columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) |
||||
} else { |
||||
columnStr = session.statement.genColumnStr() |
||||
} |
||||
} |
||||
} else { |
||||
if columnStr == "" { |
||||
if session.statement.GroupByStr != "" { |
||||
columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1)) |
||||
} else { |
||||
columnStr = "*" |
||||
} |
||||
} |
||||
} |
||||
if columnStr == "" { |
||||
columnStr = "*" |
||||
} |
||||
} |
||||
|
||||
condSQL, condArgs, err := builder.ToSQL(session.statement.cond) |
||||
if err != nil { |
||||
return "", nil, err |
||||
} |
||||
|
||||
args := append(session.statement.joinArgs, condArgs...) |
||||
sqlStr, err := session.statement.genSelectSQL(columnStr, condSQL, true) |
||||
if err != nil { |
||||
return "", nil, err |
||||
} |
||||
// for mssql and use limit
|
||||
qs := strings.Count(sqlStr, "?") |
||||
if len(args)*2 == qs { |
||||
args = append(args, args...) |
||||
} |
||||
|
||||
return sqlStr, args, nil |
||||
} |
||||
|
||||
// Query runs a raw sql and return records as []map[string][]byte
|
||||
func (session *Session) Query(sqlorArgs ...interface{}) ([]map[string][]byte, error) { |
||||
if session.isAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
sqlStr, args, err := session.genQuerySQL(sqlorArgs...) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return session.queryBytes(sqlStr, args...) |
||||
} |
||||
|
||||
func value2String(rawValue *reflect.Value) (str string, err error) { |
||||
aa := reflect.TypeOf((*rawValue).Interface()) |
||||
vv := reflect.ValueOf((*rawValue).Interface()) |
||||
switch aa.Kind() { |
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
||||
str = strconv.FormatInt(vv.Int(), 10) |
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
||||
str = strconv.FormatUint(vv.Uint(), 10) |
||||
case reflect.Float32, reflect.Float64: |
||||
str = strconv.FormatFloat(vv.Float(), 'f', -1, 64) |
||||
case reflect.String: |
||||
str = vv.String() |
||||
case reflect.Array, reflect.Slice: |
||||
switch aa.Elem().Kind() { |
||||
case reflect.Uint8: |
||||
data := rawValue.Interface().([]byte) |
||||
str = string(data) |
||||
if str == "\x00" { |
||||
str = "0" |
||||
} |
||||
default: |
||||
err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) |
||||
} |
||||
// time type
|
||||
case reflect.Struct: |
||||
if aa.ConvertibleTo(core.TimeType) { |
||||
str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano) |
||||
} else { |
||||
err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) |
||||
} |
||||
case reflect.Bool: |
||||
str = strconv.FormatBool(vv.Bool()) |
||||
case reflect.Complex128, reflect.Complex64: |
||||
str = fmt.Sprintf("%v", vv.Complex()) |
||||
/* TODO: unsupported types below |
||||
case reflect.Map: |
||||
case reflect.Ptr: |
||||
case reflect.Uintptr: |
||||
case reflect.UnsafePointer: |
||||
case reflect.Chan, reflect.Func, reflect.Interface: |
||||
*/ |
||||
default: |
||||
err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) |
||||
} |
||||
return |
||||
} |
||||
|
||||
func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) { |
||||
result := make(map[string]string) |
||||
scanResultContainers := make([]interface{}, len(fields)) |
||||
for i := 0; i < len(fields); i++ { |
||||
var scanResultContainer interface{} |
||||
scanResultContainers[i] = &scanResultContainer |
||||
} |
||||
if err := rows.Scan(scanResultContainers...); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for ii, key := range fields { |
||||
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) |
||||
// if row is null then as empty string
|
||||
if rawValue.Interface() == nil { |
||||
result[key] = "" |
||||
continue |
||||
} |
||||
|
||||
if data, err := value2String(&rawValue); err == nil { |
||||
result[key] = data |
||||
} else { |
||||
return nil, err |
||||
} |
||||
} |
||||
return result, nil |
||||
} |
||||
|
||||
func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) { |
||||
fields, err := rows.Columns() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
for rows.Next() { |
||||
result, err := row2mapStr(rows, fields) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
resultsSlice = append(resultsSlice, result) |
||||
} |
||||
|
||||
return resultsSlice, nil |
||||
} |
||||
|
||||
// QueryString runs a raw sql and return records as []map[string]string
|
||||
func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) { |
||||
if session.isAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
sqlStr, args, err := session.genQuerySQL(sqlorArgs...) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
rows, err := session.queryRows(sqlStr, args...) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer rows.Close() |
||||
|
||||
return rows2Strings(rows) |
||||
} |
||||
|
||||
func row2mapInterface(rows *core.Rows, fields []string) (resultsMap map[string]interface{}, err error) { |
||||
resultsMap = make(map[string]interface{}, len(fields)) |
||||
scanResultContainers := make([]interface{}, len(fields)) |
||||
for i := 0; i < len(fields); i++ { |
||||
var scanResultContainer interface{} |
||||
scanResultContainers[i] = &scanResultContainer |
||||
} |
||||
if err := rows.Scan(scanResultContainers...); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
for ii, key := range fields { |
||||
resultsMap[key] = reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])).Interface() |
||||
} |
||||
return |
||||
} |
||||
|
||||
func rows2Interfaces(rows *core.Rows) (resultsSlice []map[string]interface{}, err error) { |
||||
fields, err := rows.Columns() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
for rows.Next() { |
||||
result, err := row2mapInterface(rows, fields) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
resultsSlice = append(resultsSlice, result) |
||||
} |
||||
|
||||
return resultsSlice, nil |
||||
} |
||||
|
||||
// QueryInterface runs a raw sql and return records as []map[string]interface{}
|
||||
func (session *Session) QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) { |
||||
if session.isAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
sqlStr, args, err := session.genQuerySQL(sqlorArgs...) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
rows, err := session.queryRows(sqlStr, args...) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer rows.Close() |
||||
|
||||
return rows2Interfaces(rows) |
||||
} |
@ -0,0 +1,98 @@
|
||||
// Copyright 2016 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 ( |
||||
"database/sql" |
||||
"errors" |
||||
"reflect" |
||||
) |
||||
|
||||
// Count counts the records. bean's non-empty fields
|
||||
// are conditions.
|
||||
func (session *Session) Count(bean ...interface{}) (int64, error) { |
||||
if session.isAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
var sqlStr string |
||||
var args []interface{} |
||||
var err error |
||||
if session.statement.RawSQL == "" { |
||||
sqlStr, args, err = session.statement.genCountSQL(bean...) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
} else { |
||||
sqlStr = session.statement.RawSQL |
||||
args = session.statement.RawParams |
||||
} |
||||
|
||||
var total int64 |
||||
err = session.queryRow(sqlStr, args...).Scan(&total) |
||||
if err == sql.ErrNoRows || err == nil { |
||||
return total, nil |
||||
} |
||||
|
||||
return 0, err |
||||
} |
||||
|
||||
// sum call sum some column. bean's non-empty fields are conditions.
|
||||
func (session *Session) sum(res interface{}, bean interface{}, columnNames ...string) error { |
||||
if session.isAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
v := reflect.ValueOf(res) |
||||
if v.Kind() != reflect.Ptr { |
||||
return errors.New("need a pointer to a variable") |
||||
} |
||||
|
||||
var isSlice = v.Elem().Kind() == reflect.Slice |
||||
var sqlStr string |
||||
var args []interface{} |
||||
var err error |
||||
if len(session.statement.RawSQL) == 0 { |
||||
sqlStr, args, err = session.statement.genSumSQL(bean, columnNames...) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
} else { |
||||
sqlStr = session.statement.RawSQL |
||||
args = session.statement.RawParams |
||||
} |
||||
|
||||
if isSlice { |
||||
err = session.queryRow(sqlStr, args...).ScanSlice(res) |
||||
} else { |
||||
err = session.queryRow(sqlStr, args...).Scan(res) |
||||
} |
||||
if err == sql.ErrNoRows || err == nil { |
||||
return nil |
||||
} |
||||
return err |
||||
} |
||||
|
||||
// Sum call sum some column. bean's non-empty fields are conditions.
|
||||
func (session *Session) Sum(bean interface{}, columnName string) (res float64, err error) { |
||||
return res, session.sum(&res, bean, columnName) |
||||
} |
||||
|
||||
// SumInt call sum some column. bean's non-empty fields are conditions.
|
||||
func (session *Session) SumInt(bean interface{}, columnName string) (res int64, err error) { |
||||
return res, session.sum(&res, bean, columnName) |
||||
} |
||||
|
||||
// Sums call sum some columns. bean's non-empty fields are conditions.
|
||||
func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) { |
||||
var res = make([]float64, len(columnNames), len(columnNames)) |
||||
return res, session.sum(&res, bean, columnNames...) |
||||
} |
||||
|
||||
// SumsInt sum specify columns and return as []int64 instead of []float64
|
||||
func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { |
||||
var res = make([]int64, len(columnNames), len(columnNames)) |
||||
return res, session.sum(&res, bean, columnNames...) |
||||
} |
@ -1,140 +0,0 @@
|
||||
// Copyright 2016 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 "database/sql" |
||||
|
||||
// Count counts the records. bean's non-empty fields
|
||||
// are conditions.
|
||||
func (session *Session) Count(bean ...interface{}) (int64, error) { |
||||
defer session.resetStatement() |
||||
if session.IsAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
var sqlStr string |
||||
var args []interface{} |
||||
if session.Statement.RawSQL == "" { |
||||
if len(bean) == 0 { |
||||
return 0, ErrTableNotFound |
||||
} |
||||
sqlStr, args = session.Statement.genCountSQL(bean[0]) |
||||
} else { |
||||
sqlStr = session.Statement.RawSQL |
||||
args = session.Statement.RawParams |
||||
} |
||||
|
||||
session.queryPreprocess(&sqlStr, args...) |
||||
|
||||
var err error |
||||
var total int64 |
||||
if session.IsAutoCommit { |
||||
err = session.DB().QueryRow(sqlStr, args...).Scan(&total) |
||||
} else { |
||||
err = session.Tx.QueryRow(sqlStr, args...).Scan(&total) |
||||
} |
||||
|
||||
if err == sql.ErrNoRows || err == nil { |
||||
return total, nil |
||||
} |
||||
|
||||
return 0, err |
||||
} |
||||
|
||||
// Sum call sum some column. bean's non-empty fields are conditions.
|
||||
func (session *Session) Sum(bean interface{}, columnName string) (float64, error) { |
||||
defer session.resetStatement() |
||||
if session.IsAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
var sqlStr string |
||||
var args []interface{} |
||||
if len(session.Statement.RawSQL) == 0 { |
||||
sqlStr, args = session.Statement.genSumSQL(bean, columnName) |
||||
} else { |
||||
sqlStr = session.Statement.RawSQL |
||||
args = session.Statement.RawParams |
||||
} |
||||
|
||||
session.queryPreprocess(&sqlStr, args...) |
||||
|
||||
var err error |
||||
var res float64 |
||||
if session.IsAutoCommit { |
||||
err = session.DB().QueryRow(sqlStr, args...).Scan(&res) |
||||
} else { |
||||
err = session.Tx.QueryRow(sqlStr, args...).Scan(&res) |
||||
} |
||||
|
||||
if err == sql.ErrNoRows || err == nil { |
||||
return res, nil |
||||
} |
||||
return 0, err |
||||
} |
||||
|
||||
// Sums call sum some columns. bean's non-empty fields are conditions.
|
||||
func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) { |
||||
defer session.resetStatement() |
||||
if session.IsAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
var sqlStr string |
||||
var args []interface{} |
||||
if len(session.Statement.RawSQL) == 0 { |
||||
sqlStr, args = session.Statement.genSumSQL(bean, columnNames...) |
||||
} else { |
||||
sqlStr = session.Statement.RawSQL |
||||
args = session.Statement.RawParams |
||||
} |
||||
|
||||
session.queryPreprocess(&sqlStr, args...) |
||||
|
||||
var err error |
||||
var res = make([]float64, len(columnNames), len(columnNames)) |
||||
if session.IsAutoCommit { |
||||
err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res) |
||||
} else { |
||||
err = session.Tx.QueryRow(sqlStr, args...).ScanSlice(&res) |
||||
} |
||||
|
||||
if err == sql.ErrNoRows || err == nil { |
||||
return res, nil |
||||
} |
||||
return nil, err |
||||
} |
||||
|
||||
// SumsInt sum specify columns and return as []int64 instead of []float64
|
||||
func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { |
||||
defer session.resetStatement() |
||||
if session.IsAutoClose { |
||||
defer session.Close() |
||||
} |
||||
|
||||
var sqlStr string |
||||
var args []interface{} |
||||
if len(session.Statement.RawSQL) == 0 { |
||||
sqlStr, args = session.Statement.genSumSQL(bean, columnNames...) |
||||
} else { |
||||
sqlStr = session.Statement.RawSQL |
||||
args = session.Statement.RawParams |
||||
} |
||||
|
||||
session.queryPreprocess(&sqlStr, args...) |
||||
|
||||
var err error |
||||
var res = make([]int64, len(columnNames), len(columnNames)) |
||||
if session.IsAutoCommit { |
||||
err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res) |
||||
} else { |
||||
err = session.Tx.QueryRow(sqlStr, args...).ScanSlice(&res) |
||||
} |
||||
|
||||
if err == sql.ErrNoRows || err == nil { |
||||
return res, nil |
||||
} |
||||
return nil, err |
||||
} |
@ -0,0 +1 @@
|
||||
go test -db=mssql -conn_str="server=192.168.1.58;user id=sa;password=123456;database=xorm_test" -cache=true |
@ -0,0 +1 @@
|
||||
go test -db=mymysql -conn_str="xorm_test/root/" |
@ -0,0 +1 @@
|
||||
go test -db=mymysql -conn_str="xorm_test/root/" -cache=true |
@ -0,0 +1 @@
|
||||
go test -db=mysql -conn_str="root:@/xorm_test" -cache=true |
@ -0,0 +1 @@
|
||||
go test -db=postgres -conn_str="dbname=xorm_test sslmode=disable" -cache=true |
@ -0,0 +1 @@
|
||||
go test -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" -cache=true |
Loading…
Reference in new issue