fix issue 1915 and repeated link instance key for package gdb (#2250)

* fix issue #1915

* fix issue in repeated link instance key

* add configuration item Namespace for package gdb

* up

* up

* fix: pgsql list table names (#2255)

Co-authored-by: Gin <qinyuguang@gmail.com>
This commit is contained in:
John Guo
2022-11-03 20:22:36 +08:00
committed by GitHub
parent ab79134309
commit c4a5b8ca94
8 changed files with 97 additions and 62 deletions

View File

@ -179,15 +179,22 @@ type DB interface {
// Core is the base struct for database management.
type Core struct {
db DB // DB interface object.
ctx context.Context // Context for chaining operation only. Do not set a default value in Core initialization.
group string // Configuration group name.
schema string // Custom schema for this object.
debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime.
cache *gcache.Cache // Cache manager, SQL result cache only.
links *gmap.StrAnyMap // links caches all created links by node.
logger glog.ILogger // Logger for logging functionality.
config *ConfigNode // Current config node.
db DB // DB interface object.
ctx context.Context // Context for chaining operation only. Do not set a default value in Core initialization.
group string // Configuration group name.
schema string // Custom schema for this object.
debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime.
cache *gcache.Cache // Cache manager, SQL result cache only.
links *gmap.StrAnyMap // links caches all created links by node.
logger glog.ILogger // Logger for logging functionality.
config *ConfigNode // Current config node.
dynamicConfig dynamicConfig // Dynamic configurations, which can be changed in runtime.
}
type dynamicConfig struct {
MaxIdleConnCount int
MaxOpenConnCount int
MaxConnLifeTime time.Duration
}
// DoCommitInput is the input parameters for function DoCommit.
@ -237,6 +244,7 @@ type Sql struct {
Start int64 // Start execution timestamp in milliseconds.
End int64 // End execution timestamp in milliseconds.
Group string // Group is the group name of the configuration that the sql is executed from.
Schema string // Schema is the schema name of the configuration that the sql is executed from.
IsTransaction bool // IsTransaction marks whether this sql is executed in transaction.
RowsAffected int64 // RowsAffected marks retrieved or affected number with current sql statement.
}
@ -424,6 +432,10 @@ func NewByGroup(group ...string) (db DB, err error) {
}
// newDBByConfigNode creates and returns an ORM object with given configuration node and group name.
//
// Very Note:
// The parameter `node` is used for DB creation, not for underlying connection creation.
// So all db type configurations in the same group should be the same.
func newDBByConfigNode(node *ConfigNode, group string) (db DB, err error) {
if node.Link != "" {
node = parseConfigNodeLink(node)
@ -435,6 +447,11 @@ func newDBByConfigNode(node *ConfigNode, group string) (db DB, err error) {
links: gmap.NewStrAnyMap(true),
logger: glog.New(),
config: node,
dynamicConfig: dynamicConfig{
MaxIdleConnCount: node.MaxIdleConnCount,
MaxOpenConnCount: node.MaxOpenConnCount,
MaxConnLifeTime: node.MaxConnLifeTime,
},
}
if v, ok := driverMap[node.Type]; ok {
if c.db, err = v.New(c, node); err != nil {
@ -538,7 +555,9 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
for i := 0; i < len(cg); i++ {
max = min + cg[i].Weight*100
if random >= min && random < max {
// Return a copy of the ConfigNode.
// ====================================================
// Return a COPY of the ConfigNode.
// ====================================================
node := ConfigNode{}
node = cg[i]
return &node
@ -552,17 +571,23 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
// The parameter `master` specifies whether retrieves master node connection if
// master-slave nodes are configured.
func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) {
var node *ConfigNode
// Load balance.
var (
node *ConfigNode
ctx = c.db.GetCtx()
)
if c.group != "" {
// Load balance.
configs.RLock()
defer configs.RUnlock()
// Value COPY for node.
node, err = getConfigNodeByGroup(c.group, master)
if err != nil {
return nil, err
}
} else {
node = c.config
// Value COPY for node.
n := *c.db.GetConfig()
node = &n
}
if node.Charset == "" {
node.Charset = defaultCharset
@ -570,42 +595,42 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
// Changes the schema.
nodeSchema := gutil.GetOrDefaultStr(c.schema, schema...)
if nodeSchema != "" {
// Value copy.
n := *node
n.Name = nodeSchema
node = &n
node.Name = nodeSchema
}
// Update the configuration object in internal data.
internalData := c.GetInternalCtxDataFromCtx(ctx)
if internalData != nil {
internalData.ConfigNode = node
}
// Cache the underlying connection pool object by node.
instanceNameByNode := fmt.Sprintf(
`%s@%s(%s:%s)/%s`,
node.User, node.Protocol, node.Host, node.Port, node.Name,
)
v := c.links.GetOrSetFuncLock(instanceNameByNode, func() interface{} {
instanceNameByNode := fmt.Sprintf(`%+v`, node)
instanceValue := c.links.GetOrSetFuncLock(instanceNameByNode, func() interface{} {
if sqlDb, err = c.db.Open(node); err != nil {
return nil
}
if sqlDb == nil {
return nil
}
if c.config.MaxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(c.config.MaxIdleConnCount)
if c.dynamicConfig.MaxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(c.dynamicConfig.MaxIdleConnCount)
} else {
sqlDb.SetMaxIdleConns(defaultMaxIdleConnCount)
}
if c.config.MaxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(c.config.MaxOpenConnCount)
if c.dynamicConfig.MaxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(c.dynamicConfig.MaxOpenConnCount)
} else {
sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount)
}
if c.config.MaxConnLifeTime > 0 {
sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime)
if c.dynamicConfig.MaxConnLifeTime > 0 {
sqlDb.SetConnMaxLifetime(c.dynamicConfig.MaxConnLifeTime)
} else {
sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime)
}
return sqlDb
})
if v != nil && sqlDb == nil {
sqlDb = v.(*sql.DB)
if instanceValue != nil && sqlDb == nil {
// It reads from instance map.
sqlDb = instanceValue.(*sql.DB)
}
if node.Debug {
c.db.SetDebug(node.Debug)

View File

@ -665,15 +665,9 @@ func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) {
transactionIdStr = fmt.Sprintf(`[txid:%d] `, v.(uint64))
}
}
// Runtime schema.
schemaName := c.GetSchema()
if schemaName == "" {
// The original schema in configuration.
schemaName = c.config.Name
}
s := fmt.Sprintf(
"[%3d ms] [%s] [%s] [rows:%-3d] %s%s",
sql.End-sql.Start, sql.Group, schemaName, sql.RowsAffected, transactionIdStr, sql.Format,
sql.End-sql.Start, sql.Group, sql.Schema, sql.RowsAffected, transactionIdStr, sql.Format,
)
if sql.Error != nil {
s += "\nError: " + sql.Error.Error()

View File

@ -41,13 +41,14 @@ type ConfigNode struct {
Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database.
Protocol string `json:"protocol"` // (Optional, "tcp" in default) See net.Dial for more information which networks are available.
Timezone string `json:"timezone"` // (Optional) Sets the time zone for displaying and interpreting time stamps.
Namespace string `json:"namespace"` // Namespace for some databases. Eg, in pgsql, the `Name` acts as the `catalog`, the `NameSpace` acts as the `schema`.
MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool.
MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool.
MaxConnLifeTime time.Duration `json:"maxLifeTime"` // (Optional) Max amount of time a connection may be idle before being closed.
QueryTimeout time.Duration `json:"queryTimeout"` // (Optional) Max query time for per dql.
ExecTimeout time.Duration `json:"execTimeout"` // (Optional) Max exec time for dml.
TranTimeout time.Duration `json:"tranTimeout"` // (Optional) Max exec time time for a transaction.
PrepareTimeout time.Duration `json:"prepareTimeout"` // (Optional) Max exec time time for prepare operation.
TranTimeout time.Duration `json:"tranTimeout"` // (Optional) Max exec time for a transaction.
PrepareTimeout time.Duration `json:"prepareTimeout"` // (Optional) Max exec time for prepare operation.
CreatedAt string `json:"createdAt"` // (Optional) The filed name of table for automatic-filled created datetime.
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.
@ -181,7 +182,7 @@ func (c *Core) GetLogger() glog.ILogger {
// The default max idle connections is currently 2. This may change in
// a future release.
func (c *Core) SetMaxIdleConnCount(n int) {
c.config.MaxIdleConnCount = n
c.dynamicConfig.MaxIdleConnCount = n
}
// SetMaxOpenConnCount sets the maximum number of open connections to the database.
@ -193,7 +194,7 @@ func (c *Core) SetMaxIdleConnCount(n int) {
// If n <= 0, then there is no limit on the number of open connections.
// The default is 0 (unlimited).
func (c *Core) SetMaxOpenConnCount(n int) {
c.config.MaxOpenConnCount = n
c.dynamicConfig.MaxOpenConnCount = n
}
// SetMaxConnLifeTime sets the maximum amount of time a connection may be reused.
@ -202,11 +203,15 @@ func (c *Core) SetMaxOpenConnCount(n int) {
//
// If d <= 0, connections are not closed due to a connection's age.
func (c *Core) SetMaxConnLifeTime(d time.Duration) {
c.config.MaxConnLifeTime = d
c.dynamicConfig.MaxConnLifeTime = d
}
// GetConfig returns the current used node configuration.
func (c *Core) GetConfig() *ConfigNode {
internalData := c.GetInternalCtxDataFromCtx(c.db.GetCtx())
if internalData != nil && internalData.ConfigNode != nil {
return internalData.ConfigNode
}
return c.config
}
@ -247,7 +252,11 @@ func (c *Core) GetPrefix() string {
// GetSchema returns the schema configured.
func (c *Core) GetSchema() string {
return c.schema
schema := c.schema
if schema == "" {
schema = c.db.GetConfig().Name
}
return schema
}
func parseConfigNodeLink(node *ConfigNode) *ConfigNode {

View File

@ -17,6 +17,9 @@ type internalCtxData struct {
// Operation DB.
DB DB
// Used configuration node in current operation.
ConfigNode *ConfigNode
// The first column in result response from database server.
// This attribute is used for Value/Count selection statement purpose,
// which is to avoid HOOK handler that might modify the result columns

View File

@ -48,8 +48,8 @@ func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...inter
}
}
if c.GetConfig().QueryTimeout > 0 {
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
if c.db.GetConfig().QueryTimeout > 0 {
ctx, _ = context.WithTimeout(ctx, c.db.GetConfig().QueryTimeout)
}
// Sql filtering.
@ -107,9 +107,9 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf
}
}
if c.GetConfig().ExecTimeout > 0 {
if c.db.GetConfig().ExecTimeout > 0 {
var cancelFunc context.CancelFunc
ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout)
ctx, cancelFunc = context.WithTimeout(ctx, c.db.GetConfig().ExecTimeout)
defer cancelFunc()
}
@ -264,6 +264,7 @@ func (c *Core) DoCommit(ctx context.Context, in DoCommitInput) (out DoCommitOutp
Start: timestampMilli1,
End: timestampMilli2,
Group: c.db.GetGroup(),
Schema: c.db.GetSchema(),
RowsAffected: rowsAffected,
IsTransaction: in.IsTransaction,
}
@ -333,9 +334,9 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (stmt *Stmt
}
}
if c.GetConfig().PrepareTimeout > 0 {
if c.db.GetConfig().PrepareTimeout > 0 {
// DO NOT USE cancel function in prepare statement.
ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout)
ctx, _ = context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout)
}
// Link execution.

View File

@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/v2/internal/json"
)
// CacheOption is options for model cache control in query.
type CacheOption struct {
// Duration is the TTL for the cache.
// If the parameter `Duration` < 0, which means it clear the cache with given `Name`.