mirror of
https://gitee.com/johng/gf.git
synced 2024-12-02 04:07:47 +08:00
Merge branch 'develop-up' into develop
* develop-up: (38 commits) revert gerror usage for package gvalid unify error package to gerror add CtxStrict feature for package gdb rename constants of package gpage from skake to upper camel case add context for intlog improve Record/Result converting for package gdb add context for intlog/gsession;improve struct/structs converting for package gconv unify command or enviroment key names for packages improve details for package glog add file configuration support for logger in ghttp.Server version updates add more unit testing cases for package ghttp add automatic fields mapping and filtering for Model.Where statements improve Order feature for package gdb improve function Increment/Decrement for package gdb add auto fields filtering feature for function Scan of package gdb; mark funtcion Struct/Structs deprecated for gdb.Model improve record converting for package gdb improve transaction feature for package gdb improve caller path filtering for package gdebug improve caller path filtering for package gdebug ... # Conflicts: # os/glog/glog_logger.go # os/glog/glog_logger_config.go # os/glog/glog_logger_level.go
This commit is contained in:
commit
9b2497bc57
@ -10,9 +10,9 @@ func main() {
|
||||
//db := g.DB()
|
||||
|
||||
gdb.AddDefaultConfigNode(gdb.ConfigNode{
|
||||
LinkInfo: "root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local",
|
||||
Type: "mysql",
|
||||
Charset: "utf8",
|
||||
Link: "root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local",
|
||||
Type: "mysql",
|
||||
Charset: "utf8",
|
||||
})
|
||||
db, _ := gdb.New()
|
||||
|
||||
|
@ -8,8 +8,8 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
@ -123,7 +123,7 @@ func (a *Array) Set(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
a.array[index] = value
|
||||
return nil
|
||||
@ -176,7 +176,7 @@ func (a *Array) InsertBefore(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]interface{}{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
@ -189,7 +189,7 @@ func (a *Array) InsertAfter(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]interface{}{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
@ -545,7 +545,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", startIndex, len(a.array))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
|
@ -8,8 +8,8 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"math"
|
||||
"sort"
|
||||
@ -104,7 +104,7 @@ func (a *IntArray) Set(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
a.array[index] = value
|
||||
return nil
|
||||
@ -175,7 +175,7 @@ func (a *IntArray) InsertBefore(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]int{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
@ -188,7 +188,7 @@ func (a *IntArray) InsertAfter(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]int{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
@ -559,7 +559,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", startIndex, len(a.array))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
|
@ -8,8 +8,7 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
@ -91,7 +90,7 @@ func (a *StrArray) Set(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
a.array[index] = value
|
||||
return nil
|
||||
@ -163,7 +162,7 @@ func (a *StrArray) InsertBefore(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]string{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
@ -176,7 +175,7 @@ func (a *StrArray) InsertAfter(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", index, len(a.array))
|
||||
}
|
||||
rear := append([]string{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
@ -563,7 +562,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
|
||||
return gerror.Newf("index %d out of array range %d", startIndex, len(a.array))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
|
@ -8,7 +8,7 @@
|
||||
package gpool
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/glist"
|
||||
@ -66,7 +66,7 @@ func New(ttl time.Duration, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool {
|
||||
// Put puts an item to pool.
|
||||
func (p *Pool) Put(value interface{}) error {
|
||||
if p.closed.Val() {
|
||||
return errors.New("pool is closed")
|
||||
return gerror.New("pool is closed")
|
||||
}
|
||||
item := &poolItem{
|
||||
value: value,
|
||||
@ -117,7 +117,7 @@ func (p *Pool) Get() (interface{}, error) {
|
||||
if p.NewFunc != nil {
|
||||
return p.NewFunc()
|
||||
}
|
||||
return nil, errors.New("pool is empty")
|
||||
return nil, gerror.New("pool is empty")
|
||||
}
|
||||
|
||||
// Size returns the count of available items of pool.
|
||||
|
@ -21,7 +21,7 @@ type StrSet struct {
|
||||
data map[string]struct{}
|
||||
}
|
||||
|
||||
// New create and returns a new set, which contains un-repeated items.
|
||||
// NewStrSet create and returns a new set, which contains un-repeated items.
|
||||
// The parameter <safe> is used to specify whether using set in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrSet(safe ...bool) *StrSet {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -63,7 +63,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) {
|
||||
}
|
||||
blockSize := block.BlockSize()
|
||||
if len(cipherText) < blockSize {
|
||||
return nil, errors.New("cipherText too short")
|
||||
return nil, gerror.New("cipherText too short")
|
||||
}
|
||||
ivValue := ([]byte)(nil)
|
||||
if len(iv) > 0 {
|
||||
@ -72,7 +72,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) {
|
||||
ivValue = []byte(IVDefaultValue)
|
||||
}
|
||||
if len(cipherText)%blockSize != 0 {
|
||||
return nil, errors.New("cipherText is not a multiple of the block size")
|
||||
return nil, gerror.New("cipherText is not a multiple of the block size")
|
||||
}
|
||||
blockModel := cipher.NewCBCDecrypter(block, ivValue)
|
||||
plainText := make([]byte, len(cipherText))
|
||||
@ -93,22 +93,22 @@ func PKCS5Padding(src []byte, blockSize int) []byte {
|
||||
func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) {
|
||||
length := len(src)
|
||||
if blockSize <= 0 {
|
||||
return nil, errors.New("invalid blocklen")
|
||||
return nil, gerror.New("invalid blocklen")
|
||||
}
|
||||
|
||||
if length%blockSize != 0 || length == 0 {
|
||||
return nil, errors.New("invalid data len")
|
||||
return nil, gerror.New("invalid data len")
|
||||
}
|
||||
|
||||
unpadding := int(src[length-1])
|
||||
if unpadding > blockSize || unpadding == 0 {
|
||||
return nil, errors.New("invalid padding")
|
||||
return nil, gerror.New("invalid padding")
|
||||
}
|
||||
|
||||
padding := src[length-unpadding:]
|
||||
for i := 0; i < unpadding; i++ {
|
||||
if padding[i] != byte(unpadding) {
|
||||
return nil, errors.New("invalid padding")
|
||||
return nil, gerror.New("invalid padding")
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ func DecryptCFB(cipherText []byte, key []byte, unPadding int, iv ...[]byte) ([]b
|
||||
return nil, err
|
||||
}
|
||||
if len(cipherText) < aes.BlockSize {
|
||||
return nil, errors.New("cipherText too short")
|
||||
return nil, gerror.New("cipherText too short")
|
||||
}
|
||||
ivValue := ([]byte)(nil)
|
||||
if len(iv) > 0 {
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -66,7 +66,7 @@ func DecryptECB(cipherText []byte, key []byte, padding int) ([]byte, error) {
|
||||
// The length of the <key> should be either 16 or 24 bytes.
|
||||
func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error) {
|
||||
if len(key) != 16 && len(key) != 24 {
|
||||
return nil, errors.New("key length error")
|
||||
return nil, gerror.New("key length error")
|
||||
}
|
||||
|
||||
text, err := Padding(plainText, padding)
|
||||
@ -100,7 +100,7 @@ func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error)
|
||||
// The length of the <key> should be either 16 or 24 bytes.
|
||||
func DecryptECBTriple(cipherText []byte, key []byte, padding int) ([]byte, error) {
|
||||
if len(key) != 16 && len(key) != 24 {
|
||||
return nil, errors.New("key length error")
|
||||
return nil, gerror.New("key length error")
|
||||
}
|
||||
|
||||
var newKey []byte
|
||||
@ -138,7 +138,7 @@ func EncryptCBC(plainText []byte, key []byte, iv []byte, padding int) ([]byte, e
|
||||
}
|
||||
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("iv length invalid")
|
||||
return nil, gerror.New("iv length invalid")
|
||||
}
|
||||
|
||||
text, err := Padding(plainText, padding)
|
||||
@ -161,7 +161,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte,
|
||||
}
|
||||
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("iv length invalid")
|
||||
return nil, gerror.New("iv length invalid")
|
||||
}
|
||||
|
||||
text := make([]byte, len(cipherText))
|
||||
@ -179,7 +179,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte,
|
||||
// EncryptCBCTriple encrypts <plainText> using TripleDES and CBC mode.
|
||||
func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]byte, error) {
|
||||
if len(key) != 16 && len(key) != 24 {
|
||||
return nil, errors.New("key length invalid")
|
||||
return nil, gerror.New("key length invalid")
|
||||
}
|
||||
|
||||
var newKey []byte
|
||||
@ -196,7 +196,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b
|
||||
}
|
||||
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("iv length invalid")
|
||||
return nil, gerror.New("iv length invalid")
|
||||
}
|
||||
|
||||
text, err := Padding(plainText, padding)
|
||||
@ -214,7 +214,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b
|
||||
// DecryptCBCTriple decrypts <cipherText> using TripleDES and CBC mode.
|
||||
func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, error) {
|
||||
if len(key) != 16 && len(key) != 24 {
|
||||
return nil, errors.New("key length invalid")
|
||||
return nil, gerror.New("key length invalid")
|
||||
}
|
||||
|
||||
var newKey []byte
|
||||
@ -231,7 +231,7 @@ func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([]
|
||||
}
|
||||
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("iv length invalid")
|
||||
return nil, gerror.New("iv length invalid")
|
||||
}
|
||||
|
||||
text := make([]byte, len(cipherText))
|
||||
@ -262,12 +262,12 @@ func Padding(text []byte, padding int) ([]byte, error) {
|
||||
switch padding {
|
||||
case NOPADDING:
|
||||
if len(text)%8 != 0 {
|
||||
return nil, errors.New("text length invalid")
|
||||
return nil, gerror.New("text length invalid")
|
||||
}
|
||||
case PKCS5PADDING:
|
||||
return PaddingPKCS5(text, 8), nil
|
||||
default:
|
||||
return nil, errors.New("padding type error")
|
||||
return nil, gerror.New("padding type error")
|
||||
}
|
||||
|
||||
return text, nil
|
||||
@ -277,12 +277,12 @@ func UnPadding(text []byte, padding int) ([]byte, error) {
|
||||
switch padding {
|
||||
case NOPADDING:
|
||||
if len(text)%8 != 0 {
|
||||
return nil, errors.New("text length invalid")
|
||||
return nil, gerror.New("text length invalid")
|
||||
}
|
||||
case PKCS5PADDING:
|
||||
return UnPaddingPKCS5(text), nil
|
||||
default:
|
||||
return nil, errors.New("padding type error")
|
||||
return nil, gerror.New("padding type error")
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ type DB interface {
|
||||
// relational databases but also for NoSQL databases in the future. The name
|
||||
// "Table" is not proper for that purpose any more.
|
||||
// Also see Core.Table.
|
||||
// Deprecated.
|
||||
Table(tableNameOrStruct ...interface{}) *Model
|
||||
|
||||
// Model creates and returns a new ORM model from given schema.
|
||||
@ -51,6 +52,9 @@ type DB interface {
|
||||
// Also see Core.Model.
|
||||
Model(tableNameOrStruct ...interface{}) *Model
|
||||
|
||||
// Raw creates and returns a model based on a raw sql not a table.
|
||||
Raw(rawSql string, args ...interface{}) *Model
|
||||
|
||||
// Schema creates and returns a schema.
|
||||
// Also see Core.Schema.
|
||||
Schema(schema string) *Schema
|
||||
@ -66,8 +70,6 @@ type DB interface {
|
||||
|
||||
// Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
|
||||
// of current DB object and with given context in it.
|
||||
// Note that this returned DB object can be used only once, so do not assign it to
|
||||
// a global or package variable for long using.
|
||||
// Also see Core.Ctx.
|
||||
Ctx(ctx context.Context) DB
|
||||
|
||||
@ -83,16 +85,11 @@ type DB interface {
|
||||
// Common APIs for CURD.
|
||||
// ===========================================================================
|
||||
|
||||
Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert.
|
||||
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore.
|
||||
InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) // See Core.InsertAndGetId.
|
||||
Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace.
|
||||
Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save.
|
||||
|
||||
BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchInsert.
|
||||
BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchReplace.
|
||||
BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchSave.
|
||||
|
||||
Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert.
|
||||
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore.
|
||||
InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) // See Core.InsertAndGetId.
|
||||
Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace.
|
||||
Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save.
|
||||
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update.
|
||||
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete.
|
||||
|
||||
@ -100,27 +97,27 @@ type DB interface {
|
||||
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
|
||||
// ===========================================================================
|
||||
|
||||
DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery.
|
||||
DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
|
||||
DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
|
||||
DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll.
|
||||
DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoInsert.
|
||||
DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoBatchInsert.
|
||||
DoInsert(ctx context.Context, link Link, table string, data List, option DoInsertOption) (result sql.Result, err error) // See Core.DoInsert.
|
||||
DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate.
|
||||
DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete.
|
||||
DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery.
|
||||
DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
|
||||
DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) // See Core.DoCommit.
|
||||
DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
|
||||
|
||||
// ===========================================================================
|
||||
// Query APIs for convenience purpose.
|
||||
// ===========================================================================
|
||||
|
||||
GetAll(sql string, args ...interface{}) (Result, error) // See Core.GetAll.
|
||||
GetOne(sql string, args ...interface{}) (Record, error) // See Core.GetOne.
|
||||
GetValue(sql string, args ...interface{}) (Value, error) // See Core.GetValue.
|
||||
GetArray(sql string, args ...interface{}) ([]Value, error) // See Core.GetArray.
|
||||
GetCount(sql string, args ...interface{}) (int, error) // See Core.GetCount.
|
||||
GetStruct(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetStruct.
|
||||
GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error // See Core.GetStructs.
|
||||
GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan.
|
||||
GetAll(sql string, args ...interface{}) (Result, error) // See Core.GetAll.
|
||||
GetOne(sql string, args ...interface{}) (Record, error) // See Core.GetOne.
|
||||
GetValue(sql string, args ...interface{}) (Value, error) // See Core.GetValue.
|
||||
GetArray(sql string, args ...interface{}) ([]Value, error) // See Core.GetArray.
|
||||
GetCount(sql string, args ...interface{}) (int, error) // See Core.GetCount.
|
||||
GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan.
|
||||
Union(unions ...*Model) *Model // See Core.Union.
|
||||
UnionAll(unions ...*Model) *Model // See Core.UnionAll.
|
||||
|
||||
// ===========================================================================
|
||||
// Master/Slave specification support.
|
||||
@ -172,14 +169,7 @@ type DB interface {
|
||||
GetChars() (charLeft string, charRight string) // See Core.GetChars.
|
||||
Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables.
|
||||
TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields.
|
||||
FilteredLinkInfo() string // See Core.FilteredLinkInfo.
|
||||
|
||||
// HandleSqlBeforeCommit is a hook function, which deals with the sql string before
|
||||
// it's committed to underlying driver. The parameter `link` specifies the current
|
||||
// database connection operation object. You can modify the sql string `sql` and its
|
||||
// arguments `args` as you wish before they're committed to driver.
|
||||
// Also see Core.HandleSqlBeforeCommit.
|
||||
HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{})
|
||||
FilteredLink() string // FilteredLink is used for filtering sensitive information in `Link` configuration before output it to tracing server.
|
||||
}
|
||||
|
||||
// Core is the base struct for database management.
|
||||
@ -224,6 +214,14 @@ type Sql struct {
|
||||
IsTransaction bool // IsTransaction marks whether this sql is executed in transaction.
|
||||
}
|
||||
|
||||
// DoInsertOption is the input struct for function DoInsert.
|
||||
type DoInsertOption struct {
|
||||
OnDuplicateStr string
|
||||
OnDuplicateMap map[string]interface{}
|
||||
InsertOption int // Insert operation.
|
||||
BatchCount int // Batch count for batch inserting.
|
||||
}
|
||||
|
||||
// TableField is the struct for table field.
|
||||
type TableField struct {
|
||||
Index int // For ordering purpose as map is unordered.
|
||||
@ -252,6 +250,10 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
queryTypeNormal = 0
|
||||
queryTypeCount = 1
|
||||
unionTypeNormal = 0
|
||||
unionTypeAll = 1
|
||||
insertOptionDefault = 0
|
||||
insertOptionReplace = 1
|
||||
insertOptionSave = 2
|
||||
@ -263,6 +265,9 @@ const (
|
||||
ctxTimeoutTypeExec = iota
|
||||
ctxTimeoutTypeQuery
|
||||
ctxTimeoutTypePrepare
|
||||
commandEnvKeyForDryRun = "gf.gdb.dryrun"
|
||||
ctxStrictKeyName = "gf.gdb.CtxStrictEnabled"
|
||||
ctxStrictErrorStr = "context is required for database operation, did you missing call function Ctx"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -307,7 +312,7 @@ var (
|
||||
|
||||
func init() {
|
||||
// allDryRun is initialized from environment or command options.
|
||||
allDryRun = gcmd.GetOptWithEnv("gf.gdb.dryrun", false).Bool()
|
||||
allDryRun = gcmd.GetOptWithEnv(commandEnvKeyForDryRun, false).Bool()
|
||||
}
|
||||
|
||||
// Register registers custom database driver to gdb.
|
||||
@ -479,14 +484,16 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
|
||||
// Cache the underlying connection pool object by node.
|
||||
v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) {
|
||||
intlog.Printf(
|
||||
c.db.GetCtx(),
|
||||
`open new connection, master:%#v, config:%#v, node:%#v`,
|
||||
master, c.config, node,
|
||||
)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
intlog.Printf(`open new connection failed: %v, %#v`, err, node)
|
||||
intlog.Printf(c.db.GetCtx(), `open new connection failed: %v, %#v`, err, node)
|
||||
} else {
|
||||
intlog.Printf(
|
||||
c.db.GetCtx(),
|
||||
`open new connection success, master:%#v, config:%#v, node:%#v`,
|
||||
master, c.config, node,
|
||||
)
|
||||
|
@ -40,6 +40,7 @@ func (c *Core) Ctx(ctx context.Context) DB {
|
||||
if c.ctx != nil {
|
||||
return c.db
|
||||
}
|
||||
ctx = context.WithValue(ctx, ctxStrictKeyName, 1)
|
||||
// It makes a shallow copy of current db and changes its context for next chaining operation.
|
||||
var (
|
||||
err error
|
||||
@ -189,9 +190,9 @@ func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) err
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array, reflect.Slice:
|
||||
return c.db.GetStructs(pointer, sql, args...)
|
||||
return c.db.GetCore().GetStructs(pointer, sql, args...)
|
||||
case reflect.Struct:
|
||||
return c.db.GetStruct(pointer, sql, args...)
|
||||
return c.db.GetCore().GetStruct(pointer, sql, args...)
|
||||
}
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
@ -224,6 +225,39 @@ func (c *Core) GetCount(sql string, args ...interface{}) (int, error) {
|
||||
return value.Int(), nil
|
||||
}
|
||||
|
||||
// Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement.
|
||||
func (c *Core) Union(unions ...*Model) *Model {
|
||||
return c.doUnion(unionTypeNormal, unions...)
|
||||
}
|
||||
|
||||
// UnionAll does "(SELECT xxx FROM xxx) UNION ALL (SELECT xxx FROM xxx) ..." statement.
|
||||
func (c *Core) UnionAll(unions ...*Model) *Model {
|
||||
return c.doUnion(unionTypeAll, unions...)
|
||||
}
|
||||
|
||||
func (c *Core) doUnion(unionType int, unions ...*Model) *Model {
|
||||
var (
|
||||
unionTypeStr string
|
||||
composedSqlStr string
|
||||
composedArgs = make([]interface{}, 0)
|
||||
)
|
||||
if unionType == unionTypeAll {
|
||||
unionTypeStr = "UNION ALL"
|
||||
} else {
|
||||
unionTypeStr = "UNION"
|
||||
}
|
||||
for _, v := range unions {
|
||||
sqlWithHolder, holderArgs := v.getFormattedSqlAndArgs(queryTypeNormal, false)
|
||||
if composedSqlStr == "" {
|
||||
composedSqlStr += fmt.Sprintf(`(%s)`, sqlWithHolder)
|
||||
} else {
|
||||
composedSqlStr += fmt.Sprintf(` %s (%s)`, unionTypeStr, sqlWithHolder)
|
||||
}
|
||||
composedArgs = append(composedArgs, holderArgs...)
|
||||
}
|
||||
return c.db.Raw(composedSqlStr, composedArgs...)
|
||||
}
|
||||
|
||||
// PingMaster pings the master node to check authentication or keeps the connection alive.
|
||||
func (c *Core) PingMaster() error {
|
||||
if master, err := c.db.Master(); err != nil {
|
||||
@ -331,181 +365,15 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e
|
||||
// 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one;
|
||||
// 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one;
|
||||
// 3: ignore: if there's unique/primary key in the data, it ignores the inserting;
|
||||
func (c *Core) DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
|
||||
table = c.QuotePrefixTableName(table)
|
||||
func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
var (
|
||||
fields []string
|
||||
values []string
|
||||
params []interface{}
|
||||
dataMap Map
|
||||
reflectValue = reflect.ValueOf(data)
|
||||
reflectKind = reflectValue.Kind()
|
||||
keys []string // Field names.
|
||||
values []string // Value holder string array, like: (?,?,?)
|
||||
params []interface{} // Values that will be committed to underlying database driver.
|
||||
onDuplicateStr string // onDuplicateStr is used in "ON DUPLICATE KEY UPDATE" statement.
|
||||
)
|
||||
if reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return c.db.DoBatchInsert(ctx, link, table, data, option, batch...)
|
||||
case reflect.Struct:
|
||||
if _, ok := data.(apiInterfaces); ok {
|
||||
return c.db.DoBatchInsert(ctx, link, table, data, option, batch...)
|
||||
} else {
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
}
|
||||
case reflect.Map:
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
default:
|
||||
return result, gerror.New(fmt.Sprint("unsupported data type:", reflectKind))
|
||||
}
|
||||
if len(dataMap) == 0 {
|
||||
return nil, gerror.New("data cannot be empty")
|
||||
}
|
||||
var (
|
||||
charL, charR = c.db.GetChars()
|
||||
operation = GetInsertOperationByOption(option)
|
||||
updateStr = ""
|
||||
)
|
||||
for k, v := range dataMap {
|
||||
fields = append(fields, charL+k+charR)
|
||||
if s, ok := v.(Raw); ok {
|
||||
values = append(values, gconv.String(s))
|
||||
} else {
|
||||
values = append(values, "?")
|
||||
params = append(params, v)
|
||||
}
|
||||
}
|
||||
if option == insertOptionSave {
|
||||
for k, _ := range dataMap {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
if c.isSoftCreatedFiledName(k) {
|
||||
continue
|
||||
}
|
||||
if len(updateStr) > 0 {
|
||||
updateStr += ","
|
||||
}
|
||||
updateStr += fmt.Sprintf(
|
||||
"%s%s%s=VALUES(%s%s%s)",
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
)
|
||||
}
|
||||
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr)
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = c.MasterLink(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.db.DoExec(ctx, link, fmt.Sprintf(
|
||||
"%s INTO %s(%s) VALUES(%s) %s",
|
||||
operation, table, strings.Join(fields, ","),
|
||||
strings.Join(values, ","), updateStr,
|
||||
), params...)
|
||||
}
|
||||
|
||||
// BatchInsert batch inserts data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (c *Core) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(list).Batch(batch[0]).Insert()
|
||||
}
|
||||
return c.Model(table).Data(list).Insert()
|
||||
}
|
||||
|
||||
// BatchInsertIgnore batch inserts data with ignore option.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (c *Core) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(list).Batch(batch[0]).InsertIgnore()
|
||||
}
|
||||
return c.Model(table).Data(list).InsertIgnore()
|
||||
}
|
||||
|
||||
// BatchReplace batch replaces data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (c *Core) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(list).Batch(batch[0]).Replace()
|
||||
}
|
||||
return c.Model(table).Data(list).Replace()
|
||||
}
|
||||
|
||||
// BatchSave batch replaces data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(list).Batch(batch[0]).Save()
|
||||
}
|
||||
return c.Model(table).Data(list).Save()
|
||||
}
|
||||
|
||||
// DoBatchInsert batch inserts/replaces/saves data.
|
||||
// This function is usually used for custom interface definition, you do not need call it manually.
|
||||
func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
|
||||
table = c.QuotePrefixTableName(table)
|
||||
var (
|
||||
keys []string // Field names.
|
||||
values []string // Value holder string array, like: (?,?,?)
|
||||
params []interface{} // Values that will be committed to underlying database driver.
|
||||
listMap List // The data list that passed from caller.
|
||||
)
|
||||
switch value := list.(type) {
|
||||
case Result:
|
||||
listMap = value.List()
|
||||
case Record:
|
||||
listMap = List{value.Map()}
|
||||
case List:
|
||||
listMap = value
|
||||
case Map:
|
||||
listMap = List{value}
|
||||
default:
|
||||
var (
|
||||
rv = reflect.ValueOf(list)
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// If it's slice type, it then converts it to List type.
|
||||
case reflect.Slice, reflect.Array:
|
||||
listMap = make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
|
||||
}
|
||||
case reflect.Map:
|
||||
listMap = List{ConvertDataForTableRecord(value)}
|
||||
case reflect.Struct:
|
||||
if v, ok := value.(apiInterfaces); ok {
|
||||
var (
|
||||
array = v.Interfaces()
|
||||
list = make(List, len(array))
|
||||
)
|
||||
for i := 0; i < len(array); i++ {
|
||||
list[i] = ConvertDataForTableRecord(array[i])
|
||||
}
|
||||
listMap = list
|
||||
} else {
|
||||
listMap = List{ConvertDataForTableRecord(value)}
|
||||
}
|
||||
default:
|
||||
return result, gerror.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
}
|
||||
if len(listMap) < 1 {
|
||||
return result, gerror.New("data list cannot be empty")
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = c.MasterLink(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Handle the field names and place holders.
|
||||
for k, _ := range listMap[0] {
|
||||
for k, _ := range list[0] {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
// Prepare the batch result pointer.
|
||||
@ -513,54 +381,35 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list
|
||||
charL, charR = c.db.GetChars()
|
||||
batchResult = new(SqlResult)
|
||||
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
|
||||
operation = GetInsertOperationByOption(option)
|
||||
updateStr = ""
|
||||
operation = GetInsertOperationByOption(option.InsertOption)
|
||||
)
|
||||
if option == insertOptionSave {
|
||||
for _, k := range keys {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
if c.isSoftCreatedFiledName(k) {
|
||||
continue
|
||||
}
|
||||
if len(updateStr) > 0 {
|
||||
updateStr += ","
|
||||
}
|
||||
updateStr += fmt.Sprintf(
|
||||
"%s%s%s=VALUES(%s%s%s)",
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
)
|
||||
}
|
||||
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr)
|
||||
}
|
||||
batchNum := defaultBatchNumber
|
||||
if len(batch) > 0 && batch[0] > 0 {
|
||||
batchNum = batch[0]
|
||||
if option.InsertOption == insertOptionSave {
|
||||
onDuplicateStr = c.formatOnDuplicate(keys, option)
|
||||
}
|
||||
var (
|
||||
listMapLen = len(listMap)
|
||||
listLength = len(list)
|
||||
valueHolder = make([]string, 0)
|
||||
)
|
||||
for i := 0; i < listMapLen; i++ {
|
||||
for i := 0; i < listLength; i++ {
|
||||
values = values[:0]
|
||||
// Note that the map type is unordered,
|
||||
// so it should use slice+key to retrieve the value.
|
||||
for _, k := range keys {
|
||||
if s, ok := listMap[i][k].(Raw); ok {
|
||||
if s, ok := list[i][k].(Raw); ok {
|
||||
values = append(values, gconv.String(s))
|
||||
} else {
|
||||
values = append(values, "?")
|
||||
params = append(params, listMap[i][k])
|
||||
params = append(params, list[i][k])
|
||||
}
|
||||
}
|
||||
valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")")
|
||||
if len(valueHolder) == batchNum || (i == listMapLen-1 && len(valueHolder) > 0) {
|
||||
// Batch package checks: It meets the batch number or it is the last element.
|
||||
if len(valueHolder) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) {
|
||||
r, err := c.db.DoExec(ctx, link, fmt.Sprintf(
|
||||
"%s INTO %s(%s) VALUES%s %s",
|
||||
operation, table, keysStr,
|
||||
operation, c.QuotePrefixTableName(table), keysStr,
|
||||
gstr.Join(valueHolder, ","),
|
||||
updateStr,
|
||||
onDuplicateStr,
|
||||
), params...)
|
||||
if err != nil {
|
||||
return r, err
|
||||
@ -578,6 +427,52 @@ func (c *Core) DoBatchInsert(ctx context.Context, link Link, table string, list
|
||||
return batchResult, nil
|
||||
}
|
||||
|
||||
func (c *Core) formatOnDuplicate(columns []string, option DoInsertOption) string {
|
||||
var (
|
||||
onDuplicateStr string
|
||||
)
|
||||
if option.OnDuplicateStr != "" {
|
||||
onDuplicateStr = option.OnDuplicateStr
|
||||
} else if len(option.OnDuplicateMap) > 0 {
|
||||
for k, v := range option.OnDuplicateMap {
|
||||
if len(onDuplicateStr) > 0 {
|
||||
onDuplicateStr += ","
|
||||
}
|
||||
switch v.(type) {
|
||||
case Raw, *Raw:
|
||||
onDuplicateStr += fmt.Sprintf(
|
||||
"%s=%s",
|
||||
c.QuoteWord(k),
|
||||
v,
|
||||
)
|
||||
default:
|
||||
onDuplicateStr += fmt.Sprintf(
|
||||
"%s=VALUES(%s)",
|
||||
c.QuoteWord(k),
|
||||
c.QuoteWord(gconv.String(v)),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, column := range columns {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
if c.isSoftCreatedFilledName(column) {
|
||||
continue
|
||||
}
|
||||
if len(onDuplicateStr) > 0 {
|
||||
onDuplicateStr += ","
|
||||
}
|
||||
onDuplicateStr += fmt.Sprintf(
|
||||
"%s=VALUES(%s)",
|
||||
c.QuoteWord(column),
|
||||
c.QuoteWord(column),
|
||||
)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", onDuplicateStr)
|
||||
}
|
||||
|
||||
// Update does "UPDATE ... " statement for the table.
|
||||
//
|
||||
// The parameter `data` can be type of string/map/gmap/struct/*struct, etc.
|
||||
@ -711,7 +606,7 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
|
||||
}
|
||||
var (
|
||||
values = make([]interface{}, len(columnNames))
|
||||
records = make(Result, 0)
|
||||
result = make(Result, 0)
|
||||
scanArgs = make([]interface{}, len(values))
|
||||
)
|
||||
for i := range values {
|
||||
@ -719,22 +614,22 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
|
||||
}
|
||||
for {
|
||||
if err := rows.Scan(scanArgs...); err != nil {
|
||||
return records, err
|
||||
return result, err
|
||||
}
|
||||
row := make(Record)
|
||||
record := Record{}
|
||||
for i, value := range values {
|
||||
if value == nil {
|
||||
row[columnNames[i]] = gvar.New(nil)
|
||||
record[columnNames[i]] = gvar.New(nil)
|
||||
} else {
|
||||
row[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i]))
|
||||
record[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i]))
|
||||
}
|
||||
}
|
||||
records = append(records, row)
|
||||
result = append(result, record)
|
||||
if !rows.Next() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return records, nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
@ -778,8 +673,8 @@ func (c *Core) HasTable(name string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// isSoftCreatedFiledName checks and returns whether given filed name is an automatic-filled created time.
|
||||
func (c *Core) isSoftCreatedFiledName(fieldName string) bool {
|
||||
// isSoftCreatedFilledName checks and returns whether given filed name is an automatic-filled created time.
|
||||
func (c *Core) isSoftCreatedFilledName(fieldName string) bool {
|
||||
if fieldName == "" {
|
||||
return false
|
||||
}
|
||||
|
@ -30,13 +30,14 @@ type ConfigNode struct {
|
||||
Pass string `json:"pass"` // Authentication password.
|
||||
Name string `json:"name"` // Default used database name.
|
||||
Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle.
|
||||
Link string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
|
||||
Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
|
||||
Debug bool `json:"debug"` // (Optional) Debug mode enables debug information logging and output.
|
||||
Prefix string `json:"prefix"` // (Optional) Table prefix.
|
||||
DryRun bool `json:"dryRun"` // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
|
||||
Weight int `json:"weight"` // (Optional) Weight for load balance calculating, it's useless if there's just one node.
|
||||
Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database.
|
||||
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
|
||||
Timezone string `json:"timezone"` // (Optional) Sets the time zone for displaying and interpreting time stamps.
|
||||
MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool.
|
||||
MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool.
|
||||
MaxConnLifeTime time.Duration `json:"maxLifeTime"` // (Optional) Max amount of time a connection may be idle before being closed.
|
||||
@ -48,6 +49,7 @@ type ConfigNode struct {
|
||||
UpdatedAt string `json:"updatedAt"` // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
DeletedAt string `json:"deletedAt"` // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature.
|
||||
CtxStrict bool `json:"ctxStrict"` // (Optional) Strictly require context input for all database operations.
|
||||
}
|
||||
|
||||
const (
|
||||
@ -186,7 +188,7 @@ func (node *ConfigNode) String() string {
|
||||
node.MaxIdleConnCount,
|
||||
node.MaxOpenConnCount,
|
||||
node.MaxConnLifeTime,
|
||||
node.LinkInfo,
|
||||
node.Link,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
@ -34,19 +33,9 @@ const (
|
||||
tracingEventDbExecutionType = "db.execution.type"
|
||||
)
|
||||
|
||||
var (
|
||||
// tracingInternal enables tracing for internal type spans.
|
||||
// It's true in default.
|
||||
tracingInternal = true
|
||||
)
|
||||
|
||||
func init() {
|
||||
tracingInternal = gcmd.GetOptWithEnv("gf.tracing.internal", true).Bool()
|
||||
}
|
||||
|
||||
// addSqlToTracing adds sql information to tracer if it's enabled.
|
||||
func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) {
|
||||
if !tracingInternal || !gtrace.IsActivated(ctx) {
|
||||
if !gtrace.IsTracingInternal() || !gtrace.IsActivated(ctx) {
|
||||
return
|
||||
}
|
||||
tr := otel.GetTracerProvider().Tracer(
|
||||
@ -76,8 +65,8 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) {
|
||||
if c.db.GetConfig().User != "" {
|
||||
labels = append(labels, attribute.String(tracingAttrDbUser, c.db.GetConfig().User))
|
||||
}
|
||||
if filteredLinkInfo := c.db.FilteredLinkInfo(); filteredLinkInfo != "" {
|
||||
labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLinkInfo()))
|
||||
if filteredLink := c.db.FilteredLink(); filteredLink != "" {
|
||||
labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLink()))
|
||||
}
|
||||
if group := c.db.GetGroup(); group != "" {
|
||||
labels = append(labels, attribute.String(tracingAttrDbGroup, group))
|
||||
|
@ -28,6 +28,7 @@ type TX struct {
|
||||
master *sql.DB // master is the raw and underlying database manager.
|
||||
transactionId string // transactionId is an unique id generated by this object for this transaction.
|
||||
transactionCount int // transactionCount marks the times that Begins.
|
||||
isClosed bool // isClosed marks this transaction has already been committed or rolled back.
|
||||
}
|
||||
|
||||
const (
|
||||
@ -162,6 +163,9 @@ func TXFromCtx(ctx context.Context, group string) *TX {
|
||||
v := ctx.Value(transactionKeyForContext(group))
|
||||
if v != nil {
|
||||
tx := v.(*TX)
|
||||
if tx.IsClosed() {
|
||||
return nil
|
||||
}
|
||||
tx.ctx = ctx
|
||||
return tx
|
||||
}
|
||||
@ -210,6 +214,7 @@ func (tx *TX) Commit() error {
|
||||
IsTransaction: true,
|
||||
}
|
||||
)
|
||||
tx.isClosed = true
|
||||
tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj)
|
||||
if tx.db.GetDebug() {
|
||||
tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj)
|
||||
@ -243,6 +248,7 @@ func (tx *TX) Rollback() error {
|
||||
IsTransaction: true,
|
||||
}
|
||||
)
|
||||
tx.isClosed = true
|
||||
tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj)
|
||||
if tx.db.GetDebug() {
|
||||
tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj)
|
||||
@ -250,6 +256,11 @@ func (tx *TX) Rollback() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// IsClosed checks and returns this transaction has already been committed or rolled back.
|
||||
func (tx *TX) IsClosed() bool {
|
||||
return tx.isClosed
|
||||
}
|
||||
|
||||
// Begin starts a nested transaction procedure.
|
||||
func (tx *TX) Begin() error {
|
||||
_, err := tx.Exec("SAVEPOINT " + tx.transactionKeyForNestedPoint())
|
||||
@ -503,42 +514,6 @@ func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, er
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Save()
|
||||
}
|
||||
|
||||
// BatchInsert batch inserts data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Insert()
|
||||
}
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(list).Insert()
|
||||
}
|
||||
|
||||
// BatchInsertIgnore batch inserts data with ignore option.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).InsertIgnore()
|
||||
}
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(list).InsertIgnore()
|
||||
}
|
||||
|
||||
// BatchReplace batch replaces data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Replace()
|
||||
}
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(list).Replace()
|
||||
}
|
||||
|
||||
// BatchSave batch replaces data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(list).Batch(batch[0]).Save()
|
||||
}
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(list).Save()
|
||||
}
|
||||
|
||||
// Update does "UPDATE ... " statement for the table.
|
||||
//
|
||||
// The parameter `data` can be type of string/map/gmap/struct/*struct, etc.
|
||||
|
@ -10,6 +10,7 @@ package gdb
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
@ -33,12 +34,17 @@ func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...inter
|
||||
link = &txLink{tx.tx}
|
||||
}
|
||||
}
|
||||
// Link execution.
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args)
|
||||
|
||||
if c.GetConfig().QueryTimeout > 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
|
||||
}
|
||||
|
||||
// Link execution.
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args, err = c.db.DoCommit(ctx, link, sql, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
rows, err = link.QueryContext(ctx, sql, args...)
|
||||
mTime2 := gtime.TimestampMilli()
|
||||
@ -85,15 +91,19 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf
|
||||
link = &txLink{tx.tx}
|
||||
}
|
||||
}
|
||||
// Link execution.
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args = c.db.HandleSqlBeforeCommit(ctx, link, sql, args)
|
||||
|
||||
if c.GetConfig().ExecTimeout > 0 {
|
||||
var cancelFunc context.CancelFunc
|
||||
ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout)
|
||||
defer cancelFunc()
|
||||
}
|
||||
|
||||
// Link execution.
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args, err = c.db.DoCommit(ctx, link, sql, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
if !c.db.GetDryRun() {
|
||||
result, err = link.ExecContext(ctx, sql, args...)
|
||||
@ -120,6 +130,18 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf
|
||||
return result, formatError(err, sql, args...)
|
||||
}
|
||||
|
||||
// DoCommit is a hook function, which deals with the sql string before it's committed to underlying driver.
|
||||
// The parameter `link` specifies the current database connection operation object. You can modify the sql
|
||||
// string `sql` and its arguments `args` as you wish before they're committed to driver.
|
||||
func (c *Core) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
|
||||
if c.db.GetConfig().CtxStrict {
|
||||
if v := ctx.Value(ctxStrictKeyName); v == nil {
|
||||
return sql, args, gerror.New(ctxStrictErrorStr)
|
||||
}
|
||||
}
|
||||
return sql, args, nil
|
||||
}
|
||||
|
||||
// Prepare creates a prepared statement for later queries or executions.
|
||||
// Multiple queries or executions may be run concurrently from the
|
||||
// returned statement.
|
||||
@ -156,6 +178,13 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, err
|
||||
// DO NOT USE cancel function in prepare statement.
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout)
|
||||
}
|
||||
|
||||
if c.db.GetConfig().CtxStrict {
|
||||
if v := ctx.Value(ctxStrictKeyName); v == nil {
|
||||
return nil, gerror.New(ctxStrictErrorStr)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
mTime1 = gtime.TimestampMilli()
|
||||
stmt, err = link.PrepareContext(ctx, sql)
|
||||
|
@ -63,12 +63,6 @@ func (c *Core) GetChars() (charLeft string, charRight string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// HandleSqlBeforeCommit handles the sql before posts it to database.
|
||||
// It does nothing in default.
|
||||
func (c *Core) HandleSqlBeforeCommit(sql string) string {
|
||||
return sql
|
||||
}
|
||||
|
||||
// Tables retrieves and returns the tables of current schema.
|
||||
// It's mainly used in cli tool chain for automatically generating the models.
|
||||
//
|
||||
|
@ -42,15 +42,15 @@ func (d *DriverMssql) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
// Open creates and returns a underlying sql.DB object for mssql.
|
||||
func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
source := ""
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
if config.Link != "" {
|
||||
source = config.Link
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name,
|
||||
)
|
||||
}
|
||||
intlog.Printf("Open: %s", source)
|
||||
intlog.Printf(d.GetCtx(), "Open: %s", source)
|
||||
if db, err := sql.Open("sqlserver", source); err == nil {
|
||||
return db, nil
|
||||
} else {
|
||||
@ -58,17 +58,17 @@ func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for
|
||||
// FilteredLink retrieves and returns filtered `linkInfo` that can be using for
|
||||
// logging or tracing purpose.
|
||||
func (d *DriverMssql) FilteredLinkInfo() string {
|
||||
linkInfo := d.GetConfig().LinkInfo
|
||||
func (d *DriverMssql) FilteredLink() string {
|
||||
linkInfo := d.GetConfig().Link
|
||||
if linkInfo == "" {
|
||||
return ""
|
||||
}
|
||||
s, _ := gregex.ReplaceString(
|
||||
`(.+);\s*password=(.+);\s*server=(.+)`,
|
||||
`$1;password=xxx;server=$3`,
|
||||
d.GetConfig().LinkInfo,
|
||||
d.GetConfig().Link,
|
||||
)
|
||||
return s
|
||||
}
|
||||
@ -78,8 +78,11 @@ func (d *DriverMssql) GetChars() (charLeft string, charRight string) {
|
||||
return "\"", "\""
|
||||
}
|
||||
|
||||
// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver.
|
||||
func (d *DriverMssql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) {
|
||||
// DoCommit deals with the sql string before commits it to underlying sql driver.
|
||||
func (d *DriverMssql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
|
||||
defer func() {
|
||||
newSql, newArgs, err = d.Core.DoCommit(ctx, link, newSql, newArgs)
|
||||
}()
|
||||
var index int
|
||||
// Convert place holder char '?' to string "@px".
|
||||
str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string {
|
||||
@ -87,7 +90,7 @@ func (d *DriverMssql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql
|
||||
return fmt.Sprintf("@p%d", index)
|
||||
})
|
||||
str, _ = gregex.ReplaceString("\"", "", str)
|
||||
return d.parseSql(str), args
|
||||
return d.parseSql(str), args, nil
|
||||
}
|
||||
|
||||
// parseSql does some replacement of the sql before commits it to underlying driver,
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
@ -36,8 +37,8 @@ func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
// Note that it converts time.Time argument to local timezone in default.
|
||||
func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
if config.Link != "" {
|
||||
source = config.Link
|
||||
// Custom changing the schema in runtime.
|
||||
if config.Name != "" {
|
||||
source, _ = gregex.ReplaceString(`/([\w\.\-]+)+`, "/"+config.Name, source)
|
||||
@ -47,8 +48,11 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
"%s:%s@tcp(%s:%s)/%s?charset=%s",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset,
|
||||
)
|
||||
if config.Timezone != "" {
|
||||
source = fmt.Sprintf("%s&loc=%s", source, url.QueryEscape(config.Timezone))
|
||||
}
|
||||
}
|
||||
intlog.Printf("Open: %s", source)
|
||||
intlog.Printf(d.GetCtx(), "Open: %s", source)
|
||||
if db, err := sql.Open("mysql", source); err == nil {
|
||||
return db, nil
|
||||
} else {
|
||||
@ -56,10 +60,10 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for
|
||||
// FilteredLink retrieves and returns filtered `linkInfo` that can be using for
|
||||
// logging or tracing purpose.
|
||||
func (d *DriverMysql) FilteredLinkInfo() string {
|
||||
linkInfo := d.GetConfig().LinkInfo
|
||||
func (d *DriverMysql) FilteredLink() string {
|
||||
linkInfo := d.GetConfig().Link
|
||||
if linkInfo == "" {
|
||||
return ""
|
||||
}
|
||||
@ -76,9 +80,9 @@ func (d *DriverMysql) GetChars() (charLeft string, charRight string) {
|
||||
return "`", "`"
|
||||
}
|
||||
|
||||
// HandleSqlBeforeCommit handles the sql before posts it to database.
|
||||
func (d *DriverMysql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) {
|
||||
return sql, args
|
||||
// DoCommit handles the sql before posts it to database.
|
||||
func (d *DriverMysql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
|
||||
return d.Core.DoCommit(ctx, link, sql, args)
|
||||
}
|
||||
|
||||
// Tables retrieves and returns the tables of current schema.
|
||||
|
@ -32,11 +32,6 @@ type DriverOracle struct {
|
||||
*Core
|
||||
}
|
||||
|
||||
const (
|
||||
tableAlias1 = "GFORM1"
|
||||
tableAlias2 = "GFORM2"
|
||||
)
|
||||
|
||||
// New creates and returns a database object for oracle.
|
||||
// It implements the interface of gdb.Driver for extra database driver installation.
|
||||
func (d *DriverOracle) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
@ -48,15 +43,15 @@ func (d *DriverOracle) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
// Open creates and returns a underlying sql.DB object for oracle.
|
||||
func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
if config.Link != "" {
|
||||
source = config.Link
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"%s/%s@%s:%s/%s",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name,
|
||||
)
|
||||
}
|
||||
intlog.Printf("Open: %s", source)
|
||||
intlog.Printf(d.GetCtx(), "Open: %s", source)
|
||||
if db, err := sql.Open("oci8", source); err == nil {
|
||||
return db, nil
|
||||
} else {
|
||||
@ -64,10 +59,10 @@ func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for
|
||||
// FilteredLink retrieves and returns filtered `linkInfo` that can be using for
|
||||
// logging or tracing purpose.
|
||||
func (d *DriverOracle) FilteredLinkInfo() string {
|
||||
linkInfo := d.GetConfig().LinkInfo
|
||||
func (d *DriverOracle) FilteredLink() string {
|
||||
linkInfo := d.GetConfig().Link
|
||||
if linkInfo == "" {
|
||||
return ""
|
||||
}
|
||||
@ -84,8 +79,12 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) {
|
||||
return "\"", "\""
|
||||
}
|
||||
|
||||
// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver.
|
||||
func (d *DriverOracle) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) {
|
||||
// DoCommit deals with the sql string before commits it to underlying sql driver.
|
||||
func (d *DriverOracle) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
|
||||
defer func() {
|
||||
newSql, newArgs, err = d.Core.DoCommit(ctx, link, newSql, newArgs)
|
||||
}()
|
||||
|
||||
var index int
|
||||
// Convert place holder char '?' to string ":vx".
|
||||
newSql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string {
|
||||
@ -264,203 +263,40 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[
|
||||
return
|
||||
}
|
||||
|
||||
func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
|
||||
var (
|
||||
fields []string
|
||||
values []string
|
||||
params []interface{}
|
||||
dataMap Map
|
||||
rv = reflect.ValueOf(data)
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return d.DoBatchInsert(ctx, link, table, data, option, batch...)
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
default:
|
||||
return result, gerror.New(fmt.Sprint("unsupported data type:", kind))
|
||||
}
|
||||
var (
|
||||
indexes = make([]string, 0)
|
||||
indexMap = make(map[string]string)
|
||||
indexExists = false
|
||||
)
|
||||
if option != insertOptionDefault {
|
||||
index, err := d.getTableUniqueIndex(table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(index) > 0 {
|
||||
for _, v := range index {
|
||||
for k, _ := range v {
|
||||
indexes = append(indexes, k)
|
||||
}
|
||||
indexMap = v
|
||||
indexExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
var (
|
||||
subSqlStr = make([]string, 0)
|
||||
onStr = make([]string, 0)
|
||||
updateStr = make([]string, 0)
|
||||
)
|
||||
charL, charR := d.db.GetChars()
|
||||
for k, v := range dataMap {
|
||||
k = strings.ToUpper(k)
|
||||
|
||||
// 操作类型为REPLACE/SAVE时且存在唯一索引才使用merge,否则使用insert
|
||||
if (option == insertOptionReplace || option == insertOptionSave) && indexExists {
|
||||
fields = append(fields, tableAlias1+"."+charL+k+charR)
|
||||
values = append(values, tableAlias2+"."+charL+k+charR)
|
||||
params = append(params, v)
|
||||
subSqlStr = append(subSqlStr, fmt.Sprintf("%s?%s %s", charL, charR, k))
|
||||
//m erge中的on子句中由唯一索引组成, update子句中不含唯一索引
|
||||
if _, ok := indexMap[k]; ok {
|
||||
onStr = append(onStr, fmt.Sprintf("%s.%s = %s.%s ", tableAlias1, k, tableAlias2, k))
|
||||
} else {
|
||||
updateStr = append(updateStr, fmt.Sprintf("%s.%s = %s.%s ", tableAlias1, k, tableAlias2, k))
|
||||
}
|
||||
} else {
|
||||
fields = append(fields, charL+k+charR)
|
||||
values = append(values, "?")
|
||||
params = append(params, v)
|
||||
}
|
||||
}
|
||||
|
||||
if link == nil {
|
||||
if link, err = d.MasterLink(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if indexExists && option != insertOptionDefault {
|
||||
switch option {
|
||||
case
|
||||
insertOptionReplace,
|
||||
insertOptionSave:
|
||||
tmp := fmt.Sprintf(
|
||||
"MERGE INTO %s %s USING(SELECT %s FROM DUAL) %s ON(%s) WHEN MATCHED THEN UPDATE SET %s WHEN NOT MATCHED THEN INSERT (%s) VALUES(%s)",
|
||||
table, tableAlias1, strings.Join(subSqlStr, ","), tableAlias2,
|
||||
strings.Join(onStr, "AND"), strings.Join(updateStr, ","), strings.Join(fields, ","), strings.Join(values, ","),
|
||||
)
|
||||
return d.DoExec(ctx, link, tmp, params...)
|
||||
|
||||
case insertOptionIgnore:
|
||||
return d.DoExec(ctx, link, fmt.Sprintf(
|
||||
"INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(%s(%s)) */ INTO %s(%s) VALUES(%s)",
|
||||
table, strings.Join(indexes, ","), table, strings.Join(fields, ","), strings.Join(values, ","),
|
||||
), params...)
|
||||
}
|
||||
}
|
||||
|
||||
return d.DoExec(ctx, link,
|
||||
fmt.Sprintf(
|
||||
"INSERT INTO %s(%s) VALUES(%s)",
|
||||
table, strings.Join(fields, ","), strings.Join(values, ","),
|
||||
),
|
||||
params...)
|
||||
}
|
||||
|
||||
func (d *DriverOracle) DoBatchInsert(ctx context.Context, link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
|
||||
func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
var (
|
||||
keys []string
|
||||
values []string
|
||||
params []interface{}
|
||||
)
|
||||
listMap := (List)(nil)
|
||||
switch v := list.(type) {
|
||||
case Result:
|
||||
listMap = v.List()
|
||||
case Record:
|
||||
listMap = List{v.Map()}
|
||||
case List:
|
||||
listMap = v
|
||||
case Map:
|
||||
listMap = List{v}
|
||||
default:
|
||||
var (
|
||||
rv = reflect.ValueOf(list)
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
listMap = make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
|
||||
}
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
listMap = List{ConvertDataForTableRecord(list)}
|
||||
default:
|
||||
return result, gerror.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
}
|
||||
if len(listMap) < 1 {
|
||||
return result, gerror.New("empty data list")
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = d.MasterLink(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Retrieve the table fields and length.
|
||||
holders := []string(nil)
|
||||
for k, _ := range listMap[0] {
|
||||
var (
|
||||
listLength = len(list)
|
||||
valueHolder = make([]string, 0)
|
||||
)
|
||||
for k, _ := range list[0] {
|
||||
keys = append(keys, k)
|
||||
holders = append(holders, "?")
|
||||
valueHolder = append(valueHolder, "?")
|
||||
}
|
||||
var (
|
||||
batchResult = new(SqlResult)
|
||||
charL, charR = d.db.GetChars()
|
||||
keyStr = charL + strings.Join(keys, charL+","+charR) + charR
|
||||
valueHolderStr = strings.Join(holders, ",")
|
||||
valueHolderStr = strings.Join(valueHolder, ",")
|
||||
)
|
||||
if option != insertOptionDefault {
|
||||
for _, v := range listMap {
|
||||
r, err := d.DoInsert(ctx, link, table, v, option, 1)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
if n, err := r.RowsAffected(); err != nil {
|
||||
return r, err
|
||||
} else {
|
||||
batchResult.result = r
|
||||
batchResult.affected += n
|
||||
}
|
||||
}
|
||||
return batchResult, nil
|
||||
}
|
||||
|
||||
batchNum := defaultBatchNumber
|
||||
if len(batch) > 0 {
|
||||
batchNum = batch[0]
|
||||
}
|
||||
// Format "INSERT...INTO..." statement.
|
||||
intoStr := make([]string, 0)
|
||||
for i := 0; i < len(listMap); i++ {
|
||||
for i := 0; i < len(list); i++ {
|
||||
for _, k := range keys {
|
||||
params = append(params, listMap[i][k])
|
||||
params = append(params, list[i][k])
|
||||
}
|
||||
values = append(values, valueHolderStr)
|
||||
intoStr = append(intoStr, fmt.Sprintf(" INTO %s(%s) VALUES(%s) ", table, keyStr, valueHolderStr))
|
||||
if len(intoStr) == batchNum {
|
||||
r, err := d.DoExec(ctx, link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
|
||||
intoStr = append(intoStr, fmt.Sprintf("INTO %s(%s) VALUES(%s)", table, keyStr, valueHolderStr))
|
||||
if len(intoStr) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) {
|
||||
r, err := d.DoExec(ctx, link, fmt.Sprintf(
|
||||
"INSERT ALL %s SELECT * FROM DUAL",
|
||||
strings.Join(intoStr, " "),
|
||||
), params...)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
@ -474,18 +310,5 @@ func (d *DriverOracle) DoBatchInsert(ctx context.Context, link Link, table strin
|
||||
intoStr = intoStr[:0]
|
||||
}
|
||||
}
|
||||
// The leftover data.
|
||||
if len(intoStr) > 0 {
|
||||
r, err := d.DoExec(ctx, link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
if n, err := r.RowsAffected(); err != nil {
|
||||
return r, err
|
||||
} else {
|
||||
batchResult.result = r
|
||||
batchResult.affected += n
|
||||
}
|
||||
}
|
||||
return batchResult, nil
|
||||
}
|
||||
|
@ -40,15 +40,18 @@ func (d *DriverPgsql) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
// Open creates and returns a underlying sql.DB object for pgsql.
|
||||
func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
if config.Link != "" {
|
||||
source = config.Link
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"user=%s password=%s host=%s port=%s dbname=%s sslmode=disable",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name,
|
||||
)
|
||||
if config.Timezone != "" {
|
||||
source = fmt.Sprintf("%s timezone=%s", source, config.Timezone)
|
||||
}
|
||||
}
|
||||
intlog.Printf("Open: %s", source)
|
||||
intlog.Printf(d.GetCtx(), "Open: %s", source)
|
||||
if db, err := sql.Open("postgres", source); err == nil {
|
||||
return db, nil
|
||||
} else {
|
||||
@ -56,10 +59,10 @@ func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for
|
||||
// FilteredLink retrieves and returns filtered `linkInfo` that can be using for
|
||||
// logging or tracing purpose.
|
||||
func (d *DriverPgsql) FilteredLinkInfo() string {
|
||||
linkInfo := d.GetConfig().LinkInfo
|
||||
func (d *DriverPgsql) FilteredLink() string {
|
||||
linkInfo := d.GetConfig().Link
|
||||
if linkInfo == "" {
|
||||
return ""
|
||||
}
|
||||
@ -76,16 +79,20 @@ func (d *DriverPgsql) GetChars() (charLeft string, charRight string) {
|
||||
return "\"", "\""
|
||||
}
|
||||
|
||||
// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver.
|
||||
func (d *DriverPgsql) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) {
|
||||
// DoCommit deals with the sql string before commits it to underlying sql driver.
|
||||
func (d *DriverPgsql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
|
||||
defer func() {
|
||||
newSql, newArgs, err = d.Core.DoCommit(ctx, link, newSql, newArgs)
|
||||
}()
|
||||
|
||||
var index int
|
||||
// Convert place holder char '?' to string "$x".
|
||||
sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string {
|
||||
index++
|
||||
return fmt.Sprintf("$%d", index)
|
||||
})
|
||||
sql, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $2 OFFSET $1`, sql)
|
||||
return sql, args
|
||||
newSql, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $2 OFFSET $1`, sql)
|
||||
return newSql, args, nil
|
||||
}
|
||||
|
||||
// Tables retrieves and returns the tables of current schema.
|
||||
|
@ -38,8 +38,8 @@ func (d *DriverSqlite) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
// Open creates and returns a underlying sql.DB object for sqlite.
|
||||
func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
if config.Link != "" {
|
||||
source = config.Link
|
||||
} else {
|
||||
source = config.Name
|
||||
}
|
||||
@ -47,7 +47,7 @@ func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
if absolutePath, _ := gfile.Search(source); absolutePath != "" {
|
||||
source = absolutePath
|
||||
}
|
||||
intlog.Printf("Open: %s", source)
|
||||
intlog.Printf(d.GetCtx(), "Open: %s", source)
|
||||
if db, err := sql.Open("sqlite3", source); err == nil {
|
||||
return db, nil
|
||||
} else {
|
||||
@ -55,10 +55,10 @@ func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// FilteredLinkInfo retrieves and returns filtered `linkInfo` that can be using for
|
||||
// FilteredLink retrieves and returns filtered `linkInfo` that can be using for
|
||||
// logging or tracing purpose.
|
||||
func (d *DriverSqlite) FilteredLinkInfo() string {
|
||||
return d.GetConfig().LinkInfo
|
||||
func (d *DriverSqlite) FilteredLink() string {
|
||||
return d.GetConfig().Link
|
||||
}
|
||||
|
||||
// GetChars returns the security char for this type of database.
|
||||
@ -66,11 +66,9 @@ func (d *DriverSqlite) GetChars() (charLeft string, charRight string) {
|
||||
return "`", "`"
|
||||
}
|
||||
|
||||
// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver.
|
||||
// TODO 需要增加对Save方法的支持,可使用正则来实现替换,
|
||||
// TODO 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE)
|
||||
func (d *DriverSqlite) HandleSqlBeforeCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) {
|
||||
return sql, args
|
||||
// DoCommit deals with the sql string before commits it to underlying sql driver.
|
||||
func (d *DriverSqlite) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
|
||||
return d.Core.DoCommit(ctx, link, sql, args)
|
||||
}
|
||||
|
||||
// Tables retrieves and returns the tables of current schema.
|
||||
|
@ -142,8 +142,8 @@ func GetInsertOperationByOption(option int) string {
|
||||
// ConvertDataForTableRecord is a very important function, which does converting for any data that
|
||||
// will be inserted into table as a record.
|
||||
//
|
||||
// The parameter `obj` should be type of *map/map/*struct/struct.
|
||||
// It supports inherit struct definition for struct.
|
||||
// The parameter `value` should be type of *map/map/*struct/struct.
|
||||
// It supports embedded struct definition for struct.
|
||||
func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
|
||||
var (
|
||||
rvValue reflect.Value
|
||||
@ -164,12 +164,15 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
|
||||
// Convert the value to JSON.
|
||||
data[k], _ = json.Marshal(v)
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
switch v.(type) {
|
||||
case time.Time, *time.Time, gtime.Time, *gtime.Time:
|
||||
continue
|
||||
|
||||
case Counter, *Counter:
|
||||
continue
|
||||
|
||||
default:
|
||||
// Use string conversion in default.
|
||||
if s, ok := v.(apiString); ok {
|
||||
@ -186,7 +189,7 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
|
||||
|
||||
// DataToMapDeep converts `value` to map type recursively.
|
||||
// The parameter `value` should be type of *map/map/*struct/struct.
|
||||
// It supports inherit struct definition for struct.
|
||||
// It supports embedded struct definition for struct.
|
||||
func DataToMapDeep(value interface{}) map[string]interface{} {
|
||||
if v, ok := value.(apiMapStrAny); ok {
|
||||
return v.MapStrAny()
|
||||
@ -445,7 +448,7 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa
|
||||
}
|
||||
|
||||
// formatWhere formats where statement and its arguments for `Where` and `Having` statements.
|
||||
func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (newWhere string, newArgs []interface{}) {
|
||||
func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, schema, table string) (newWhere string, newArgs []interface{}) {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
rv = reflect.ValueOf(where)
|
||||
@ -483,7 +486,12 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
|
||||
})
|
||||
break
|
||||
}
|
||||
for key, value := range DataToMapDeep(where) {
|
||||
// Automatically mapping and filtering the struct attribute.
|
||||
data := DataToMapDeep(where)
|
||||
if table != "" {
|
||||
data, _ = db.GetCore().mappingAndFilterData(schema, table, data, true)
|
||||
}
|
||||
for key, value := range data {
|
||||
if omitEmpty && empty.IsEmpty(value) {
|
||||
continue
|
||||
}
|
||||
|
@ -17,10 +17,11 @@ import (
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
// Model is the DAO for ORM.
|
||||
// Model is core struct implementing the DAO for ORM.
|
||||
type Model struct {
|
||||
db DB // Underlying DB interface.
|
||||
tx *TX // Underlying TX interface.
|
||||
rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model.
|
||||
schema string // Custom database schema.
|
||||
linkType int // Mark for operation on master or slave.
|
||||
tablesInit string // Table names when model initialization.
|
||||
@ -48,6 +49,8 @@ type Model struct {
|
||||
cacheName string // Cache name for custom operation.
|
||||
unscoped bool // Disables soft deleting features when select/delete operations.
|
||||
safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
|
||||
onDuplicate interface{} // onDuplicate is used for ON "DUPLICATE KEY UPDATE" statement.
|
||||
onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement.
|
||||
}
|
||||
|
||||
// whereHolder is the holder for where condition preparing.
|
||||
@ -77,29 +80,32 @@ func (c *Core) Table(tableNameQueryOrStruct ...interface{}) *Model {
|
||||
// Model creates and returns a new ORM model from given schema.
|
||||
// The parameter `tableNameQueryOrStruct` can be more than one table names, and also alias name, like:
|
||||
// 1. Model names:
|
||||
// Model("user")
|
||||
// Model("user u")
|
||||
// Model("user, user_detail")
|
||||
// Model("user u, user_detail ud")
|
||||
// 2. Model name with alias: Model("user", "u")
|
||||
// db.Model("user")
|
||||
// db.Model("user u")
|
||||
// db.Model("user, user_detail")
|
||||
// db.Model("user u, user_detail ud")
|
||||
// 2. Model name with alias:
|
||||
// db.Model("user", "u")
|
||||
// 3. Model name with sub-query:
|
||||
// db.Model("? AS a, ? AS b", subQuery1, subQuery2)
|
||||
func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
|
||||
var (
|
||||
tableStr string
|
||||
tableName string
|
||||
extraArgs []interface{}
|
||||
tableNames = make([]string, len(tableNameQueryOrStruct))
|
||||
tableStr string
|
||||
tableName string
|
||||
extraArgs []interface{}
|
||||
)
|
||||
// Model creation with sub-query.
|
||||
if len(tableNameQueryOrStruct) > 1 {
|
||||
conditionStr := gconv.String(tableNameQueryOrStruct[0])
|
||||
if gstr.Contains(conditionStr, "?") {
|
||||
tableStr, extraArgs = formatWhere(
|
||||
c.db, conditionStr, tableNameQueryOrStruct[1:], false,
|
||||
c.db, conditionStr, tableNameQueryOrStruct[1:], false, "", "",
|
||||
)
|
||||
}
|
||||
}
|
||||
// Normal model creation.
|
||||
if tableStr == "" {
|
||||
tableNames := make([]string, len(tableNameQueryOrStruct))
|
||||
for k, v := range tableNameQueryOrStruct {
|
||||
if s, ok := v.(string); ok {
|
||||
tableNames[k] = s
|
||||
@ -107,7 +113,6 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
|
||||
tableNames[k] = tableName
|
||||
}
|
||||
}
|
||||
|
||||
if len(tableNames) > 1 {
|
||||
tableStr = fmt.Sprintf(
|
||||
`%s AS %s`, c.QuotePrefixTableName(tableNames[0]), c.QuoteWord(tableNames[1]),
|
||||
@ -129,17 +134,36 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
|
||||
}
|
||||
}
|
||||
|
||||
// Raw creates and returns a model based on a raw sql not a table.
|
||||
// Example:
|
||||
// db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result)
|
||||
func (c *Core) Raw(rawSql string, args ...interface{}) *Model {
|
||||
model := c.Model()
|
||||
model.rawSql = rawSql
|
||||
model.extraArgs = args
|
||||
return model
|
||||
}
|
||||
|
||||
// Raw creates and returns a model based on a raw sql not a table.
|
||||
// Example:
|
||||
// db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result)
|
||||
// See Core.Raw.
|
||||
func (m *Model) Raw(rawSql string, args ...interface{}) *Model {
|
||||
model := m.db.Raw(rawSql, args...)
|
||||
model.db = m.db
|
||||
model.tx = m.tx
|
||||
return model
|
||||
}
|
||||
|
||||
func (tx *TX) Raw(rawSql string, args ...interface{}) *Model {
|
||||
return tx.Model().Raw(rawSql, args...)
|
||||
}
|
||||
|
||||
// With creates and returns an ORM model based on meta data of given object.
|
||||
func (c *Core) With(objects ...interface{}) *Model {
|
||||
return c.db.Model().With(objects...)
|
||||
}
|
||||
|
||||
// Table is alias of tx.Model.
|
||||
// Deprecated, use Model instead.
|
||||
func (tx *TX) Table(tableNameQueryOrStruct ...interface{}) *Model {
|
||||
return tx.Model(tableNameQueryOrStruct...)
|
||||
}
|
||||
|
||||
// Model acts like Core.Model except it operates on transaction.
|
||||
// See Core.Model.
|
||||
func (tx *TX) Model(tableNameQueryOrStruct ...interface{}) *Model {
|
||||
|
@ -62,6 +62,8 @@ func (m *Model) WherePri(where interface{}, args ...interface{}) *Model {
|
||||
}
|
||||
|
||||
// Wheref builds condition string using fmt.Sprintf and arguments.
|
||||
// Note that if the number of `args` is more than the place holder in `format`,
|
||||
// the extra `args` will be used as the where condition arguments of the Model.
|
||||
func (m *Model) Wheref(format string, args ...interface{}) *Model {
|
||||
var (
|
||||
placeHolderCount = gstr.Count(format, "?")
|
||||
@ -275,6 +277,9 @@ func (m *Model) Order(orderBy ...string) *Model {
|
||||
return m
|
||||
}
|
||||
model := m.getModel()
|
||||
if model.orderBy != "" {
|
||||
model.orderBy += ","
|
||||
}
|
||||
model.orderBy = m.db.GetCore().QuoteString(strings.Join(orderBy, " "))
|
||||
return model
|
||||
}
|
||||
@ -285,6 +290,9 @@ func (m *Model) OrderAsc(column string) *Model {
|
||||
return m
|
||||
}
|
||||
model := m.getModel()
|
||||
if model.orderBy != "" {
|
||||
model.orderBy += ","
|
||||
}
|
||||
model.orderBy = m.db.GetCore().QuoteWord(column) + " ASC"
|
||||
return model
|
||||
}
|
||||
@ -295,6 +303,9 @@ func (m *Model) OrderDesc(column string) *Model {
|
||||
return m
|
||||
}
|
||||
model := m.getModel()
|
||||
if model.orderBy != "" {
|
||||
model.orderBy += ","
|
||||
}
|
||||
model.orderBy = m.db.GetCore().QuoteWord(column) + " DESC"
|
||||
return model
|
||||
}
|
||||
@ -375,7 +386,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
case whereHolderWhere:
|
||||
if conditionWhere == "" {
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
conditionWhere = newWhere
|
||||
@ -387,7 +398,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
|
||||
case whereHolderAnd:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
if len(conditionWhere) == 0 {
|
||||
@ -402,7 +413,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
|
||||
case whereHolderOr:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
if len(conditionWhere) == 0 {
|
||||
@ -419,7 +430,13 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
}
|
||||
// Soft deletion.
|
||||
softDeletingCondition := m.getConditionForSoftDeleting()
|
||||
if !m.unscoped && softDeletingCondition != "" {
|
||||
if m.rawSql != "" && conditionWhere != "" {
|
||||
if gstr.ContainsI(m.rawSql, " WHERE ") {
|
||||
conditionWhere = " AND " + conditionWhere
|
||||
} else {
|
||||
conditionWhere = " WHERE " + conditionWhere
|
||||
}
|
||||
} else if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition)
|
||||
} else {
|
||||
@ -430,6 +447,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
conditionWhere = " WHERE " + conditionWhere
|
||||
}
|
||||
}
|
||||
|
||||
// GROUP BY.
|
||||
if m.groupBy != "" {
|
||||
conditionExtra += " GROUP BY " + m.groupBy
|
||||
@ -437,7 +455,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
// HAVING.
|
||||
if len(m.having) > 0 {
|
||||
havingStr, havingArgs := formatWhere(
|
||||
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0,
|
||||
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0, m.schema, m.tables,
|
||||
)
|
||||
if len(havingStr) > 0 {
|
||||
conditionExtra += " HAVING " + havingStr
|
||||
|
@ -8,6 +8,8 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
@ -51,16 +53,20 @@ func (m *Model) Data(data ...interface{}) *Model {
|
||||
switch params := data[0].(type) {
|
||||
case Result:
|
||||
model.data = params.List()
|
||||
|
||||
case Record:
|
||||
model.data = params.Map()
|
||||
|
||||
case List:
|
||||
list := make(List, len(params))
|
||||
for k, v := range params {
|
||||
list[k] = gutil.MapCopy(v)
|
||||
}
|
||||
model.data = list
|
||||
|
||||
case Map:
|
||||
model.data = gutil.MapCopy(params)
|
||||
|
||||
default:
|
||||
var (
|
||||
rv = reflect.ValueOf(params)
|
||||
@ -100,6 +106,48 @@ func (m *Model) Data(data ...interface{}) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// OnDuplicate sets the operations when columns conflicts occurs.
|
||||
// In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement.
|
||||
// The parameter `onDuplicate` can be type of string/Raw/*Raw/map/slice.
|
||||
// Example:
|
||||
// OnDuplicate("nickname, age")
|
||||
// OnDuplicate("nickname", "age")
|
||||
// OnDuplicate(g.Map{
|
||||
// "nickname": gdb.Raw("CONCAT('name_', VALUES(`nickname`))"),
|
||||
// })
|
||||
// OnDuplicate(g.Map{
|
||||
// "nickname": "passport",
|
||||
// })
|
||||
func (m *Model) OnDuplicate(onDuplicate ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if len(onDuplicate) > 1 {
|
||||
model.onDuplicate = onDuplicate
|
||||
} else {
|
||||
model.onDuplicate = onDuplicate[0]
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// OnDuplicateEx sets the excluding columns for operations when columns conflicts occurs.
|
||||
// In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement.
|
||||
// The parameter `onDuplicateEx` can be type of string/map/slice.
|
||||
// Example:
|
||||
// OnDuplicateEx("passport, password")
|
||||
// OnDuplicateEx("passport", "password")
|
||||
// OnDuplicateEx(g.Map{
|
||||
// "passport": "",
|
||||
// "password": "",
|
||||
// })
|
||||
func (m *Model) OnDuplicateEx(onDuplicateEx ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if len(onDuplicateEx) > 1 {
|
||||
model.onDuplicateEx = onDuplicateEx
|
||||
} else {
|
||||
model.onDuplicateEx = onDuplicateEx[0]
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// Insert does "INSERT INTO ..." statement for the model.
|
||||
// The optional parameter `data` is the same as the parameter of Model.Data function,
|
||||
// see Model.Data.
|
||||
@ -156,7 +204,7 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) {
|
||||
}
|
||||
|
||||
// doInsertWithOption inserts data with option parameter.
|
||||
func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) {
|
||||
func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
m.checkAndRemoveCache()
|
||||
@ -166,68 +214,206 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) {
|
||||
return nil, gerror.New("inserting into table with empty data")
|
||||
}
|
||||
var (
|
||||
list List
|
||||
nowString = gtime.Now().String()
|
||||
fieldNameCreate = m.getSoftFieldNameCreated()
|
||||
fieldNameUpdate = m.getSoftFieldNameUpdated()
|
||||
fieldNameDelete = m.getSoftFieldNameDeleted()
|
||||
)
|
||||
// Batch operation.
|
||||
if list, ok := m.data.(List); ok {
|
||||
batch := defaultBatchNumber
|
||||
if m.batch > 0 {
|
||||
batch = m.batch
|
||||
}
|
||||
newData, err := m.filterDataForInsertOrUpdate(list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list = newData.(List)
|
||||
// Automatic handling for creating/updating time.
|
||||
if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") {
|
||||
for k, v := range list {
|
||||
gutil.MapDelete(v, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
|
||||
if fieldNameCreate != "" {
|
||||
v[fieldNameCreate] = nowString
|
||||
}
|
||||
if fieldNameUpdate != "" {
|
||||
v[fieldNameUpdate] = nowString
|
||||
}
|
||||
list[k] = v
|
||||
}
|
||||
}
|
||||
return m.db.DoBatchInsert(
|
||||
m.GetCtx(),
|
||||
m.getLink(true),
|
||||
m.tables,
|
||||
newData,
|
||||
option,
|
||||
batch,
|
||||
)
|
||||
newData, err := m.filterDataForInsertOrUpdate(m.data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Single operation.
|
||||
if data, ok := m.data.(Map); ok {
|
||||
newData, err := m.filterDataForInsertOrUpdate(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
// It converts any data to List type for inserting.
|
||||
switch value := newData.(type) {
|
||||
case Result:
|
||||
list = value.List()
|
||||
|
||||
case Record:
|
||||
list = List{value.Map()}
|
||||
|
||||
case List:
|
||||
list = value
|
||||
for i, v := range list {
|
||||
list[i] = ConvertDataForTableRecord(v)
|
||||
}
|
||||
data = newData.(Map)
|
||||
// Automatic handling for creating/updating time.
|
||||
if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") {
|
||||
gutil.MapDelete(data, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
|
||||
|
||||
case Map:
|
||||
list = List{ConvertDataForTableRecord(value)}
|
||||
|
||||
default:
|
||||
var (
|
||||
rv = reflect.ValueOf(newData)
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// If it's slice type, it then converts it to List type.
|
||||
case reflect.Slice, reflect.Array:
|
||||
list = make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
list[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
list = List{ConvertDataForTableRecord(value)}
|
||||
|
||||
case reflect.Struct:
|
||||
if v, ok := value.(apiInterfaces); ok {
|
||||
var (
|
||||
array = v.Interfaces()
|
||||
)
|
||||
list = make(List, len(array))
|
||||
for i := 0; i < len(array); i++ {
|
||||
list[i] = ConvertDataForTableRecord(array[i])
|
||||
}
|
||||
} else {
|
||||
list = List{ConvertDataForTableRecord(value)}
|
||||
}
|
||||
|
||||
default:
|
||||
return result, gerror.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
}
|
||||
|
||||
if len(list) < 1 {
|
||||
return result, gerror.New("data list cannot be empty")
|
||||
}
|
||||
|
||||
// Automatic handling for creating/updating time.
|
||||
if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") {
|
||||
for k, v := range list {
|
||||
gutil.MapDelete(v, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
|
||||
if fieldNameCreate != "" {
|
||||
data[fieldNameCreate] = nowString
|
||||
v[fieldNameCreate] = nowString
|
||||
}
|
||||
if fieldNameUpdate != "" {
|
||||
data[fieldNameUpdate] = nowString
|
||||
v[fieldNameUpdate] = nowString
|
||||
}
|
||||
list[k] = v
|
||||
}
|
||||
}
|
||||
// Format DoInsertOption, especially for "ON DUPLICATE KEY UPDATE" statement.
|
||||
columnNames := make([]string, 0, len(list[0]))
|
||||
for k, _ := range list[0] {
|
||||
columnNames = append(columnNames, k)
|
||||
}
|
||||
doInsertOption, err := m.formatDoInsertOption(insertOption, columnNames)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return m.db.DoInsert(m.GetCtx(), m.getLink(true), m.tables, list, doInsertOption)
|
||||
}
|
||||
|
||||
func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (option DoInsertOption, err error) {
|
||||
option = DoInsertOption{
|
||||
InsertOption: insertOption,
|
||||
BatchCount: m.getBatch(),
|
||||
}
|
||||
if insertOption == insertOptionSave {
|
||||
onDuplicateExKeys, err := m.formatOnDuplicateExKeys(m.onDuplicateEx)
|
||||
if err != nil {
|
||||
return option, err
|
||||
}
|
||||
var (
|
||||
onDuplicateExKeySet = gset.NewStrSetFrom(onDuplicateExKeys)
|
||||
)
|
||||
if m.onDuplicate != nil {
|
||||
switch m.onDuplicate.(type) {
|
||||
case Raw, *Raw:
|
||||
option.OnDuplicateStr = gconv.String(m.onDuplicate)
|
||||
|
||||
default:
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(m.onDuplicate)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.String:
|
||||
option.OnDuplicateMap = make(map[string]interface{})
|
||||
for _, v := range gstr.SplitAndTrim(reflectValue.String(), ",") {
|
||||
if onDuplicateExKeySet.Contains(v) {
|
||||
continue
|
||||
}
|
||||
option.OnDuplicateMap[v] = v
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
option.OnDuplicateMap = make(map[string]interface{})
|
||||
for k, v := range gconv.Map(m.onDuplicate) {
|
||||
if onDuplicateExKeySet.Contains(k) {
|
||||
continue
|
||||
}
|
||||
option.OnDuplicateMap[k] = v
|
||||
}
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
option.OnDuplicateMap = make(map[string]interface{})
|
||||
for _, v := range gconv.Strings(m.onDuplicate) {
|
||||
if onDuplicateExKeySet.Contains(v) {
|
||||
continue
|
||||
}
|
||||
option.OnDuplicateMap[v] = v
|
||||
}
|
||||
|
||||
default:
|
||||
return option, gerror.Newf(`unsupported OnDuplicate parameter type "%s"`, reflect.TypeOf(m.onDuplicate))
|
||||
}
|
||||
}
|
||||
} else if onDuplicateExKeySet.Size() > 0 {
|
||||
option.OnDuplicateMap = make(map[string]interface{})
|
||||
for _, v := range columnNames {
|
||||
if onDuplicateExKeySet.Contains(v) {
|
||||
continue
|
||||
}
|
||||
option.OnDuplicateMap[v] = v
|
||||
}
|
||||
}
|
||||
return m.db.DoInsert(
|
||||
m.GetCtx(),
|
||||
m.getLink(true),
|
||||
m.tables,
|
||||
newData,
|
||||
option,
|
||||
)
|
||||
}
|
||||
return nil, gerror.New("inserting into table with invalid data type")
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Model) formatOnDuplicateExKeys(onDuplicateEx interface{}) ([]string, error) {
|
||||
if onDuplicateEx == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(onDuplicateEx)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.String:
|
||||
return gstr.SplitAndTrim(reflectValue.String(), ","), nil
|
||||
|
||||
case reflect.Map:
|
||||
return gutil.Keys(onDuplicateEx), nil
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
return gconv.Strings(onDuplicateEx), nil
|
||||
|
||||
default:
|
||||
return nil, gerror.Newf(`unsupported OnDuplicateEx parameter type "%s"`, reflect.TypeOf(onDuplicateEx))
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) getBatch() int {
|
||||
batch := defaultBatchNumber
|
||||
if m.batch > 0 {
|
||||
batch = m.batch
|
||||
}
|
||||
return batch
|
||||
}
|
||||
|
@ -56,9 +56,9 @@ func (m *Model) InnerJoin(table ...string) *Model {
|
||||
// doJoin does "LEFT/RIGHT/INNER JOIN ... ON ..." statement on the model.
|
||||
// The parameter `table` can be joined table and its joined condition,
|
||||
// and also with its alias name, like:
|
||||
// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
// Table("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid")
|
||||
// Model("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Model("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
// Model("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid")
|
||||
// Related issues:
|
||||
// https://github.com/gogf/gf/issues/1024
|
||||
func (m *Model) doJoin(operator string, table ...string) *Model {
|
||||
|
@ -8,6 +8,7 @@ package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/container/gset"
|
||||
@ -18,11 +19,6 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
const (
|
||||
queryTypeNormal = "NormalQuery"
|
||||
queryTypeCount = "CountQuery"
|
||||
)
|
||||
|
||||
// Select is alias of Model.All.
|
||||
// See Model.All.
|
||||
// Deprecated, use All instead.
|
||||
@ -200,6 +196,15 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) {
|
||||
return all.Array(), nil
|
||||
}
|
||||
|
||||
// Struct retrieves one record from table and converts it into given struct.
|
||||
// The parameter `pointer` should be type of *struct/**struct. If type **struct is given,
|
||||
// it can create the struct internally during converting.
|
||||
//
|
||||
// Deprecated, use Scan instead.
|
||||
func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
|
||||
return m.doStruct(pointer, where...)
|
||||
}
|
||||
|
||||
// Struct retrieves one record from table and converts it into given struct.
|
||||
// The parameter `pointer` should be type of *struct/**struct. If type **struct is given,
|
||||
// it can create the struct internally during converting.
|
||||
@ -207,24 +212,38 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) {
|
||||
// The optional parameter `where` is the same as the parameter of Model.Where function,
|
||||
// see Model.Where.
|
||||
//
|
||||
// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
|
||||
// from table and `pointer` is not nil.
|
||||
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
|
||||
// default value and there's no record retrieved with the given conditions from table.
|
||||
//
|
||||
// Eg:
|
||||
// Example:
|
||||
// user := new(User)
|
||||
// err := db.Model("user").Where("id", 1).Struct(user)
|
||||
// err := db.Model("user").Where("id", 1).Scan(user)
|
||||
//
|
||||
// user := (*User)(nil)
|
||||
// err := db.Model("user").Where("id", 1).Struct(&user)
|
||||
func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
|
||||
one, err := m.One(where...)
|
||||
// err := db.Model("user").Where("id", 1).Scan(&user)
|
||||
func (m *Model) doStruct(pointer interface{}, where ...interface{}) error {
|
||||
model := m
|
||||
// Auto selecting fields by struct attributes.
|
||||
if model.fieldsEx == "" && (model.fields == "" || model.fields == "*") {
|
||||
model = m.Fields(pointer)
|
||||
}
|
||||
one, err := model.One(where...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = one.Struct(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.doWithScanStruct(pointer)
|
||||
return model.doWithScanStruct(pointer)
|
||||
}
|
||||
|
||||
// Structs retrieves records from table and converts them into given struct slice.
|
||||
// The parameter `pointer` should be type of *[]struct/*[]*struct. It can create and fill the struct
|
||||
// slice internally during converting.
|
||||
//
|
||||
// Deprecated, use Scan instead.
|
||||
func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
|
||||
return m.doStructs(pointer, where...)
|
||||
}
|
||||
|
||||
// Structs retrieves records from table and converts them into given struct slice.
|
||||
@ -234,37 +253,45 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
|
||||
// The optional parameter `where` is the same as the parameter of Model.Where function,
|
||||
// see Model.Where.
|
||||
//
|
||||
// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
|
||||
// from table and `pointer` is not empty.
|
||||
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
|
||||
// default value and there's no record retrieved with the given conditions from table.
|
||||
//
|
||||
// Eg:
|
||||
// Example:
|
||||
// users := ([]User)(nil)
|
||||
// err := db.Model("user").Structs(&users)
|
||||
// err := db.Model("user").Scan(&users)
|
||||
//
|
||||
// users := ([]*User)(nil)
|
||||
// err := db.Model("user").Structs(&users)
|
||||
func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
|
||||
all, err := m.All(where...)
|
||||
// err := db.Model("user").Scan(&users)
|
||||
func (m *Model) doStructs(pointer interface{}, where ...interface{}) error {
|
||||
model := m
|
||||
// Auto selecting fields by struct attributes.
|
||||
if model.fieldsEx == "" && (model.fields == "" || model.fields == "*") {
|
||||
model = m.Fields(
|
||||
reflect.New(
|
||||
reflect.ValueOf(pointer).Elem().Type().Elem(),
|
||||
).Interface(),
|
||||
)
|
||||
}
|
||||
all, err := model.All(where...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = all.Structs(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.doWithScanStructs(pointer)
|
||||
return model.doWithScanStructs(pointer)
|
||||
}
|
||||
|
||||
// Scan automatically calls Struct or Structs function according to the type of parameter `pointer`.
|
||||
// It calls function Struct if `pointer` is type of *struct/**struct.
|
||||
// It calls function Structs if `pointer` is type of *[]struct/*[]*struct.
|
||||
// It calls function doStruct if `pointer` is type of *struct/**struct.
|
||||
// It calls function doStructs if `pointer` is type of *[]struct/*[]*struct.
|
||||
//
|
||||
// The optional parameter `where` is the same as the parameter of Model.Where function,
|
||||
// see Model.Where.
|
||||
// The optional parameter `where` is the same as the parameter of Model.Where function, see Model.Where.
|
||||
//
|
||||
// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
|
||||
// from table.
|
||||
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
|
||||
// default value and there's no record retrieved with the given conditions from table.
|
||||
//
|
||||
// Eg:
|
||||
// Example:
|
||||
// user := new(User)
|
||||
// err := db.Model("user").Where("id", 1).Scan(user)
|
||||
//
|
||||
@ -277,16 +304,35 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
|
||||
// users := ([]*User)(nil)
|
||||
// err := db.Model("user").Scan(&users)
|
||||
func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
|
||||
var reflectType reflect.Type
|
||||
var (
|
||||
reflectValue reflect.Value
|
||||
reflectKind reflect.Kind
|
||||
)
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
reflectType = v.Type()
|
||||
reflectValue = v
|
||||
} else {
|
||||
reflectType = reflect.TypeOf(pointer)
|
||||
reflectValue = reflect.ValueOf(pointer)
|
||||
}
|
||||
if gstr.Contains(reflectType.String(), "[]") {
|
||||
return m.Structs(pointer, where...)
|
||||
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind != reflect.Ptr {
|
||||
return gerror.New(`the parameter "pointer" for function Scan should type of pointer`)
|
||||
}
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return m.doStructs(pointer, where...)
|
||||
|
||||
case reflect.Struct, reflect.Invalid:
|
||||
return m.doStruct(pointer, where...)
|
||||
|
||||
default:
|
||||
return gerror.New(`element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`)
|
||||
}
|
||||
return m.Struct(pointer, where...)
|
||||
}
|
||||
|
||||
// ScanList converts `r` to struct slice which contains other complex struct attributes.
|
||||
@ -458,6 +504,16 @@ func (m *Model) FindScan(pointer interface{}, where ...interface{}) error {
|
||||
return m.Scan(pointer)
|
||||
}
|
||||
|
||||
// Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement for the model.
|
||||
func (m *Model) Union(unions ...*Model) *Model {
|
||||
return m.db.Union(unions...)
|
||||
}
|
||||
|
||||
// UnionAll does "(SELECT xxx FROM xxx) UNION ALL (SELECT xxx FROM xxx) ..." statement for the model.
|
||||
func (m *Model) UnionAll(unions ...*Model) *Model {
|
||||
return m.db.UnionAll(unions...)
|
||||
}
|
||||
|
||||
// doGetAllBySql does the select statement on the database.
|
||||
func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, err error) {
|
||||
cacheKey := ""
|
||||
@ -490,18 +546,18 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e
|
||||
if cacheKey != "" && err == nil {
|
||||
if m.cacheDuration < 0 {
|
||||
if _, err := cacheObj.Remove(cacheKey); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(m.GetCtx(), err)
|
||||
}
|
||||
} else {
|
||||
if err := cacheObj.Set(cacheKey, result, m.cacheDuration); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(m.GetCtx(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (m *Model) getFormattedSqlAndArgs(queryType string, limit1 bool) (sqlWithHolder string, holderArgs []interface{}) {
|
||||
func (m *Model) getFormattedSqlAndArgs(queryType int, limit1 bool) (sqlWithHolder string, holderArgs []interface{}) {
|
||||
switch queryType {
|
||||
case queryTypeCount:
|
||||
countFields := "COUNT(1)"
|
||||
@ -510,6 +566,11 @@ func (m *Model) getFormattedSqlAndArgs(queryType string, limit1 bool) (sqlWithHo
|
||||
// DISTINCT t.user_id uid
|
||||
countFields = fmt.Sprintf(`COUNT(%s%s)`, m.distinct, m.fields)
|
||||
}
|
||||
// Raw SQL Model.
|
||||
if m.rawSql != "" {
|
||||
sqlWithHolder = fmt.Sprintf("SELECT %s FROM (%s) AS T", countFields, m.rawSql)
|
||||
return sqlWithHolder, nil
|
||||
}
|
||||
conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true)
|
||||
sqlWithHolder = fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra)
|
||||
if len(m.groupBy) > 0 {
|
||||
@ -519,6 +580,15 @@ func (m *Model) getFormattedSqlAndArgs(queryType string, limit1 bool) (sqlWithHo
|
||||
|
||||
default:
|
||||
conditionWhere, conditionExtra, conditionArgs := m.formatCondition(limit1, false)
|
||||
// Raw SQL Model, especially for UNION/UNION ALL featured SQL.
|
||||
if m.rawSql != "" {
|
||||
sqlWithHolder = fmt.Sprintf(
|
||||
"%s%s",
|
||||
m.rawSql,
|
||||
conditionWhere+conditionExtra,
|
||||
)
|
||||
return sqlWithHolder, conditionArgs
|
||||
}
|
||||
// DO NOT quote the m.fields where, in case of fields like:
|
||||
// DISTINCT t.user_id uid
|
||||
sqlWithHolder = fmt.Sprintf(
|
||||
|
@ -173,6 +173,9 @@ func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string {
|
||||
|
||||
// getPrimaryTableName parses and returns the primary table name.
|
||||
func (m *Model) getPrimaryTableName() string {
|
||||
if m.tables == "" {
|
||||
return ""
|
||||
}
|
||||
array1 := gstr.SplitAndTrim(m.tables, ",")
|
||||
array2 := gstr.SplitAndTrim(array1[0], " ")
|
||||
array3 := gstr.SplitAndTrim(array2[0], ".")
|
||||
|
@ -93,17 +93,19 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
|
||||
}
|
||||
|
||||
// Increment increments a column's value by a given amount.
|
||||
func (m *Model) Increment(column string, amount float64) (sql.Result, error) {
|
||||
// The parameter `amount` can be type of float or integer.
|
||||
func (m *Model) Increment(column string, amount interface{}) (sql.Result, error) {
|
||||
return m.getModel().Data(column, &Counter{
|
||||
Field: column,
|
||||
Value: amount,
|
||||
Value: gconv.Float64(amount),
|
||||
}).Update()
|
||||
}
|
||||
|
||||
// Decrement decrements a column's value by a given amount.
|
||||
func (m *Model) Decrement(column string, amount float64) (sql.Result, error) {
|
||||
// The parameter `amount` can be type of float or integer.
|
||||
func (m *Model) Decrement(column string, amount interface{}) (sql.Result, error) {
|
||||
return m.getModel().Data(column, &Counter{
|
||||
Field: column,
|
||||
Value: -amount,
|
||||
Value: -gconv.Float64(amount),
|
||||
}).Update()
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ const (
|
||||
)
|
||||
|
||||
// doStmtCommit commits statement according to given `stmtType`.
|
||||
func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interface{}) (result interface{}, err error) {
|
||||
func (s *Stmt) doStmtCommit(ctx context.Context, stmtType string, args ...interface{}) (result interface{}, err error) {
|
||||
var (
|
||||
cancelFuncForTimeout context.CancelFunc
|
||||
timestampMilli1 = gtime.TimestampMilli()
|
||||
@ -86,7 +86,7 @@ func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interf
|
||||
// ExecContext executes a prepared statement with the given arguments and
|
||||
// returns a Result summarizing the effect of the statement.
|
||||
func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) {
|
||||
result, err := s.doStmtCommit(stmtTypeExecContext, ctx, args...)
|
||||
result, err := s.doStmtCommit(ctx, stmtTypeExecContext, args...)
|
||||
if result != nil {
|
||||
return result.(sql.Result), err
|
||||
}
|
||||
@ -96,7 +96,7 @@ func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result
|
||||
// QueryContext executes a prepared query statement with the given arguments
|
||||
// and returns the query results as a *Rows.
|
||||
func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows, error) {
|
||||
result, err := s.doStmtCommit(stmtTypeQueryContext, ctx, args...)
|
||||
result, err := s.doStmtCommit(ctx, stmtTypeQueryContext, args...)
|
||||
if result != nil {
|
||||
return result.(*sql.Rows), err
|
||||
}
|
||||
@ -110,7 +110,7 @@ func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows
|
||||
// Otherwise, the *Row's Scan scans the first selected row and discards
|
||||
// the rest.
|
||||
func (s *Stmt) QueryRowContext(ctx context.Context, args ...interface{}) *sql.Row {
|
||||
result, _ := s.doStmtCommit(stmtTypeQueryRowContext, ctx, args...)
|
||||
result, _ := s.doStmtCommit(ctx, stmtTypeQueryRowContext, args...)
|
||||
if result != nil {
|
||||
return result.(*sql.Row)
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func (r Record) Struct(pointer interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return gconv.StructTag(r.Map(), pointer, OrmTagForStruct)
|
||||
return gconv.StructTag(r, pointer, OrmTagForStruct)
|
||||
}
|
||||
|
||||
// IsEmpty checks and returns whether `r` is empty.
|
||||
|
@ -1,27 +0,0 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
// Deprecated, use Json instead.
|
||||
func (r Record) ToJson() string {
|
||||
return r.Json()
|
||||
}
|
||||
|
||||
// Deprecated, use Xml instead.
|
||||
func (r Record) ToXml(rootTag ...string) string {
|
||||
return r.Xml(rootTag...)
|
||||
}
|
||||
|
||||
// Deprecated, use Map instead.
|
||||
func (r Record) ToMap() Map {
|
||||
return r.Map()
|
||||
}
|
||||
|
||||
// Deprecated, use Struct instead.
|
||||
func (r Record) ToStruct(pointer interface{}) error {
|
||||
return r.Struct(pointer)
|
||||
}
|
@ -153,7 +153,7 @@ func (r Result) MapKeyUint(key string) map[uint]Map {
|
||||
return m
|
||||
}
|
||||
|
||||
// RecordKeyInt converts `r` to a map[int]Record of which key is specified by `key`.
|
||||
// RecordKeyStr converts `r` to a map[string]Record of which key is specified by `key`.
|
||||
func (r Result) RecordKeyStr(key string) map[string]Record {
|
||||
m := make(map[string]Record)
|
||||
for _, item := range r {
|
||||
@ -189,5 +189,5 @@ func (r Result) RecordKeyUint(key string) map[uint]Record {
|
||||
// Structs converts `r` to struct slice.
|
||||
// Note that the parameter `pointer` should be type of *[]struct/*[]*struct.
|
||||
func (r Result) Structs(pointer interface{}) (err error) {
|
||||
return gconv.StructsTag(r.List(), pointer, OrmTagForStruct)
|
||||
return gconv.StructsTag(r, pointer, OrmTagForStruct)
|
||||
}
|
||||
|
@ -1,57 +0,0 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
// Deprecated, use Json instead.
|
||||
func (r Result) ToJson() string {
|
||||
return r.Json()
|
||||
}
|
||||
|
||||
// Deprecated, use Xml instead.
|
||||
func (r Result) ToXml(rootTag ...string) string {
|
||||
return r.Xml(rootTag...)
|
||||
}
|
||||
|
||||
// Deprecated, use List instead.
|
||||
func (r Result) ToList() List {
|
||||
return r.List()
|
||||
}
|
||||
|
||||
// Deprecated, use MapKeyStr instead.
|
||||
func (r Result) ToStringMap(key string) map[string]Map {
|
||||
return r.MapKeyStr(key)
|
||||
}
|
||||
|
||||
// Deprecated, use MapKetInt instead.
|
||||
func (r Result) ToIntMap(key string) map[int]Map {
|
||||
return r.MapKeyInt(key)
|
||||
}
|
||||
|
||||
// Deprecated, use MapKeyUint instead.
|
||||
func (r Result) ToUintMap(key string) map[uint]Map {
|
||||
return r.MapKeyUint(key)
|
||||
}
|
||||
|
||||
// Deprecated, use RecordKeyStr instead.
|
||||
func (r Result) ToStringRecord(key string) map[string]Record {
|
||||
return r.RecordKeyStr(key)
|
||||
}
|
||||
|
||||
// Deprecated, use RecordKetInt instead.
|
||||
func (r Result) ToIntRecord(key string) map[int]Record {
|
||||
return r.RecordKeyInt(key)
|
||||
}
|
||||
|
||||
// Deprecated, use RecordKetUint instead.
|
||||
func (r Result) ToUintRecord(key string) map[uint]Record {
|
||||
return r.RecordKeyUint(key)
|
||||
}
|
||||
|
||||
// Deprecated, use Structs instead.
|
||||
func (r Result) ToStructs(pointer interface{}) (err error) {
|
||||
return r.Structs(pointer)
|
||||
}
|
@ -18,9 +18,9 @@ import (
|
||||
|
||||
// MyDriver is a custom database driver, which is used for testing only.
|
||||
// For simplifying the unit testing case purpose, MyDriver struct inherits the mysql driver
|
||||
// gdb.DriverMysql and overwrites its function HandleSqlBeforeCommit.
|
||||
// So if there's any sql execution, it goes through MyDriver.HandleSqlBeforeCommit firstly and
|
||||
// then gdb.DriverMysql.HandleSqlBeforeCommit.
|
||||
// gdb.DriverMysql and overwrites its function DoCommit.
|
||||
// So if there's any sql execution, it goes through MyDriver.DoCommit firstly and
|
||||
// then gdb.DriverMysql.DoCommit.
|
||||
// You can call it sql "HOOK" or "HiJack" as your will.
|
||||
type MyDriver struct {
|
||||
*gdb.DriverMysql
|
||||
@ -41,11 +41,11 @@ func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HandleSqlBeforeCommit handles the sql before posts it to database.
|
||||
// DoCommit handles the sql before posts it to database.
|
||||
// It here overwrites the same method of gdb.DriverMysql and makes some custom changes.
|
||||
func (d *MyDriver) HandleSqlBeforeCommit(ctx context.Context, link gdb.Link, sql string, args []interface{}) (string, []interface{}) {
|
||||
func (d *MyDriver) DoCommit(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
|
||||
latestSqlString.Set(sql)
|
||||
return d.DriverMysql.HandleSqlBeforeCommit(ctx, link, sql, args)
|
||||
return d.DriverMysql.DoCommit(ctx, link, sql, args)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -7,6 +7,7 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
@ -29,9 +30,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
db gdb.DB
|
||||
dbPrefix gdb.DB
|
||||
configNode gdb.ConfigNode
|
||||
db gdb.DB
|
||||
dbPrefix gdb.DB
|
||||
dbCtxStrict gdb.DB
|
||||
configNode gdb.ConfigNode
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -56,9 +58,15 @@ func init() {
|
||||
}
|
||||
nodePrefix := configNode
|
||||
nodePrefix.Prefix = TableNamePrefix1
|
||||
|
||||
nodeCtxStrict := configNode
|
||||
nodeCtxStrict.CtxStrict = true
|
||||
|
||||
gdb.AddConfigNode("test", configNode)
|
||||
gdb.AddConfigNode("prefix", nodePrefix)
|
||||
gdb.AddConfigNode("ctxstrict", nodeCtxStrict)
|
||||
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
|
||||
|
||||
// Default db.
|
||||
if r, err := gdb.New(); err != nil {
|
||||
gtest.Error(err)
|
||||
@ -87,6 +95,20 @@ func init() {
|
||||
gtest.Error(err)
|
||||
}
|
||||
dbPrefix.SetSchema(TestSchema1)
|
||||
|
||||
// CtxStrict db.
|
||||
if r, err := gdb.New("ctxstrict"); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
dbCtxStrict = r
|
||||
}
|
||||
if _, err := dbCtxStrict.Ctx(context.TODO()).Exec(fmt.Sprintf(schemaTemplate, TestSchema1)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := dbCtxStrict.Ctx(context.TODO()).Exec(fmt.Sprintf(schemaTemplate, TestSchema2)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
dbCtxStrict.SetSchema(TestSchema1)
|
||||
}
|
||||
|
||||
func createTable(table ...string) string {
|
||||
@ -111,7 +133,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
|
||||
switch configNode.Type {
|
||||
case "sqlite":
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
passport varchar(45),
|
||||
@ -124,7 +146,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
case "pgsql":
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id bigint NOT NULL,
|
||||
passport varchar(45),
|
||||
@ -137,7 +159,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
case "mssql":
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(`
|
||||
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='%s' and xtype='U')
|
||||
CREATE TABLE %s (
|
||||
ID numeric(10,0) NOT NULL,
|
||||
@ -151,7 +173,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
case "oracle":
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
ID NUMBER(10) NOT NULL,
|
||||
PASSPORT VARCHAR(45) NOT NULL,
|
||||
@ -164,7 +186,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
case "mysql":
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
passport varchar(45) NULL,
|
||||
@ -195,7 +217,7 @@ func createInitTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
})
|
||||
}
|
||||
|
||||
result, err := db.BatchInsert(name, array.Slice())
|
||||
result, err := db.Ctx(context.TODO()).Insert(name, array.Slice())
|
||||
gtest.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
@ -205,7 +227,7 @@ func createInitTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
}
|
||||
|
||||
func dropTableWithDb(db gdb.DB, table string) {
|
||||
if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
|
||||
if _, err := db.Ctx(context.TODO()).Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
@ -62,3 +62,23 @@ func Test_Ctx_Model(t *testing.T) {
|
||||
db.Model(table).All()
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Ctx_Strict(t *testing.T) {
|
||||
table := createInitTableWithDb(dbCtxStrict)
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, err := dbCtxStrict.Query("select 1")
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := dbCtxStrict.Model(table).All()
|
||||
t.AssertNE(err, nil)
|
||||
t.Assert(len(r), 0)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := dbCtxStrict.Model(table).Ctx(context.TODO()).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(r), TableSize)
|
||||
})
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ func Test_DB_BatchInsert(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
r, err := db.BatchInsert(table, g.List{
|
||||
r, err := db.Insert(table, g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "t2",
|
||||
@ -357,7 +357,7 @@ func Test_DB_BatchInsert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
// []interface{}
|
||||
r, err := db.BatchInsert(table, g.Slice{
|
||||
r, err := db.Insert(table, g.Slice{
|
||||
g.Map{
|
||||
"id": 2,
|
||||
"passport": "t2",
|
||||
@ -382,7 +382,7 @@ func Test_DB_BatchInsert(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.BatchInsert(table, g.Map{
|
||||
result, err := db.Insert(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "p1",
|
||||
@ -416,7 +416,7 @@ func Test_DB_BatchInsert_Struct(t *testing.T) {
|
||||
NickName: "T1",
|
||||
CreateTime: gtime.Now(),
|
||||
}
|
||||
result, err := db.BatchInsert(table, user)
|
||||
result, err := db.Insert(table, user)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
@ -584,7 +584,7 @@ func Test_DB_GetStruct(t *testing.T) {
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
@ -597,7 +597,7 @@ func Test_DB_GetStruct(t *testing.T) {
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
@ -615,7 +615,7 @@ func Test_DB_GetStructs(t *testing.T) {
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
@ -635,7 +635,7 @@ func Test_DB_GetStructs(t *testing.T) {
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
@ -1283,7 +1283,7 @@ func Test_DB_Prefix(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
result, err := db.BatchInsert(name, array.Slice())
|
||||
result, err := db.Insert(name, array.Slice())
|
||||
t.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
|
@ -899,7 +899,7 @@ func Test_Model_Struct(t *testing.T) {
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Model(table).Where("id=1").Struct(user)
|
||||
err := db.Model(table).Where("id=1").Scan(user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_1")
|
||||
t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
@ -913,7 +913,7 @@ func Test_Model_Struct(t *testing.T) {
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Model(table).Where("id=1").Struct(user)
|
||||
err := db.Model(table).Where("id=1").Scan(user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_1")
|
||||
t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
@ -928,7 +928,7 @@ func Test_Model_Struct(t *testing.T) {
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := (*User)(nil)
|
||||
err := db.Model(table).Where("id=1").Struct(&user)
|
||||
err := db.Model(table).Where("id=1").Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_1")
|
||||
t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
@ -960,7 +960,7 @@ func Test_Model_Struct(t *testing.T) {
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Model(table).Where("id=-1").Struct(user)
|
||||
err := db.Model(table).Where("id=-1").Scan(user)
|
||||
t.Assert(err, sql.ErrNoRows)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -972,7 +972,7 @@ func Test_Model_Struct(t *testing.T) {
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var user *User
|
||||
err := db.Model(table).Where("id=-1").Struct(&user)
|
||||
err := db.Model(table).Where("id=-1").Scan(&user)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
@ -992,7 +992,7 @@ func Test_Model_Struct_CustomType(t *testing.T) {
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Model(table).Where("id=1").Struct(user)
|
||||
err := db.Model(table).Where("id=1").Scan(user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_1")
|
||||
t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
@ -1012,7 +1012,7 @@ func Test_Model_Structs(t *testing.T) {
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.Model(table).Order("id asc").Structs(&users)
|
||||
err := db.Model(table).Order("id asc").Scan(&users)
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
@ -1035,7 +1035,7 @@ func Test_Model_Structs(t *testing.T) {
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Model(table).Order("id asc").Structs(&users)
|
||||
err := db.Model(table).Order("id asc").Scan(&users)
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
@ -1081,38 +1081,40 @@ func Test_Model_Structs(t *testing.T) {
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Model(table).Where("id<0").Structs(&users)
|
||||
err := db.Model(table).Where("id<0").Scan(&users)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_StructsWithJsonTag(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Uid int `json:"id"`
|
||||
Passport string
|
||||
Password string
|
||||
Name string `json:"nick_name"`
|
||||
Time gtime.Time `json:"create_time"`
|
||||
}
|
||||
var users []User
|
||||
err := db.Model(table).Order("id asc").Structs(&users)
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Uid, 1)
|
||||
t.Assert(users[1].Uid, 2)
|
||||
t.Assert(users[2].Uid, 3)
|
||||
t.Assert(users[0].Name, "name_1")
|
||||
t.Assert(users[1].Name, "name_2")
|
||||
t.Assert(users[2].Name, "name_3")
|
||||
t.Assert(users[0].Time.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
}
|
||||
// JSON tag is only used for JSON Marshal/Unmarshal, DO NOT use it in multiple purposes!
|
||||
//func Test_Model_StructsWithJsonTag(t *testing.T) {
|
||||
// table := createInitTable()
|
||||
// defer dropTable(table)
|
||||
//
|
||||
// db.SetDebug(true)
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// type User struct {
|
||||
// Uid int `json:"id"`
|
||||
// Passport string
|
||||
// Password string
|
||||
// Name string `json:"nick_name"`
|
||||
// Time gtime.Time `json:"create_time"`
|
||||
// }
|
||||
// var users []User
|
||||
// err := db.Model(table).Order("id asc").Scan(&users)
|
||||
// if err != nil {
|
||||
// gtest.Error(err)
|
||||
// }
|
||||
// t.Assert(len(users), TableSize)
|
||||
// t.Assert(users[0].Uid, 1)
|
||||
// t.Assert(users[1].Uid, 2)
|
||||
// t.Assert(users[2].Uid, 3)
|
||||
// t.Assert(users[0].Name, "name_1")
|
||||
// t.Assert(users[1].Name, "name_2")
|
||||
// t.Assert(users[2].Name, "name_3")
|
||||
// t.Assert(users[0].Time.String(), "2018-10-24 10:00:00")
|
||||
// })
|
||||
//}
|
||||
|
||||
func Test_Model_Scan(t *testing.T) {
|
||||
table := createInitTable()
|
||||
@ -1469,11 +1471,11 @@ func Test_Model_Where(t *testing.T) {
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
// struct
|
||||
// struct, automatic mapping and filtering.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int `json:"id"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
Id int
|
||||
Nickname string
|
||||
}
|
||||
result, err := db.Model(table).Where(User{3, "name_3"}).One()
|
||||
t.AssertNil(err)
|
||||
@ -3098,7 +3100,7 @@ func Test_TimeZoneInsert(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, _ = db.Model(tableName).Unscoped().Insert(u)
|
||||
userEntity := &User{}
|
||||
err := db.Model(tableName).Where("id", 1).Unscoped().Struct(&userEntity)
|
||||
err := db.Model(tableName).Where("id", 1).Unscoped().Scan(&userEntity)
|
||||
t.AssertNil(err)
|
||||
t.Assert(userEntity.CreatedAt.String(), "2020-11-22 04:23:45")
|
||||
t.Assert(userEntity.UpdatedAt.String(), "2020-11-22 05:23:45")
|
||||
@ -3129,7 +3131,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) {
|
||||
XXX_TYPE int
|
||||
}
|
||||
var a = A{}
|
||||
err := db.Model(table).Fields(a).Where("id", 1).Struct(&a)
|
||||
err := db.Model(table).Fields(a).Where("id", 1).Scan(&a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.ID, 1)
|
||||
t.Assert(a.PASSPORT, "user_1")
|
||||
@ -3143,7 +3145,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) {
|
||||
XXX_TYPE int
|
||||
}
|
||||
var a *A
|
||||
err := db.Model(table).Fields(a).Where("id", 1).Struct(&a)
|
||||
err := db.Model(table).Fields(a).Where("id", 1).Scan(&a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.ID, 1)
|
||||
t.Assert(a.PASSPORT, "user_1")
|
||||
@ -3157,7 +3159,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) {
|
||||
XXX_TYPE int
|
||||
}
|
||||
var a *A
|
||||
err := db.Model(table).Fields(&a).Where("id", 1).Struct(&a)
|
||||
err := db.Model(table).Fields(&a).Where("id", 1).Scan(&a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.ID, 1)
|
||||
t.Assert(a.PASSPORT, "user_1")
|
||||
@ -3551,3 +3553,179 @@ func Test_Model_Increment_Decrement(t *testing.T) {
|
||||
t.Assert(count, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_OnDuplicate(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
// string.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": "pp1",
|
||||
"password": "pw1",
|
||||
"nickname": "n1",
|
||||
"create_time": "2016-06-06",
|
||||
}
|
||||
_, err := db.Model(table).OnDuplicate("passport,password").Data(data).Save()
|
||||
t.AssertNil(err)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data["passport"])
|
||||
t.Assert(one["password"], data["password"])
|
||||
t.Assert(one["nickname"], "name_1")
|
||||
})
|
||||
|
||||
// slice.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": "pp1",
|
||||
"password": "pw1",
|
||||
"nickname": "n1",
|
||||
"create_time": "2016-06-06",
|
||||
}
|
||||
_, err := db.Model(table).OnDuplicate(g.Slice{"passport", "password"}).Data(data).Save()
|
||||
t.AssertNil(err)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data["passport"])
|
||||
t.Assert(one["password"], data["password"])
|
||||
t.Assert(one["nickname"], "name_1")
|
||||
})
|
||||
|
||||
// map.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": "pp1",
|
||||
"password": "pw1",
|
||||
"nickname": "n1",
|
||||
"create_time": "2016-06-06",
|
||||
}
|
||||
_, err := db.Model(table).OnDuplicate(g.Map{
|
||||
"passport": "nickname",
|
||||
"password": "nickname",
|
||||
}).Data(data).Save()
|
||||
t.AssertNil(err)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data["nickname"])
|
||||
t.Assert(one["password"], data["nickname"])
|
||||
t.Assert(one["nickname"], "name_1")
|
||||
})
|
||||
|
||||
// map+raw.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapStrStr{
|
||||
"id": "1",
|
||||
"passport": "pp1",
|
||||
"password": "pw1",
|
||||
"nickname": "n1",
|
||||
"create_time": "2016-06-06",
|
||||
}
|
||||
_, err := db.Model(table).OnDuplicate(g.Map{
|
||||
"passport": gdb.Raw("CONCAT(VALUES(`passport`), '1')"),
|
||||
"password": gdb.Raw("CONCAT(VALUES(`password`), '2')"),
|
||||
}).Data(data).Save()
|
||||
t.AssertNil(err)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data["passport"]+"1")
|
||||
t.Assert(one["password"], data["password"]+"2")
|
||||
t.Assert(one["nickname"], "name_1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_OnDuplicateEx(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
// string.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": "pp1",
|
||||
"password": "pw1",
|
||||
"nickname": "n1",
|
||||
"create_time": "2016-06-06",
|
||||
}
|
||||
_, err := db.Model(table).OnDuplicateEx("nickname,create_time").Data(data).Save()
|
||||
t.AssertNil(err)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data["passport"])
|
||||
t.Assert(one["password"], data["password"])
|
||||
t.Assert(one["nickname"], "name_1")
|
||||
})
|
||||
|
||||
// slice.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": "pp1",
|
||||
"password": "pw1",
|
||||
"nickname": "n1",
|
||||
"create_time": "2016-06-06",
|
||||
}
|
||||
_, err := db.Model(table).OnDuplicateEx(g.Slice{"nickname", "create_time"}).Data(data).Save()
|
||||
t.AssertNil(err)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data["passport"])
|
||||
t.Assert(one["password"], data["password"])
|
||||
t.Assert(one["nickname"], "name_1")
|
||||
})
|
||||
|
||||
// map.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": "pp1",
|
||||
"password": "pw1",
|
||||
"nickname": "n1",
|
||||
"create_time": "2016-06-06",
|
||||
}
|
||||
_, err := db.Model(table).OnDuplicateEx(g.Map{
|
||||
"nickname": "nickname",
|
||||
"create_time": "nickname",
|
||||
}).Data(data).Save()
|
||||
t.AssertNil(err)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data["passport"])
|
||||
t.Assert(one["password"], data["password"])
|
||||
t.Assert(one["nickname"], "name_1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Raw(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.
|
||||
Raw(fmt.Sprintf("select * from %s where id in (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
|
||||
WhereLT("id", 8).
|
||||
WhereIn("id", g.Slice{1, 2, 3, 4, 5, 6, 7}).
|
||||
OrderDesc("id").
|
||||
Limit(2).
|
||||
All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 2)
|
||||
t.Assert(all[0]["id"], 7)
|
||||
t.Assert(all[1]["id"], 5)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.
|
||||
Raw(fmt.Sprintf("select * from %s where id in (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
|
||||
WhereLT("id", 8).
|
||||
WhereIn("id", g.Slice{1, 2, 3, 4, 5, 6, 7}).
|
||||
OrderDesc("id").
|
||||
Limit(2).
|
||||
Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 6)
|
||||
})
|
||||
}
|
||||
|
@ -8,10 +8,13 @@ package gdb_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -121,7 +124,7 @@ func Test_Struct_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := new(User)
|
||||
err := db.Model(table).Struct(user, "id=1")
|
||||
err := db.Model(table).Scan(user, "id=1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(*user.Id, 1)
|
||||
t.Assert(*user.Passport, "user_1")
|
||||
@ -130,7 +133,7 @@ func Test_Struct_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Model(table).Struct(&user, "id=1")
|
||||
err := db.Model(table).Scan(&user, "id=1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(*user.Id, 1)
|
||||
t.Assert(*user.Passport, "user_1")
|
||||
@ -201,7 +204,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
users := make([]User, 0)
|
||||
err := db.Model(table).Structs(&users, "id < 3")
|
||||
err := db.Model(table).Scan(&users, "id < 3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
@ -211,7 +214,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
users := make([]*User, 0)
|
||||
err := db.Model(table).Structs(&users, "id < 3")
|
||||
err := db.Model(table).Scan(&users, "id < 3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
@ -221,7 +224,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.Model(table).Structs(&users, "id < 3")
|
||||
err := db.Model(table).Scan(&users, "id < 3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
@ -231,7 +234,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.Model(table).Structs(&users, "id < 3")
|
||||
err := db.Model(table).Scan(&users, "id < 3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
@ -254,7 +257,7 @@ func Test_Struct_Empty(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := new(User)
|
||||
err := db.Model(table).Where("id=100").Struct(user)
|
||||
err := db.Model(table).Where("id=100").Scan(user)
|
||||
t.Assert(err, sql.ErrNoRows)
|
||||
t.AssertNE(user, nil)
|
||||
})
|
||||
@ -269,7 +272,7 @@ func Test_Struct_Empty(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Model(table).Where("id=100").Struct(&user)
|
||||
err := db.Model(table).Where("id=100").Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user, nil)
|
||||
})
|
||||
@ -395,17 +398,17 @@ type User struct {
|
||||
}
|
||||
|
||||
func (user *User) UnmarshalValue(value interface{}) error {
|
||||
switch result := value.(type) {
|
||||
case map[string]interface{}:
|
||||
user.Id = result["id"].(int)
|
||||
user.Passport = result["passport"].(string)
|
||||
user.Password = ""
|
||||
user.Nickname = result["nickname"].(string)
|
||||
user.CreateTime = gtime.New(result["create_time"])
|
||||
if record, ok := value.(gdb.Record); ok {
|
||||
*user = User{
|
||||
Id: record["id"].Int(),
|
||||
Passport: record["passport"].String(),
|
||||
Password: "",
|
||||
Nickname: record["nickname"].String(),
|
||||
CreateTime: record["create_time"].GTime(),
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return gconv.Struct(value, user)
|
||||
}
|
||||
return gerror.Newf(`unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value))
|
||||
}
|
||||
|
||||
func Test_Model_Scan_UnmarshalValue(t *testing.T) {
|
||||
@ -452,3 +455,27 @@ func Test_Model_Scan_Map(t *testing.T) {
|
||||
t.Assert(users[9].CreateTime.String(), CreateTime)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Scan_AutoFilteringByStructAttributes(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
}
|
||||
//db.SetDebug(true)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Model(table).OrderAsc("id").Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.Model(table).OrderAsc("id").Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
})
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ func Test_TX_BatchInsert(t *testing.T) {
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.BatchInsert(table, g.List{
|
||||
if _, err := tx.Insert(table, g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "t",
|
||||
@ -201,7 +201,7 @@ func Test_TX_BatchReplace(t *testing.T) {
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.BatchReplace(table, g.List{
|
||||
if _, err := tx.Replace(table, g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "USER_2",
|
||||
@ -244,7 +244,7 @@ func Test_TX_BatchSave(t *testing.T) {
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.BatchSave(table, g.List{
|
||||
if _, err := tx.Save(table, g.List{
|
||||
{
|
||||
"id": 4,
|
||||
"passport": "USER_4",
|
||||
@ -349,7 +349,7 @@ func Test_TX_Update(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
_, err = tx.Table(table).Fields("create_time").Where("id", 3).Value()
|
||||
_, err = tx.Model(table).Fields("create_time").Where("id", 3).Value()
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Model(table).Fields("create_time").Where("id", 3).Value(); err != nil {
|
||||
@ -666,7 +666,6 @@ func Test_TX_GetScan(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_TX_Delete(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
@ -685,6 +684,8 @@ func Test_TX_Delete(t *testing.T) {
|
||||
} else {
|
||||
t.Assert(n, 0)
|
||||
}
|
||||
|
||||
t.Assert(tx.IsClosed(), true)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -697,7 +698,7 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if _, err := tx.Delete(table, 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := tx.Table(table).Count(); err != nil {
|
||||
if n, err := tx.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 0)
|
||||
@ -711,6 +712,8 @@ func Test_TX_Delete(t *testing.T) {
|
||||
t.Assert(n, TableSize)
|
||||
t.AssertNE(n, 0)
|
||||
}
|
||||
|
||||
t.Assert(tx.IsClosed(), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -721,7 +724,7 @@ func Test_Transaction(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ctx := context.TODO()
|
||||
err := db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
|
||||
if _, err := tx.Replace(table, g.Map{
|
||||
if _, err := tx.Ctx(ctx).Replace(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "USER_1",
|
||||
"password": "PASS_1",
|
||||
@ -730,11 +733,12 @@ func Test_Transaction(t *testing.T) {
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Assert(tx.IsClosed(), false)
|
||||
return gerror.New("error")
|
||||
})
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Ctx(ctx).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "name_1")
|
||||
@ -956,8 +960,8 @@ func Test_Transaction_Nested_TX_Transaction_UseDB(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
//db.SetDebug(true)
|
||||
//defer db.SetDebug(false)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
|
@ -87,7 +87,7 @@ func Test_Types(t *testing.T) {
|
||||
TinyInt bool
|
||||
}
|
||||
var obj *T
|
||||
err = db.Model("types").Struct(&obj)
|
||||
err = db.Model("types").Scan(&obj)
|
||||
t.AssertNil(err)
|
||||
t.Assert(obj.Id, 1)
|
||||
t.Assert(obj.Blob, data["blob"])
|
||||
|
146
database/gdb/gdb_z_mysql_union_test.go
Normal file
146
database/gdb/gdb_z_mysql_union_test.go
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Union(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Union(
|
||||
db.Model(table).Where("id", 1),
|
||||
db.Model(table).Where("id", 2),
|
||||
db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"),
|
||||
).OrderDesc("id").All()
|
||||
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(len(r), 3)
|
||||
t.Assert(r[0]["id"], 3)
|
||||
t.Assert(r[1]["id"], 2)
|
||||
t.Assert(r[2]["id"], 1)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Union(
|
||||
db.Model(table).Where("id", 1),
|
||||
db.Model(table).Where("id", 2),
|
||||
db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"),
|
||||
).OrderDesc("id").One()
|
||||
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(r["id"], 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_UnionAll(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.UnionAll(
|
||||
db.Model(table).Where("id", 1),
|
||||
db.Model(table).Where("id", 2),
|
||||
db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"),
|
||||
).OrderDesc("id").All()
|
||||
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(len(r), 5)
|
||||
t.Assert(r[0]["id"], 3)
|
||||
t.Assert(r[1]["id"], 2)
|
||||
t.Assert(r[2]["id"], 2)
|
||||
t.Assert(r[3]["id"], 1)
|
||||
t.Assert(r[4]["id"], 1)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.UnionAll(
|
||||
db.Model(table).Where("id", 1),
|
||||
db.Model(table).Where("id", 2),
|
||||
db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"),
|
||||
).OrderDesc("id").One()
|
||||
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(r["id"], 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Union(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Model(table).Union(
|
||||
db.Model(table).Where("id", 1),
|
||||
db.Model(table).Where("id", 2),
|
||||
db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"),
|
||||
).OrderDesc("id").All()
|
||||
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(len(r), 3)
|
||||
t.Assert(r[0]["id"], 3)
|
||||
t.Assert(r[1]["id"], 2)
|
||||
t.Assert(r[2]["id"], 1)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Model(table).Union(
|
||||
db.Model(table).Where("id", 1),
|
||||
db.Model(table).Where("id", 2),
|
||||
db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"),
|
||||
).OrderDesc("id").One()
|
||||
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(r["id"], 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_UnionAll(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Model(table).UnionAll(
|
||||
db.Model(table).Where("id", 1),
|
||||
db.Model(table).Where("id", 2),
|
||||
db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"),
|
||||
).OrderDesc("id").All()
|
||||
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(len(r), 5)
|
||||
t.Assert(r[0]["id"], 3)
|
||||
t.Assert(r[1]["id"], 2)
|
||||
t.Assert(r[2]["id"], 2)
|
||||
t.Assert(r[3]["id"], 1)
|
||||
t.Assert(r[4]["id"], 1)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Model(table).UnionAll(
|
||||
db.Model(table).Where("id", 1),
|
||||
db.Model(table).Where("id", 2),
|
||||
db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).OrderDesc("id"),
|
||||
).OrderDesc("id").One()
|
||||
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(r["id"], 3)
|
||||
})
|
||||
}
|
@ -114,7 +114,7 @@ func New(config *Config) *Redis {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
intlog.Printf(`open new connection, config:%+v`, config)
|
||||
intlog.Printf(context.TODO(), `open new connection, config:%+v`, config)
|
||||
// AUTH
|
||||
if len(config.Pass) > 0 {
|
||||
if _, err := c.Do("AUTH", config.Pass); err != nil {
|
||||
|
@ -7,6 +7,7 @@
|
||||
package gredis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
|
||||
@ -36,7 +37,7 @@ func SetConfig(config *Config, name ...string) {
|
||||
configs.Set(group, config)
|
||||
instances.Remove(group)
|
||||
|
||||
intlog.Printf(`SetConfig for group "%s": %+v`, group, config)
|
||||
intlog.Printf(context.TODO(), `SetConfig for group "%s": %+v`, group, config)
|
||||
}
|
||||
|
||||
// SetConfigByStr sets the global configuration for specified group with string.
|
||||
@ -78,7 +79,7 @@ func RemoveConfig(name ...string) {
|
||||
configs.Remove(group)
|
||||
instances.Remove(group)
|
||||
|
||||
intlog.Printf(`RemoveConfig: %s`, group)
|
||||
intlog.Printf(context.TODO(), `RemoveConfig: %s`, group)
|
||||
}
|
||||
|
||||
// ConfigFromStr parses and returns config from given str.
|
||||
|
@ -8,8 +8,8 @@ package gredis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
@ -50,7 +50,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{}
|
||||
if timeout > 0 {
|
||||
conn, ok := c.Conn.(redis.ConnWithTimeout)
|
||||
if !ok {
|
||||
return gvar.New(nil), errors.New(`current connection does not support "ConnWithTimeout"`)
|
||||
return gvar.New(nil), gerror.New(`current connection does not support "ConnWithTimeout"`)
|
||||
}
|
||||
return conn.DoWithTimeout(timeout, commandName, args...)
|
||||
}
|
||||
@ -107,7 +107,7 @@ func (c *Conn) ReceiveVar() (*gvar.Var, error) {
|
||||
func (c *Conn) ReceiveVarWithTimeout(timeout time.Duration) (*gvar.Var, error) {
|
||||
conn, ok := c.Conn.(redis.ConnWithTimeout)
|
||||
if !ok {
|
||||
return gvar.New(nil), errors.New(`current connection does not support "ConnWithTimeout"`)
|
||||
return gvar.New(nil), gerror.New(`current connection does not support "ConnWithTimeout"`)
|
||||
}
|
||||
return resultToVar(conn.ReceiveWithTimeout(timeout))
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"github.com/gogf/gf"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
@ -38,19 +37,9 @@ const (
|
||||
tracingEventRedisExecutionArguments = "redis.execution.arguments"
|
||||
)
|
||||
|
||||
var (
|
||||
// tracingInternal enables tracing for internal type spans.
|
||||
// It's true in default.
|
||||
tracingInternal = true
|
||||
)
|
||||
|
||||
func init() {
|
||||
tracingInternal = gcmd.GetOptWithEnv("gf.tracing.internal", true).Bool()
|
||||
}
|
||||
|
||||
// addTracingItem checks and adds redis tracing information to OpenTelemetry.
|
||||
func (c *Conn) addTracingItem(item *tracingItem) {
|
||||
if !tracingInternal || !gtrace.IsActivated(c.ctx) {
|
||||
if !gtrace.IsTracingInternal() || !gtrace.IsActivated(c.ctx) {
|
||||
return
|
||||
}
|
||||
tr := otel.GetTracerProvider().Tracer(
|
||||
|
@ -8,6 +8,7 @@ package gdebug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -53,11 +54,13 @@ func Caller(skip ...int) (function string, path string, line int) {
|
||||
//
|
||||
// The parameter <filter> is used to filter the path of the caller.
|
||||
func CallerWithFilter(filter string, skip ...int) (function string, path string, line int) {
|
||||
number := 0
|
||||
var (
|
||||
number = 0
|
||||
ok = true
|
||||
)
|
||||
if len(skip) > 0 {
|
||||
number = skip[0]
|
||||
}
|
||||
ok := true
|
||||
pc, file, line, start := callerFromIndex([]string{filter})
|
||||
if start != -1 {
|
||||
for i := start + number; i < maxCallerDepth; i++ {
|
||||
@ -65,12 +68,6 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string,
|
||||
pc, file, line, ok = runtime.Caller(i)
|
||||
}
|
||||
if ok {
|
||||
if filter != "" && strings.Contains(file, filter) {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
}
|
||||
function := ""
|
||||
if fn := runtime.FuncForPC(pc); fn == nil {
|
||||
function = "unknown"
|
||||
@ -104,8 +101,14 @@ func callerFromIndex(filters []string) (pc uintptr, file string, line int, index
|
||||
if filtered {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
if !utils.IsDebugEnabled() {
|
||||
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if index > 0 {
|
||||
index--
|
||||
|
@ -80,14 +80,17 @@ func StackWithFilters(filters []string, skip ...int) string {
|
||||
if filtered {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !utils.IsDebugEnabled() {
|
||||
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if fn := runtime.FuncForPC(pc); fn == nil {
|
||||
name = "unknown"
|
||||
} else {
|
||||
|
@ -21,8 +21,7 @@ package gcharset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
@ -60,11 +59,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err
|
||||
transform.NewReader(bytes.NewReader([]byte(src)), e.NewDecoder()),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s to utf8 failed. %v", srcCharset, err)
|
||||
return "", gerror.Newf("%s to utf8 failed. %v", srcCharset, err)
|
||||
}
|
||||
src = string(tmp)
|
||||
} else {
|
||||
return dst, errors.New(fmt.Sprintf("unsupport srcCharset: %s", srcCharset))
|
||||
return dst, gerror.Newf("unsupport srcCharset: %s", srcCharset)
|
||||
}
|
||||
}
|
||||
// Do the converting from UTF-8 to <dstCharset>.
|
||||
@ -74,11 +73,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err
|
||||
transform.NewReader(bytes.NewReader([]byte(src)), e.NewEncoder()),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("utf to %s failed. %v", dstCharset, err)
|
||||
return "", gerror.Newf("utf to %s failed. %v", dstCharset, err)
|
||||
}
|
||||
dst = string(tmp)
|
||||
} else {
|
||||
return dst, errors.New(fmt.Sprintf("unsupport dstCharset: %s", dstCharset))
|
||||
return dst, gerror.Newf("unsupport dstCharset: %s", dstCharset)
|
||||
}
|
||||
} else {
|
||||
dst = src
|
||||
|
@ -9,6 +9,7 @@ package gcompress
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
@ -92,7 +93,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix
|
||||
headerPrefix = strings.Replace(headerPrefix, "//", "/", -1)
|
||||
for _, file := range files {
|
||||
if exclude == file {
|
||||
intlog.Printf(`exclude file path: %s`, file)
|
||||
intlog.Printf(context.TODO(), `exclude file path: %s`, file)
|
||||
continue
|
||||
}
|
||||
dir := gfile.Dir(file[len(path):])
|
||||
|
@ -10,8 +10,8 @@ package gini
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"io"
|
||||
"strings"
|
||||
@ -70,7 +70,7 @@ func Decode(data []byte) (res map[string]interface{}, err error) {
|
||||
}
|
||||
|
||||
if haveSection == false {
|
||||
return nil, errors.New("failed to parse INI file, section not found")
|
||||
return nil, gerror.New("failed to parse INI file, section not found")
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -319,23 +319,11 @@ func (j *Json) GetStruct(pattern string, pointer interface{}, mapping ...map[str
|
||||
return gconv.Struct(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructDeep does GetStruct recursively.
|
||||
// Deprecated, use GetStruct instead.
|
||||
func (j *Json) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructs converts any slice to given struct slice.
|
||||
func (j *Json) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Structs(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructsDeep converts any slice to given struct slice recursively.
|
||||
// Deprecated, use GetStructs instead.
|
||||
func (j *Json) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructsDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetScan automatically calls Struct or Structs function according to the type of parameter
|
||||
// <pointer> to implement the converting..
|
||||
func (j *Json) GetScan(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
|
@ -70,7 +70,7 @@ func (j *Json) MustToJsonIndentString() string {
|
||||
// ========================================================================
|
||||
|
||||
func (j *Json) ToXml(rootTag ...string) ([]byte, error) {
|
||||
return gxml.Encode(j.ToMap(), rootTag...)
|
||||
return gxml.Encode(j.Map(), rootTag...)
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlString(rootTag ...string) (string, error) {
|
||||
@ -79,7 +79,7 @@ func (j *Json) ToXmlString(rootTag ...string) (string, error) {
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlIndent(rootTag ...string) ([]byte, error) {
|
||||
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
|
||||
return gxml.EncodeWithIndent(j.Map(), rootTag...)
|
||||
}
|
||||
|
||||
func (j *Json) ToXmlIndentString(rootTag ...string) (string, error) {
|
||||
|
@ -8,8 +8,8 @@ package gjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/internal/json"
|
||||
@ -264,7 +264,7 @@ func doLoadContentWithOptions(dataType string, data []byte, options Options) (*J
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
err = errors.New("unsupported type for loading")
|
||||
err = gerror.New("unsupported type for loading")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1,104 +0,0 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gjson
|
||||
|
||||
import "github.com/gogf/gf/util/gconv"
|
||||
|
||||
// ToMap converts current Json object to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
// Deprecated, use Map instead.
|
||||
func (j *Json) ToMap() map[string]interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Map(*(j.p))
|
||||
}
|
||||
|
||||
// ToArray converts current Json object to []interface{}.
|
||||
// It returns nil if fails.
|
||||
// Deprecated, use Array instead.
|
||||
func (j *Json) ToArray() []interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Interfaces(*(j.p))
|
||||
}
|
||||
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <pointer> should be a pointer type of *struct.
|
||||
// Deprecated, use Struct instead.
|
||||
func (j *Json) ToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Struct(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructDeep converts current Json object to specified object recursively.
|
||||
// The <pointer> should be a pointer type of *struct.
|
||||
// Deprecated, use Struct instead.
|
||||
func (j *Json) ToStructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.StructDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructs converts current Json object to specified object slice.
|
||||
// The <pointer> should be a pointer type of []struct/*struct.
|
||||
// Deprecated, use Structs instead.
|
||||
func (j *Json) ToStructs(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Structs(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructsDeep converts current Json object to specified object slice recursively.
|
||||
// The <pointer> should be a pointer type of []struct/*struct.
|
||||
// Deprecated, use Structs instead.
|
||||
func (j *Json) ToStructsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.StructsDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToScan automatically calls Struct or Structs function according to the type of parameter
|
||||
// <pointer> to implement the converting..
|
||||
// Deprecated, use Scan instead.
|
||||
func (j *Json) ToScan(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Scan(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToScanDeep automatically calls StructDeep or StructsDeep function according to the type of
|
||||
// parameter <pointer> to implement the converting..
|
||||
// Deprecated, use Scan instead.
|
||||
func (j *Json) ToScanDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.ScanDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMap converts current Json object to specified map variable.
|
||||
// The parameter of <pointer> should be type of *map.
|
||||
// Deprecated, use MapToMap instead.
|
||||
func (j *Json) ToMapToMap(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.MapToMap(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMaps converts current Json object to specified map variable slice.
|
||||
// The parameter of <pointer> should be type of []map/*map.
|
||||
// Deprecated, use MapToMaps instead.
|
||||
func (j *Json) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.MapToMaps(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMapsDeep converts current Json object to specified map variable slice recursively.
|
||||
// The parameter of <pointer> should be type of []map/*map.
|
||||
// Deprecated, use MapToMaps instead.
|
||||
func (j *Json) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.MapToMapsDeep(*(j.p), pointer, mapping...)
|
||||
}
|
@ -100,7 +100,7 @@ func Example_conversionToStruct() {
|
||||
Array []string
|
||||
}
|
||||
users := new(Users)
|
||||
if err := j.ToStruct(users); err != nil {
|
||||
if err := j.Struct(users); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf(`%+v`, users)
|
||||
|
@ -353,7 +353,7 @@ func Test_Convert2(t *testing.T) {
|
||||
t.Assert(j.GetGTime("time").Format("Y-m-d"), "2019-06-12")
|
||||
t.Assert(j.GetDuration("time").String(), "0s")
|
||||
|
||||
err := j.ToStruct(&name)
|
||||
err := j.Struct(&name)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(name.Name, "gf")
|
||||
//j.Dump()
|
||||
@ -369,7 +369,7 @@ func Test_Convert2(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
j = gjson.New(`[1,2,3]`)
|
||||
t.Assert(len(j.ToArray()), 3)
|
||||
t.Assert(len(j.Array()), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ func Test_Basic(t *testing.T) {
|
||||
err = j.Remove("1")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(j.Get("0"), 1)
|
||||
t.Assert(len(j.ToArray()), 2)
|
||||
t.Assert(len(j.Array()), 2)
|
||||
|
||||
j = gjson.New(`[1,2,3]`)
|
||||
// If index 0 is delete, its next item will be at index 0.
|
||||
@ -408,13 +408,13 @@ func Test_Basic(t *testing.T) {
|
||||
t.Assert(j.Remove("0"), nil)
|
||||
t.Assert(j.Remove("0"), nil)
|
||||
t.Assert(j.Get("0"), nil)
|
||||
t.Assert(len(j.ToArray()), 0)
|
||||
t.Assert(len(j.Array()), 0)
|
||||
|
||||
j = gjson.New(`[1,2,3]`)
|
||||
err = j.Remove("3")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(j.Get("0"), 1)
|
||||
t.Assert(len(j.ToArray()), 3)
|
||||
t.Assert(len(j.Array()), 3)
|
||||
|
||||
j = gjson.New(`[1,2,3]`)
|
||||
err = j.Remove("0.3")
|
||||
|
@ -61,7 +61,7 @@ func Test_MapAttributeConvert(t *testing.T) {
|
||||
Title map[string]interface{}
|
||||
}{}
|
||||
|
||||
err = j.ToStruct(&tx)
|
||||
err = j.Struct(&tx)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(tx.Title, g.Map{
|
||||
"l1": "标签1", "l2": "标签2",
|
||||
@ -76,7 +76,7 @@ func Test_MapAttributeConvert(t *testing.T) {
|
||||
Title map[string]string
|
||||
}{}
|
||||
|
||||
err = j.ToStruct(&tx)
|
||||
err = j.Struct(&tx)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(tx.Title, g.Map{
|
||||
"l1": "标签1", "l2": "标签2",
|
||||
|
@ -230,14 +230,14 @@ func Test_Convert(t *testing.T) {
|
||||
err := p.GetStruct("person", &name)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(name.Name, "gf")
|
||||
t.Assert(p.ToMap()["name"], "gf")
|
||||
err = p.ToStruct(&name)
|
||||
t.Assert(p.Map()["name"], "gf")
|
||||
err = p.Struct(&name)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(name.Name, "gf")
|
||||
//p.Dump()
|
||||
|
||||
p = gparser.New(`[0,1,2]`)
|
||||
t.Assert(p.ToArray()[0], 0)
|
||||
t.Assert(p.Array()[0], 0)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package errors provides simple functions to manipulate errors.
|
||||
// Package gerror provides simple functions to manipulate errors.
|
||||
//
|
||||
// Very note that, this package is quite a base package, which should not import extra
|
||||
// packages except standard packages, to avoid cycle imports.
|
||||
@ -237,7 +237,7 @@ func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{}
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the error code of current error.
|
||||
// Code returns the error code of current error.
|
||||
// It returns -1 if it has no error code or it does not implements interface Code.
|
||||
func Code(err error) int {
|
||||
if err != nil {
|
||||
|
@ -7,6 +7,7 @@
|
||||
package gins
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
@ -92,11 +93,11 @@ func Database(name ...string) gdb.DB {
|
||||
}
|
||||
if len(cg) > 0 {
|
||||
if gdb.GetConfig(group) == nil {
|
||||
intlog.Printf("add configuration for group: %s, %#v", g, cg)
|
||||
intlog.Printf(context.TODO(), "add configuration for group: %s, %#v", g, cg)
|
||||
gdb.SetConfigGroup(g, cg)
|
||||
} else {
|
||||
intlog.Printf("ignore configuration as it already exists for group: %s, %#v", g, cg)
|
||||
intlog.Printf("%s, %#v", g, cg)
|
||||
intlog.Printf(context.TODO(), "ignore configuration as it already exists for group: %s, %#v", g, cg)
|
||||
intlog.Printf(context.TODO(), "%s, %#v", g, cg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,17 +105,17 @@ func Database(name ...string) gdb.DB {
|
||||
// which is the default group configuration.
|
||||
if node := parseDBConfigNode(configMap); node != nil {
|
||||
cg := gdb.ConfigGroup{}
|
||||
if node.LinkInfo != "" || node.Host != "" {
|
||||
if node.Link != "" || node.Host != "" {
|
||||
cg = append(cg, *node)
|
||||
}
|
||||
|
||||
if len(cg) > 0 {
|
||||
if gdb.GetConfig(group) == nil {
|
||||
intlog.Printf("add configuration for group: %s, %#v", gdb.DefaultGroupName, cg)
|
||||
intlog.Printf(context.TODO(), "add configuration for group: %s, %#v", gdb.DefaultGroupName, cg)
|
||||
gdb.SetConfigGroup(gdb.DefaultGroupName, cg)
|
||||
} else {
|
||||
intlog.Printf("ignore configuration as it already exists for group: %s, %#v", gdb.DefaultGroupName, cg)
|
||||
intlog.Printf("%s, %#v", gdb.DefaultGroupName, cg)
|
||||
intlog.Printf(context.TODO(), "ignore configuration as it already exists for group: %s, %#v", gdb.DefaultGroupName, cg)
|
||||
intlog.Printf(context.TODO(), "%s, %#v", gdb.DefaultGroupName, cg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,15 +157,19 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, v := gutil.MapPossibleItemByKey(nodeMap, "link"); v != nil {
|
||||
node.LinkInfo = gconv.String(v)
|
||||
// To be compatible with old version.
|
||||
if _, v := gutil.MapPossibleItemByKey(nodeMap, "LinkInfo"); v != nil {
|
||||
node.Link = gconv.String(v)
|
||||
}
|
||||
if _, v := gutil.MapPossibleItemByKey(nodeMap, "Link"); v != nil {
|
||||
node.Link = gconv.String(v)
|
||||
}
|
||||
// Parse link syntax.
|
||||
if node.LinkInfo != "" && node.Type == "" {
|
||||
match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.LinkInfo)
|
||||
if node.Link != "" && node.Type == "" {
|
||||
match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.Link)
|
||||
if len(match) == 3 {
|
||||
node.Type = gstr.Trim(match[1])
|
||||
node.LinkInfo = gstr.Trim(match[2])
|
||||
node.Link = gstr.Trim(match[2])
|
||||
}
|
||||
}
|
||||
return node
|
||||
|
@ -24,17 +24,30 @@ func Server(name ...interface{}) *ghttp.Server {
|
||||
s := ghttp.GetServer(name...)
|
||||
// To avoid file no found error while it's not necessary.
|
||||
if Config().Available() {
|
||||
var m map[string]interface{}
|
||||
var (
|
||||
serverConfigMap map[string]interface{}
|
||||
serverLoggerConfigMap map[string]interface{}
|
||||
)
|
||||
nodeKey, _ := gutil.MapPossibleItemByKey(Config().GetMap("."), configNodeNameServer)
|
||||
if nodeKey == "" {
|
||||
nodeKey = configNodeNameServer
|
||||
}
|
||||
m = Config().GetMap(fmt.Sprintf(`%s.%s`, nodeKey, s.GetName()))
|
||||
if len(m) == 0 {
|
||||
m = Config().GetMap(nodeKey)
|
||||
// Server configuration.
|
||||
serverConfigMap = Config().GetMap(fmt.Sprintf(`%s.%s`, nodeKey, s.GetName()))
|
||||
if len(serverConfigMap) == 0 {
|
||||
serverConfigMap = Config().GetMap(nodeKey)
|
||||
}
|
||||
if len(m) > 0 {
|
||||
if err := s.SetConfigWithMap(m); err != nil {
|
||||
if len(serverConfigMap) > 0 {
|
||||
if err := s.SetConfigWithMap(serverConfigMap); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
// Server logger configuration.
|
||||
serverLoggerConfigMap = Config().GetMap(
|
||||
fmt.Sprintf(`%s.%s.%s`, nodeKey, s.GetName(), configNodeNameLogger),
|
||||
)
|
||||
if len(serverLoggerConfigMap) > 0 {
|
||||
if err := s.Logger().SetConfigWithMap(serverLoggerConfigMap); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.14
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28
|
||||
github.com/fatih/color v1.12.0 // indirect
|
||||
github.com/fatih/color v1.12.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
|
8
go.sum
8
go.sum
@ -4,6 +4,8 @@ github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUao
|
||||
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 h1:pP/uEy52biKDytlgK/ug8kiYPAiYu6KajKVUHfGrtyw=
|
||||
@ -16,6 +18,10 @@ github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvK
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA=
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
@ -44,6 +50,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -8,8 +8,8 @@ package gi18n
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -70,7 +70,7 @@ func New(options ...Options) *Manager {
|
||||
gregex.Quote(opts.Delimiters[1]),
|
||||
),
|
||||
}
|
||||
intlog.Printf(`New: %#v`, m)
|
||||
intlog.Printf(context.TODO(), `New: %#v`, m)
|
||||
return m
|
||||
}
|
||||
|
||||
@ -101,24 +101,24 @@ func (m *Manager) SetPath(path string) error {
|
||||
} else {
|
||||
realPath, _ := gfile.Search(path)
|
||||
if realPath == "" {
|
||||
return errors.New(fmt.Sprintf(`%s does not exist`, path))
|
||||
return gerror.Newf(`%s does not exist`, path)
|
||||
}
|
||||
m.options.Path = realPath
|
||||
}
|
||||
intlog.Printf(`SetPath: %s`, m.options.Path)
|
||||
intlog.Printf(context.TODO(), `SetPath: %s`, m.options.Path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLanguage sets the language for translator.
|
||||
func (m *Manager) SetLanguage(language string) {
|
||||
m.options.Language = language
|
||||
intlog.Printf(`SetLanguage: %s`, m.options.Language)
|
||||
intlog.Printf(context.TODO(), `SetLanguage: %s`, m.options.Language)
|
||||
}
|
||||
|
||||
// SetDelimiters sets the delimiters for translator.
|
||||
func (m *Manager) SetDelimiters(left, right string) {
|
||||
m.pattern = fmt.Sprintf(`%s(\w+)%s`, gregex.Quote(left), gregex.Quote(right))
|
||||
intlog.Printf(`SetDelimiters: %v`, m.pattern)
|
||||
intlog.Printf(context.TODO(), `SetDelimiters: %v`, m.pattern)
|
||||
}
|
||||
|
||||
// T is alias of Translate for convenience.
|
||||
@ -139,7 +139,7 @@ func (m *Manager) TranslateFormat(ctx context.Context, format string, values ...
|
||||
|
||||
// Translate translates <content> with configured language.
|
||||
func (m *Manager) Translate(ctx context.Context, content string) string {
|
||||
m.init()
|
||||
m.init(ctx)
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
transLang := m.options.Language
|
||||
@ -163,14 +163,14 @@ func (m *Manager) Translate(ctx context.Context, content string) string {
|
||||
}
|
||||
return match[0]
|
||||
})
|
||||
intlog.Printf(`Translate for language: %s`, transLang)
|
||||
intlog.Printf(ctx, `Translate for language: %s`, transLang)
|
||||
return result
|
||||
}
|
||||
|
||||
// GetContent retrieves and returns the configured content for given key and specified language.
|
||||
// It returns an empty string if not found.
|
||||
func (m *Manager) GetContent(ctx context.Context, key string) string {
|
||||
m.init()
|
||||
m.init(ctx)
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
transLang := m.options.Language
|
||||
@ -185,7 +185,7 @@ func (m *Manager) GetContent(ctx context.Context, key string) string {
|
||||
|
||||
// init initializes the manager for lazy initialization design.
|
||||
// The i18n manager is only initialized once.
|
||||
func (m *Manager) init() {
|
||||
func (m *Manager) init(ctx context.Context) {
|
||||
m.mu.RLock()
|
||||
// If the data is not nil, means it's already initialized.
|
||||
if m.data != nil {
|
||||
@ -223,17 +223,13 @@ func (m *Manager) init() {
|
||||
m.data[lang][k] = gconv.String(v)
|
||||
}
|
||||
} else {
|
||||
intlog.Errorf("load i18n file '%s' failed: %v", name, err)
|
||||
intlog.Errorf(ctx, "load i18n file '%s' failed: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if m.options.Path != "" {
|
||||
files, _ := gfile.ScanDirFile(m.options.Path, "*.*", true)
|
||||
if len(files) == 0 {
|
||||
//intlog.Printf(
|
||||
// "no i18n files found in configured directory: %s",
|
||||
// m.options.Path,
|
||||
//)
|
||||
return
|
||||
}
|
||||
var (
|
||||
@ -258,7 +254,7 @@ func (m *Manager) init() {
|
||||
m.data[lang][k] = gconv.String(v)
|
||||
}
|
||||
} else {
|
||||
intlog.Errorf("load i18n file '%s' failed: %v", file, err)
|
||||
intlog.Errorf(ctx, "load i18n file '%s' failed: %v", file, err)
|
||||
}
|
||||
}
|
||||
// Monitor changes of i18n files for hot reload feature.
|
||||
|
@ -8,9 +8,12 @@
|
||||
package intlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
@ -39,42 +42,56 @@ func SetEnabled(enabled bool) {
|
||||
|
||||
// Print prints `v` with newline using fmt.Println.
|
||||
// The parameter `v` can be multiple variables.
|
||||
func Print(v ...interface{}) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
}
|
||||
fmt.Println(append([]interface{}{now(), "[INTE]", file()}, v...)...)
|
||||
func Print(ctx context.Context, v ...interface{}) {
|
||||
doPrint(ctx, fmt.Sprint(v...), false)
|
||||
}
|
||||
|
||||
// Printf prints `v` with format `format` using fmt.Printf.
|
||||
// The parameter `v` can be multiple variables.
|
||||
func Printf(format string, v ...interface{}) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
}
|
||||
fmt.Printf(now()+" [INTE] "+file()+" "+format+"\n", v...)
|
||||
func Printf(ctx context.Context, format string, v ...interface{}) {
|
||||
doPrint(ctx, fmt.Sprintf(format, v...), false)
|
||||
}
|
||||
|
||||
// Error prints `v` with newline using fmt.Println.
|
||||
// The parameter `v` can be multiple variables.
|
||||
func Error(v ...interface{}) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
}
|
||||
array := append([]interface{}{now(), "[INTE]", file()}, v...)
|
||||
array = append(array, "\n"+gdebug.StackWithFilter(stackFilterKey))
|
||||
fmt.Println(array...)
|
||||
func Error(ctx context.Context, v ...interface{}) {
|
||||
doPrint(ctx, fmt.Sprint(v...), true)
|
||||
}
|
||||
|
||||
// Errorf prints `v` with format `format` using fmt.Printf.
|
||||
func Errorf(format string, v ...interface{}) {
|
||||
func Errorf(ctx context.Context, format string, v ...interface{}) {
|
||||
doPrint(ctx, fmt.Sprintf(format, v...), true)
|
||||
}
|
||||
|
||||
func doPrint(ctx context.Context, content string, stack bool) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
}
|
||||
fmt.Printf(
|
||||
now()+" [INTE] "+file()+" "+format+"\n%s\n",
|
||||
append(v, gdebug.StackWithFilter(stackFilterKey))...,
|
||||
)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteString(now())
|
||||
buffer.WriteString(" [INTE] ")
|
||||
buffer.WriteString(file())
|
||||
if s := traceIdStr(ctx); s != "" {
|
||||
buffer.WriteString(" " + s)
|
||||
}
|
||||
buffer.WriteString(content)
|
||||
buffer.WriteString("\n")
|
||||
if stack {
|
||||
buffer.WriteString(gdebug.StackWithFilter(stackFilterKey))
|
||||
}
|
||||
fmt.Print(buffer.String())
|
||||
}
|
||||
|
||||
// traceIdStr retrieves and returns the trace id string for logging output.
|
||||
func traceIdStr(ctx context.Context) string {
|
||||
if ctx == nil {
|
||||
return ""
|
||||
}
|
||||
spanCtx := trace.SpanContextFromContext(ctx)
|
||||
if traceId := spanCtx.TraceID(); traceId.IsValid() {
|
||||
return "{" + traceId.String() + "}"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// now returns current time string.
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
debugKey = "gf.debug" // Debug key for checking if in debug mode.
|
||||
StackFilterKeyForGoFrame = "/github.com/gogf/gf/" // Stack filtering key for all GoFrame module paths.
|
||||
commandEnvKeyForDebugKey = "gf.debug" // Debug key for checking if in debug mode.
|
||||
StackFilterKeyForGoFrame = "github.com/gogf/gf@" // Stack filtering key for all GoFrame module paths.
|
||||
)
|
||||
|
||||
var (
|
||||
@ -22,7 +22,7 @@ var (
|
||||
|
||||
func init() {
|
||||
// Debugging configured.
|
||||
value := command.GetOptWithEnv(debugKey)
|
||||
value := command.GetOptWithEnv(commandEnvKeyForDebugKey)
|
||||
if value == "" || value == "0" || value == "false" {
|
||||
isDebugEnabled = false
|
||||
} else {
|
||||
|
@ -19,11 +19,11 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
// Server wraps the http.Server and provides more feature.
|
||||
// Server wraps the http.Server and provides more rich features.
|
||||
Server struct {
|
||||
name string // Unique name for instance management.
|
||||
config ServerConfig // Configuration.
|
||||
plugins []Plugin // Plugin array.
|
||||
plugins []Plugin // Plugin array to extends server functionality.
|
||||
servers []*gracefulServer // Underlying http.Server array.
|
||||
serverCount *gtype.Int // Underlying http.Server count.
|
||||
closeChan chan struct{} // Used for underlying server closing event notification.
|
||||
@ -44,7 +44,7 @@ type (
|
||||
Priority int // Just for reference.
|
||||
}
|
||||
|
||||
// Router item just for route dumps.
|
||||
// RouterItem is just for route dumps.
|
||||
RouterItem struct {
|
||||
Server string // Server name.
|
||||
Address string // Listening address.
|
||||
@ -98,7 +98,7 @@ type (
|
||||
Stack() string
|
||||
}
|
||||
|
||||
// Request handler function.
|
||||
// HandlerFunc is request handler function.
|
||||
HandlerFunc = func(r *Request)
|
||||
|
||||
// Listening file descriptor mapping.
|
||||
@ -107,10 +107,6 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
HOOK_BEFORE_SERVE = "HOOK_BEFORE_SERVE" // Deprecated, use HookBeforeServe instead.
|
||||
HOOK_AFTER_SERVE = "HOOK_AFTER_SERVE" // Deprecated, use HookAfterServe instead.
|
||||
HOOK_BEFORE_OUTPUT = "HOOK_BEFORE_OUTPUT" // Deprecated, use HookBeforeOutput instead.
|
||||
HOOK_AFTER_OUTPUT = "HOOK_AFTER_OUTPUT" // Deprecated, use HookAfterOutput instead.
|
||||
HookBeforeServe = "HOOK_BEFORE_SERVE"
|
||||
HookAfterServe = "HOOK_AFTER_SERVE"
|
||||
HookBeforeOutput = "HOOK_BEFORE_OUTPUT"
|
||||
|
@ -73,7 +73,10 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
|
||||
EnterTime: gtime.TimestampMilli(),
|
||||
}
|
||||
request.Cookie = GetCookie(request)
|
||||
request.Session = s.sessionManager.New(request.GetSessionId())
|
||||
request.Session = s.sessionManager.New(
|
||||
r.Context(),
|
||||
request.GetSessionId(),
|
||||
)
|
||||
request.Response.Request = request
|
||||
request.Middleware = &middleware{
|
||||
request: request,
|
||||
@ -84,7 +87,7 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
|
||||
address = request.RemoteAddr
|
||||
header = fmt.Sprintf("%v", request.Header)
|
||||
)
|
||||
intlog.Print(address, header)
|
||||
intlog.Print(r.Context(), address, header)
|
||||
return guid.S([]byte(address), []byte(header))
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -7,7 +7,8 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
@ -21,6 +22,7 @@ import (
|
||||
// UploadFile wraps the multipart uploading file with more and convenient features.
|
||||
type UploadFile struct {
|
||||
*multipart.FileHeader
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// UploadFiles is array type for *UploadFile.
|
||||
@ -33,14 +35,14 @@ type UploadFiles []*UploadFile
|
||||
// Note that it will OVERWRITE the target file if there's already a same name file exist.
|
||||
func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename string, err error) {
|
||||
if f == nil {
|
||||
return "", errors.New("file is empty, maybe you retrieve it from invalid field name or form enctype")
|
||||
return "", gerror.New("file is empty, maybe you retrieve it from invalid field name or form enctype")
|
||||
}
|
||||
if !gfile.Exists(dirPath) {
|
||||
if err = gfile.Mkdir(dirPath); err != nil {
|
||||
return
|
||||
}
|
||||
} else if !gfile.IsDir(dirPath) {
|
||||
return "", errors.New(`parameter "dirPath" should be a directory path`)
|
||||
return "", gerror.New(`parameter "dirPath" should be a directory path`)
|
||||
}
|
||||
|
||||
file, err := f.Open()
|
||||
@ -60,7 +62,7 @@ func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename stri
|
||||
return "", err
|
||||
}
|
||||
defer newFile.Close()
|
||||
intlog.Printf(`save upload file: %s`, filePath)
|
||||
intlog.Printf(f.ctx, `save upload file: %s`, filePath)
|
||||
if _, err := io.Copy(newFile, file); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -74,7 +76,7 @@ func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename stri
|
||||
// The parameter <randomlyRename> specifies whether randomly renames all the file names.
|
||||
func (fs UploadFiles) Save(dirPath string, randomlyRename ...bool) (filenames []string, err error) {
|
||||
if len(fs) == 0 {
|
||||
return nil, errors.New("file array is empty, maybe you retrieve it from invalid field name or form enctype")
|
||||
return nil, gerror.New("file array is empty, maybe you retrieve it from invalid field name or form enctype")
|
||||
}
|
||||
for _, f := range fs {
|
||||
if filename, err := f.Save(dirPath, randomlyRename...); err != nil {
|
||||
@ -114,6 +116,7 @@ func (r *Request) GetUploadFiles(name string) UploadFiles {
|
||||
uploadFiles := make(UploadFiles, len(multipartFiles))
|
||||
for k, v := range multipartFiles {
|
||||
uploadFiles[k] = &UploadFile{
|
||||
ctx: r.Context(),
|
||||
FileHeader: v,
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// GetPage creates and returns the pagination object for given <totalSize> and <pageSize>.
|
||||
// NOTE THAT the page parameter name from client is constantly defined as gpage.PAGE_NAME
|
||||
// NOTE THAT the page parameter name from client is constantly defined as gpage.DefaultPageName
|
||||
// for simplification and convenience.
|
||||
func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page {
|
||||
// It must has Router object attribute.
|
||||
@ -27,7 +27,7 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page {
|
||||
// Check the page variable in the URI.
|
||||
if len(r.Router.RegNames) > 0 {
|
||||
for _, name := range r.Router.RegNames {
|
||||
if name == gpage.PAGE_NAME {
|
||||
if name == gpage.DefaultPageName {
|
||||
uriHasPageName = true
|
||||
break
|
||||
}
|
||||
@ -38,8 +38,8 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page {
|
||||
urlTemplate = r.Router.Uri
|
||||
for i, name := range r.Router.RegNames {
|
||||
rule := fmt.Sprintf(`[:\*]%s|\{%s\}`, name, name)
|
||||
if name == gpage.PAGE_NAME {
|
||||
urlTemplate, _ = gregex.ReplaceString(rule, gpage.PAGE_PLACE_HOLDER, urlTemplate)
|
||||
if name == gpage.DefaultPageName {
|
||||
urlTemplate, _ = gregex.ReplaceString(rule, gpage.DefaultPagePlaceHolder, urlTemplate)
|
||||
} else {
|
||||
urlTemplate, _ = gregex.ReplaceString(rule, match[i+1], urlTemplate)
|
||||
}
|
||||
@ -51,7 +51,7 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page {
|
||||
// Check the page variable in the query string.
|
||||
if !uriHasPageName {
|
||||
values := url.Query()
|
||||
values.Set(gpage.PAGE_NAME, gpage.PAGE_PLACE_HOLDER)
|
||||
values.Set(gpage.DefaultPageName, gpage.DefaultPagePlaceHolder)
|
||||
url.RawQuery = values.Encode()
|
||||
// Replace the encoded "{.page}" to original "{.page}".
|
||||
url.RawQuery = gstr.Replace(url.RawQuery, "%7B.page%7D", "{.page}")
|
||||
@ -60,5 +60,5 @@ func (r *Request) GetPage(totalSize, pageSize int) *gpage.Page {
|
||||
urlTemplate += "?" + url.RawQuery
|
||||
}
|
||||
|
||||
return gpage.New(totalSize, pageSize, r.GetInt(gpage.PAGE_NAME), urlTemplate)
|
||||
return gpage.New(totalSize, pageSize, r.GetInt(gpage.DefaultPageName), urlTemplate)
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package ghttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
@ -70,10 +71,10 @@ func serverProcessInit() {
|
||||
// Process message handler.
|
||||
// It's enabled only graceful feature is enabled.
|
||||
if gracefulEnabled {
|
||||
intlog.Printf("%d: graceful reload feature is enabled", gproc.Pid())
|
||||
intlog.Printf(context.TODO(), "%d: graceful reload feature is enabled", gproc.Pid())
|
||||
go handleProcessMessage()
|
||||
} else {
|
||||
intlog.Printf("%d: graceful reload feature is disabled", gproc.Pid())
|
||||
intlog.Printf(context.TODO(), "%d: graceful reload feature is disabled", gproc.Pid())
|
||||
}
|
||||
|
||||
// It's an ugly calling for better initializing the main package path
|
||||
@ -195,7 +196,7 @@ func (s *Server) Start() error {
|
||||
if gproc.IsChild() {
|
||||
gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout)*time.Second, func() {
|
||||
if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil {
|
||||
//glog.Error("server error in process communication:", err)
|
||||
intlog.Error(context.TODO(), "server error in process communication:", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -315,9 +316,9 @@ func (s *Server) Run() {
|
||||
// Remove plugins.
|
||||
if len(s.plugins) > 0 {
|
||||
for _, p := range s.plugins {
|
||||
intlog.Printf(`remove plugin: %s`, p.Name())
|
||||
intlog.Printf(context.TODO(), `remove plugin: %s`, p.Name())
|
||||
if err := p.Remove(); err != nil {
|
||||
intlog.Errorf("%+v", err)
|
||||
intlog.Errorf(context.TODO(), "%+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -333,7 +334,7 @@ func Wait() {
|
||||
s := v.(*Server)
|
||||
if len(s.plugins) > 0 {
|
||||
for _, p := range s.plugins {
|
||||
intlog.Printf(`remove plugin: %s`, p.Name())
|
||||
intlog.Printf(context.TODO(), `remove plugin: %s`, p.Name())
|
||||
p.Remove()
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,9 @@ package ghttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"os"
|
||||
@ -51,7 +52,7 @@ var serverProcessStatus = gtype.NewInt()
|
||||
// The optional parameter <newExeFilePath> specifies the new binary file for creating process.
|
||||
func RestartAllServer(newExeFilePath ...string) error {
|
||||
if !gracefulEnabled {
|
||||
return errors.New("graceful reload feature is disabled")
|
||||
return gerror.New("graceful reload feature is disabled")
|
||||
}
|
||||
serverActionLocker.Lock()
|
||||
defer serverActionLocker.Unlock()
|
||||
@ -84,9 +85,9 @@ func checkProcessStatus() error {
|
||||
if status > 0 {
|
||||
switch status {
|
||||
case adminActionRestarting:
|
||||
return errors.New("server is restarting")
|
||||
return gerror.New("server is restarting")
|
||||
case adminActionShuttingDown:
|
||||
return errors.New("server is shutting down")
|
||||
return gerror.New("server is shutting down")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -97,7 +98,7 @@ func checkProcessStatus() error {
|
||||
func checkActionFrequency() error {
|
||||
interval := gtime.TimestampMilli() - serverActionLastTime.Val()
|
||||
if interval < adminActionIntervalLimit {
|
||||
return errors.New(fmt.Sprintf("too frequent action, please retry in %d ms", adminActionIntervalLimit-interval))
|
||||
return gerror.Newf("too frequent action, please retry in %d ms", adminActionIntervalLimit-interval)
|
||||
}
|
||||
serverActionLastTime.Set(gtime.TimestampMilli())
|
||||
return nil
|
||||
@ -173,7 +174,7 @@ func bufferToServerFdMap(buffer []byte) map[string]listenerFdMap {
|
||||
sfm := make(map[string]listenerFdMap)
|
||||
if len(buffer) > 0 {
|
||||
j, _ := gjson.LoadContent(buffer)
|
||||
for k, _ := range j.ToMap() {
|
||||
for k, _ := range j.Map() {
|
||||
m := make(map[string]string)
|
||||
for k, v := range j.GetMap(k) {
|
||||
m[k] = gconv.String(v)
|
||||
@ -266,10 +267,10 @@ func handleProcessMessage() {
|
||||
for {
|
||||
if msg := gproc.Receive(adminGProcCommGroup); msg != nil {
|
||||
if bytes.EqualFold(msg.Data, []byte("exit")) {
|
||||
intlog.Printf("%d: process message: exit", gproc.Pid())
|
||||
intlog.Printf(context.TODO(), "%d: process message: exit", gproc.Pid())
|
||||
shutdownWebServersGracefully()
|
||||
allDoneChan <- struct{}{}
|
||||
intlog.Printf("%d: process message: exit done", gproc.Pid())
|
||||
intlog.Printf(context.TODO(), "%d: process message: exit done", gproc.Pid())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"os"
|
||||
"os/signal"
|
||||
@ -33,7 +34,7 @@ func handleProcessSignal() {
|
||||
)
|
||||
for {
|
||||
sig = <-procSignalChan
|
||||
intlog.Printf(`signal received: %s`, sig.String())
|
||||
intlog.Printf(context.TODO(), `signal received: %s`, sig.String())
|
||||
switch sig {
|
||||
// Shutdown the servers.
|
||||
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT:
|
||||
@ -49,7 +50,7 @@ func handleProcessSignal() {
|
||||
// Restart the servers.
|
||||
case syscall.SIGUSR1:
|
||||
if err := restartWebServers(sig.String()); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
}
|
||||
return
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
@ -223,13 +224,14 @@ type ServerConfig struct {
|
||||
GracefulTimeout uint8 `json:"gracefulTimeout"`
|
||||
}
|
||||
|
||||
// Config creates and returns a ServerConfig object with default configurations.
|
||||
// Deprecated. Use NewConfig instead.
|
||||
func Config() ServerConfig {
|
||||
return NewConfig()
|
||||
}
|
||||
|
||||
// NewConfig creates and returns a ServerConfig object with default configurations.
|
||||
// Note that, do not define this default configuration to local package variable, as there're
|
||||
// Note that, do not define this default configuration to local package variable, as there are
|
||||
// some pointer attributes that may be shared in different servers.
|
||||
func NewConfig() ServerConfig {
|
||||
return ServerConfig{
|
||||
@ -331,10 +333,12 @@ func (s *Server) SetConfig(c ServerConfig) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.config.Logger.SetLevelStr(s.config.LogLevel)
|
||||
if err := s.config.Logger.SetLevelStr(s.config.LogLevel); err != nil {
|
||||
intlog.Error(context.TODO(), err)
|
||||
}
|
||||
|
||||
SetGraceful(c.Graceful)
|
||||
intlog.Printf("SetConfig: %+v", s.config)
|
||||
intlog.Printf(context.TODO(), "SetConfig: %+v", s.config)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
package ghttp
|
||||
|
||||
import "github.com/gogf/gf/os/glog"
|
||||
|
||||
// SetLogPath sets the log path for server.
|
||||
// It logs content to file only if the log path is set.
|
||||
func (s *Server) SetLogPath(path string) error {
|
||||
@ -23,6 +25,17 @@ func (s *Server) SetLogPath(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLogger sets the logger for logging responsibility.
|
||||
// Note that it cannot be set in runtime as there may be concurrent safety issue.
|
||||
func (s *Server) SetLogger(logger *glog.Logger) {
|
||||
s.config.Logger = logger
|
||||
}
|
||||
|
||||
// Logger is alias of GetLogger.
|
||||
func (s *Server) Logger() *glog.Logger {
|
||||
return s.config.Logger
|
||||
}
|
||||
|
||||
// SetLogLevel sets logging level by level string.
|
||||
func (s *Server) SetLogLevel(level string) {
|
||||
s.config.LogLevel = level
|
||||
|
@ -9,8 +9,8 @@ package ghttp
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gproc"
|
||||
"github.com/gogf/gf/os/gres"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
@ -122,7 +122,7 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig .
|
||||
|
||||
}
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error()))
|
||||
return gerror.Newf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error())
|
||||
}
|
||||
ln, err := s.getNetListener()
|
||||
if err != nil {
|
||||
|
@ -7,6 +7,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
@ -80,9 +81,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Close the request and response body
|
||||
// to release the file descriptor in time.
|
||||
_ = request.Request.Body.Close()
|
||||
err := request.Request.Body.Close()
|
||||
if err != nil {
|
||||
intlog.Error(request.Context(), err)
|
||||
}
|
||||
if request.Request.Response != nil {
|
||||
_ = request.Request.Response.Body.Close()
|
||||
err = request.Request.Response.Body.Close()
|
||||
if err != nil {
|
||||
intlog.Error(request.Context(), err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@ -188,9 +195,11 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// searchStaticFile searches the file with given URI.
|
||||
// It returns a file struct specifying the file information.
|
||||
func (s *Server) searchStaticFile(uri string) *staticFile {
|
||||
var file *gres.File
|
||||
var path string
|
||||
var dir bool
|
||||
var (
|
||||
file *gres.File
|
||||
path string
|
||||
dir bool
|
||||
)
|
||||
// Firstly search the StaticPaths mapping.
|
||||
if len(s.config.StaticPaths) > 0 {
|
||||
for _, item := range s.config.StaticPaths {
|
||||
|
@ -9,14 +9,8 @@ package ghttp
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
// Logger returns the logger of the server.
|
||||
func (s *Server) Logger() *glog.Logger {
|
||||
return s.config.Logger
|
||||
}
|
||||
|
||||
// handleAccessLog handles the access logging for server.
|
||||
func (s *Server) handleAccessLog(r *Request) {
|
||||
if !s.IsAccessLogEnabled() {
|
||||
@ -26,7 +20,7 @@ func (s *Server) handleAccessLog(r *Request) {
|
||||
if r.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
s.Logger().File(s.config.AccessLogPattern).
|
||||
s.Logger().Ctx(r.Context()).File(s.config.AccessLogPattern).
|
||||
Stdout(s.config.LogStdout).
|
||||
Printf(
|
||||
`%d "%s %s %s %s %s" %.3f, %s, "%s", "%s"`,
|
||||
@ -63,7 +57,7 @@ func (s *Server) handleErrorLog(err error, r *Request) {
|
||||
} else {
|
||||
content += ", " + err.Error()
|
||||
}
|
||||
s.config.Logger.
|
||||
s.Logger().Ctx(r.Context()).
|
||||
File(s.config.ErrorLogPattern).
|
||||
Stdout(s.config.LogStdout).
|
||||
Print(content)
|
||||
|
@ -7,9 +7,9 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
@ -53,7 +53,7 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err
|
||||
}
|
||||
}
|
||||
if path == "" {
|
||||
err = errors.New("invalid pattern: URI should not be empty")
|
||||
err = gerror.New("invalid pattern: URI should not be empty")
|
||||
}
|
||||
if path != "/" {
|
||||
path = strings.TrimRight(path, "/")
|
||||
|
@ -621,3 +621,89 @@ func Test_Params_Parse_Validation(t *testing.T) {
|
||||
t.Assert(client.GetContent("/parse?name=john11&password1=123456&password2=123456"), `ok`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_Parse_EmbeddedWithAliasName1(t *testing.T) {
|
||||
// 获取内容列表
|
||||
type ContentGetListInput struct {
|
||||
Type string
|
||||
CategoryId uint
|
||||
Page int
|
||||
Size int
|
||||
Sort int
|
||||
UserId uint
|
||||
}
|
||||
// 获取内容列表
|
||||
type ContentGetListReq struct {
|
||||
ContentGetListInput
|
||||
CategoryId uint `p:"cate"`
|
||||
Page int `d:"1" v:"min:0#分页号码错误"`
|
||||
Size int `d:"10" v:"max:50#分页数量最大50条"`
|
||||
}
|
||||
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/parse", func(r *ghttp.Request) {
|
||||
var req *ContentGetListReq
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.Write(err)
|
||||
} else {
|
||||
r.Response.Write(req.ContentGetListInput)
|
||||
}
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", p)
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent("/parse?cate=1&page=2&size=10"), `{"Type":"","CategoryId":0,"Page":2,"Size":10,"Sort":0,"UserId":0}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_Parse_EmbeddedWithAliasName2(t *testing.T) {
|
||||
// 获取内容列表
|
||||
type ContentGetListInput struct {
|
||||
Type string
|
||||
CategoryId uint `p:"cate"`
|
||||
Page int
|
||||
Size int
|
||||
Sort int
|
||||
UserId uint
|
||||
}
|
||||
// 获取内容列表
|
||||
type ContentGetListReq struct {
|
||||
ContentGetListInput
|
||||
CategoryId uint `p:"cate"`
|
||||
Page int `d:"1" v:"min:0#分页号码错误"`
|
||||
Size int `d:"10" v:"max:50#分页数量最大50条"`
|
||||
}
|
||||
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/parse", func(r *ghttp.Request) {
|
||||
var req *ContentGetListReq
|
||||
if err := r.Parse(&req); err != nil {
|
||||
r.Response.Write(err)
|
||||
} else {
|
||||
r.Response.Write(req.ContentGetListInput)
|
||||
}
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", p)
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent("/parse?cate=1&page=2&size=10"), `{"Type":"","CategoryId":1,"Page":2,"Size":10,"Sort":0,"UserId":0}`)
|
||||
})
|
||||
}
|
||||
|
@ -335,15 +335,15 @@ func Test_Router_DomainGroup(t *testing.T) {
|
||||
d.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Group("/app", func(gApp *ghttp.RouterGroup) {
|
||||
gApp.GET("/{table}/list/{page}.html", func(r *ghttp.Request) {
|
||||
intlog.Print("/{table}/list/{page}.html")
|
||||
intlog.Print(r.Context(), "/{table}/list/{page}.html")
|
||||
r.Response.Write(r.Get("table"), "&", r.Get("page"))
|
||||
})
|
||||
gApp.GET("/order/info/{order_id}", func(r *ghttp.Request) {
|
||||
intlog.Print("/order/info/{order_id}")
|
||||
intlog.Print(r.Context(), "/order/info/{order_id}")
|
||||
r.Response.Write(r.Get("order_id"))
|
||||
})
|
||||
gApp.DELETE("/comment/{id}", func(r *ghttp.Request) {
|
||||
intlog.Print("/comment/{id}")
|
||||
intlog.Print(r.Context(), "/comment/{id}")
|
||||
r.Response.Write(r.Get("id"))
|
||||
})
|
||||
})
|
||||
|
@ -9,8 +9,7 @@ package client
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
@ -189,18 +188,18 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h
|
||||
if len(array[1]) > 6 && strings.Compare(array[1][0:6], "@file:") == 0 {
|
||||
path := array[1][6:]
|
||||
if !gfile.Exists(path) {
|
||||
return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
|
||||
return nil, gerror.Newf(`"%s" does not exist`, path)
|
||||
}
|
||||
if file, err := writer.CreateFormFile(array[0], gfile.Basename(path)); err == nil {
|
||||
if f, err := os.Open(path); err == nil {
|
||||
if _, err = io.Copy(file, f); err != nil {
|
||||
if err := f.Close(); err != nil {
|
||||
intlog.Errorf(`%+v`, err)
|
||||
intlog.Errorf(c.ctx, `%+v`, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
intlog.Errorf(`%+v`, err)
|
||||
intlog.Errorf(c.ctx, `%+v`, err)
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
@ -303,7 +302,7 @@ func (c *Client) callRequest(req *http.Request) (resp *Response, err error) {
|
||||
// The response might not be nil when err != nil.
|
||||
if resp.Response != nil {
|
||||
if err := resp.Response.Body.Close(); err != nil {
|
||||
intlog.Errorf(`%+v`, err)
|
||||
intlog.Errorf(c.ctx, `%+v`, err)
|
||||
}
|
||||
}
|
||||
if c.retryCount > 0 {
|
||||
|
@ -8,7 +8,7 @@
|
||||
package gipv4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -38,7 +38,7 @@ func GetIntranetIp() (ip string, err error) {
|
||||
return "", err
|
||||
}
|
||||
if len(ips) == 0 {
|
||||
return "", errors.New("no intranet ip found")
|
||||
return "", gerror.New("no intranet ip found")
|
||||
}
|
||||
return ips[0], nil
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ package gtcp
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
@ -116,7 +116,7 @@ func (s *Server) Close() error {
|
||||
// Run starts running the TCP Server.
|
||||
func (s *Server) Run() (err error) {
|
||||
if s.handler == nil {
|
||||
err = errors.New("start running failed: socket handler not defined")
|
||||
err = gerror.New("start running failed: socket handler not defined")
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ package gtrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/net/gipv4"
|
||||
@ -23,15 +22,17 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tracingCommonKeyIpIntranet = `ip.intranet`
|
||||
tracingCommonKeyIpHostname = `hostname`
|
||||
cmdEnvKey = "gf.gtrace" // Configuration key for command argument or environment.
|
||||
tracingCommonKeyIpIntranet = `ip.intranet`
|
||||
tracingCommonKeyIpHostname = `hostname`
|
||||
commandEnvKeyForMaxContentLogSize = "gf.gtrace.maxcontentlogsize"
|
||||
commandEnvKeyForTracingInternal = "gf.gtrace.tracinginternal"
|
||||
)
|
||||
|
||||
var (
|
||||
intranetIps, _ = gipv4.GetIntranetIpArray()
|
||||
intranetIpStr = strings.Join(intranetIps, ",")
|
||||
hostname, _ = os.Hostname()
|
||||
tracingInternal = true // tracingInternal enables tracing for internal type spans.
|
||||
tracingMaxContentLogSize = 256 * 1024 // Max log size for request and response body, especially for HTTP/RPC request.
|
||||
// defaultTextMapPropagator is the default propagator for context propagation between peers.
|
||||
defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator(
|
||||
@ -41,12 +42,18 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
if maxContentLogSize := gcmd.GetOptWithEnv(fmt.Sprintf("%s.maxcontentlogsize", cmdEnvKey)).Int(); maxContentLogSize > 0 {
|
||||
tracingInternal = gcmd.GetOptWithEnv(commandEnvKeyForTracingInternal, true).Bool()
|
||||
if maxContentLogSize := gcmd.GetOptWithEnv(commandEnvKeyForMaxContentLogSize).Int(); maxContentLogSize > 0 {
|
||||
tracingMaxContentLogSize = maxContentLogSize
|
||||
}
|
||||
CheckSetDefaultTextMapPropagator()
|
||||
}
|
||||
|
||||
// IsTracingInternal returns whether tracing spans of internal components.
|
||||
func IsTracingInternal() bool {
|
||||
return tracingInternal
|
||||
}
|
||||
|
||||
// MaxContentLogSize returns the max log size for request and response body, especially for HTTP/RPC request.
|
||||
func MaxContentLogSize() int {
|
||||
return tracingMaxContentLogSize
|
||||
|
@ -7,7 +7,7 @@
|
||||
package gudp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"net"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
@ -78,7 +78,7 @@ func (s *Server) Close() error {
|
||||
// Run starts listening UDP connection.
|
||||
func (s *Server) Run() error {
|
||||
if s.handler == nil {
|
||||
err := errors.New("start running failed: socket handler not defined")
|
||||
err := gerror.New("start running failed: socket handler not defined")
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
package gbuild
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/encoding/gbase64"
|
||||
@ -26,13 +27,13 @@ func init() {
|
||||
if builtInVarStr != "" {
|
||||
err := json.UnmarshalUseNumber(gbase64.MustDecodeString(builtInVarStr), &builtInVarMap)
|
||||
if err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
}
|
||||
builtInVarMap["gfVersion"] = gf.VERSION
|
||||
builtInVarMap["goVersion"] = runtime.Version()
|
||||
intlog.Printf("build variables: %+v", builtInVarMap)
|
||||
intlog.Printf(context.TODO(), "build variables: %+v", builtInVarMap)
|
||||
} else {
|
||||
intlog.Print("no build variables")
|
||||
intlog.Print(context.TODO(), "no build variables")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
package gcfg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
@ -23,10 +24,11 @@ type Config struct {
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultName = "config" // DefaultName is the default group name for instance usage.
|
||||
DefaultConfigFile = "config.toml" // DefaultConfigFile is the default configuration file name.
|
||||
cmdEnvKey = "gf.gcfg" // cmdEnvKey is the configuration key for command argument or environment.
|
||||
errorPrintKey = "gf.gcfg.errorprint" // errorPrintKey is used to specify the key controlling error printing to stdout.
|
||||
DefaultName = "config" // DefaultName is the default group name for instance usage.
|
||||
DefaultConfigFile = "config.toml" // DefaultConfigFile is the default configuration file name.
|
||||
commandEnvKeyForFile = "gf.gcfg.file" // commandEnvKeyForFile is the configuration key for command argument or environment configuring file name.
|
||||
commandEnvKeyForPath = "gf.gcfg.path" // commandEnvKeyForPath is the configuration key for command argument or environment configuring directory path.
|
||||
commandEnvKeyForErrorPrint = "gf.gcfg.errorprint" // commandEnvKeyForErrorPrint is used to specify the key controlling error printing to stdout.
|
||||
)
|
||||
|
||||
var (
|
||||
@ -81,7 +83,7 @@ func RemoveContent(file ...string) {
|
||||
}
|
||||
})
|
||||
|
||||
intlog.Printf(`RemoveContent: %s`, name)
|
||||
intlog.Printf(context.TODO(), `RemoveContent: %s`, name)
|
||||
}
|
||||
|
||||
// ClearContent removes all global configuration contents.
|
||||
@ -94,10 +96,10 @@ func ClearContent() {
|
||||
}
|
||||
})
|
||||
|
||||
intlog.Print(`RemoveConfig`)
|
||||
intlog.Print(context.TODO(), `RemoveConfig`)
|
||||
}
|
||||
|
||||
// errorPrint checks whether printing error to stdout.
|
||||
func errorPrint() bool {
|
||||
return gcmd.GetOptWithEnv(errorPrintKey, true).Bool()
|
||||
return gcmd.GetOptWithEnv(commandEnvKeyForErrorPrint, true).Bool()
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ package gcfg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
@ -33,7 +33,7 @@ func New(file ...string) *Config {
|
||||
name = file[0]
|
||||
} else {
|
||||
// Custom default configuration file name from command line or environment.
|
||||
if customFile := gcmd.GetOptWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" {
|
||||
if customFile := gcmd.GetOptWithEnv(commandEnvKeyForFile).String(); customFile != "" {
|
||||
name = customFile
|
||||
}
|
||||
}
|
||||
@ -43,7 +43,7 @@ func New(file ...string) *Config {
|
||||
jsonMap: gmap.NewStrAnyMap(true),
|
||||
}
|
||||
// Customized dir path from env/cmd.
|
||||
if customPath := gcmd.GetOptWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" {
|
||||
if customPath := gcmd.GetOptWithEnv(commandEnvKeyForPath).String(); customPath != "" {
|
||||
if gfile.Exists(customPath) {
|
||||
_ = c.SetPath(customPath)
|
||||
} else {
|
||||
@ -54,20 +54,20 @@ func New(file ...string) *Config {
|
||||
} else {
|
||||
// Dir path of working dir.
|
||||
if err := c.AddPath(gfile.Pwd()); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
}
|
||||
|
||||
// Dir path of main package.
|
||||
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
|
||||
if err := c.AddPath(mainPath); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Dir path of binary.
|
||||
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
|
||||
if err := c.AddPath(selfPath); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,7 +142,7 @@ func (c *Config) SetPath(path string) error {
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path))
|
||||
}
|
||||
err := errors.New(buffer.String())
|
||||
err := gerror.New(buffer.String())
|
||||
if errorPrint() {
|
||||
glog.Error(err)
|
||||
}
|
||||
@ -163,7 +163,7 @@ func (c *Config) SetPath(path string) error {
|
||||
c.jsonMap.Clear()
|
||||
c.searchPaths.Clear()
|
||||
c.searchPaths.Append(realPath)
|
||||
intlog.Print("SetPath:", realPath)
|
||||
intlog.Print(context.TODO(), "SetPath:", realPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -237,7 +237,7 @@ func (c *Config) AddPath(path string) error {
|
||||
return nil
|
||||
}
|
||||
c.searchPaths.Append(realPath)
|
||||
intlog.Print("AddPath:", realPath)
|
||||
intlog.Print(context.TODO(), "AddPath:", realPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
package gcfg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
@ -295,16 +295,7 @@ func (c *Config) GetStruct(pattern string, pointer interface{}, mapping ...map[s
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetStruct(pattern, pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetStructDeep does GetStruct recursively.
|
||||
// Deprecated, use GetStruct instead.
|
||||
func (c *Config) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetStructDeep(pattern, pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetStructs converts any slice to given struct slice.
|
||||
@ -312,16 +303,7 @@ func (c *Config) GetStructs(pattern string, pointer interface{}, mapping ...map[
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetStructs(pattern, pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetStructsDeep converts any slice to given struct slice recursively.
|
||||
// Deprecated, use GetStructs instead.
|
||||
func (c *Config) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetStructsDeep(pattern, pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetMapToMap retrieves the value by specified `pattern` and converts it to specified map variable.
|
||||
@ -330,7 +312,7 @@ func (c *Config) GetMapToMap(pattern string, pointer interface{}, mapping ...map
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetMapToMap(pattern, pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetMapToMaps retrieves the value by specified `pattern` and converts it to specified map slice
|
||||
@ -340,7 +322,7 @@ func (c *Config) GetMapToMaps(pattern string, pointer interface{}, mapping ...ma
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetMapToMaps(pattern, pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// GetMapToMapsDeep retrieves the value by specified `pattern` and converts it to specified map slice
|
||||
@ -350,88 +332,60 @@ func (c *Config) GetMapToMapsDeep(pattern string, pointer interface{}, mapping .
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.GetMapToMapsDeep(pattern, pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToMap converts current Json object to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
func (c *Config) ToMap() map[string]interface{} {
|
||||
// Map converts current Json object to map[string]interface{}. It returns nil if fails.
|
||||
func (c *Config) Map() map[string]interface{} {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToMap()
|
||||
return j.Map()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToArray converts current Json object to []interface{}.
|
||||
// Array converts current Json object to []interface{}.
|
||||
// It returns nil if fails.
|
||||
func (c *Config) ToArray() []interface{} {
|
||||
func (c *Config) Array() []interface{} {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToArray()
|
||||
return j.Array()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// Struct converts current Json object to specified object.
|
||||
// The `pointer` should be a pointer type of *struct.
|
||||
func (c *Config) ToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (c *Config) Struct(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToStruct(pointer, mapping...)
|
||||
return j.Struct(pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToStructDeep converts current Json object to specified object recursively.
|
||||
// The `pointer` should be a pointer type of *struct.
|
||||
func (c *Config) ToStructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToStructDeep(pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToStructs converts current Json object to specified object slice.
|
||||
// Structs converts current Json object to specified object slice.
|
||||
// The `pointer` should be a pointer type of []struct/*struct.
|
||||
func (c *Config) ToStructs(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (c *Config) Structs(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToStructs(pointer, mapping...)
|
||||
return j.Structs(pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToStructsDeep converts current Json object to specified object slice recursively.
|
||||
// The `pointer` should be a pointer type of []struct/*struct.
|
||||
func (c *Config) ToStructsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToStructsDeep(pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToMapToMap converts current Json object to specified map variable.
|
||||
// MapToMap converts current Json object to specified map variable.
|
||||
// The parameter of `pointer` should be type of *map.
|
||||
func (c *Config) ToMapToMap(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (c *Config) MapToMap(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToMapToMap(pointer, mapping...)
|
||||
return j.MapToMap(pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToMapToMaps converts current Json object to specified map variable slice.
|
||||
// MapToMaps converts current Json object to specified map variable slice.
|
||||
// The parameter of `pointer` should be type of []map/*map.
|
||||
func (c *Config) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (c *Config) MapToMaps(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToMapToMaps(pointer, mapping...)
|
||||
return j.MapToMaps(pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
}
|
||||
|
||||
// ToMapToMapsDeep converts current Json object to specified map variable slice recursively.
|
||||
// The parameter of `pointer` should be type of []map/*map.
|
||||
func (c *Config) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
if j := c.getJson(); j != nil {
|
||||
return j.ToMapToMapsDeep(pointer, mapping...)
|
||||
}
|
||||
return errors.New("configuration not found")
|
||||
return gerror.New("configuration not found")
|
||||
}
|
||||
|
||||
// Clear removes all parsed configuration files content cache,
|
||||
|
@ -8,13 +8,13 @@
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
// BindHandle registers callback function <f> with <cmd>.
|
||||
func BindHandle(cmd string, f func()) error {
|
||||
if _, ok := defaultCommandFuncMap[cmd]; ok {
|
||||
return errors.New("duplicated handle for command:" + cmd)
|
||||
return gerror.New("duplicated handle for command:" + cmd)
|
||||
} else {
|
||||
defaultCommandFuncMap[cmd] = f
|
||||
}
|
||||
@ -37,7 +37,7 @@ func RunHandle(cmd string) error {
|
||||
if handle, ok := defaultCommandFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
return errors.New("no handle found for command:" + cmd)
|
||||
return gerror.New("no handle found for command:" + cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -49,10 +49,10 @@ func AutoRun() error {
|
||||
if handle, ok := defaultCommandFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
return errors.New("no handle found for command:" + cmd)
|
||||
return gerror.New("no handle found for command:" + cmd)
|
||||
}
|
||||
} else {
|
||||
return errors.New("no command found")
|
||||
return gerror.New("no command found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -8,15 +8,13 @@
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
@ -96,7 +94,7 @@ func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bo
|
||||
i++
|
||||
continue
|
||||
} else if parser.strict {
|
||||
return nil, errors.New(fmt.Sprintf(`invalid option '%s'`, args[i]))
|
||||
return nil, gerror.Newf(`invalid option '%s'`, args[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,20 +8,20 @@
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
)
|
||||
|
||||
// BindHandle registers callback function <f> with <cmd>.
|
||||
func (p *Parser) BindHandle(cmd string, f func()) error {
|
||||
if _, ok := p.commandFuncMap[cmd]; ok {
|
||||
return errors.New("duplicated handle for command:" + cmd)
|
||||
return gerror.New("duplicated handle for command:" + cmd)
|
||||
} else {
|
||||
p.commandFuncMap[cmd] = f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindHandle registers callback function with map <m>.
|
||||
// BindHandleMap registers callback function with map <m>.
|
||||
func (p *Parser) BindHandleMap(m map[string]func()) error {
|
||||
var err error
|
||||
for k, v := range m {
|
||||
@ -37,7 +37,7 @@ func (p *Parser) RunHandle(cmd string) error {
|
||||
if handle, ok := p.commandFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
return errors.New("no handle found for command:" + cmd)
|
||||
return gerror.New("no handle found for command:" + cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -49,10 +49,10 @@ func (p *Parser) AutoRun() error {
|
||||
if handle, ok := p.commandFuncMap[cmd]; ok {
|
||||
handle()
|
||||
} else {
|
||||
return errors.New("no handle found for command:" + cmd)
|
||||
return gerror.New("no handle found for command:" + cmd)
|
||||
}
|
||||
} else {
|
||||
return errors.New("no command found")
|
||||
return gerror.New("no command found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -7,8 +7,7 @@
|
||||
package gcron
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
@ -63,7 +62,7 @@ func (c *Cron) GetLogLevel() int {
|
||||
func (c *Cron) Add(pattern string, job func(), name ...string) (*Entry, error) {
|
||||
if len(name) > 0 {
|
||||
if c.Search(name[0]) != nil {
|
||||
return nil, errors.New(fmt.Sprintf(`cron job "%s" already exists`, name[0]))
|
||||
return nil, gerror.Newf(`cron job "%s" already exists`, name[0])
|
||||
}
|
||||
}
|
||||
return c.addEntry(pattern, job, false, name...)
|
||||
|
@ -7,8 +7,7 @@
|
||||
package gcron
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -91,7 +90,7 @@ func newSchedule(pattern string) (*cronSchedule, error) {
|
||||
}, nil
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf(`invalid pattern: "%s"`, pattern))
|
||||
return nil, gerror.Newf(`invalid pattern: "%s"`, pattern)
|
||||
}
|
||||
}
|
||||
// Handle the common cron pattern, like:
|
||||
@ -140,7 +139,7 @@ func newSchedule(pattern string) (*cronSchedule, error) {
|
||||
}
|
||||
return schedule, nil
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf(`invalid pattern: "%s"`, pattern))
|
||||
return nil, gerror.Newf(`invalid pattern: "%s"`, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +156,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s
|
||||
intervalArray := strings.Split(item, "/")
|
||||
if len(intervalArray) == 2 {
|
||||
if i, err := strconv.Atoi(intervalArray[1]); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item))
|
||||
return nil, gerror.Newf(`invalid pattern item: "%s"`, item)
|
||||
} else {
|
||||
interval = i
|
||||
}
|
||||
@ -179,7 +178,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s
|
||||
// Eg: */5
|
||||
if rangeArray[0] != "*" {
|
||||
if i, err := parseItemValue(rangeArray[0], fieldType); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item))
|
||||
return nil, gerror.Newf(`invalid pattern item: "%s"`, item)
|
||||
} else {
|
||||
rangeMin = i
|
||||
rangeMax = i
|
||||
@ -187,7 +186,7 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s
|
||||
}
|
||||
if len(rangeArray) == 2 {
|
||||
if i, err := parseItemValue(rangeArray[1], fieldType); err != nil {
|
||||
return nil, errors.New(fmt.Sprintf(`invalid pattern item: "%s"`, item))
|
||||
return nil, gerror.Newf(`invalid pattern item: "%s"`, item)
|
||||
} else {
|
||||
rangeMax = i
|
||||
}
|
||||
@ -221,7 +220,7 @@ func parseItemValue(value string, fieldType byte) (int, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, errors.New(fmt.Sprintf(`invalid pattern value: "%s"`, value))
|
||||
return 0, gerror.Newf(`invalid pattern value: "%s"`, value)
|
||||
}
|
||||
|
||||
// meet checks if the given time <t> meets the runnable point for the job.
|
||||
|
@ -14,19 +14,19 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// Default expire time for file content caching in seconds.
|
||||
gDEFAULT_CACHE_EXPIRE = time.Minute
|
||||
defaultCacheExpire = time.Minute // defaultCacheExpire is the expire time for file content caching in seconds.
|
||||
commandEnvKeyForCache = "gf.gfile.cache" // commandEnvKeyForCache is the configuration key for command argument or environment configuring cache expire duration.
|
||||
)
|
||||
|
||||
var (
|
||||
// Default expire time for file content caching.
|
||||
cacheExpire = gcmd.GetOptWithEnv("gf.gfile.cache", gDEFAULT_CACHE_EXPIRE).Duration()
|
||||
cacheExpire = gcmd.GetOptWithEnv(commandEnvKeyForCache, defaultCacheExpire).Duration()
|
||||
|
||||
// internalCache is the memory cache for internal usage.
|
||||
internalCache = gcache.New()
|
||||
)
|
||||
|
||||
// GetContents returns string content of given file by <path> from cache.
|
||||
// GetContentsWithCache returns string content of given file by <path> from cache.
|
||||
// If there's no content in the cache, it will read it from disk file specified by <path>.
|
||||
// The parameter <expire> specifies the caching time for this file content in seconds.
|
||||
func GetContentsWithCache(path string, duration ...time.Duration) string {
|
||||
@ -62,5 +62,5 @@ func GetBytesWithCache(path string, duration ...time.Duration) []byte {
|
||||
|
||||
// cacheKey produces the cache key for gcache.
|
||||
func cacheKey(path string) string {
|
||||
return "gf.gfile.cache:" + path
|
||||
return commandEnvKeyForCache + path
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
package gfsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"context"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"sync"
|
||||
"time"
|
||||
@ -88,7 +88,7 @@ func New() (*Watcher, error) {
|
||||
if watcher, err := fsnotify.NewWatcher(); err == nil {
|
||||
w.watcher = watcher
|
||||
} else {
|
||||
intlog.Printf("New watcher failed: %v", err)
|
||||
intlog.Printf(context.TODO(), "New watcher failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
w.watchLoop()
|
||||
@ -139,7 +139,7 @@ func RemoveCallback(callbackId int) error {
|
||||
callback = r.(*Callback)
|
||||
}
|
||||
if callback == nil {
|
||||
return errors.New(fmt.Sprintf(`callback for id %d not found`, callbackId))
|
||||
return gerror.Newf(`callback for id %d not found`, callbackId)
|
||||
}
|
||||
w.RemoveCallback(callbackId)
|
||||
return nil
|
||||
|
@ -7,8 +7,8 @@
|
||||
package gfsnotify
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"context"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
|
||||
"github.com/gogf/gf/container/glist"
|
||||
@ -45,9 +45,9 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re
|
||||
for _, subPath := range fileAllDirs(path) {
|
||||
if fileIsDir(subPath) {
|
||||
if err := w.watcher.Add(subPath); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
} else {
|
||||
intlog.Printf("watcher adds monitor for: %s", subPath)
|
||||
intlog.Printf(context.TODO(), "watcher adds monitor for: %s", subPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +65,7 @@ func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), re
|
||||
func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
|
||||
// Check and convert the given path to absolute path.
|
||||
if t := fileRealPath(path); t == "" {
|
||||
return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
|
||||
return nil, gerror.Newf(`"%s" does not exist`, path)
|
||||
} else {
|
||||
path = t
|
||||
}
|
||||
@ -93,9 +93,9 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event
|
||||
})
|
||||
// Add the path to underlying monitor.
|
||||
if err := w.watcher.Add(path); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
} else {
|
||||
intlog.Printf("watcher adds monitor for: %s", path)
|
||||
intlog.Printf(context.TODO(), "watcher adds monitor for: %s", path)
|
||||
}
|
||||
// Add the callback to global callback map.
|
||||
callbackIdMap.Set(callback.Id, callback)
|
||||
@ -108,7 +108,7 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event
|
||||
func (w *Watcher) Close() {
|
||||
w.events.Close()
|
||||
if err := w.watcher.Close(); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
}
|
||||
close(w.closeChan)
|
||||
}
|
||||
@ -131,7 +131,7 @@ func (w *Watcher) Remove(path string) error {
|
||||
for _, subPath := range subPaths {
|
||||
if w.checkPathCanBeRemoved(subPath) {
|
||||
if err := w.watcher.Remove(subPath); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(context.TODO(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user