add GetVar function for package genv; add DryRun feature for package gdb

This commit is contained in:
John 2020-04-02 20:52:37 +08:00
parent be2d4b080e
commit 7bcc596308
11 changed files with 120 additions and 23 deletions

View File

@ -69,7 +69,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|1*1x|wechat|¥100.00|
|[ywanbing](https://github.com/ywanbing)|wechat|¥66.66|
|[侯哥](http://www.macnie.com)|wechat|¥10.00|
|如果🍋|alipay|¥100.00| 错的奶茶^_^
|如果🍋|alipay|¥100.00| 错的奶茶^_^
|蔡蔡|wechat|¥666.00| gf真强大让项目省心

View File

@ -89,6 +89,8 @@ type DB interface {
SetSchema(schema string)
GetSchema() string
GetPrefix() string
SetDryRun(dryrun bool)
GetDryRun() bool
SetLogger(logger *glog.Logger)
GetLogger() *glog.Logger
SetMaxIdleConnCount(n int)
@ -124,6 +126,7 @@ type Core struct {
debug *gtype.Bool // Enable debug mode for the database.
cache *gcache.Cache // Cache manager.
schema *gtype.String // Custom schema for this object.
dryrun *gtype.Bool // Dry run.
prefix string // Table prefix.
logger *glog.Logger // Logger.
maxIdleConnCount int // Max idle connection count.
@ -234,6 +237,7 @@ func New(name ...string) (db DB, err error) {
debug: gtype.NewBool(),
cache: gcache.New(),
schema: gtype.NewString(),
dryrun: gtype.NewBool(),
logger: glog.New(),
prefix: node.Prefix,
maxIdleConnCount: gDEFAULT_CONN_MAX_IDLE_COUNT,
@ -401,5 +405,8 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
if node.Debug {
c.DB.SetDebug(node.Debug)
}
if node.Debug {
c.DB.SetDryRun(node.DryRun)
}
return
}

View File

@ -99,7 +99,11 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re
sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args)
if c.DB.GetDebug() {
mTime1 := gtime.TimestampMilli()
result, err = link.Exec(sql, args...)
if !c.DB.GetDryRun() {
result, err = link.Exec(sql, args...)
} else {
result = new(SqlResult)
}
mTime2 := gtime.TimestampMilli()
s := &Sql{
Sql: sql,
@ -111,7 +115,11 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re
}
c.writeSqlToLogger(s)
} else {
result, err = link.Exec(sql, args...)
if !c.DB.GetDryRun() {
result, err = link.Exec(sql, args...)
} else {
result = new(SqlResult)
}
}
return result, formatError(err, sql, args...)
}

View File

@ -36,6 +36,7 @@ type ConfigNode struct {
Role string // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
Debug bool // (Optional) Debug mode enables debug information logging and output.
Prefix string // (Optional) Table prefix.
DryRun bool // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
Weight int // (Optional) Weight for load balance calculating, it's useless if there's just one node.
Charset string // (Optional, "utf8mb4" in default) Custom charset when operating on database.
LinkInfo string // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
@ -142,24 +143,19 @@ func (c *Core) SetMaxConnLifetime(d time.Duration) {
// String returns the node as string.
func (node *ConfigNode) String() string {
if node.LinkInfo != "" {
return node.LinkInfo
}
return fmt.Sprintf(
`%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d`,
`%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d#%s`,
node.User, node.Host, node.Port,
node.Name, node.Type, node.Role, node.Charset, node.Debug,
node.MaxIdleConnCount,
node.MaxOpenConnCount,
node.MaxConnLifetime,
node.LinkInfo,
)
}
// SetDebug enables/disables the debug mode.
func (c *Core) SetDebug(debug bool) {
if c.debug.Val() == debug {
return
}
c.debug.Set(debug)
}
@ -178,6 +174,16 @@ func (c *Core) GetPrefix() string {
return c.prefix
}
// SetDryRun enables/disables the DryRun feature.
func (c *Core) SetDryRun(dryrun bool) {
c.dryrun.Set(dryrun)
}
// GetDryRun returns the DryRun value.
func (c *Core) GetDryRun() bool {
return c.dryrun.Val()
}
// SetSchema changes the schema for this database connection object.
// Importantly note that when schema configuration changed for the database,
// it affects all operations on the database object in the future.

View File

@ -10,6 +10,7 @@ import (
"database/sql"
"fmt"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/text/gstr"
_ "github.com/go-sql-driver/mysql"
@ -33,6 +34,10 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
// Custom changing the schema in runtime.
if config.Name != "" {
source, _ = gregex.ReplaceString(`/([\w\.\-]+)+`, "/"+config.Name, source)
}
} else {
source = fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true&loc=Local",

View File

@ -38,10 +38,16 @@ func (r *SqlResult) RowsAffected() (int64, error) {
if r.affected > 0 {
return r.affected, nil
}
if r.result == nil {
return 0, nil
}
return r.result.RowsAffected()
}
// see sql.Result.LastInsertId
func (r *SqlResult) LastInsertId() (int64, error) {
if r.result == nil {
return 0, nil
}
return r.result.LastInsertId()
}

View File

@ -9,11 +9,33 @@ package gdb
import (
"database/sql"
"fmt"
"math"
"reflect"
"github.com/gogf/gf/encoding/gparser"
)
// Chunk splits an Result into multiple Results,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
func (r Result) Chunk(size int) []Result {
if size < 1 {
return nil
}
length := len(r)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n []Result
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, r[i*size:end])
i++
}
return n
}
// Json converts <r> to JSON format content.
func (r Result) Json() string {
content, _ := gparser.VarToJson(r.List())

View File

@ -2123,3 +2123,38 @@ func Test_Model_OmitEmpty_Time(t *testing.T) {
t.Assert(n, 1)
})
}
func Test_Result_Chunk(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
r, err := db.Table(table).Order("id asc").All()
t.Assert(err, nil)
chunks := r.Chunk(3)
t.Assert(len(chunks), 4)
t.Assert(chunks[0][0]["id"].Int(), 1)
t.Assert(chunks[1][0]["id"].Int(), 4)
t.Assert(chunks[2][0]["id"].Int(), 7)
t.Assert(chunks[3][0]["id"].Int(), 10)
})
}
func Test_Model_DryRun(t *testing.T) {
table := createInitTable()
defer dropTable(table)
db.SetDryRun(true)
defer db.SetDryRun(false)
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(one["id"], 1)
})
gtest.C(t, func(t *gtest.T) {
r, err := db.Table(table).Data("passport", "port_1").WherePri(1).Update()
t.Assert(err, nil)
n, err := r.RowsAffected()
t.Assert(err, nil)
t.Assert(n, 0)
})
}

