mirror of
https://gitee.com/johng/gf
synced 2026-06-10 11:27:17 +08:00
Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9ea58df64 | |||
| 65fff6feae | |||
| dbded5e753 | |||
| 3043645605 | |||
| 685bf56a30 | |||
| a4497ed547 | |||
| 28cb0bef25 | |||
| 114cdb2351 | |||
| a2bb8ad2f2 | |||
| 5a4de52900 | |||
| ff70e54e3e | |||
| fddc21670a | |||
| f02372cf58 | |||
| ab5f809074 | |||
| 121c1a0125 | |||
| 7678540270 | |||
| 839ebd5b51 | |||
| fa64df6f91 | |||
| 0acd118c03 | |||
| 2b5244a54b | |||
| 2472dd5fac | |||
| 5899f676f7 | |||
| bb57dc1ae7 | |||
| 5a6c2c27df | |||
| 9f096fc63d | |||
| 4267aadd78 | |||
| ef77a54c7e | |||
| afb0af4afd | |||
| 15aabfb4e7 | |||
| c83e899f1f | |||
| ebe90dcaa8 | |||
| 1c3ae11eba | |||
| b718aa88a2 | |||
| 6240c3d90b | |||
| c78f9d19f5 | |||
| 0ddacdd7e2 | |||
| f8486474aa | |||
| 906c54ce61 | |||
| f72d991c36 | |||
| 528f0e5434 | |||
| 88009ee278 | |||
| b192b7dd60 | |||
| 03d51bd18c | |||
| 5c6c932a75 | |||
| 92c3c136f9 | |||
| 5069436fd2 | |||
| 141ba2e951 | |||
| fae4dea37a | |||
| 860b22aba4 | |||
| 30dbccf99e | |||
| 9b2497bc57 | |||
| 0140808460 | |||
| 84aa30d9c2 | |||
| bc724deb5e | |||
| fbfc23211c | |||
| 3d4d3a763a | |||
| 046749566d | |||
| 3b1b8a8306 | |||
| 5e92747737 | |||
| 82ad7e2acc | |||
| 2d319d0856 | |||
| c060904f3f | |||
| a63c4b6441 | |||
| 9df860a202 | |||
| 2a1634fd6f | |||
| 2970864158 | |||
| 8e76d7a8ed | |||
| 50e5dd5bd0 | |||
| 7e2605188d | |||
| e5ae1cb85c | |||
| 1e78734f2c | |||
| d5fad88c56 | |||
| 35a81b868f | |||
| 84355c1ddd | |||
| 012121ea77 | |||
| 1e628b9edb | |||
| bfdeb6c4f5 | |||
| c42a9d6e50 | |||
| 083e32fd9e | |||
| 2a350fd3ab | |||
| 03928f1977 | |||
| a2771c7558 | |||
| 968e1db94d | |||
| bb0a3e09d6 | |||
| d109706ad3 | |||
| b958689264 | |||
| 859ea150ed | |||
| 91ca79b300 | |||
| 8210f40469 | |||
| 50ffaef33f | |||
| c25f88293b | |||
| 025cdd66c5 | |||
| 237f172ae5 | |||
| 5ef4ef61f0 | |||
| b935a8c652 | |||
| f3983cd6b7 |
15
.example/os/glog/glog_color.go
Normal file
15
.example/os/glog/glog_color.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.Log().Print("Print")
|
||||
g.Log().Debug("Debug")
|
||||
g.Log().Info("Info")
|
||||
g.Log().Notice("Notice")
|
||||
g.Log().Warning("Warning")
|
||||
g.Log().Error("Error")
|
||||
g.Log().Critical("Critical")
|
||||
}
|
||||
35
.example/util/gvalid/gvalid_i18n_http.go
Normal file
35
.example/util/gvalid/gvalid_i18n_http.go
Normal file
@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/i18n/gi18n"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type User struct {
|
||||
Name string `v:"required#ReuiredUserName"`
|
||||
Type int `v:"required#ReuiredUserType"`
|
||||
Project string `v:"size:10#MustSize"`
|
||||
}
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(func(r *ghttp.Request) {
|
||||
lang := r.GetString("lang", "zh-CN")
|
||||
r.SetCtx(gi18n.WithLanguage(r.Context(), lang))
|
||||
r.Middleware.Next()
|
||||
})
|
||||
group.GET("/validate", func(r *ghttp.Request) {
|
||||
var (
|
||||
err error
|
||||
user = User{}
|
||||
)
|
||||
if err = r.Parse(&user); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(user)
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
}
|
||||
71
.github/workflows/go.yml
vendored
Normal file
71
.github/workflows/go.yml
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
name: GoFrame CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
branches: [master, develop]
|
||||
env:
|
||||
GF_DEBUG: 1
|
||||
|
||||
jobs:
|
||||
code-test:
|
||||
runs-on: ubuntu-latest
|
||||
# Service containers to run with `code-test`
|
||||
services:
|
||||
redis:
|
||||
image : redis
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 6379 on service container to the host
|
||||
- 6379:6379
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
- 3306:3306
|
||||
|
||||
# strategy set
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.14", "1.15", "1.16"]
|
||||
|
||||
steps:
|
||||
- name: Set Up Timezone
|
||||
uses: szenius/set-timezone@v1.0
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
|
||||
- name: Checkout Repositary
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Before Script
|
||||
run: |
|
||||
date
|
||||
find . -name "*.go" | xargs gofmt -w
|
||||
git diff --name-only --exit-code || exit 1
|
||||
sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Run i386 Arch Test
|
||||
run: GOARCH=386 go test -v ./... || exit 1
|
||||
|
||||
- name: Run amd64 Arch Test
|
||||
run: GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
- name: Report Coverage
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,3 +15,5 @@ cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
.example/other/
|
||||
main
|
||||
gf
|
||||
@ -1,7 +1,7 @@
|
||||
# GoFrame
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://github.com/gogf/gf/actions/workflows/go.yml)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
|
||||
@ -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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCodef(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidOperation, "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.NewCode(gerror.CodeInvalidOperation, "pool is empty")
|
||||
}
|
||||
|
||||
// Size returns the count of available items of pool.
|
||||
|
||||
@ -7,92 +7,45 @@
|
||||
package gvar
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"reflect"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
)
|
||||
|
||||
// IsNil checks whether <v> is nil.
|
||||
// IsNil checks whether `v` is nil.
|
||||
func (v *Var) IsNil() bool {
|
||||
return v.Val() == nil
|
||||
return utils.IsNil(v.Val())
|
||||
}
|
||||
|
||||
// IsEmpty checks whether <v> is empty.
|
||||
// IsEmpty checks whether `v` is empty.
|
||||
func (v *Var) IsEmpty() bool {
|
||||
return empty.IsEmpty(v.Val())
|
||||
return utils.IsEmpty(v.Val())
|
||||
}
|
||||
|
||||
// IsInt checks whether <v> is type of int.
|
||||
// IsInt checks whether `v` is type of int.
|
||||
func (v *Var) IsInt() bool {
|
||||
switch v.Val().(type) {
|
||||
case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsInt(v.Val())
|
||||
}
|
||||
|
||||
// IsUint checks whether <v> is type of uint.
|
||||
// IsUint checks whether `v` is type of uint.
|
||||
func (v *Var) IsUint() bool {
|
||||
switch v.Val().(type) {
|
||||
case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsUint(v.Val())
|
||||
}
|
||||
|
||||
// IsFloat checks whether <v> is type of float.
|
||||
// IsFloat checks whether `v` is type of float.
|
||||
func (v *Var) IsFloat() bool {
|
||||
switch v.Val().(type) {
|
||||
case float32, *float32, float64, *float64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsFloat(v.Val())
|
||||
}
|
||||
|
||||
// IsSlice checks whether <v> is type of slice.
|
||||
// IsSlice checks whether `v` is type of slice.
|
||||
func (v *Var) IsSlice() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsSlice(v.Val())
|
||||
}
|
||||
|
||||
// IsMap checks whether <v> is type of map.
|
||||
// IsMap checks whether `v` is type of map.
|
||||
func (v *Var) IsMap() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsMap(v.Val())
|
||||
}
|
||||
|
||||
// IsStruct checks whether <v> is type of struct.
|
||||
// IsStruct checks whether `v` is type of struct.
|
||||
func (v *Var) IsStruct() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsStruct(v.Val())
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ func (v *Var) MapDeep(tags ...string) map[string]interface{} {
|
||||
return gconv.MapDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapDeep converts and returns <v> as map[string]string recursively.
|
||||
// MapStrStrDeep converts and returns <v> as map[string]string recursively.
|
||||
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
|
||||
return gconv.MapStrStrDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
@ -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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "invalid blocklen")
|
||||
}
|
||||
|
||||
if length%blockSize != 0 || length == 0 {
|
||||
return nil, errors.New("invalid data len")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid data len")
|
||||
}
|
||||
|
||||
unpadding := int(src[length-1])
|
||||
if unpadding > blockSize || unpadding == 0 {
|
||||
return nil, errors.New("invalid padding")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "text length invalid")
|
||||
}
|
||||
case PKCS5PADDING:
|
||||
return PaddingPKCS5(text, 8), nil
|
||||
default:
|
||||
return nil, errors.New("padding type error")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "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.NewCode(gerror.CodeInvalidParameter, "text length invalid")
|
||||
}
|
||||
case PKCS5PADDING:
|
||||
return UnPaddingPKCS5(text), nil
|
||||
default:
|
||||
return nil, errors.New("padding type error")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "padding type error")
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ package gdb
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
@ -70,8 +69,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
|
||||
|
||||
@ -105,23 +102,21 @@ type DB interface {
|
||||
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{}) // See Core.DoCommit.
|
||||
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.
|
||||
Union(unions ...*Model) *Model // See Core.Union.
|
||||
UnionAll(unions ...*Model) *Model // See Core.UnionAll.
|
||||
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.
|
||||
@ -157,8 +152,8 @@ type DB interface {
|
||||
GetGroup() string // See Core.GetGroup.
|
||||
SetDryRun(enabled bool) // See Core.SetDryRun.
|
||||
GetDryRun() bool // See Core.GetDryRun.
|
||||
SetLogger(logger *glog.Logger) // See Core.SetLogger.
|
||||
GetLogger() *glog.Logger // See Core.GetLogger.
|
||||
SetLogger(logger Logger) // See Core.SetLogger.
|
||||
GetLogger() Logger // See Core.GetLogger.
|
||||
GetConfig() *ConfigNode // See Core.GetConfig.
|
||||
SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount.
|
||||
SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount.
|
||||
@ -173,7 +168,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.
|
||||
FilteredLink() string
|
||||
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.
|
||||
@ -184,7 +179,7 @@ type Core struct {
|
||||
debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime.
|
||||
cache *gcache.Cache // Cache manager, SQL result cache only.
|
||||
schema *gtype.String // Custom schema for this object.
|
||||
logger *glog.Logger // Logger.
|
||||
logger Logger // Logger for logging functionality.
|
||||
config *ConfigNode // Current config node.
|
||||
}
|
||||
|
||||
@ -205,6 +200,12 @@ type Link interface {
|
||||
IsTransaction() bool
|
||||
}
|
||||
|
||||
// Logger is the logging interface for DB.
|
||||
type Logger interface {
|
||||
Error(ctx context.Context, s string)
|
||||
Debug(ctx context.Context, s string)
|
||||
}
|
||||
|
||||
// Sql is the sql recording struct.
|
||||
type Sql struct {
|
||||
Sql string // SQL string(may contain reserved char '?').
|
||||
@ -269,12 +270,12 @@ 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 (
|
||||
// ErrNoRows is alias of sql.ErrNoRows.
|
||||
ErrNoRows = sql.ErrNoRows
|
||||
|
||||
// instances is the management map for instances.
|
||||
instances = gmap.NewStrAnyMap(true)
|
||||
|
||||
@ -313,7 +314,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.
|
||||
@ -334,7 +335,10 @@ func New(group ...string) (db DB, err error) {
|
||||
defer configs.RUnlock()
|
||||
|
||||
if len(configs.config) < 1 {
|
||||
return nil, gerror.New("database configuration is empty, please set the database configuration before using")
|
||||
return nil, gerror.NewCode(
|
||||
gerror.CodeInvalidConfiguration,
|
||||
"database configuration is empty, please set the database configuration before using",
|
||||
)
|
||||
}
|
||||
if _, ok := configs.config[groupName]; ok {
|
||||
if node, err := getConfigNodeByGroup(groupName, true); err == nil {
|
||||
@ -343,7 +347,7 @@ func New(group ...string) (db DB, err error) {
|
||||
debug: gtype.NewBool(),
|
||||
cache: gcache.New(),
|
||||
schema: gtype.NewString(),
|
||||
logger: glog.New(),
|
||||
logger: LoggerImp{glog.New()},
|
||||
config: node,
|
||||
}
|
||||
if v, ok := driverMap[node.Type]; ok {
|
||||
@ -353,7 +357,8 @@ func New(group ...string) (db DB, err error) {
|
||||
}
|
||||
return c.db, nil
|
||||
} else {
|
||||
return nil, gerror.Newf(
|
||||
return nil, gerror.NewCodef(
|
||||
gerror.CodeInvalidConfiguration,
|
||||
`cannot find database driver for specified database type "%s", did you misspell type name "%s" or forget importing the database driver?`,
|
||||
node.Type, node.Type,
|
||||
)
|
||||
@ -362,7 +367,8 @@ func New(group ...string) (db DB, err error) {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, gerror.Newf(
|
||||
return nil, gerror.NewCodef(
|
||||
gerror.CodeInvalidConfiguration,
|
||||
`database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`,
|
||||
groupName, groupName,
|
||||
)
|
||||
@ -405,7 +411,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
}
|
||||
}
|
||||
if len(masterList) < 1 {
|
||||
return nil, gerror.New("at least one master node configuration's need to make sense")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidConfiguration, "at least one master node configuration's need to make sense")
|
||||
}
|
||||
if len(slaveList) < 1 {
|
||||
slaveList = masterList
|
||||
@ -416,7 +422,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
return getConfigNodeByWeight(slaveList), nil
|
||||
}
|
||||
} else {
|
||||
return nil, gerror.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
|
||||
return nil, gerror.NewCodef(gerror.CodeInvalidConfiguration, "empty database configuration for item name '%s'", group)
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,14 +491,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
|
||||
@ -88,7 +89,7 @@ func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Cont
|
||||
return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout)
|
||||
}
|
||||
default:
|
||||
panic(gerror.Newf("invalid context timeout type: %d", timeoutType))
|
||||
panic(gerror.NewCodef(gerror.CodeInvalidParameter, "invalid context timeout type: %d", timeoutType))
|
||||
}
|
||||
return ctx, func() {}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -352,7 +353,7 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e
|
||||
return c.Model(table).Data(data).Save()
|
||||
}
|
||||
|
||||
// DoInsert inserts or updates data for given table.
|
||||
// DoInsert inserts or updates data forF given table.
|
||||
// This function is usually used for custom interface definition, you do not need call it manually.
|
||||
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
|
||||
// Eg:
|
||||
@ -509,29 +510,34 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
|
||||
switch kind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
var (
|
||||
fields []string
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
fields []string
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
counterHandler = func(column string, counter Counter) {
|
||||
if counter.Value != 0 {
|
||||
var (
|
||||
column = c.QuoteWord(column)
|
||||
columnRef = c.QuoteWord(counter.Field)
|
||||
columnVal = counter.Value
|
||||
operator = "+"
|
||||
)
|
||||
if columnVal < 0 {
|
||||
operator = "-"
|
||||
columnVal = -columnVal
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s%s?", column, columnRef, operator))
|
||||
params = append(params, columnVal)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
for k, v := range dataMap {
|
||||
switch value := v.(type) {
|
||||
case *Counter:
|
||||
if value.Value != 0 {
|
||||
column := k
|
||||
if value.Field != "" {
|
||||
column = c.QuoteWord(value.Field)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
|
||||
params = append(params, value.Value)
|
||||
}
|
||||
counterHandler(k, *value)
|
||||
|
||||
case Counter:
|
||||
if value.Value != 0 {
|
||||
column := k
|
||||
if value.Field != "" {
|
||||
column = c.QuoteWord(value.Field)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
|
||||
params = append(params, value.Value)
|
||||
}
|
||||
counterHandler(k, value)
|
||||
|
||||
default:
|
||||
if s, ok := v.(Raw); ok {
|
||||
fields = append(fields, c.QuoteWord(k)+"="+gconv.String(s))
|
||||
@ -542,11 +548,12 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
|
||||
}
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
|
||||
default:
|
||||
updates = gconv.String(data)
|
||||
}
|
||||
if len(updates) == 0 {
|
||||
return nil, gerror.New("data cannot be empty")
|
||||
return nil, gerror.NewCode(gerror.CodeMissingParameter, "data cannot be empty")
|
||||
}
|
||||
if len(params) > 0 {
|
||||
args = append(params, args...)
|
||||
@ -652,9 +659,9 @@ func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) {
|
||||
s := fmt.Sprintf("[%3d ms] [%s] %s%s", sql.End-sql.Start, sql.Group, transactionIdStr, sql.Format)
|
||||
if sql.Error != nil {
|
||||
s += "\nError: " + sql.Error.Error()
|
||||
c.logger.Ctx(ctx).Error(s)
|
||||
c.logger.Error(ctx, s)
|
||||
} else {
|
||||
c.logger.Ctx(ctx).Debug(s)
|
||||
c.logger.Debug(ctx, s)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,8 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
// Config is the configuration management object.
|
||||
@ -49,6 +47,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 (
|
||||
@ -134,12 +133,12 @@ func IsConfigured() bool {
|
||||
}
|
||||
|
||||
// SetLogger sets the logger for orm.
|
||||
func (c *Core) SetLogger(logger *glog.Logger) {
|
||||
func (c *Core) SetLogger(logger Logger) {
|
||||
c.logger = logger
|
||||
}
|
||||
|
||||
// GetLogger returns the logger of the orm.
|
||||
func (c *Core) GetLogger() *glog.Logger {
|
||||
func (c *Core) GetLogger() Logger {
|
||||
return c.logger
|
||||
}
|
||||
|
||||
|
||||
27
database/gdb/gdb_core_logger.go
Normal file
27
database/gdb/gdb_core_logger.go
Normal file
@ -0,0 +1,27 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
// LoggerImp is the default implementation of interface Logger for DB.
|
||||
type LoggerImp struct {
|
||||
*glog.Logger
|
||||
}
|
||||
|
||||
// Error implements function Error for interface Logger.
|
||||
func (l LoggerImp) Error(ctx context.Context, s string) {
|
||||
l.Ctx(ctx).Error(s)
|
||||
}
|
||||
|
||||
// Debug implements function Debug for interface Logger.
|
||||
func (l LoggerImp) Debug(ctx context.Context, s string) {
|
||||
l.Ctx(ctx).Debug(s)
|
||||
}
|
||||
@ -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(
|
||||
|
||||
@ -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.DoCommit(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.DoCommit(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.NewCode(gerror.CodeMissingParameter, 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.NewCode(gerror.CodeMissingParameter, ctxStrictErrorStr)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
mTime1 = gtime.TimestampMilli()
|
||||
stmt, err = link.PrepareContext(ctx, sql)
|
||||
|
||||
@ -63,14 +63,6 @@ func (c *Core) GetChars() (charLeft string, charRight string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Also see Core.DoCommit.
|
||||
func (c *Core) DoCommit(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.
|
||||
//
|
||||
|
||||
@ -50,7 +50,7 @@ func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
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 {
|
||||
@ -79,7 +79,10 @@ func (d *DriverMssql) GetChars() (charLeft string, charRight string) {
|
||||
}
|
||||
|
||||
// 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{}) (string, []interface{}) {
|
||||
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) DoCommit(ctx context.Context, link Link, sql string, args
|
||||
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,
|
||||
@ -211,7 +214,7 @@ func (d *DriverMssql) TableFields(ctx context.Context, table string, schema ...s
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
|
||||
}
|
||||
useSchema := d.db.GetSchema()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
@ -284,3 +287,17 @@ ORDER BY a.id,a.colorder`,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DoInsert is not supported in mssql.
|
||||
func (d *DriverMssql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case insertOptionSave:
|
||||
return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by mssql driver`)
|
||||
|
||||
case insertOptionReplace:
|
||||
return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by mssql driver`)
|
||||
|
||||
default:
|
||||
return d.Core.DoInsert(ctx, link, table, list, option)
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
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 {
|
||||
@ -81,8 +81,8 @@ func (d *DriverMysql) GetChars() (charLeft string, charRight string) {
|
||||
}
|
||||
|
||||
// DoCommit handles the sql before posts it to database.
|
||||
func (d *DriverMysql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) {
|
||||
return sql, args
|
||||
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.
|
||||
@ -121,7 +121,7 @@ func (d *DriverMysql) TableFields(ctx context.Context, table string, schema ...s
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
|
||||
}
|
||||
useSchema := d.schema.Val()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
|
||||
@ -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) {
|
||||
@ -56,7 +51,7 @@ func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
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 {
|
||||
@ -85,7 +80,11 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) {
|
||||
}
|
||||
|
||||
// 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{}) {
|
||||
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 {
|
||||
@ -187,7 +186,7 @@ func (d *DriverOracle) TableFields(ctx context.Context, table string, schema ...
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
|
||||
}
|
||||
useSchema := d.db.GetSchema()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
@ -264,7 +263,27 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[
|
||||
return
|
||||
}
|
||||
|
||||
// DoInsert inserts or updates data for given table.
|
||||
// This function is usually used for custom interface definition, you do not need call it manually.
|
||||
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
|
||||
// Eg:
|
||||
// Data(g.Map{"uid": 10000, "name":"john"})
|
||||
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
|
||||
//
|
||||
// The parameter `option` values are as follows:
|
||||
// 0: insert: just insert, if there's unique/primary key in the data, it returns error;
|
||||
// 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 (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case insertOptionSave:
|
||||
return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by mssql driver`)
|
||||
|
||||
case insertOptionReplace:
|
||||
return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by mssql driver`)
|
||||
}
|
||||
|
||||
var (
|
||||
keys []string
|
||||
values []string
|
||||
|
||||
@ -51,7 +51,7 @@ func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
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 {
|
||||
@ -80,15 +80,19 @@ func (d *DriverPgsql) GetChars() (charLeft string, charRight string) {
|
||||
}
|
||||
|
||||
// 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{}) (string, []interface{}) {
|
||||
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.
|
||||
@ -122,7 +126,7 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
|
||||
}
|
||||
table, _ = gregex.ReplaceString("\"", "", table)
|
||||
useSchema := d.db.GetSchema()
|
||||
@ -138,9 +142,18 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s
|
||||
result Result
|
||||
link, err = d.SlaveLink(useSchema)
|
||||
structureSql = fmt.Sprintf(`
|
||||
SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a
|
||||
LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t
|
||||
WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid
|
||||
SELECT a.attname AS field, t.typname AS type,a.attnotnull as null,
|
||||
(case when d.contype is not null then 'pri' else '' end) as key
|
||||
,ic.column_default as default_value,b.description as comment
|
||||
,coalesce(character_maximum_length, numeric_precision, -1) as length
|
||||
,numeric_scale as scale
|
||||
FROM pg_attribute a
|
||||
left join pg_class c on a.attrelid = c.oid
|
||||
left join pg_constraint d on d.conrelid = c.oid and a.attnum = d.conkey[1]
|
||||
left join pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid
|
||||
left join pg_type t ON a.atttypid = t.oid
|
||||
left join information_schema.columns ic on ic.column_name = a.attname and ic.table_name = c.relname
|
||||
WHERE c.relname = '%s' and a.attnum > 0
|
||||
ORDER BY a.attnum`,
|
||||
strings.ToLower(table),
|
||||
)
|
||||
@ -156,9 +169,13 @@ ORDER BY a.attnum`,
|
||||
fields = make(map[string]*TableField)
|
||||
for i, m := range result {
|
||||
fields[m["field"].String()] = &TableField{
|
||||
Index: i,
|
||||
Name: m["field"].String(),
|
||||
Type: m["type"].String(),
|
||||
Index: i,
|
||||
Name: m["field"].String(),
|
||||
Type: m["type"].String(),
|
||||
Null: m["null"].Bool(),
|
||||
Key: m["key"].String(),
|
||||
Default: m["default_value"].Val(),
|
||||
Comment: m["comment"].String(),
|
||||
}
|
||||
}
|
||||
return fields
|
||||
@ -168,3 +185,17 @@ ORDER BY a.attnum`,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DoInsert is not supported in pgsql.
|
||||
func (d *DriverPgsql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case insertOptionSave:
|
||||
return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by pgsql driver`)
|
||||
|
||||
case insertOptionReplace:
|
||||
return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by pgsql driver`)
|
||||
|
||||
default:
|
||||
return d.Core.DoInsert(ctx, link, table, list, option)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
@ -67,10 +67,8 @@ func (d *DriverSqlite) GetChars() (charLeft string, charRight string) {
|
||||
}
|
||||
|
||||
// DoCommit 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) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (string, []interface{}) {
|
||||
return sql, args
|
||||
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.
|
||||
@ -101,7 +99,7 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ...
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
|
||||
}
|
||||
useSchema := d.db.GetSchema()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
@ -138,3 +136,17 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ...
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DoInsert is not supported in sqlite.
|
||||
func (d *DriverSqlite) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case insertOptionSave:
|
||||
return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by sqlite driver`)
|
||||
|
||||
case insertOptionReplace:
|
||||
return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by sqlite driver`)
|
||||
|
||||
default:
|
||||
return d.Core.DoInsert(ctx, link, table, list, option)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@ -55,11 +56,13 @@ type apiTableName interface {
|
||||
}
|
||||
|
||||
const (
|
||||
OrmTagForStruct = "orm"
|
||||
OrmTagForUnique = "unique"
|
||||
OrmTagForPrimary = "primary"
|
||||
OrmTagForTable = "table"
|
||||
OrmTagForWith = "with"
|
||||
OrmTagForStruct = "orm"
|
||||
OrmTagForUnique = "unique"
|
||||
OrmTagForPrimary = "primary"
|
||||
OrmTagForTable = "table"
|
||||
OrmTagForWith = "with"
|
||||
OrmTagForWithWhere = "where"
|
||||
OrmTagForWithOrder = "order"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -70,6 +73,32 @@ var (
|
||||
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
|
||||
)
|
||||
|
||||
// guessPrimaryTableName parses and returns the primary table name.
|
||||
func (m *Model) guessPrimaryTableName(tableStr string) string {
|
||||
if tableStr == "" {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
guessedTableName = ""
|
||||
array1 = gstr.SplitAndTrim(tableStr, ",")
|
||||
array2 = gstr.SplitAndTrim(array1[0], " ")
|
||||
array3 = gstr.SplitAndTrim(array2[0], ".")
|
||||
)
|
||||
if len(array3) >= 2 {
|
||||
guessedTableName = array3[1]
|
||||
} else {
|
||||
guessedTableName = array3[0]
|
||||
}
|
||||
charL, charR := m.db.GetChars()
|
||||
if charL != "" || charR != "" {
|
||||
guessedTableName = gstr.Trim(guessedTableName, charL+charR)
|
||||
}
|
||||
if !gregex.IsMatchString(regularFieldNameRegPattern, guessedTableName) {
|
||||
return ""
|
||||
}
|
||||
return guessedTableName
|
||||
}
|
||||
|
||||
// getTableNameFromOrmTag retrieves and returns the table name from struct object.
|
||||
func getTableNameFromOrmTag(object interface{}) string {
|
||||
var tableName string
|
||||
@ -239,7 +268,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} {
|
||||
name = ""
|
||||
fieldTag = rtField.Tag
|
||||
for _, tag := range structTagPriority {
|
||||
if s := fieldTag.Get(tag); s != "" && gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, s) {
|
||||
if s := fieldTag.Get(tag); s != "" {
|
||||
name = s
|
||||
break
|
||||
}
|
||||
@ -328,14 +357,15 @@ func doQuoteWord(s, charLeft, charRight string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// doQuoteString quotes string with quote chars. It handles strings like:
|
||||
// "user",
|
||||
// "user u",
|
||||
// "user,user_detail",
|
||||
// "user u, user_detail ut",
|
||||
// "user.user u, user.user_detail ut",
|
||||
// "u.id, u.name, u.age",
|
||||
// "u.id asc".
|
||||
// doQuoteString quotes string with quote chars.
|
||||
// For example, if quote char is '`':
|
||||
// "user" => "`user`"
|
||||
// "user u" => "`user` u"
|
||||
// "user,user_detail" => "`user`,`user_detail`"
|
||||
// "user u, user_detail ut" => "`user` u,`user_detail` ut"
|
||||
// "user.user u, user.user_detail ut" => "`user`.`user` u,`user`.`user_detail` ut"
|
||||
// "u.id, u.name, u.age" => "`u`.`id`,`u`.`name`,`u`.`age`"
|
||||
// "u.id asc" => "`u`.`id` asc"
|
||||
func doQuoteString(s, charLeft, charRight string) string {
|
||||
array1 := gstr.SplitAndTrim(s, ",")
|
||||
for k1, v1 := range array1 {
|
||||
@ -777,9 +807,9 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
|
||||
}
|
||||
|
||||
// formatError customizes and returns the SQL error.
|
||||
func formatError(err error, sql string, args ...interface{}) error {
|
||||
if err != nil && err != ErrNoRows {
|
||||
return gerror.New(fmt.Sprintf("%s, %s\n", err.Error(), FormatSqlWithArgs(sql, args)))
|
||||
func formatError(err error, s string, args ...interface{}) error {
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return gerror.NewCodef(gerror.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(s, args))
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -789,7 +819,9 @@ func formatError(err error, sql string, args ...interface{}) error {
|
||||
func FormatSqlWithArgs(sql string, args []interface{}) string {
|
||||
index := -1
|
||||
newQuery, _ := gregex.ReplaceStringFunc(
|
||||
`(\?|:v\d+|\$\d+|@p\d+)`, sql, func(s string) string {
|
||||
`(\?|:v\d+|\$\d+|@p\d+)`,
|
||||
sql,
|
||||
func(s string) string {
|
||||
index++
|
||||
if len(args) > index {
|
||||
if args[index] == nil {
|
||||
@ -809,6 +841,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
|
||||
switch kind {
|
||||
case reflect.String, reflect.Map, reflect.Slice, reflect.Array:
|
||||
return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
|
||||
|
||||
case reflect.Struct:
|
||||
if t, ok := args[index].(time.Time); ok {
|
||||
return `'` + t.Format(`2006-01-02 15:04:05`) + `'`
|
||||
|
||||
@ -19,55 +19,60 @@ import (
|
||||
|
||||
// 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.
|
||||
tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
|
||||
fields string // Operation fields, multiple fields joined using char ','.
|
||||
fieldsEx string // Excluded operation fields, multiple fields joined using char ','.
|
||||
withArray []interface{} // Arguments for With feature.
|
||||
withAll bool // Enable model association operations on all objects that have "with" tag in the struct.
|
||||
extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver.
|
||||
whereHolder []*whereHolder // Condition strings for where operation.
|
||||
groupBy string // Used for "group by" statement.
|
||||
orderBy string // Used for "order by" statement.
|
||||
having []interface{} // Used for "having..." statement.
|
||||
start int // Used for "select ... start, limit ..." statement.
|
||||
limit int // Used for "select ... start, limit ..." statement.
|
||||
option int // Option for extra operation features.
|
||||
offset int // Offset statement for some databases grammar.
|
||||
data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc.
|
||||
batch int // Batch number for batch Insert/Replace/Save operations.
|
||||
filter bool // Filter data and where key-value pairs according to the fields of the table.
|
||||
distinct string // Force the query to only return distinct results.
|
||||
lockInfo string // Lock for update or in shared lock.
|
||||
cacheEnabled bool // Enable sql result cache feature.
|
||||
cacheDuration time.Duration // Cache TTL duration.
|
||||
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.
|
||||
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.
|
||||
tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
|
||||
fields string // Operation fields, multiple fields joined using char ','.
|
||||
fieldsEx string // Excluded operation fields, multiple fields joined using char ','.
|
||||
withArray []interface{} // Arguments for With feature.
|
||||
withAll bool // Enable model association operations on all objects that have "with" tag in the struct.
|
||||
extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver.
|
||||
whereHolder []ModelWhereHolder // Condition strings for where operation.
|
||||
groupBy string // Used for "group by" statement.
|
||||
orderBy string // Used for "order by" statement.
|
||||
having []interface{} // Used for "having..." statement.
|
||||
start int // Used for "select ... start, limit ..." statement.
|
||||
limit int // Used for "select ... start, limit ..." statement.
|
||||
option int // Option for extra operation features.
|
||||
offset int // Offset statement for some databases grammar.
|
||||
data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc.
|
||||
batch int // Batch number for batch Insert/Replace/Save operations.
|
||||
filter bool // Filter data and where key-value pairs according to the fields of the table.
|
||||
distinct string // Force the query to only return distinct results.
|
||||
lockInfo string // Lock for update or in shared lock.
|
||||
cacheEnabled bool // Enable sql result cache feature.
|
||||
cacheDuration time.Duration // Cache TTL duration (< 1 for removing cache, >= 0 for saving cache).
|
||||
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.
|
||||
type whereHolder struct {
|
||||
operator int // Operator for this holder.
|
||||
where interface{} // Where parameter.
|
||||
args []interface{} // Arguments for where parameter.
|
||||
// ModelHandler is a function that handles given Model and returns a new Model that is custom modified.
|
||||
type ModelHandler func(m *Model) *Model
|
||||
|
||||
// ChunkHandler is a function that is used in function Chunk, which handles given Result and error.
|
||||
// It returns true if it wants continue chunking, or else it returns false to stop chunking.
|
||||
type ChunkHandler func(result Result, err error) bool
|
||||
|
||||
// ModelWhereHolder is the holder for where condition preparing.
|
||||
type ModelWhereHolder struct {
|
||||
Operator int // Operator for this holder.
|
||||
Where interface{} // Where parameter, which can commonly be type of string/map/struct.
|
||||
Args []interface{} // Arguments for where parameter.
|
||||
}
|
||||
|
||||
const (
|
||||
OptionOmitEmpty = 1
|
||||
OptionAllowEmpty = 2
|
||||
linkTypeMaster = 1
|
||||
linkTypeSlave = 2
|
||||
whereHolderWhere = 1
|
||||
whereHolderAnd = 2
|
||||
whereHolderOr = 3
|
||||
linkTypeMaster = 1
|
||||
linkTypeSlave = 2
|
||||
whereHolderOperatorWhere = 1
|
||||
whereHolderOperatorAnd = 2
|
||||
whereHolderOperatorOr = 3
|
||||
)
|
||||
|
||||
// Table is alias of Core.Model.
|
||||
@ -128,7 +133,6 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
option: OptionAllowEmpty,
|
||||
filter: true,
|
||||
extraArgs: extraArgs,
|
||||
}
|
||||
@ -206,7 +210,7 @@ func (m *Model) As(as string) *Model {
|
||||
if m.tables != "" {
|
||||
model := m.getModel()
|
||||
split := " JOIN "
|
||||
if gstr.Contains(model.tables, split) {
|
||||
if gstr.ContainsI(model.tables, split) {
|
||||
// For join table.
|
||||
array := gstr.Split(model.tables, split)
|
||||
array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])
|
||||
@ -258,7 +262,7 @@ func (m *Model) Clone() *Model {
|
||||
copy(newModel.extraArgs, m.extraArgs)
|
||||
}
|
||||
if n := len(m.whereHolder); n > 0 {
|
||||
newModel.whereHolder = make([]*whereHolder, n)
|
||||
newModel.whereHolder = make([]ModelWhereHolder, n)
|
||||
copy(newModel.whereHolder, m.whereHolder)
|
||||
}
|
||||
if n := len(m.withArray); n > 0 {
|
||||
@ -300,3 +304,13 @@ func (m *Model) Args(args ...interface{}) *Model {
|
||||
model.extraArgs = append(model.extraArgs, args)
|
||||
return model
|
||||
}
|
||||
|
||||
// Handler calls each of `handlers` on current Model and returns a new Model.
|
||||
// ModelHandler is a function that handles given Model and returns a new Model that is custom modified.
|
||||
func (m *Model) Handler(handlers ...ModelHandler) *Model {
|
||||
model := m.getModel()
|
||||
for _, handler := range handlers {
|
||||
model = handler(model)
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
@ -27,12 +27,12 @@ import (
|
||||
func (m *Model) Where(where interface{}, args ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if model.whereHolder == nil {
|
||||
model.whereHolder = make([]*whereHolder, 0)
|
||||
model.whereHolder = make([]ModelWhereHolder, 0)
|
||||
}
|
||||
model.whereHolder = append(model.whereHolder, &whereHolder{
|
||||
operator: whereHolderWhere,
|
||||
where: where,
|
||||
args: args,
|
||||
model.whereHolder = append(model.whereHolder, ModelWhereHolder{
|
||||
Operator: whereHolderOperatorWhere,
|
||||
Where: where,
|
||||
Args: args,
|
||||
})
|
||||
return model
|
||||
}
|
||||
@ -149,12 +149,12 @@ func (m *Model) WhereNotNull(columns ...string) *Model {
|
||||
func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if model.whereHolder == nil {
|
||||
model.whereHolder = make([]*whereHolder, 0)
|
||||
model.whereHolder = make([]ModelWhereHolder, 0)
|
||||
}
|
||||
model.whereHolder = append(model.whereHolder, &whereHolder{
|
||||
operator: whereHolderOr,
|
||||
where: where,
|
||||
args: args,
|
||||
model.whereHolder = append(model.whereHolder, ModelWhereHolder{
|
||||
Operator: whereHolderOperatorOr,
|
||||
Where: where,
|
||||
Args: args,
|
||||
})
|
||||
return model
|
||||
}
|
||||
@ -248,12 +248,12 @@ func (m *Model) Group(groupBy string) *Model {
|
||||
func (m *Model) And(where interface{}, args ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if model.whereHolder == nil {
|
||||
model.whereHolder = make([]*whereHolder, 0)
|
||||
model.whereHolder = make([]ModelWhereHolder, 0)
|
||||
}
|
||||
model.whereHolder = append(model.whereHolder, &whereHolder{
|
||||
operator: whereHolderAnd,
|
||||
where: where,
|
||||
args: args,
|
||||
model.whereHolder = append(model.whereHolder, ModelWhereHolder{
|
||||
Operator: whereHolderOperatorAnd,
|
||||
Where: where,
|
||||
Args: args,
|
||||
})
|
||||
return model
|
||||
}
|
||||
@ -382,11 +382,11 @@ func (m *Model) ForPage(page, limit int) *Model {
|
||||
func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) {
|
||||
if len(m.whereHolder) > 0 {
|
||||
for _, v := range m.whereHolder {
|
||||
switch v.operator {
|
||||
case whereHolderWhere:
|
||||
switch v.Operator {
|
||||
case whereHolderOperatorWhere:
|
||||
if conditionWhere == "" {
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
|
||||
m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
conditionWhere = newWhere
|
||||
@ -396,9 +396,9 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case whereHolderAnd:
|
||||
case whereHolderOperatorAnd:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
|
||||
m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
if len(conditionWhere) == 0 {
|
||||
@ -411,9 +411,9 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
conditionArgs = append(conditionArgs, newArgs...)
|
||||
}
|
||||
|
||||
case whereHolderOr:
|
||||
case whereHolderOperatorOr:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
|
||||
m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
if len(conditionWhere) == 0 {
|
||||
@ -455,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.schema, m.tables,
|
||||
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&optionOmitEmptyWhere > 0, m.schema, m.tables,
|
||||
)
|
||||
if len(havingStr) > 0 {
|
||||
conditionExtra += " HAVING " + havingStr
|
||||
|
||||
@ -44,7 +44,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
|
||||
}
|
||||
conditionStr := conditionWhere + conditionExtra
|
||||
if !gstr.ContainsI(conditionStr, " WHERE ") {
|
||||
return nil, gerror.New("there should be WHERE condition statement for DELETE operation")
|
||||
return nil, gerror.NewCode(gerror.CodeMissingParameter, "there should be WHERE condition statement for DELETE operation")
|
||||
}
|
||||
return m.db.DoDelete(m.GetCtx(), m.getLink(true), m.tables, conditionStr, conditionArgs...)
|
||||
}
|
||||
|
||||
@ -90,13 +90,13 @@ func (m *Model) FieldsStr(prefix ...string) string {
|
||||
}
|
||||
|
||||
// GetFieldsStr retrieves and returns all fields from the table, joined with char ','.
|
||||
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u.").
|
||||
// The optional parameter `prefix` specifies the prefix for each field, eg: GetFieldsStr("u.").
|
||||
func (m *Model) GetFieldsStr(prefix ...string) string {
|
||||
prefixStr := ""
|
||||
if len(prefix) > 0 {
|
||||
prefixStr = prefix[0]
|
||||
}
|
||||
tableFields, err := m.TableFields(m.tables)
|
||||
tableFields, err := m.TableFields(m.tablesInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -136,7 +136,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
|
||||
if len(prefix) > 0 {
|
||||
prefixStr = prefix[0]
|
||||
}
|
||||
tableFields, err := m.TableFields(m.tables)
|
||||
tableFields, err := m.TableFields(m.tablesInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -164,7 +164,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
|
||||
|
||||
// HasField determine whether the field exists in the table.
|
||||
func (m *Model) HasField(field string) (bool, error) {
|
||||
tableFields, err := m.TableFields(m.tables)
|
||||
tableFields, err := m.TableFields(m.tablesInit)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"reflect"
|
||||
|
||||
@ -211,7 +210,7 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err
|
||||
}
|
||||
}()
|
||||
if m.data == nil {
|
||||
return nil, gerror.New("inserting into table with empty data")
|
||||
return nil, gerror.NewCode(gerror.CodeMissingParameter, "inserting into table with empty data")
|
||||
}
|
||||
var (
|
||||
list List
|
||||
@ -276,12 +275,12 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err
|
||||
}
|
||||
|
||||
default:
|
||||
return result, gerror.New(fmt.Sprint("unsupported list type:", kind))
|
||||
return result, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported list type:%v", kind)
|
||||
}
|
||||
}
|
||||
|
||||
if len(list) < 1 {
|
||||
return result, gerror.New("data list cannot be empty")
|
||||
return result, gerror.NewCode(gerror.CodeMissingParameter, "data list cannot be empty")
|
||||
}
|
||||
|
||||
// Automatic handling for creating/updating time.
|
||||
@ -366,7 +365,11 @@ func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (op
|
||||
}
|
||||
|
||||
default:
|
||||
return option, gerror.Newf(`unsupported OnDuplicate parameter type "%s"`, reflect.TypeOf(m.onDuplicate))
|
||||
return option, gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`unsupported OnDuplicate parameter type "%s"`,
|
||||
reflect.TypeOf(m.onDuplicate),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if onDuplicateExKeySet.Size() > 0 {
|
||||
@ -406,7 +409,11 @@ func (m *Model) formatOnDuplicateExKeys(onDuplicateEx interface{}) ([]string, er
|
||||
return gconv.Strings(onDuplicateEx), nil
|
||||
|
||||
default:
|
||||
return nil, gerror.Newf(`unsupported OnDuplicateEx parameter type "%s"`, reflect.TypeOf(onDuplicateEx))
|
||||
return nil, gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`unsupported OnDuplicateEx parameter type "%s"`,
|
||||
reflect.TypeOf(onDuplicateEx),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,22 +6,40 @@
|
||||
|
||||
package gdb
|
||||
|
||||
const (
|
||||
optionOmitEmpty = optionOmitEmptyWhere | optionOmitEmptyData
|
||||
optionOmitEmptyWhere = 1 << iota // 8
|
||||
optionOmitEmptyData // 16
|
||||
)
|
||||
|
||||
// Option adds extra operation option for the model.
|
||||
// Deprecated, use separate operations instead.
|
||||
func (m *Model) Option(option int) *Model {
|
||||
model := m.getModel()
|
||||
model.option = model.option | option
|
||||
return model
|
||||
}
|
||||
|
||||
// OptionOmitEmpty sets OptionOmitEmpty option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
// Deprecated, use OmitEmpty instead.
|
||||
func (m *Model) OptionOmitEmpty() *Model {
|
||||
return m.Option(OptionOmitEmpty)
|
||||
// OmitEmpty sets OmitEmpty option for the model, which automatically filers
|
||||
// the data and where parameters for `empty` values.
|
||||
func (m *Model) OmitEmpty() *Model {
|
||||
model := m.getModel()
|
||||
model.option = model.option | optionOmitEmpty
|
||||
return model
|
||||
}
|
||||
|
||||
// OmitEmpty sets OptionOmitEmpty option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
func (m *Model) OmitEmpty() *Model {
|
||||
return m.Option(OptionOmitEmpty)
|
||||
// OmitEmptyWhere sets OmitEmptyWhere option for the model, which automatically filers
|
||||
// the Where/Having parameters for `empty` values.
|
||||
func (m *Model) OmitEmptyWhere() *Model {
|
||||
model := m.getModel()
|
||||
model.option = model.option | optionOmitEmptyWhere
|
||||
return model
|
||||
}
|
||||
|
||||
// OmitEmptyData sets OmitEmptyData option for the model, which automatically filers
|
||||
// the Data parameters for `empty` values.
|
||||
func (m *Model) OmitEmptyData() *Model {
|
||||
model := m.getModel()
|
||||
model.option = model.option | optionOmitEmptyData
|
||||
return model
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ func (m *Model) getFieldsFiltered() string {
|
||||
panic("function FieldsEx supports only single table operations")
|
||||
}
|
||||
// Filter table fields with fieldEx.
|
||||
tableFields, err := m.TableFields(m.tables)
|
||||
tableFields, err := m.TableFields(m.tablesInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -101,27 +101,27 @@ func (m *Model) getFieldsFiltered() string {
|
||||
return newFields
|
||||
}
|
||||
|
||||
// Chunk iterates the query result with given size and callback function.
|
||||
func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) {
|
||||
// Chunk iterates the query result with given `size` and `handler` function.
|
||||
func (m *Model) Chunk(size int, handler ChunkHandler) {
|
||||
page := m.start
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
model := m
|
||||
for {
|
||||
model = model.Page(page, limit)
|
||||
model = model.Page(page, size)
|
||||
data, err := model.All()
|
||||
if err != nil {
|
||||
callback(nil, err)
|
||||
handler(nil, err)
|
||||
break
|
||||
}
|
||||
if len(data) == 0 {
|
||||
break
|
||||
}
|
||||
if callback(data, err) == false {
|
||||
if handler(data, err) == false {
|
||||
break
|
||||
}
|
||||
if len(data) < limit {
|
||||
if len(data) < size {
|
||||
break
|
||||
}
|
||||
page++
|
||||
@ -316,7 +316,7 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
|
||||
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind != reflect.Ptr {
|
||||
return gerror.New(`the parameter "pointer" for function Scan should type of pointer`)
|
||||
return gerror.NewCode(gerror.CodeInvalidParameter, `the parameter "pointer" for function Scan should type of pointer`)
|
||||
}
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
@ -331,7 +331,10 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
|
||||
return m.doStruct(pointer, where...)
|
||||
|
||||
default:
|
||||
return gerror.New(`element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`)
|
||||
return gerror.NewCode(
|
||||
gerror.CodeInvalidParameter,
|
||||
`element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,11 +361,11 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
|
||||
// parameter.
|
||||
// See the example or unit testing cases for clear understanding for this function.
|
||||
func (m *Model) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) {
|
||||
all, err := m.All()
|
||||
result, err := m.All()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.ScanList(listPointer, attributeName, relation...)
|
||||
return doScanList(m, result, listPointer, attributeName, relation...)
|
||||
}
|
||||
|
||||
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
|
||||
@ -546,11 +549,15 @@ 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 {
|
||||
// In case of Cache Penetration.
|
||||
if result == nil {
|
||||
result = Result{}
|
||||
}
|
||||
if err := cacheObj.Set(cacheKey, result, m.cacheDuration); err != nil {
|
||||
intlog.Error(err)
|
||||
intlog.Error(m.GetCtx(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ func (m *Model) getSoftFieldNameCreated(table ...string) string {
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
} else {
|
||||
tableName = m.getPrimaryTableName()
|
||||
tableName = m.tablesInit
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.CreatedAt != "" {
|
||||
@ -61,7 +61,7 @@ func (m *Model) getSoftFieldNameUpdated(table ...string) (field string) {
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
} else {
|
||||
tableName = m.getPrimaryTableName()
|
||||
tableName = m.tablesInit
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.UpdatedAt != "" {
|
||||
@ -82,7 +82,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) {
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
} else {
|
||||
tableName = m.getPrimaryTableName()
|
||||
tableName = m.tablesInit
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.UpdatedAt != "" {
|
||||
@ -170,17 +170,3 @@ func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string {
|
||||
}
|
||||
return fmt.Sprintf(`%s.%s IS NULL`, m.db.GetCore().QuoteWord(table), m.db.GetCore().QuoteWord(field))
|
||||
}
|
||||
|
||||
// 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], ".")
|
||||
if len(array3) >= 2 {
|
||||
return array3[1]
|
||||
}
|
||||
return array3[0]
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
|
||||
}
|
||||
}()
|
||||
if m.data == nil {
|
||||
return nil, gerror.New("updating table with empty data")
|
||||
return nil, gerror.NewCode(gerror.CodeMissingParameter, "updating table with empty data")
|
||||
}
|
||||
var (
|
||||
updateData = m.data
|
||||
@ -80,7 +80,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
|
||||
}
|
||||
conditionStr := conditionWhere + conditionExtra
|
||||
if !gstr.ContainsI(conditionStr, " WHERE ") {
|
||||
return nil, gerror.New("there should be WHERE condition statement for UPDATE operation")
|
||||
return nil, gerror.NewCode(gerror.CodeMissingParameter, "there should be WHERE condition statement for UPDATE operation")
|
||||
}
|
||||
return m.db.DoUpdate(
|
||||
m.GetCtx(),
|
||||
|
||||
@ -21,15 +21,12 @@ import (
|
||||
// schema.
|
||||
//
|
||||
// Also see DriverMysql.TableFields.
|
||||
func (m *Model) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
|
||||
charL, charR := m.db.GetChars()
|
||||
if charL != "" || charR != "" {
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
func (m *Model) TableFields(tableStr string, schema ...string) (fields map[string]*TableField, err error) {
|
||||
useSchema := m.schema
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
useSchema = schema[0]
|
||||
}
|
||||
if !gregex.IsMatchString(regularFieldNameRegPattern, table) {
|
||||
return nil, nil
|
||||
}
|
||||
return m.db.TableFields(m.GetCtx(), table, schema...)
|
||||
return m.db.TableFields(m.GetCtx(), m.guessPrimaryTableName(tableStr), useSchema)
|
||||
}
|
||||
|
||||
// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns
|
||||
@ -47,7 +44,7 @@ func (m *Model) getModel() *Model {
|
||||
// ID -> id
|
||||
// NICK_Name -> nickname
|
||||
func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string {
|
||||
fieldsMap, err := m.TableFields(m.tables)
|
||||
fieldsMap, err := m.TableFields(m.tablesInit)
|
||||
if err != nil || len(fieldsMap) == 0 {
|
||||
return fields
|
||||
}
|
||||
@ -106,12 +103,14 @@ func (m *Model) filterDataForInsertOrUpdate(data interface{}) (interface{}, erro
|
||||
// Note that, it does not filter list item, which is also type of map, for "omit empty" feature.
|
||||
func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEmpty bool) (Map, error) {
|
||||
var err error
|
||||
data, err = m.db.GetCore().mappingAndFilterData(m.schema, m.tables, data, m.filter)
|
||||
data, err = m.db.GetCore().mappingAndFilterData(
|
||||
m.schema, m.guessPrimaryTableName(m.tablesInit), data, m.filter,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Remove key-value pairs of which the value is empty.
|
||||
if allowOmitEmpty && m.option&OptionOmitEmpty > 0 {
|
||||
if allowOmitEmpty && m.option&optionOmitEmptyData > 0 {
|
||||
tempMap := make(Map, len(data))
|
||||
for k, v := range data {
|
||||
if empty.IsEmpty(v) {
|
||||
@ -201,7 +200,7 @@ func (m *Model) getLink(master bool) Link {
|
||||
// It parses m.tables to retrieve the primary table name, supporting m.tables like:
|
||||
// "user", "user u", "user as u, user_detail as ud".
|
||||
func (m *Model) getPrimaryKey() string {
|
||||
table := gstr.SplitAndTrim(m.tables, " ")[0]
|
||||
table := gstr.SplitAndTrim(m.tablesInit, " ")[0]
|
||||
tableFields, err := m.TableFields(table)
|
||||
if err != nil {
|
||||
return ""
|
||||
|
||||
@ -39,7 +39,10 @@ func (m *Model) With(objects ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
for _, object := range objects {
|
||||
if m.tables == "" {
|
||||
m.tables = m.db.GetCore().QuotePrefixTableName(getTableNameFromOrmTag(object))
|
||||
m.tablesInit = m.db.GetCore().QuotePrefixTableName(
|
||||
getTableNameFromOrmTag(object),
|
||||
)
|
||||
m.tables = m.tablesInit
|
||||
return model
|
||||
}
|
||||
model.withArray = append(model.withArray, object)
|
||||
@ -60,7 +63,11 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
err error
|
||||
allowedTypeStrArray = make([]string, 0)
|
||||
)
|
||||
fieldMap, err := structs.FieldMap(pointer, nil, false)
|
||||
fieldMap, err := structs.FieldMap(structs.FieldMapInput{
|
||||
Pointer: pointer,
|
||||
PriorityTagArray: nil,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -76,7 +83,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
withItemReflectValueTypeStr = gstr.TrimAll(withItemReflectValueType.String(), "*[]")
|
||||
)
|
||||
// It does select operation if the field type is in the specified with type array.
|
||||
// It does select operation if the field type is in the specified "with" type array.
|
||||
if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 {
|
||||
allowedTypeStrArray = append(allowedTypeStrArray, fieldTypeStr)
|
||||
}
|
||||
@ -85,28 +92,21 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
}
|
||||
for _, field := range fieldMap {
|
||||
var (
|
||||
withTag string
|
||||
ormTag = field.Tag(OrmTagForStruct)
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith),
|
||||
ormTag,
|
||||
)
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
parsedTagOutput = m.parseWithTagInFieldStruct(field)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
withTag = match[1]
|
||||
}
|
||||
if withTag == "" {
|
||||
if parsedTagOutput.With == "" {
|
||||
continue
|
||||
}
|
||||
// Just handler "with" type attribute struct.
|
||||
if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) {
|
||||
continue
|
||||
}
|
||||
array := gstr.SplitAndTrim(withTag, "=")
|
||||
array := gstr.SplitAndTrim(parsedTagOutput.With, "=")
|
||||
if len(array) == 1 {
|
||||
// It supports using only one column name
|
||||
// if both tables associates using the same column name.
|
||||
array = append(array, withTag)
|
||||
array = append(array, parsedTagOutput.With)
|
||||
}
|
||||
var (
|
||||
model *Model
|
||||
@ -123,9 +123,10 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
}
|
||||
}
|
||||
if relatedFieldValue == nil {
|
||||
return gerror.Newf(
|
||||
`cannot find the related value for attribute name "%s" of with tag "%s"`,
|
||||
relatedAttrName, withTag,
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`cannot find the related value of attribute name "%s" in with tag "%s" for attribute "%s.%s"`,
|
||||
relatedAttrName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(),
|
||||
)
|
||||
}
|
||||
bindToReflectValue := field.Value
|
||||
@ -150,6 +151,12 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
} else {
|
||||
model = model.With(m.withArray...)
|
||||
}
|
||||
if parsedTagOutput.Where != "" {
|
||||
model = model.Where(parsedTagOutput.Where)
|
||||
}
|
||||
if parsedTagOutput.Order != "" {
|
||||
model = model.Order(parsedTagOutput.Order)
|
||||
}
|
||||
|
||||
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).Scan(bindToReflectValue)
|
||||
if err != nil {
|
||||
@ -163,11 +170,19 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
// doWithScanStructs handles model association operations feature for struct slice.
|
||||
// Also see doWithScanStruct.
|
||||
func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
pointer = v.Interface()
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
allowedTypeStrArray = make([]string, 0)
|
||||
)
|
||||
fieldMap, err := structs.FieldMap(pointer, nil, false)
|
||||
fieldMap, err := structs.FieldMap(structs.FieldMapInput{
|
||||
Pointer: pointer,
|
||||
PriorityTagArray: nil,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -193,28 +208,20 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
|
||||
for fieldName, field := range fieldMap {
|
||||
var (
|
||||
withTag string
|
||||
ormTag = field.Tag(OrmTagForStruct)
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith),
|
||||
ormTag,
|
||||
)
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
parsedTagOutput = m.parseWithTagInFieldStruct(field)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
withTag = match[1]
|
||||
}
|
||||
if withTag == "" {
|
||||
if parsedTagOutput.With == "" {
|
||||
continue
|
||||
}
|
||||
if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) {
|
||||
continue
|
||||
}
|
||||
array := gstr.SplitAndTrim(withTag, "=")
|
||||
array := gstr.SplitAndTrim(parsedTagOutput.With, "=")
|
||||
if len(array) == 1 {
|
||||
// It supports using only one column name
|
||||
// if both tables associates using the same column name.
|
||||
array = append(array, withTag)
|
||||
array = append(array, parsedTagOutput.With)
|
||||
}
|
||||
var (
|
||||
model *Model
|
||||
@ -231,9 +238,10 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
}
|
||||
}
|
||||
if relatedFieldValue == nil {
|
||||
return gerror.Newf(
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`cannot find the related value for attribute name "%s" of with tag "%s"`,
|
||||
relatedAttrName, withTag,
|
||||
relatedAttrName, parsedTagOutput.With,
|
||||
)
|
||||
}
|
||||
|
||||
@ -251,11 +259,58 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
} else {
|
||||
model = model.With(m.withArray...)
|
||||
}
|
||||
if parsedTagOutput.Where != "" {
|
||||
model = model.Where(parsedTagOutput.Where)
|
||||
}
|
||||
if parsedTagOutput.Order != "" {
|
||||
model = model.Order(parsedTagOutput.Order)
|
||||
}
|
||||
|
||||
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, withTag)
|
||||
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, parsedTagOutput.With)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type parseWithTagInFieldStructOutput struct {
|
||||
With string
|
||||
Where string
|
||||
Order string
|
||||
}
|
||||
|
||||
func (m *Model) parseWithTagInFieldStruct(field *structs.Field) (output parseWithTagInFieldStructOutput) {
|
||||
var (
|
||||
match []string
|
||||
ormTag = field.Tag(OrmTagForStruct)
|
||||
)
|
||||
// with tag.
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:\s*([^,]+),{0,1}`, OrmTagForWith),
|
||||
ormTag,
|
||||
)
|
||||
if len(match) > 1 {
|
||||
output.With = match[1]
|
||||
}
|
||||
if len(match) > 2 {
|
||||
output.Where = gstr.Trim(match[2])
|
||||
}
|
||||
// where string.
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:.+,\s*%s:\s*([^,]+),{0,1}`, OrmTagForWith, OrmTagForWithWhere),
|
||||
ormTag,
|
||||
)
|
||||
if len(match) > 1 {
|
||||
output.Where = gstr.Trim(match[1])
|
||||
}
|
||||
// order string.
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:.+,\s*%s:\s*([^,]+),{0,1}`, OrmTagForWith, OrmTagForWithOrder),
|
||||
ormTag,
|
||||
)
|
||||
if len(match) > 1 {
|
||||
output.Order = gstr.Trim(match[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -33,7 +33,10 @@ func (r *SqlResult) MustGetInsertId() int64 {
|
||||
return id
|
||||
}
|
||||
|
||||
// see sql.Result.RowsAffected
|
||||
// RowsAffected returns the number of rows affected by an
|
||||
// update, insert, or delete. Not every database or database
|
||||
// driver may support this.
|
||||
// Also See sql.Result.
|
||||
func (r *SqlResult) RowsAffected() (int64, error) {
|
||||
if r.affected > 0 {
|
||||
return r.affected, nil
|
||||
@ -44,7 +47,12 @@ func (r *SqlResult) RowsAffected() (int64, error) {
|
||||
return r.result.RowsAffected()
|
||||
}
|
||||
|
||||
// see sql.Result.LastInsertId
|
||||
// LastInsertId returns the integer generated by the database
|
||||
// in response to a command. Typically this will be from an
|
||||
// "auto increment" column when inserting a new row. Not all
|
||||
// databases support this feature, and the syntax of such
|
||||
// statements varies.
|
||||
// Also See sql.Result.
|
||||
func (r *SqlResult) LastInsertId() (int64, error) {
|
||||
if r.result == nil {
|
||||
return 0, nil
|
||||
|
||||
@ -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()
|
||||
@ -59,7 +59,7 @@ func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interf
|
||||
result = s.Stmt.QueryRowContext(ctx, args...)
|
||||
|
||||
default:
|
||||
panic(gerror.Newf(`invalid stmtType: %s`, stmtType))
|
||||
panic(gerror.NewCodef(gerror.CodeInvalidParameter, `invalid stmtType: %s`, stmtType))
|
||||
}
|
||||
var (
|
||||
timestampMilli2 = 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)
|
||||
}
|
||||
|
||||
@ -14,6 +14,11 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Interface converts and returns `r` as type of interface{}.
|
||||
func (r Record) Interface() interface{} {
|
||||
return r
|
||||
}
|
||||
|
||||
// Json converts `r` to JSON format content.
|
||||
func (r Record) Json() string {
|
||||
content, _ := gparser.VarToJson(r.Map())
|
||||
@ -52,7 +57,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.
|
||||
|
||||
@ -13,6 +13,11 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// Interface converts and returns `r` as type of interface{}.
|
||||
func (r Result) Interface() interface{} {
|
||||
return r
|
||||
}
|
||||
|
||||
// IsEmpty checks and returns whether `r` is empty.
|
||||
func (r Result) IsEmpty() bool {
|
||||
return r.Len() == 0
|
||||
@ -72,6 +77,7 @@ func (r Result) List() List {
|
||||
|
||||
// Array retrieves and returns specified column values as slice.
|
||||
// The parameter `field` is optional is the column field is only one.
|
||||
// The default `field` is the first field name of the first item in `Result` if parameter `field` is not given.
|
||||
func (r Result) Array(field ...string) []Value {
|
||||
array := make([]Value, len(r))
|
||||
if len(r) == 0 {
|
||||
@ -189,5 +195,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)
|
||||
}
|
||||
|
||||
@ -42,21 +42,21 @@ import (
|
||||
//
|
||||
// See the example or unit testing cases for clear understanding for this function.
|
||||
func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
|
||||
if r.IsEmpty() {
|
||||
return doScanList(nil, r, listPointer, bindToAttrName, relationKV...)
|
||||
}
|
||||
|
||||
// doScanList converts `result` to struct slice which contains other complex struct attributes recursively.
|
||||
// The parameter `model` is used for recursively scanning purpose, which means, it can scans the attribute struct/structs recursively but
|
||||
// it needs the Model for database accessing.
|
||||
// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct.
|
||||
func doScanList(model *Model, result Result, listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
|
||||
if result.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
// Necessary checks for parameters.
|
||||
if bindToAttrName == "" {
|
||||
return gerror.New(`bindToAttrName should not be empty`)
|
||||
return gerror.NewCode(gerror.CodeInvalidParameter, `bindToAttrName should not be empty`)
|
||||
}
|
||||
//if len(relation) > 0 {
|
||||
// if len(relation) < 2 {
|
||||
// return gerror.New(`relation name and key should are both necessary`)
|
||||
// }
|
||||
// if relation[0] == "" || relation[1] == "" {
|
||||
// return gerror.New(`relation name and key should not be empty`)
|
||||
// }
|
||||
//}
|
||||
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(listPointer)
|
||||
@ -67,14 +67,14 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Ptr {
|
||||
return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
return gerror.NewCodef(gerror.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
|
||||
return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
return gerror.NewCodef(gerror.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
length := len(r)
|
||||
length := len(result)
|
||||
if length == 0 {
|
||||
// The pointed slice is not empty.
|
||||
if reflectValue.Len() > 0 {
|
||||
@ -134,8 +134,9 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// uid:UserId
|
||||
relationResultFieldName = array[0]
|
||||
relationBindToSubAttrName = array[1]
|
||||
if key, _ := gutil.MapPossibleItemByKey(r[0].Map(), relationResultFieldName); key == "" {
|
||||
return gerror.Newf(
|
||||
if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationResultFieldName); key == "" {
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`cannot find possible related table field name "%s" from given relation key "%s"`,
|
||||
relationResultFieldName,
|
||||
relationKVStr,
|
||||
@ -144,13 +145,14 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
relationResultFieldName = key
|
||||
}
|
||||
} else {
|
||||
return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
|
||||
return gerror.NewCode(gerror.CodeInvalidParameter, `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
|
||||
}
|
||||
if relationResultFieldName != "" {
|
||||
relationDataMap = r.MapKeyValue(relationResultFieldName)
|
||||
// Note that the value might be type of slice.
|
||||
relationDataMap = result.MapKeyValue(relationResultFieldName)
|
||||
}
|
||||
if len(relationDataMap) == 0 {
|
||||
return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV)
|
||||
return gerror.NewCodef(gerror.CodeInvalidParameter, `cannot find the relation data map, maybe invalid relation given "%v"`, relationKV)
|
||||
}
|
||||
}
|
||||
// Bind to target attribute.
|
||||
@ -163,11 +165,19 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
)
|
||||
if arrayItemType.Kind() == reflect.Ptr {
|
||||
if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok {
|
||||
return gerror.Newf(`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName)
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
|
||||
bindToAttrName,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok {
|
||||
return gerror.Newf(`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName)
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
|
||||
bindToAttrName,
|
||||
)
|
||||
}
|
||||
}
|
||||
bindToAttrType = bindToAttrField.Type
|
||||
@ -209,7 +219,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
relationFromAttrValue = arrayElemValue
|
||||
}
|
||||
if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() {
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
// Check and find possible bind to attribute name.
|
||||
if relationKVStr != "" && !relationBindToSubAttrNameChecked {
|
||||
@ -223,7 +233,8 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
filedMap[relationFromAttrType.Field(i).Name] = struct{}{}
|
||||
}
|
||||
if key, _ := gutil.MapPossibleItemByKey(filedMap, relationBindToSubAttrName); key == "" {
|
||||
return gerror.Newf(
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`cannot find possible related attribute name "%s" from given relation key "%s"`,
|
||||
relationBindToSubAttrName,
|
||||
relationKVStr,
|
||||
@ -239,18 +250,29 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
if len(relationDataMap) > 0 {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||
if relationFromAttrField.IsValid() {
|
||||
if err = gconv.Structs(
|
||||
relationDataMap[gconv.String(relationFromAttrField.Interface())],
|
||||
bindToAttrValue.Addr(),
|
||||
); err != nil {
|
||||
results := make(Result, 0)
|
||||
for _, v := range relationDataMap[gconv.String(relationFromAttrField.Interface())].Slice() {
|
||||
results = append(results, v.(Record))
|
||||
}
|
||||
if err = results.Structs(bindToAttrValue.Addr()); err != nil {
|
||||
return err
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStructs(bindToAttrValue.Addr()); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// May be the attribute does not exist yet.
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
return gerror.Newf(`relationKey should not be empty as field "%s" is slice`, bindToAttrName)
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`relationKey should not be empty as field "%s" is slice`,
|
||||
bindToAttrName,
|
||||
)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
@ -268,24 +290,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(v, element); err != nil {
|
||||
return err
|
||||
if v.IsSlice() {
|
||||
if err = v.Slice()[0].(Record).Struct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = v.Val().(Record).Struct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// May be the attribute does not exist yet.
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
if i >= len(r) {
|
||||
if i >= len(result) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
v := r[i]
|
||||
v := result[i]
|
||||
if v == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(v, element); err != nil {
|
||||
if err = v.Struct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStruct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -300,30 +334,42 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
||||
return err
|
||||
if relationDataItem.IsSlice() {
|
||||
if err = relationDataItem.Slice()[0].(Record).Struct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = relationDataItem.Val().(Record).Struct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// May be the attribute does not exist yet.
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
if i >= len(r) {
|
||||
if i >= len(result) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
relationDataItem := r[i]
|
||||
relationDataItem := result[i]
|
||||
if relationDataItem == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
||||
if err = relationDataItem.Struct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStruct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return gerror.Newf(`unsupported attribute type: %s`, bindToAttrKind.String())
|
||||
return gerror.NewCodef(gerror.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String())
|
||||
}
|
||||
}
|
||||
reflect.ValueOf(listPointer).Elem().Set(arrayValue)
|
||||
|
||||
@ -43,7 +43,7 @@ func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
|
||||
|
||||
// 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) DoCommit(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.DoCommit(ctx, link, sql, args)
|
||||
}
|
||||
|
||||
@ -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.Insert(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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,12 +8,83 @@ package gdb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/*
|
||||
mysql> show tables;
|
||||
+----------------+
|
||||
| Tables_in_test |
|
||||
+----------------+
|
||||
| user |
|
||||
| user_detail |
|
||||
| user_score |
|
||||
+----------------+
|
||||
3 rows in set (0.01 sec)
|
||||
|
||||
mysql> select * from `user`;
|
||||
+----+--------+
|
||||
| id | name |
|
||||
+----+--------+
|
||||
| 1 | name_1 |
|
||||
| 2 | name_2 |
|
||||
| 3 | name_3 |
|
||||
| 4 | name_4 |
|
||||
| 5 | name_5 |
|
||||
+----+--------+
|
||||
5 rows in set (0.01 sec)
|
||||
|
||||
mysql> select * from `user_detail`;
|
||||
+-----+-----------+
|
||||
| uid | address |
|
||||
+-----+-----------+
|
||||
| 1 | address_1 |
|
||||
| 2 | address_2 |
|
||||
| 3 | address_3 |
|
||||
| 4 | address_4 |
|
||||
| 5 | address_5 |
|
||||
+-----+-----------+
|
||||
5 rows in set (0.00 sec)
|
||||
|
||||
mysql> select * from `user_score`;
|
||||
+----+-----+-------+
|
||||
| id | uid | score |
|
||||
+----+-----+-------+
|
||||
| 1 | 1 | 1 |
|
||||
| 2 | 1 | 2 |
|
||||
| 3 | 1 | 3 |
|
||||
| 4 | 1 | 4 |
|
||||
| 5 | 1 | 5 |
|
||||
| 6 | 2 | 1 |
|
||||
| 7 | 2 | 2 |
|
||||
| 8 | 2 | 3 |
|
||||
| 9 | 2 | 4 |
|
||||
| 10 | 2 | 5 |
|
||||
| 11 | 3 | 1 |
|
||||
| 12 | 3 | 2 |
|
||||
| 13 | 3 | 3 |
|
||||
| 14 | 3 | 4 |
|
||||
| 15 | 3 | 5 |
|
||||
| 16 | 4 | 1 |
|
||||
| 17 | 4 | 2 |
|
||||
| 18 | 4 | 3 |
|
||||
| 19 | 4 | 4 |
|
||||
| 20 | 4 | 5 |
|
||||
| 21 | 5 | 1 |
|
||||
| 22 | 5 | 2 |
|
||||
| 23 | 5 | 3 |
|
||||
| 24 | 5 | 4 |
|
||||
| 25 | 5 | 5 |
|
||||
+----+-----+-------+
|
||||
25 rows in set (0.00 sec)
|
||||
*/
|
||||
|
||||
func Test_Table_Relation_With_Scan(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
@ -667,7 +738,143 @@ PRIMARY KEY (id)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Embedded(t *testing.T) {
|
||||
func Test_Table_Relation_WithAllCondition_List(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id, where:uid > 3"`
|
||||
UserScores []*UserScores `orm:"with:uid=id, where:score>1 and score<5, order:score desc"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.Assert(users[0].UserDetail, nil)
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 3)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 4)
|
||||
t.Assert(users[1].UserScores[2].Uid, 4)
|
||||
t.Assert(users[1].UserScores[2].Score, 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.Assert(users[0].UserDetail, nil)
|
||||
|
||||
t.Assert(len(users[0].UserScores), 3)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 4)
|
||||
t.Assert(users[0].UserScores[2].Uid, 3)
|
||||
t.Assert(users[0].UserScores[2].Score, 2)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 3)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 4)
|
||||
t.Assert(users[1].UserScores[2].Uid, 4)
|
||||
t.Assert(users[1].UserScores[2].Score, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Embedded_With_SelfMaintained_Attributes(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
@ -782,6 +989,245 @@ PRIMARY KEY (id)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Embedded_Without_SelfMaintained_Attributes(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
// For Test Only
|
||||
type UserEmbedded struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
*UserDetail `orm:"with:uid=id"`
|
||||
UserEmbedded
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 3)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 3)
|
||||
t.Assert(user.UserDetail.Address, `address_3`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 3)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 3)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Embedded_WithoutMeta(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetailBase struct {
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserDetail struct {
|
||||
UserDetailBase
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
*UserDetail `orm:"with:uid=id"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 3)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 3)
|
||||
t.Assert(user.UserDetail.Address, `address_3`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 3)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 3)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_AttributeStructAlsoHasWithTag(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
@ -1189,3 +1635,234 @@ PRIMARY KEY (id)
|
||||
t.Assert(user.UserDetail.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_With_MultipleDepends1(t *testing.T) {
|
||||
defer func() {
|
||||
dropTable("table_a")
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TableC struct {
|
||||
gmeta.Meta `orm:"table_c"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableBId int `orm:"table_b_id" json:"table_b_id"`
|
||||
}
|
||||
|
||||
type TableB struct {
|
||||
gmeta.Meta `orm:"table_b"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableAId int `orm:"table_a_id" json:"table_a_id"`
|
||||
TableC *TableC `orm:"with:table_b_id=id" json:"table_c"`
|
||||
}
|
||||
|
||||
type TableA struct {
|
||||
gmeta.Meta `orm:"table_a"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableB *TableB `orm:"with:table_a_id=id" json:"table_b"`
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
// Struct.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA *TableA
|
||||
err := db.Model("table_a").WithAll().Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(tableA, nil)
|
||||
t.Assert(tableA.Id, 1)
|
||||
|
||||
t.AssertNE(tableA.TableB, nil)
|
||||
t.AssertNE(tableA.TableB.TableC, nil)
|
||||
t.Assert(tableA.TableB.TableAId, 1)
|
||||
t.Assert(tableA.TableB.TableC.Id, 100)
|
||||
t.Assert(tableA.TableB.TableC.TableBId, 10)
|
||||
})
|
||||
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA []*TableA
|
||||
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(tableA), 2)
|
||||
t.AssertNE(tableA[0].TableB, nil)
|
||||
t.AssertNE(tableA[1].TableB, nil)
|
||||
t.AssertNE(tableA[0].TableB.TableC, nil)
|
||||
t.AssertNE(tableA[1].TableB.TableC, nil)
|
||||
|
||||
t.Assert(tableA[0].Id, 1)
|
||||
t.Assert(tableA[0].TableB.Id, 10)
|
||||
t.Assert(tableA[0].TableB.TableC.Id, 100)
|
||||
|
||||
t.Assert(tableA[1].Id, 2)
|
||||
t.Assert(tableA[1].TableB.Id, 20)
|
||||
t.Assert(tableA[1].TableB.TableC.Id, 300)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_With_MultipleDepends2(t *testing.T) {
|
||||
defer func() {
|
||||
dropTable("table_a")
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TableC struct {
|
||||
gmeta.Meta `orm:"table_c"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableBId int `orm:"table_b_id" json:"table_b_id"`
|
||||
}
|
||||
|
||||
type TableB struct {
|
||||
gmeta.Meta `orm:"table_b"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableAId int `orm:"table_a_id" json:"table_a_id"`
|
||||
TableC []*TableC `orm:"with:table_b_id=id" json:"table_c"`
|
||||
}
|
||||
|
||||
type TableA struct {
|
||||
gmeta.Meta `orm:"table_a"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableB []*TableB `orm:"with:table_a_id=id" json:"table_b"`
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
// Struct.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA *TableA
|
||||
err := db.Model("table_a").WithAll().Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(tableA, nil)
|
||||
t.Assert(tableA.Id, 1)
|
||||
|
||||
t.Assert(len(tableA.TableB), 2)
|
||||
t.Assert(tableA.TableB[0].Id, 10)
|
||||
t.Assert(tableA.TableB[1].Id, 30)
|
||||
|
||||
t.Assert(len(tableA.TableB[0].TableC), 2)
|
||||
t.Assert(len(tableA.TableB[1].TableC), 1)
|
||||
t.Assert(tableA.TableB[0].TableC[0].Id, 100)
|
||||
t.Assert(tableA.TableB[0].TableC[0].TableBId, 10)
|
||||
t.Assert(tableA.TableB[0].TableC[1].Id, 200)
|
||||
t.Assert(tableA.TableB[0].TableC[1].TableBId, 10)
|
||||
t.Assert(tableA.TableB[1].TableC[0].Id, 400)
|
||||
t.Assert(tableA.TableB[1].TableC[0].TableBId, 30)
|
||||
})
|
||||
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA []*TableA
|
||||
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(tableA), 2)
|
||||
|
||||
t.Assert(len(tableA[0].TableB), 2)
|
||||
t.Assert(tableA[0].TableB[0].Id, 10)
|
||||
t.Assert(tableA[0].TableB[1].Id, 30)
|
||||
|
||||
t.Assert(len(tableA[0].TableB[0].TableC), 2)
|
||||
t.Assert(len(tableA[0].TableB[1].TableC), 1)
|
||||
t.Assert(tableA[0].TableB[0].TableC[0].Id, 100)
|
||||
t.Assert(tableA[0].TableB[0].TableC[0].TableBId, 10)
|
||||
t.Assert(tableA[0].TableB[0].TableC[1].Id, 200)
|
||||
t.Assert(tableA[0].TableB[0].TableC[1].TableBId, 10)
|
||||
t.Assert(tableA[0].TableB[1].TableC[0].Id, 400)
|
||||
t.Assert(tableA[0].TableB[1].TableC[0].TableBId, 30)
|
||||
|
||||
t.Assert(tableA[1].TableB[0].TableC[0].Id, 300)
|
||||
t.Assert(tableA[1].TableB[0].TableC[0].TableBId, 20)
|
||||
|
||||
t.Assert(tableA[1].TableB[1].Id, 40)
|
||||
t.Assert(tableA[1].TableB[1].TableAId, 2)
|
||||
t.Assert(tableA[1].TableB[1].TableC, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) {
|
||||
defer func() {
|
||||
dropTable("table_a")
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TableC struct {
|
||||
gmeta.Meta `orm:"table_c"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableBId int `orm:"table_b_id" json:"table_b_id"`
|
||||
}
|
||||
|
||||
type TableB struct {
|
||||
gmeta.Meta `orm:"table_b"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableAId int `orm:"table_a_id" json:"table_a_id"`
|
||||
*TableC `orm:"with:table_b_id=id" json:"table_c"`
|
||||
}
|
||||
|
||||
type TableA struct {
|
||||
gmeta.Meta `orm:"table_a"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
*TableB `orm:"with:table_a_id=id" json:"table_b"`
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
// Struct.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA *TableA
|
||||
err := db.Model("table_a").WithAll().Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(tableA, nil)
|
||||
t.Assert(tableA.Id, 1)
|
||||
|
||||
t.AssertNE(tableA.TableB, nil)
|
||||
t.AssertNE(tableA.TableB.TableC, nil)
|
||||
t.Assert(tableA.TableB.TableAId, 1)
|
||||
t.Assert(tableA.TableB.TableC.Id, 100)
|
||||
t.Assert(tableA.TableB.TableC.TableBId, 10)
|
||||
})
|
||||
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA []*TableA
|
||||
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(tableA), 2)
|
||||
t.AssertNE(tableA[0].TableB, nil)
|
||||
t.AssertNE(tableA[1].TableB, nil)
|
||||
t.AssertNE(tableA[0].TableB.TableC, nil)
|
||||
t.AssertNE(tableA[1].TableB.TableC, nil)
|
||||
|
||||
t.Assert(tableA[0].Id, 1)
|
||||
t.Assert(tableA[0].TableB.Id, 10)
|
||||
t.Assert(tableA[0].TableB.TableC.Id, 100)
|
||||
|
||||
t.Assert(tableA[1].Id, 2)
|
||||
t.Assert(tableA[1].TableB.Id, 20)
|
||||
t.Assert(tableA[1].TableB.TableC.Id, 300)
|
||||
})
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ func Test_Ctx(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_Ctx_Query(t *testing.T) {
|
||||
db.GetLogger().SetCtxKeys("SpanId", "TraceId")
|
||||
db.GetLogger().(gdb.LoggerImp).SetCtxKeys("SpanId", "TraceId")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
@ -48,7 +48,7 @@ func Test_Ctx_Query(t *testing.T) {
|
||||
func Test_Ctx_Model(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
db.GetLogger().SetCtxKeys("SpanId", "TraceId")
|
||||
db.GetLogger().(gdb.LoggerImp).SetCtxKeys("SpanId", "TraceId")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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)
|
||||
@ -1436,7 +1436,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
gdbCounter := &gdb.Counter{
|
||||
Field: "views",
|
||||
Field: "id",
|
||||
Value: 1,
|
||||
}
|
||||
updateData := g.Map{
|
||||
@ -1449,7 +1449,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
one, err := db.Model(tableName).Where("id", 1).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 2)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -1468,7 +1468,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
one, err := db.Model(tableName).Where("id", 1).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 0)
|
||||
t.Assert(one["views"].Int(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -1938,7 +1938,7 @@ func Test_Model_Option_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
r, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{
|
||||
r, err := db.Model(table).OmitEmptyData().Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": 0,
|
||||
"password": 0,
|
||||
@ -1958,7 +1958,7 @@ func Test_Model_Option_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{
|
||||
_, err := db.Model(table).OmitEmptyData().Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": 0,
|
||||
"password": 0,
|
||||
@ -1994,7 +1994,7 @@ func Test_Model_Option_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{
|
||||
_, err := db.Model(table).OmitEmptyData().Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": 0,
|
||||
"password": 0,
|
||||
@ -2031,7 +2031,7 @@ func Test_Model_Option_Map(t *testing.T) {
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
_, err = db.Model(table).Option(gdb.OptionOmitEmpty).Data(g.Map{"nickname": ""}).Where("id", 2).Update()
|
||||
_, err = db.Model(table).OmitEmptyData().Data(g.Map{"nickname": ""}).Where("id", 2).Update()
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
r, err = db.Model(table).OmitEmpty().Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update()
|
||||
@ -2289,6 +2289,21 @@ func Test_Model_Prefix(t *testing.T) {
|
||||
t.Assert(r[0]["id"], "1")
|
||||
t.Assert(r[1]["id"], "2")
|
||||
})
|
||||
// Select with alias to struct.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
}
|
||||
var users []User
|
||||
err := db.Model(table+" u").Where("u.id in (?)", g.Slice{1, 5}).Order("u.id asc").Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 5)
|
||||
})
|
||||
// Select with alias and join statement.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Model(table+" as u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All()
|
||||
@ -3729,3 +3744,27 @@ func Test_Model_Raw(t *testing.T) {
|
||||
t.Assert(count, 6)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Handler(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := db.Model(table).Safe().Handler(
|
||||
func(m *gdb.Model) *gdb.Model {
|
||||
return m.Page(0, 3)
|
||||
},
|
||||
func(m *gdb.Model) *gdb.Model {
|
||||
return m.Where("id", g.Slice{1, 2, 3, 4, 5, 6})
|
||||
},
|
||||
func(m *gdb.Model) *gdb.Model {
|
||||
return m.OrderDesc("id")
|
||||
},
|
||||
)
|
||||
all, err := m.All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 3)
|
||||
t.Assert(all[0]["id"], 6)
|
||||
t.Assert(all[2]["id"], 4)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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"
|
||||
)
|
||||
|
||||
@ -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.NewCodef(gerror.CodeInvalidParameter, `unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value))
|
||||
}
|
||||
|
||||
func Test_Model_Scan_UnmarshalValue(t *testing.T) {
|
||||
|
||||
33
database/gdb/testdata/with_multiple_depends.sql
vendored
Normal file
33
database/gdb/testdata/with_multiple_depends.sql
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
CREATE TABLE `table_a` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`alias` varchar(255) NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
INSERT INTO `table_a` VALUES (1, 'table_a_test1');
|
||||
INSERT INTO `table_a` VALUES (2, 'table_a_test2');
|
||||
|
||||
CREATE TABLE `table_b` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`table_a_id` int(11) NOT NULL,
|
||||
`alias` varchar(255) NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
INSERT INTO `table_b` VALUES (10, 1, 'table_b_test1');
|
||||
INSERT INTO `table_b` VALUES (20, 2, 'table_b_test2');
|
||||
INSERT INTO `table_b` VALUES (30, 1, 'table_b_test3');
|
||||
INSERT INTO `table_b` VALUES (40, 2, 'table_b_test4');
|
||||
|
||||
CREATE TABLE `table_c` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`table_b_id` int(11) NOT NULL,
|
||||
`alias` varchar(255) NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
INSERT INTO `table_c` VALUES (100, 10, 'table_c_test1');
|
||||
INSERT INTO `table_c` VALUES (200, 10, 'table_c_test2');
|
||||
INSERT INTO `table_c` VALUES (300, 20, 'table_c_test3');
|
||||
INSERT INTO `table_c` VALUES (400, 30, 'table_c_test4');
|
||||
@ -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.
|
||||
@ -113,7 +114,7 @@ func ConfigFromStr(str string) (config *Config, err error) {
|
||||
config.Port = DefaultRedisPort
|
||||
}
|
||||
} else {
|
||||
err = gerror.Newf(`invalid redis configuration: "%s"`, str)
|
||||
err = gerror.NewCodef(gerror.CodeInvalidConfiguration, `invalid redis configuration: "%s"`, str)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -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.NewCode(gerror.CodeNotSupported, `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.NewCode(gerror.CodeNotSupported, `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(
|
||||
|
||||
@ -140,7 +140,7 @@ func Test_Instance(t *testing.T) {
|
||||
func Test_Error(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
config1 := &gredis.Config{
|
||||
Host: "127.0.0.2",
|
||||
Host: "192.111.0.2",
|
||||
Port: 6379,
|
||||
Db: 1,
|
||||
ConnectTimeout: time.Second,
|
||||
@ -149,16 +149,6 @@ func Test_Error(t *testing.T) {
|
||||
_, err := redis.Do("info")
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
config1 = &gredis.Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: 6379,
|
||||
Db: 1,
|
||||
Pass: "666666",
|
||||
}
|
||||
redis = gredis.New(config1)
|
||||
_, err = redis.Do("info")
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
config1 = &gredis.Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: 6379,
|
||||
|
||||
@ -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.WrapCodef(gerror.CodeInternalError, err, "%s to utf8 failed", srcCharset)
|
||||
}
|
||||
src = string(tmp)
|
||||
} else {
|
||||
return dst, errors.New(fmt.Sprintf("unsupport srcCharset: %s", srcCharset))
|
||||
return dst, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported 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.WrapCodef(gerror.CodeInternalError, err, "utf to %s failed", dstCharset)
|
||||
}
|
||||
dst = string(tmp)
|
||||
} else {
|
||||
return dst, errors.New(fmt.Sprintf("unsupport dstCharset: %s", dstCharset))
|
||||
return dst, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported 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.NewCode(gerror.CodeInvalidParameter, "failed to parse INI file, section not found")
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
package gjson
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -37,11 +38,23 @@ type Options struct {
|
||||
StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64.
|
||||
}
|
||||
|
||||
// apiInterface is used for type assert api for Interface().
|
||||
type apiInterface interface {
|
||||
Interface() interface{}
|
||||
}
|
||||
|
||||
// setValue sets <value> to <j> by <pattern>.
|
||||
// Note:
|
||||
// 1. If value is nil and removed is true, means deleting this value;
|
||||
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
|
||||
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
if value != nil {
|
||||
if utils.IsStruct(value) {
|
||||
if v, ok := value.(apiInterface); ok {
|
||||
value = v.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
array := strings.Split(pattern, string(j.c))
|
||||
length := len(array)
|
||||
value = j.convertValue(value)
|
||||
|
||||
@ -18,7 +18,13 @@ import (
|
||||
)
|
||||
|
||||
// Value returns the json value.
|
||||
// Deprecated, use Interface instead.
|
||||
func (j *Json) Value() interface{} {
|
||||
return j.Interface()
|
||||
}
|
||||
|
||||
// Interface returns the json value.
|
||||
func (j *Json) Interface() interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return *(j.p)
|
||||
@ -319,23 +325,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.NewCode(gerror.CodeInvalidParameter, "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")
|
||||
@ -485,3 +485,17 @@ func TestJson_IsNil(t *testing.T) {
|
||||
t.Assert(j.IsNil(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestJson_Set_With_Struct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v := gjson.New(g.Map{
|
||||
"user1": g.Map{"name": "user1"},
|
||||
"user2": g.Map{"name": "user2"},
|
||||
"user3": g.Map{"name": "user3"},
|
||||
})
|
||||
user1 := v.GetJson("user1")
|
||||
user1.Set("id", 111)
|
||||
v.Set("user1", user1)
|
||||
t.Assert(v.Get("user1.id"), 111)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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.
|
||||
@ -49,7 +49,7 @@ func New(text string) error {
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: text,
|
||||
code: -1,
|
||||
code: CodeNil,
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ func Newf(format string, args ...interface{}) error {
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
code: -1,
|
||||
code: CodeNil,
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ func NewSkip(skip int, text string) error {
|
||||
return &Error{
|
||||
stack: callers(skip),
|
||||
text: text,
|
||||
code: -1,
|
||||
code: CodeNil,
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ func NewSkipf(skip int, format string, args ...interface{}) error {
|
||||
return &Error{
|
||||
stack: callers(skip),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
code: -1,
|
||||
code: CodeNil,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
errors/gerror/gerror_code.go
Normal file
40
errors/gerror/gerror_code.go
Normal file
@ -0,0 +1,40 @@
|
||||
// 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 gerror
|
||||
|
||||
// Reserved internal error code of framework: code < 1000.
|
||||
|
||||
const (
|
||||
// ===============================================================================
|
||||
// Common system codes.
|
||||
// ===============================================================================
|
||||
|
||||
CodeNil = -1 // No error code specified.
|
||||
CodeOk = 0 // It is OK.
|
||||
CodeInternalError = 50 + iota // An error occurred internally.
|
||||
CodeValidationFailed // Data validation failed.
|
||||
CodeDbOperationError // Database operation error.
|
||||
CodeInvalidParameter // The given parameter for current operation is invalid.
|
||||
CodeMissingParameter // Parameter for current operation is missing.
|
||||
CodeInvalidOperation // The function cannot be used like this.
|
||||
CodeInvalidConfiguration // The configuration is invalid for current operation.
|
||||
CodeMissingConfiguration // The configuration is missing for current operation.
|
||||
CodeNotImplemented // The operation is not implemented yet.
|
||||
CodeNotSupported // The operation is not supported yet.
|
||||
CodeOperationFailed // I tried, but I cannot give you what you want.
|
||||
CodeNotAuthorized // Not Authorized.
|
||||
CodeSecurityReason // Security Reason.
|
||||
CodeServerBusy // Server is busy, please try again later.
|
||||
CodeUnknown // Unknown error.
|
||||
CodeResourceNotExist // Resource does not exist.
|
||||
|
||||
// ===============================================================================
|
||||
// Common business codes.
|
||||
// ===============================================================================
|
||||
|
||||
CodeBusinessValidationFailed = 300 + iota // Business validation failed.
|
||||
)
|
||||
@ -179,6 +179,9 @@ func (err *Error) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// formatSubStack formats the stack for error.
|
||||
func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
if st == nil {
|
||||
return
|
||||
}
|
||||
index := 1
|
||||
space := " "
|
||||
for _, p := range st {
|
||||
|
||||
29
errors/gerror/gerror_option.go
Normal file
29
errors/gerror/gerror_option.go
Normal file
@ -0,0 +1,29 @@
|
||||
// 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 gerror
|
||||
|
||||
// Option is option for creating error.
|
||||
type Option struct {
|
||||
Error error // Wrapped error if any.
|
||||
Stack bool // Whether recording stack information into error.
|
||||
Text string // Error text, which is created by New* functions.
|
||||
Code int // Error code if necessary.
|
||||
}
|
||||
|
||||
// NewOption creates and returns an error with Option.
|
||||
// It is the senior usage for creating error, which is often used internally in framework.
|
||||
func NewOption(option Option) error {
|
||||
err := &Error{
|
||||
error: option.Error,
|
||||
text: option.Text,
|
||||
code: option.Code,
|
||||
}
|
||||
if option.Stack {
|
||||
err.stack = callers()
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -7,6 +7,7 @@
|
||||
package g
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
@ -75,3 +76,8 @@ func IsNil(value interface{}, traceSource ...bool) bool {
|
||||
func IsEmpty(value interface{}) bool {
|
||||
return empty.IsEmpty(value)
|
||||
}
|
||||
|
||||
// RequestFromCtx retrieves and returns the Request object from context.
|
||||
func RequestFromCtx(ctx context.Context) *ghttp.Request {
|
||||
return ghttp.RequestFromCtx(ctx)
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gins
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
@ -52,21 +53,24 @@ func Database(name ...string) gdb.DB {
|
||||
if configFilePath == "" {
|
||||
exampleFileName := "config.example.toml"
|
||||
if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" {
|
||||
panic(gerror.Wrapf(
|
||||
panic(gerror.WrapCodef(
|
||||
gerror.CodeMissingConfiguration,
|
||||
err,
|
||||
`configuration file "%s" not found, but found "%s", did you miss renaming the example configuration file?`,
|
||||
Config().GetFileName(),
|
||||
exampleFileName,
|
||||
))
|
||||
} else {
|
||||
panic(gerror.Wrapf(
|
||||
panic(gerror.WrapCodef(
|
||||
gerror.CodeMissingConfiguration,
|
||||
err,
|
||||
`configuration file "%s" not found, did you miss the configuration file or the misspell the configuration file name?`,
|
||||
Config().GetFileName(),
|
||||
))
|
||||
}
|
||||
}
|
||||
panic(gerror.Wrapf(
|
||||
panic(gerror.WrapCodef(
|
||||
gerror.CodeMissingConfiguration,
|
||||
err,
|
||||
`database initialization failed: "%s" node not found, is configuration file or configuration node missing?`,
|
||||
configNodeNameDatabase,
|
||||
@ -92,11 +96,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,11 +114,11 @@ func Database(name ...string) gdb.DB {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,8 +132,10 @@ func Database(name ...string) gdb.DB {
|
||||
loggerConfigMap = Config().GetMap(configNodeKey)
|
||||
}
|
||||
if len(loggerConfigMap) > 0 {
|
||||
if err := db.GetLogger().SetConfigWithMap(loggerConfigMap); err != nil {
|
||||
panic(err)
|
||||
if logger, ok := db.GetLogger().(gdb.LoggerImp); ok {
|
||||
if err := logger.SetConfigWithMap(loggerConfigMap); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmvc provides basic object classes for MVC.
|
||||
// Deprecated, no longer suggested.
|
||||
package gmvc
|
||||
|
||||
import (
|
||||
@ -12,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// Controller is used for controller register of ghttp.Server.
|
||||
// Deprecated, no longer suggested.
|
||||
type Controller struct {
|
||||
Request *ghttp.Request
|
||||
Response *ghttp.Response
|
||||
|
||||
@ -9,6 +9,11 @@ package gmvc
|
||||
import "github.com/gogf/gf/database/gdb"
|
||||
|
||||
type (
|
||||
M = Model // M is alias for Model, just for short write purpose.
|
||||
Model = *gdb.Model // Model is alias for *gdb.Model.
|
||||
// M is alias for Model, just for short write purpose.
|
||||
// Deprecated, no longer suggested.
|
||||
M = *gdb.Model
|
||||
|
||||
// Model is alias for *gdb.Model.
|
||||
// Deprecated, no longer suggested.
|
||||
Model = *gdb.Model
|
||||
)
|
||||
|
||||
@ -19,6 +19,7 @@ import (
|
||||
// View is the view object for controller.
|
||||
// It's initialized when controller request initializes and destroyed
|
||||
// when the controller request closes.
|
||||
// Deprecated, no longer suggested.
|
||||
type View struct {
|
||||
mu sync.RWMutex
|
||||
view *gview.View
|
||||
@ -27,6 +28,7 @@ type View struct {
|
||||
}
|
||||
|
||||
// NewView creates and returns a controller view object.
|
||||
// Deprecated, no longer suggested.
|
||||
func NewView(w *ghttp.Response) *View {
|
||||
return &View{
|
||||
view: gins.View(),
|
||||
@ -76,7 +78,7 @@ func (view *View) LockFunc(f func(data gview.Params)) {
|
||||
f(view.data)
|
||||
}
|
||||
|
||||
// LockFunc locks reading for template variables by callback function <f>.
|
||||
// RLockFunc locks reading for template variables by callback function <f>.
|
||||
func (view *View) RLockFunc(f func(data gview.Params)) {
|
||||
view.mu.RLock()
|
||||
defer view.mu.RUnlock()
|
||||
|
||||
7
go.mod
7
go.mod
@ -5,6 +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
|
||||
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
|
||||
@ -12,9 +13,9 @@ require (
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf
|
||||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
go.opentelemetry.io/otel v0.19.0
|
||||
go.opentelemetry.io/otel/oteltest v0.19.0
|
||||
go.opentelemetry.io/otel/trace v0.19.0
|
||||
go.opentelemetry.io/otel v1.0.0-RC2
|
||||
go.opentelemetry.io/otel/oteltest v1.0.0-RC2
|
||||
go.opentelemetry.io/otel/trace v1.0.0-RC2
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
|
||||
golang.org/x/text v0.3.4
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||
|
||||
26
go.sum
26
go.sum
@ -4,18 +4,24 @@ 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=
|
||||
github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
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=
|
||||
@ -28,14 +34,12 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng=
|
||||
go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
|
||||
go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg=
|
||||
go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
|
||||
go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q=
|
||||
go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
|
||||
go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
|
||||
go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
|
||||
go.opentelemetry.io/otel v1.0.0-RC2 h1:SHhxSjB+omnGZPgGlKe+QMp3MyazcOHdQ8qwo89oKbg=
|
||||
go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM=
|
||||
go.opentelemetry.io/otel/oteltest v1.0.0-RC2 h1:xNKqMhlZYkASSyvF4JwObZFMq0jhFN3c3SP+2rCzVPk=
|
||||
go.opentelemetry.io/otel/oteltest v1.0.0-RC2/go.mod h1:kiQ4tw5tAL4JLTbcOYwK1CWI1HkT5aiLzHovgOVnz/A=
|
||||
go.opentelemetry.io/otel/trace v1.0.0-RC2 h1:dunAP0qDULMIT82atj34m5RgvsIK6LcsXf1c/MsYg1w=
|
||||
go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@ -44,6 +48,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.NewCodef(gerror.CodeInvalidParameter, `%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,83 @@ 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)
|
||||
}
|
||||
|
||||
// PrintFunc prints the output from function `f`.
|
||||
// It only calls function `f` if debug mode is enabled.
|
||||
func PrintFunc(ctx context.Context, f func() string) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
}
|
||||
fmt.Printf(
|
||||
now()+" [INTE] "+file()+" "+format+"\n%s\n",
|
||||
append(v, gdebug.StackWithFilter(stackFilterKey))...,
|
||||
)
|
||||
s := f()
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
doPrint(ctx, s, false)
|
||||
}
|
||||
|
||||
// ErrorFunc prints the output from function `f`.
|
||||
// It only calls function `f` if debug mode is enabled.
|
||||
func ErrorFunc(ctx context.Context, f func() string) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
}
|
||||
s := f()
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
doPrint(ctx, s, true)
|
||||
}
|
||||
|
||||
func doPrint(ctx context.Context, content string, stack bool) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteString(now())
|
||||
buffer.WriteString(" [INTE] ")
|
||||
buffer.WriteString(file())
|
||||
buffer.WriteString(" ")
|
||||
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.
|
||||
|
||||
@ -29,6 +29,11 @@ func (f *Field) IsEmbedded() bool {
|
||||
return f.Field.Anonymous
|
||||
}
|
||||
|
||||
// TagStr returns the tag string of the field.
|
||||
func (f *Field) TagStr() string {
|
||||
return string(f.Field.Tag)
|
||||
}
|
||||
|
||||
// IsExported returns true if the given field is exported.
|
||||
func (f *Field) IsExported() bool {
|
||||
return f.Field.PkgPath == ""
|
||||
@ -64,6 +69,25 @@ func (f *Field) OriginalKind() reflect.Kind {
|
||||
return kind
|
||||
}
|
||||
|
||||
const (
|
||||
RecursiveOptionNone = 0 // No recursively retrieving fields as map if the field is an embedded struct.
|
||||
RecursiveOptionEmbedded = 1 // Recursively retrieving fields as map if the field is an embedded struct.
|
||||
RecursiveOptionEmbeddedNoTag = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag.
|
||||
)
|
||||
|
||||
type FieldMapInput struct {
|
||||
// Pointer should be type of struct/*struct.
|
||||
Pointer interface{}
|
||||
|
||||
// PriorityTagArray specifies the priority tag array for retrieving from high to low.
|
||||
// If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name.
|
||||
PriorityTagArray []string
|
||||
|
||||
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
|
||||
// is an embedded struct. It is RecursiveOptionNone in default.
|
||||
RecursiveOption int
|
||||
}
|
||||
|
||||
// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`.
|
||||
//
|
||||
// The parameter `pointer` should be type of struct/*struct.
|
||||
@ -75,8 +99,8 @@ func (f *Field) OriginalKind() reflect.Kind {
|
||||
// is an embedded struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func FieldMap(pointer interface{}, priority []string, recursive bool) (map[string]*Field, error) {
|
||||
fields, err := getFieldValues(pointer)
|
||||
func FieldMap(input FieldMapInput) (map[string]*Field, error) {
|
||||
fields, err := getFieldValues(input.Pointer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -90,7 +114,7 @@ func FieldMap(pointer interface{}, priority []string, recursive bool) (map[strin
|
||||
continue
|
||||
}
|
||||
tagValue = ""
|
||||
for _, p := range priority {
|
||||
for _, p := range input.PriorityTagArray {
|
||||
tagValue = field.Tag(p)
|
||||
if tagValue != "" && tagValue != "-" {
|
||||
break
|
||||
@ -101,15 +125,28 @@ func FieldMap(pointer interface{}, priority []string, recursive bool) (map[strin
|
||||
if tagValue != "" {
|
||||
mapField[tagValue] = tempField
|
||||
} else {
|
||||
if recursive && field.IsEmbedded() {
|
||||
m, err := FieldMap(field.Value, priority, recursive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range m {
|
||||
if _, ok := mapField[k]; !ok {
|
||||
tempV := v
|
||||
mapField[k] = tempV
|
||||
if input.RecursiveOption != RecursiveOptionNone && field.IsEmbedded() {
|
||||
switch input.RecursiveOption {
|
||||
case RecursiveOptionEmbeddedNoTag:
|
||||
if field.TagStr() != "" {
|
||||
mapField[field.Name()] = tempField
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case RecursiveOptionEmbedded:
|
||||
m, err := FieldMap(FieldMapInput{
|
||||
Pointer: field.Value,
|
||||
PriorityTagArray: input.PriorityTagArray,
|
||||
RecursiveOption: input.RecursiveOption,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range m {
|
||||
if _, ok := mapField[k]; !ok {
|
||||
tempV := v
|
||||
mapField[k] = tempV
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -7,7 +7,8 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
@ -49,9 +50,11 @@ exitLoop:
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Struct {
|
||||
return nil, gerror.Newf(
|
||||
`invalid object kind "%s", kind of "struct" is required`,
|
||||
reflectKind,
|
||||
return nil, errors.New(
|
||||
fmt.Sprintf(
|
||||
`invalid object kind "%s", kind of "struct" is required`,
|
||||
reflectKind,
|
||||
),
|
||||
)
|
||||
}
|
||||
reflectType = reflectValue.Type()
|
||||
|
||||
@ -110,7 +110,11 @@ func Test_FieldMap(t *testing.T) {
|
||||
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
|
||||
}
|
||||
var user *User
|
||||
m, _ := structs.FieldMap(user, []string{"params"}, true)
|
||||
m, _ := structs.FieldMap(structs.FieldMapInput{
|
||||
Pointer: user,
|
||||
PriorityTagArray: []string{"params"},
|
||||
RecursiveOption: structs.RecursiveOptionEmbedded,
|
||||
})
|
||||
t.Assert(len(m), 3)
|
||||
_, ok := m["Id"]
|
||||
t.Assert(ok, true)
|
||||
@ -130,7 +134,11 @@ func Test_FieldMap(t *testing.T) {
|
||||
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
|
||||
}
|
||||
var user *User
|
||||
m, _ := structs.FieldMap(user, nil, true)
|
||||
m, _ := structs.FieldMap(structs.FieldMapInput{
|
||||
Pointer: user,
|
||||
PriorityTagArray: nil,
|
||||
RecursiveOption: structs.RecursiveOptionEmbedded,
|
||||
})
|
||||
t.Assert(len(m), 3)
|
||||
_, ok := m["Id"]
|
||||
t.Assert(ok, true)
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
debugKey = "gf.debug" // Debug key for checking if in debug mode.
|
||||
commandEnvKeyForDebugKey = "gf.debug" // Debug key for checking if in debug mode.
|
||||
StackFilterKeyForGoFrame = "github.com/gogf/gf@" // Stack filtering key for all GoFrame module paths.
|
||||
)
|
||||
|
||||
@ -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 {
|
||||
|
||||
98
internal/utils/utils_is.go
Normal file
98
internal/utils/utils_is.go
Normal file
@ -0,0 +1,98 @@
|
||||
// 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 utils
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// IsNil checks whether `value` is nil.
|
||||
func IsNil(value interface{}) bool {
|
||||
return value == nil
|
||||
}
|
||||
|
||||
// IsEmpty checks whether `value` is empty.
|
||||
func IsEmpty(value interface{}) bool {
|
||||
return empty.IsEmpty(value)
|
||||
}
|
||||
|
||||
// IsInt checks whether `value` is type of int.
|
||||
func IsInt(value interface{}) bool {
|
||||
switch value.(type) {
|
||||
case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUint checks whether `value` is type of uint.
|
||||
func IsUint(value interface{}) bool {
|
||||
switch value.(type) {
|
||||
case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsFloat checks whether `value` is type of float.
|
||||
func IsFloat(value interface{}) bool {
|
||||
switch value.(type) {
|
||||
case float32, *float32, float64, *float64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSlice checks whether `value` is type of slice.
|
||||
func IsSlice(value interface{}) bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsMap checks whether `value` is type of map.
|
||||
func IsMap(value interface{}) bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsStruct checks whether `value` is type of struct.
|
||||
func IsStruct(value interface{}) bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
171
internal/utils/utils_z_is_test.go
Normal file
171
internal/utils/utils_z_is_test.go
Normal file
@ -0,0 +1,171 @@
|
||||
// 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 utils_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVar_IsNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsNil(0), false)
|
||||
t.Assert(utils.IsNil(nil), true)
|
||||
t.Assert(utils.IsNil(g.Map{}), false)
|
||||
t.Assert(utils.IsNil(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsNil(1), false)
|
||||
t.Assert(utils.IsNil(0.1), false)
|
||||
t.Assert(utils.IsNil(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsNil(g.Slice{0}), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsEmpty(0), true)
|
||||
t.Assert(utils.IsEmpty(nil), true)
|
||||
t.Assert(utils.IsEmpty(g.Map{}), true)
|
||||
t.Assert(utils.IsEmpty(g.Slice{}), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsEmpty(1), false)
|
||||
t.Assert(utils.IsEmpty(0.1), false)
|
||||
t.Assert(utils.IsEmpty(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsEmpty(g.Slice{0}), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsInt(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsInt(0), true)
|
||||
t.Assert(utils.IsInt(nil), false)
|
||||
t.Assert(utils.IsInt(g.Map{}), false)
|
||||
t.Assert(utils.IsInt(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsInt(1), true)
|
||||
t.Assert(utils.IsInt(-1), true)
|
||||
t.Assert(utils.IsInt(0.1), false)
|
||||
t.Assert(utils.IsInt(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsInt(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsInt(int8(1)), true)
|
||||
t.Assert(utils.IsInt(uint8(1)), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsUint(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsUint(0), false)
|
||||
t.Assert(utils.IsUint(nil), false)
|
||||
t.Assert(utils.IsUint(g.Map{}), false)
|
||||
t.Assert(utils.IsUint(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsUint(1), false)
|
||||
t.Assert(utils.IsUint(-1), false)
|
||||
t.Assert(utils.IsUint(0.1), false)
|
||||
t.Assert(utils.IsUint(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsUint(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsUint(int8(1)), false)
|
||||
t.Assert(utils.IsUint(uint8(1)), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsFloat(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsFloat(0), false)
|
||||
t.Assert(utils.IsFloat(nil), false)
|
||||
t.Assert(utils.IsFloat(g.Map{}), false)
|
||||
t.Assert(utils.IsFloat(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsFloat(1), false)
|
||||
t.Assert(utils.IsFloat(-1), false)
|
||||
t.Assert(utils.IsFloat(0.1), true)
|
||||
t.Assert(utils.IsFloat(float64(1)), true)
|
||||
t.Assert(utils.IsFloat(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsFloat(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsFloat(int8(1)), false)
|
||||
t.Assert(utils.IsFloat(uint8(1)), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsSlice(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsSlice(0), false)
|
||||
t.Assert(utils.IsSlice(nil), false)
|
||||
t.Assert(utils.IsSlice(g.Map{}), false)
|
||||
t.Assert(utils.IsSlice(g.Slice{}), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsSlice(1), false)
|
||||
t.Assert(utils.IsSlice(-1), false)
|
||||
t.Assert(utils.IsSlice(0.1), false)
|
||||
t.Assert(utils.IsSlice(float64(1)), false)
|
||||
t.Assert(utils.IsSlice(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsSlice(g.Slice{0}), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsSlice(int8(1)), false)
|
||||
t.Assert(utils.IsSlice(uint8(1)), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsMap(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsMap(0), false)
|
||||
t.Assert(utils.IsMap(nil), false)
|
||||
t.Assert(utils.IsMap(g.Map{}), true)
|
||||
t.Assert(utils.IsMap(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsMap(1), false)
|
||||
t.Assert(utils.IsMap(-1), false)
|
||||
t.Assert(utils.IsMap(0.1), false)
|
||||
t.Assert(utils.IsMap(float64(1)), false)
|
||||
t.Assert(utils.IsMap(g.Map{"k": "v"}), true)
|
||||
t.Assert(utils.IsMap(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsMap(int8(1)), false)
|
||||
t.Assert(utils.IsMap(uint8(1)), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsStruct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsStruct(0), false)
|
||||
t.Assert(utils.IsStruct(nil), false)
|
||||
t.Assert(utils.IsStruct(g.Map{}), false)
|
||||
t.Assert(utils.IsStruct(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsStruct(1), false)
|
||||
t.Assert(utils.IsStruct(-1), false)
|
||||
t.Assert(utils.IsStruct(0.1), false)
|
||||
t.Assert(utils.IsStruct(float64(1)), false)
|
||||
t.Assert(utils.IsStruct(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsStruct(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := &struct {
|
||||
}{}
|
||||
t.Assert(utils.IsStruct(a), true)
|
||||
t.Assert(utils.IsStruct(*a), true)
|
||||
t.Assert(utils.IsStruct(&a), true)
|
||||
})
|
||||
}
|
||||
@ -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.
|
||||
@ -58,38 +58,41 @@ type (
|
||||
handler *handlerItem // The handler.
|
||||
}
|
||||
|
||||
// HandlerFunc is request handler function.
|
||||
HandlerFunc = func(r *Request)
|
||||
|
||||
// handlerFuncInfo contains the HandlerFunc address and its reflect type.
|
||||
handlerFuncInfo struct {
|
||||
Func HandlerFunc // Handler function address.
|
||||
Type reflect.Type // Reflect type information for current handler, which is used for extension of handler feature.
|
||||
Value reflect.Value // Reflect value information for current handler, which is used for extension of handler feature.
|
||||
}
|
||||
|
||||
// handlerItem is the registered handler for route handling,
|
||||
// including middleware and hook functions.
|
||||
handlerItem struct {
|
||||
itemId int // Unique handler item id mark.
|
||||
itemName string // Handler name, which is automatically retrieved from runtime stack when registered.
|
||||
itemType int // Handler type: object/handler/controller/middleware/hook.
|
||||
itemFunc HandlerFunc // Handler address.
|
||||
initFunc HandlerFunc // Initialization function when request enters the object(only available for object register type).
|
||||
shutFunc HandlerFunc // Shutdown function when request leaves out the object(only available for object register type).
|
||||
middleware []HandlerFunc // Bound middleware array.
|
||||
ctrlInfo *handlerController // Controller information for reflect usage.
|
||||
hookName string // Hook type name.
|
||||
router *Router // Router object.
|
||||
source string // Source file path:line when registering.
|
||||
Id int // Unique handler item id mark.
|
||||
Name string // Handler name, which is automatically retrieved from runtime stack when registered.
|
||||
Type int // Handler type: object/handler/controller/middleware/hook.
|
||||
Info handlerFuncInfo // Handler function information.
|
||||
InitFunc HandlerFunc // Initialization function when request enters the object (only available for object register type).
|
||||
ShutFunc HandlerFunc // Shutdown function when request leaves out the object (only available for object register type).
|
||||
Middleware []HandlerFunc // Bound middleware array.
|
||||
HookName string // Hook type name, only available for hook type.
|
||||
Router *Router // Router object.
|
||||
Source string // Registering source file `path:line`.
|
||||
}
|
||||
|
||||
// handlerParsedItem is the item parsed from URL.Path.
|
||||
handlerParsedItem struct {
|
||||
handler *handlerItem // Handler information.
|
||||
values map[string]string // Router values parsed from URL.Path.
|
||||
}
|
||||
|
||||
// handlerController is the controller information used for reflect.
|
||||
handlerController struct {
|
||||
name string // Handler method name.
|
||||
reflect reflect.Type // Reflect type of the controller.
|
||||
Handler *handlerItem // Handler information.
|
||||
Values map[string]string // Router values parsed from URL.Path.
|
||||
}
|
||||
|
||||
// registeredRouteItem stores the information of the router and is used for route map.
|
||||
registeredRouteItem struct {
|
||||
source string // Source file path and its line number.
|
||||
handler *handlerItem // Handler object.
|
||||
Source string // Source file path and its line number.
|
||||
Handler *handlerItem // Handler object.
|
||||
}
|
||||
|
||||
// errorStack is the interface for Stack feature.
|
||||
@ -98,19 +101,12 @@ type (
|
||||
Stack() string
|
||||
}
|
||||
|
||||
// Request handler function.
|
||||
HandlerFunc = func(r *Request)
|
||||
|
||||
// Listening file descriptor mapping.
|
||||
// The key is either "http" or "https" and the value is its FD.
|
||||
listenerFdMap = map[string]string
|
||||
)
|
||||
|
||||
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"
|
||||
@ -130,6 +126,10 @@ const (
|
||||
exceptionExitAll = "exit_all"
|
||||
exceptionExitHook = "exit_hook"
|
||||
routeCacheDuration = time.Hour
|
||||
methodNameInit = "Init"
|
||||
methodNameShut = "Shut"
|
||||
methodNameExit = "Exit"
|
||||
ctxKeyForRequest = "gHttpRequestObject"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -1,13 +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 ghttp
|
||||
|
||||
// Controller is the base struct for controller.
|
||||
type Controller interface {
|
||||
Init(*Request)
|
||||
Shut()
|
||||
}
|
||||
@ -26,6 +26,7 @@ func niceCallFunc(f func()) {
|
||||
switch exception {
|
||||
case exceptionExit, exceptionExitAll:
|
||||
return
|
||||
|
||||
default:
|
||||
if _, ok := exception.(errorStack); ok {
|
||||
// It's already an error that has stack info.
|
||||
@ -35,9 +36,13 @@ func niceCallFunc(f func()) {
|
||||
// Note that there's a skip pointing the start stacktrace
|
||||
// of the real error point.
|
||||
if err, ok := exception.(error); ok {
|
||||
panic(gerror.Wrap(err, ""))
|
||||
if gerror.Code(err) != gerror.CodeNil {
|
||||
panic(gerror.Wrap(err, ""))
|
||||
} else {
|
||||
panic(gerror.WrapCode(gerror.CodeInternalError, err, ""))
|
||||
}
|
||||
} else {
|
||||
panic(gerror.NewSkipf(1, "%v", exception))
|
||||
panic(gerror.NewCodeSkipf(gerror.CodeInternalError, 1, "%v", exception))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
net/ghttp/ghttp_middleware_handler_response.go
Normal file
52
net/ghttp/ghttp_middleware_handler_response.go
Normal file
@ -0,0 +1,52 @@
|
||||
// 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 ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
)
|
||||
|
||||
type DefaultHandlerResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// MiddlewareHandlerResponse is the default middleware handling handler response object and its error.
|
||||
func MiddlewareHandlerResponse(r *Request) {
|
||||
r.Middleware.Next()
|
||||
var (
|
||||
err error
|
||||
res interface{}
|
||||
internalErr error
|
||||
)
|
||||
res, err = r.GetHandlerResponse()
|
||||
if err != nil {
|
||||
code := gerror.Code(err)
|
||||
if code == gerror.CodeNil {
|
||||
code = gerror.CodeInternalError
|
||||
}
|
||||
internalErr = r.Response.WriteJson(DefaultHandlerResponse{
|
||||
Code: code,
|
||||
Message: err.Error(),
|
||||
Data: nil,
|
||||
})
|
||||
if internalErr != nil {
|
||||
intlog.Error(r.Context(), internalErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
internalErr = r.Response.WriteJson(DefaultHandlerResponse{
|
||||
Code: gerror.CodeOk,
|
||||
Message: "",
|
||||
Data: res,
|
||||
})
|
||||
if internalErr != nil {
|
||||
intlog.Error(r.Context(), internalErr)
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,7 @@ type Request struct {
|
||||
StaticFile *staticFile // Static file object for static file serving.
|
||||
context context.Context // Custom context for internal usage purpose.
|
||||
handlers []*handlerParsedItem // All matched handlers containing handler, hook and middleware for this request.
|
||||
handlerResponse handlerResponse // Handler response object and its error value.
|
||||
hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose.
|
||||
hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose.
|
||||
parsedQuery bool // A bool marking whether the GET parameters parsed.
|
||||
@ -57,6 +58,11 @@ type Request struct {
|
||||
viewParams gview.Params // Custom template view variables for this response.
|
||||
}
|
||||
|
||||
type handlerResponse struct {
|
||||
Object interface{}
|
||||
Error error
|
||||
}
|
||||
|
||||
// staticFile is the file struct for static file service.
|
||||
type staticFile struct {
|
||||
File *gres.File // Resource file object.
|
||||
@ -73,7 +79,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 +93,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 {
|
||||
@ -233,3 +242,8 @@ func (r *Request) ReloadParam() {
|
||||
r.parsedQuery = false
|
||||
r.bodyContent = nil
|
||||
}
|
||||
|
||||
// GetHandlerResponse retrieves and returns the handler response object and its error.
|
||||
func (r *Request) GetHandlerResponse() (res interface{}, err error) {
|
||||
return r.handlerResponse.Object, r.handlerResponse.Error
|
||||
}
|
||||
|
||||
@ -34,20 +34,20 @@ func (m *middleware) Next() {
|
||||
}
|
||||
item = m.request.handlers[m.handlerIndex]
|
||||
// Filter the HOOK handlers, which are designed to be called in another standalone procedure.
|
||||
if item.handler.itemType == handlerTypeHook {
|
||||
if item.Handler.Type == handlerTypeHook {
|
||||
m.handlerIndex++
|
||||
continue
|
||||
}
|
||||
// Current router switching.
|
||||
m.request.Router = item.handler.router
|
||||
m.request.Router = item.Handler.Router
|
||||
|
||||
// Router values switching.
|
||||
m.request.routerMap = item.values
|
||||
m.request.routerMap = item.Values
|
||||
|
||||
gutil.TryCatch(func() {
|
||||
// Execute bound middleware array of the item if it's not empty.
|
||||
if m.handlerMDIndex < len(item.handler.middleware) {
|
||||
md := item.handler.middleware[m.handlerMDIndex]
|
||||
if m.handlerMDIndex < len(item.Handler.Middleware) {
|
||||
md := item.Handler.Middleware[m.handlerMDIndex]
|
||||
m.handlerMDIndex++
|
||||
niceCallFunc(func() {
|
||||
md(m.request)
|
||||
@ -57,47 +57,24 @@ func (m *middleware) Next() {
|
||||
}
|
||||
m.handlerIndex++
|
||||
|
||||
switch item.handler.itemType {
|
||||
// Service controller.
|
||||
case handlerTypeController:
|
||||
m.served = true
|
||||
if m.request.IsExited() {
|
||||
break
|
||||
}
|
||||
c := reflect.New(item.handler.ctrlInfo.reflect)
|
||||
niceCallFunc(func() {
|
||||
c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(m.request)})
|
||||
})
|
||||
if !m.request.IsExited() {
|
||||
niceCallFunc(func() {
|
||||
c.MethodByName(item.handler.ctrlInfo.name).Call(nil)
|
||||
})
|
||||
}
|
||||
if !m.request.IsExited() {
|
||||
niceCallFunc(func() {
|
||||
c.MethodByName("Shut").Call(nil)
|
||||
})
|
||||
}
|
||||
|
||||
switch item.Handler.Type {
|
||||
// Service object.
|
||||
case handlerTypeObject:
|
||||
m.served = true
|
||||
if m.request.IsExited() {
|
||||
break
|
||||
}
|
||||
if item.handler.initFunc != nil {
|
||||
if item.Handler.InitFunc != nil {
|
||||
niceCallFunc(func() {
|
||||
item.handler.initFunc(m.request)
|
||||
item.Handler.InitFunc(m.request)
|
||||
})
|
||||
}
|
||||
if !m.request.IsExited() {
|
||||
niceCallFunc(func() {
|
||||
item.handler.itemFunc(m.request)
|
||||
})
|
||||
m.callHandlerFunc(item.Handler.Info)
|
||||
}
|
||||
if !m.request.IsExited() && item.handler.shutFunc != nil {
|
||||
if !m.request.IsExited() && item.Handler.ShutFunc != nil {
|
||||
niceCallFunc(func() {
|
||||
item.handler.shutFunc(m.request)
|
||||
item.Handler.ShutFunc(m.request)
|
||||
})
|
||||
}
|
||||
|
||||
@ -108,13 +85,13 @@ func (m *middleware) Next() {
|
||||
break
|
||||
}
|
||||
niceCallFunc(func() {
|
||||
item.handler.itemFunc(m.request)
|
||||
m.callHandlerFunc(item.Handler.Info)
|
||||
})
|
||||
|
||||
// Global middleware array.
|
||||
case handlerTypeMiddleware:
|
||||
niceCallFunc(func() {
|
||||
item.handler.itemFunc(m.request)
|
||||
item.Handler.Info.Func(m.request)
|
||||
})
|
||||
// It does not continue calling next middleware after another middleware done.
|
||||
// There should be a "Next" function to be called in the middleware in order to manage the workflow.
|
||||
@ -128,7 +105,7 @@ func (m *middleware) Next() {
|
||||
// Create a new error with stack info.
|
||||
// Note that there's a skip pointing the start stacktrace
|
||||
// of the real error point.
|
||||
m.request.error = gerror.WrapSkip(1, exception, "")
|
||||
m.request.error = gerror.WrapCodeSkip(gerror.CodeInternalError, 1, exception, "")
|
||||
}
|
||||
m.request.Response.WriteStatus(http.StatusInternalServerError, exception)
|
||||
loop = false
|
||||
@ -145,3 +122,46 @@ func (m *middleware) Next() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *middleware) callHandlerFunc(funcInfo handlerFuncInfo) {
|
||||
niceCallFunc(func() {
|
||||
if funcInfo.Func != nil {
|
||||
funcInfo.Func(m.request)
|
||||
} else {
|
||||
var inputValues = []reflect.Value{
|
||||
reflect.ValueOf(m.request.Context()),
|
||||
}
|
||||
if funcInfo.Type.NumIn() == 2 {
|
||||
var (
|
||||
request reflect.Value
|
||||
)
|
||||
if funcInfo.Type.In(1).Kind() == reflect.Ptr {
|
||||
request = reflect.New(funcInfo.Type.In(1).Elem())
|
||||
m.request.handlerResponse.Error = m.request.Parse(request.Interface())
|
||||
} else {
|
||||
request = reflect.New(funcInfo.Type.In(1).Elem()).Elem()
|
||||
m.request.handlerResponse.Error = m.request.Parse(request.Addr().Interface())
|
||||
}
|
||||
if m.request.handlerResponse.Error != nil {
|
||||
return
|
||||
}
|
||||
inputValues = append(inputValues, request)
|
||||
}
|
||||
|
||||
// Call handler with dynamic created parameter values.
|
||||
results := funcInfo.Value.Call(inputValues)
|
||||
switch len(results) {
|
||||
case 1:
|
||||
m.request.handlerResponse.Error = results[0].Interface().(error)
|
||||
|
||||
case 2:
|
||||
m.request.handlerResponse.Object = results[0].Interface()
|
||||
if !results[1].IsNil() {
|
||||
if v := results[1].Interface(); v != nil {
|
||||
m.request.handlerResponse.Error = v.(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -11,6 +11,14 @@ import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
)
|
||||
|
||||
// RequestFromCtx retrieves and returns the Request object from context.
|
||||
func RequestFromCtx(ctx context.Context) *Request {
|
||||
if v := ctx.Value(ctxKeyForRequest); v != nil {
|
||||
return v.(*Request)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Context is alias for function GetCtx.
|
||||
// This function overwrites the http.Request.Context function.
|
||||
// See GetCtx.
|
||||
@ -18,6 +26,10 @@ func (r *Request) Context() context.Context {
|
||||
if r.context == nil {
|
||||
r.context = r.Request.Context()
|
||||
}
|
||||
// Inject Request object into context.
|
||||
if RequestFromCtx(r.context) == nil {
|
||||
r.context = context.WithValue(r.context, ctxKeyForRequest, r)
|
||||
}
|
||||
return r.context
|
||||
}
|
||||
|
||||
|
||||
@ -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,17 @@ 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.NewCode(
|
||||
gerror.CodeMissingParameter,
|
||||
"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.NewCode(gerror.CodeInvalidParameter, `parameter "dirPath" should be a directory path`)
|
||||
}
|
||||
|
||||
file, err := f.Open()
|
||||
@ -60,7 +65,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 +79,10 @@ 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.NewCode(
|
||||
gerror.CodeMissingParameter,
|
||||
"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 +122,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)
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ func (r *Response) RedirectBack(code ...int) {
|
||||
r.RedirectTo(r.Request.GetReferer(), code...)
|
||||
}
|
||||
|
||||
// BufferString returns the buffered content as []byte.
|
||||
// Buffer returns the buffered content as []byte.
|
||||
func (r *Response) Buffer() []byte {
|
||||
return r.buffer.Bytes()
|
||||
}
|
||||
@ -136,7 +136,7 @@ func (r *Response) ClearBuffer() {
|
||||
r.buffer.Reset()
|
||||
}
|
||||
|
||||
// Output outputs the buffer content to the client and clears the buffer.
|
||||
// Flush outputs the buffer content to the client and clears the buffer.
|
||||
func (r *Response) Flush() {
|
||||
if r.Server.config.ServerAgent != "" {
|
||||
r.Header().Set("Server", r.Server.config.ServerAgent)
|
||||
|
||||
@ -10,6 +10,7 @@ package ghttp
|
||||
import (
|
||||
"github.com/gogf/gf/os/gcfg"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gmode"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
@ -62,7 +63,7 @@ func (r *Response) ParseTpl(tpl string, params ...gview.Params) (string, error)
|
||||
return r.Request.GetView().Parse(r.Request.Context(), tpl, r.buildInVars(params...))
|
||||
}
|
||||
|
||||
// ParseDefault parses the default template file with params.
|
||||
// ParseTplDefault parses the default template file with params.
|
||||
func (r *Response) ParseTplDefault(params ...gview.Params) (string, error) {
|
||||
return r.Request.GetView().ParseDefault(r.Request.Context(), r.buildInVars(params...))
|
||||
}
|
||||
@ -81,17 +82,18 @@ func (r *Response) buildInVars(params ...map[string]interface{}) map[string]inte
|
||||
gutil.MapMerge(m, params[0])
|
||||
}
|
||||
// Retrieve custom template variables from request object.
|
||||
sessionMap := gconv.MapDeep(r.Request.Session.Map())
|
||||
gutil.MapMerge(m, map[string]interface{}{
|
||||
"Form": r.Request.GetFormMap(),
|
||||
"Query": r.Request.GetQueryMap(),
|
||||
"Request": r.Request.GetMap(),
|
||||
"Cookie": r.Request.Cookie.Map(),
|
||||
"Session": r.Request.Session.Map(),
|
||||
"Session": sessionMap,
|
||||
})
|
||||
// Note that it should assign no Config variable to template
|
||||
// if there's no configuration file.
|
||||
if c := gcfg.Instance(); c.Available() {
|
||||
m["Config"] = c.GetMap(".")
|
||||
m["Config"] = c.Map()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -124,7 +125,7 @@ func (s *Server) Start() error {
|
||||
|
||||
// Server can only be run once.
|
||||
if s.Status() == ServerStatusRunning {
|
||||
return gerror.New("server is already running")
|
||||
return gerror.NewCode(gerror.CodeInvalidOperation, "server is already running")
|
||||
}
|
||||
|
||||
// Logging path setting check.
|
||||
@ -140,7 +141,7 @@ func (s *Server) Start() error {
|
||||
path = gfile.Join(s.config.SessionPath, s.name)
|
||||
if !gfile.Exists(path) {
|
||||
if err := gfile.Mkdir(path); err != nil {
|
||||
return gerror.Wrapf(err, `mkdir failed for "%s"`, path)
|
||||
return gerror.WrapCodef(gerror.CodeInternalError, err, `mkdir failed for "%s"`, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,7 +175,10 @@ func (s *Server) Start() error {
|
||||
// If there's no route registered and no static service enabled,
|
||||
// it then returns an error of invalid usage of server.
|
||||
if len(s.routesMap) == 0 && !s.config.FileServerEnabled {
|
||||
return gerror.New(`there's no route set or static feature enabled, did you forget import the router?`)
|
||||
return gerror.NewCode(
|
||||
gerror.CodeInvalidOperation,
|
||||
`there's no route set or static feature enabled, did you forget import the router?`,
|
||||
)
|
||||
}
|
||||
|
||||
// Start the HTTP server.
|
||||
@ -195,7 +199,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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -220,7 +224,7 @@ func (s *Server) dumpRouterMap() {
|
||||
data[2] = item.Address
|
||||
data[3] = item.Method
|
||||
data[4] = item.Route
|
||||
data[5] = item.handler.itemName
|
||||
data[5] = item.handler.Name
|
||||
data[6] = item.Middleware
|
||||
table.Append(data)
|
||||
}
|
||||
@ -247,21 +251,21 @@ func (s *Server) GetRouterArray() []RouterItem {
|
||||
Server: s.name,
|
||||
Address: address,
|
||||
Domain: array[4],
|
||||
Type: registeredItem.handler.itemType,
|
||||
Type: registeredItem.Handler.Type,
|
||||
Middleware: array[1],
|
||||
Method: array[2],
|
||||
Route: array[3],
|
||||
Priority: len(registeredItems) - index - 1,
|
||||
handler: registeredItem.handler,
|
||||
handler: registeredItem.Handler,
|
||||
}
|
||||
switch item.handler.itemType {
|
||||
switch item.handler.Type {
|
||||
case handlerTypeController, handlerTypeObject, handlerTypeHandler:
|
||||
item.IsServiceHandler = true
|
||||
case handlerTypeMiddleware:
|
||||
item.Middleware = "GLOBAL MIDDLEWARE"
|
||||
}
|
||||
if len(item.handler.middleware) > 0 {
|
||||
for _, v := range item.handler.middleware {
|
||||
if len(item.handler.Middleware) > 0 {
|
||||
for _, v := range item.handler.Middleware {
|
||||
if item.Middleware != "" {
|
||||
item.Middleware += ","
|
||||
}
|
||||
@ -279,9 +283,9 @@ func (s *Server) GetRouterArray() []RouterItem {
|
||||
if r = strings.Compare(item1.Domain, item2.Domain); r == 0 {
|
||||
if r = strings.Compare(item1.Route, item2.Route); r == 0 {
|
||||
if r = strings.Compare(item1.Method, item2.Method); r == 0 {
|
||||
if item1.handler.itemType == handlerTypeMiddleware && item2.handler.itemType != handlerTypeMiddleware {
|
||||
if item1.handler.Type == handlerTypeMiddleware && item2.handler.Type != handlerTypeMiddleware {
|
||||
return -1
|
||||
} else if item1.handler.itemType == handlerTypeMiddleware && item2.handler.itemType == handlerTypeMiddleware {
|
||||
} else if item1.handler.Type == handlerTypeMiddleware && item2.handler.Type == handlerTypeMiddleware {
|
||||
return 1
|
||||
} else if r = strings.Compare(item1.Middleware, item2.Middleware); r == 0 {
|
||||
r = item2.Priority - item1.Priority
|
||||
@ -315,9 +319,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 +337,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.NewCode(gerror.CodeInvalidOperation, "graceful reload feature is disabled")
|
||||
}
|
||||
serverActionLocker.Lock()
|
||||
defer serverActionLocker.Unlock()
|
||||
@ -84,9 +85,10 @@ func checkProcessStatus() error {
|
||||
if status > 0 {
|
||||
switch status {
|
||||
case adminActionRestarting:
|
||||
return errors.New("server is restarting")
|
||||
return gerror.NewCode(gerror.CodeInvalidOperation, "server is restarting")
|
||||
|
||||
case adminActionShuttingDown:
|
||||
return errors.New("server is shutting down")
|
||||
return gerror.NewCode(gerror.CodeInvalidOperation, "server is shutting down")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -97,7 +99,11 @@ 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.NewCodef(
|
||||
gerror.CodeInvalidOperation,
|
||||
"too frequent action, please retry in %d ms",
|
||||
adminActionIntervalLimit-interval,
|
||||
)
|
||||
}
|
||||
serverActionLastTime.Set(gtime.TimestampMilli())
|
||||
return nil
|
||||
@ -173,7 +179,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 +272,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
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user