View File

@ -189,15 +189,9 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
req.Header.Set(k, v)
}
}
// For server requests Host specifies the host on which the
// URL is sought. Per RFC 2616, this is either the value of
// the "Host" header or the host name given in the URL itself.
// It may be of the form "host:port".
//
// For client requests Host optionally overrides the Host
// header to send. If empty, the Request.Write method uses
// the value of URL.Host.
if host := req.Header.Get("Host"); host != "" {
// It's necessary set the req.Host if you want to custom the host value of the request.
// It uses the "Host" value of the header.
if host := req.Header.Get("Host"); host != "" && req.Host == "" {
req.Host = host
}
// Custom Cookie.

View File

@ -107,7 +107,7 @@ func (r *Response) WriteJson(content interface{}) error {
switch content.(type) {
case string, []byte:
r.Header().Set("Content-Type", "application/json")
r.Write(content)
r.Write(gconv.String(content))
return nil
}
// Else use json.Marshal function to encode the parameter.
@ -139,7 +139,7 @@ func (r *Response) WriteJsonP(content interface{}) error {
switch content.(type) {
case string, []byte:
r.Header().Set("Content-Type", "application/json")
r.Write(content)
r.Write(gconv.String(content))
return nil
}
// Else use json.Marshal function to encode the parameter.
@ -179,7 +179,7 @@ func (r *Response) WriteXml(content interface{}, rootTag ...string) error {
switch content.(type) {
case string, []byte:
r.Header().Set("Content-Type", "application/xml")
r.Write(content)
r.Write(gconv.String(content))
return nil
}
// Else use gparser.VarToXml function to encode the parameter.

View File

@ -7,7 +7,10 @@
// Package genv provides operations for environment variables of system.
package genv
import "os"
import (
"github.com/gogf/gf/container/gvar"
"os"
)
import "strings"
// All returns a copy of strings representing the environment,
@ -37,6 +40,17 @@ func Get(key string, def ...string) string {
return v
}
// GetVar creates and returns a Var with the value of the environment variable
// named by the <key>. It uses the given <def> if the variable does not exist
// in the environment.
func GetVar(key string, def ...interface{}) *gvar.Var {
v, ok := os.LookupEnv(key)
if !ok && len(def) > 0 {
return gvar.New(def[0])
}
return gvar.New(v)
}
// Set sets the value of the environment variable named by the <key>.
// It returns an error, if any.
func Set(key, value string) error {