improving package gdb

This commit is contained in:
John
2020-03-08 00:17:42 +08:00
parent 6665d62e7e
commit 0e52d467d3
19 changed files with 594 additions and 388 deletions

View File

@ -0,0 +1,121 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 driver
import (
"database/sql"
"fmt"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gstr"
_ "github.com/gf-third/mysql"
)
type MyDriver struct {
*gdb.Core
}
// Open creates and returns a underlying sql.DB object for mysql.
func (d *MyDriver) Open(config *gdb.ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true&loc=Local",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset,
)
}
intlog.Printf("Open: %s", source)
if db, err := sql.Open("gf-mysql", source); err == nil {
return db, nil
} else {
return nil, err
}
}
// getChars returns the security char for this type of database.
func (d *MyDriver) GetChars() (charLeft string, charRight string) {
return "`", "`"
}
// handleSqlBeforeExec handles the sql before posts it to database.
func (d *MyDriver) HandleSqlBeforeExec(sql string) string {
return sql
}
// Tables retrieves and returns the tables of current schema.
func (d *MyDriver) Tables(schema ...string) (tables []string, err error) {
var result gdb.Result
link, err := d.DB.GetSlave(schema...)
if err != nil {
return nil, err
}
result, err = d.DB.DoGetAll(link, `SHOW TABLES`)
if err != nil {
return
}
for _, m := range result {
for _, v := range m {
tables = append(tables, v.String())
}
}
return
}
// gdb.TableFields retrieves and returns the fields information of specified table of current schema.
//
// Note that it returns a map containing the field name and its corresponding fields.
// As a map is unsorted, the gdb.TableField struct has a "Index" field marks its sequence in the fields.
//
// It's using cache feature to enhance the performance, which is never expired util the process restarts.
func (d *MyDriver) TableFields(table string, schema ...string) (fields map[string]*gdb.TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function gdb.TableFields supports only single table operations")
}
checkSchema := d.DB.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v := d.DB.GetCache().GetOrSetFunc(
fmt.Sprintf(`mysql_table_fields_%s_%s`, table, checkSchema),
func() interface{} {
var result gdb.Result
var link *sql.DB
link, err = d.DB.GetSlave(checkSchema)
if err != nil {
return nil
}
result, err = d.DB.DoGetAll(
link,
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.DB.QuoteWord(table)),
)
if err != nil {
return nil
}
fields = make(map[string]*gdb.TableField)
for i, m := range result {
fields[m["Field"].String()] = &gdb.TableField{
Index: i,
Name: m["Field"].String(),
Type: m["Type"].String(),
Null: m["Null"].Bool(),
Key: m["Key"].String(),
Default: m["Default"].Val(),
Extra: m["Extra"].String(),
Comment: m["Comment"].String(),
}
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]*gdb.TableField)
}
return
}

View File

@ -11,11 +11,11 @@ func main() {
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
r, e := db.Table("test").OrderBy("id asc").All()
r, e := db.Table("test").Order("id asc").All()
if e != nil {
panic(e)
fmt.Println(e)
}
if r != nil {
fmt.Println(r.ToList())
fmt.Println(r.List())
}
}

View File

@ -9,5 +9,4 @@ func main() {
db.SetDebug(true)
db.Table("user").Fields("DISTINCT id,nickname").Filter().All()
}

View File

@ -34,14 +34,14 @@ type DB interface {
Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error)
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
doGetAll(link dbLink, query string, args ...interface{}) (result Result, err error)
doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
doPrepare(link dbLink, query string) (*sql.Stmt, error)
doInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error)
doBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error)
doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error)
DoQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
DoGetAll(link dbLink, query string, args ...interface{}) (result Result, err error)
DoExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
DoPrepare(link dbLink, query string) (*sql.Stmt, error)
DoInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error)
DoBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error)
DoUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
DoDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error)
// Query APIs for convenience purpose.
GetAll(query string, args ...interface{}) (Result, error)
@ -52,11 +52,11 @@ type DB interface {
GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error
GetScan(objPointer interface{}, query string, args ...interface{}) error
// Master/Slave support.
// Master/Slave specification support.
Master() (*sql.DB, error)
Slave() (*sql.DB, error)
// Ping.
// Ping-Pong.
PingMaster() error
PingSlave() error
@ -75,48 +75,44 @@ type DB interface {
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
// Create model.
// Model creation.
From(tables string) *Model
Table(tables string) *Model
Schema(schema string) *Schema
// Configuration methods.
GetCache() *gcache.Cache
SetDebug(debug bool)
GetDebug() bool
SetSchema(schema string)
GetSchema() string
GetPrefix() string
SetLogger(logger *glog.Logger)
GetLogger() *glog.Logger
SetMaxIdleConnCount(n int)
SetMaxOpenConnCount(n int)
SetMaxConnLifetime(d time.Duration)
// Utility methods.
GetChars() (charLeft string, charRight string)
GetMaster(schema ...string) (*sql.DB, error)
GetSlave(schema ...string) (*sql.DB, error)
QuoteWord(s string) string
QuoteString(s string) string
HandleSqlBeforeExec(sql string) string
Tables(schema ...string) (tables []string, err error)
TableFields(table string, schema ...string) (map[string]*TableField, error)
// Internal methods.
getCache() *gcache.Cache
getChars() (charLeft string, charRight string)
getDebug() bool
getPrefix() string
getMaster(schema ...string) (*sql.DB, error)
getSlave(schema ...string) (*sql.DB, error)
quoteWord(s string) string
quoteString(s string) string
handleTableName(table string) string
filterFields(schema, table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue []byte, fieldType string) interface{}
rowsToResult(rows *sql.Rows) (Result, error)
handleSqlBeforeExec(sql string) string
}
// dbLink is a common database function wrapper interface for internal usage.
type dbLink interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string) (*sql.Stmt, error)
}
// dbBase is the base struct for database management.
type dbBase struct {
db DB // DB interface object.
// Core is the base struct for database management.
type Core struct {
DB DB // DB interface object.
group string // Configuration group name.
debug *gtype.Bool // Enable debug mode for the database.
cache *gcache.Cache // Cache manager.
@ -128,6 +124,11 @@ type dbBase struct {
maxConnLifetime time.Duration // Max TTL for a connection.
}
// Driver is the interface for integrating sql drivers into package gdb.
type Driver interface {
New(core *Core, node *ConfigNode) (DB, error)
}
// Sql is the sql recording struct.
type Sql struct {
Sql string // SQL string(may contain reserved char '?').
@ -150,6 +151,13 @@ type TableField struct {
Comment string // Comment.
}
// dbLink is a common database function wrapper interface for internal usage.
type dbLink interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string) (*sql.Stmt, error)
}
// Value is the field value type.
type Value = *gvar.Var
@ -176,10 +184,23 @@ const (
)
var (
// Instance map.
// instances is the management map for instances.
instances = gmap.NewStrAnyMap(true)
// driverMap manages all custom registered driver.
driverMap = map[string]Driver{
"mysql": &DriverMysql{},
"mssql": &DriverMssql{},
"oracle": &DriverOracle{},
"sqlite": &DriverSqlite{},
}
)
// Register registers custom database driver to gdb.
func Register(name string, driver Driver) error {
driverMap[name] = driver
return nil
}
// New creates and returns an ORM object with global configurations.
// The parameter <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
@ -196,31 +217,24 @@ func New(name ...string) (db DB, err error) {
}
if _, ok := configs.config[group]; ok {
if node, err := getConfigNodeByGroup(group, true); err == nil {
base := &dbBase{
group: group,
debug: gtype.NewBool(),
cache: gcache.New(),
schema: gtype.NewString(),
logger: glog.New(),
prefix: node.Prefix,
// Default max connection life time if user does not configure.
maxConnLifetime: gDEFAULT_CONN_MAX_LIFE_TIME,
c := &Core{
group: group,
debug: gtype.NewBool(),
cache: gcache.New(),
schema: gtype.NewString(),
logger: glog.New(),
prefix: node.Prefix,
maxConnLifetime: gDEFAULT_CONN_MAX_LIFE_TIME, // Default max connection life time if user does not configure.
}
switch node.Type {
case "mysql":
base.db = &dbMysql{dbBase: base}
case "pgsql":
base.db = &dbPgsql{dbBase: base}
case "mssql":
base.db = &dbMssql{dbBase: base}
case "sqlite":
base.db = &dbSqlite{dbBase: base}
case "oracle":
base.db = &dbOracle{dbBase: base}
default:
if v, ok := driverMap[node.Type]; ok {
c.DB, err = v.New(c, node)
if err != nil {
return nil, err
}
return c.DB, nil
} else {
return nil, errors.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
}
return base.db, nil
} else {
return nil, err
}
@ -321,9 +335,9 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
// getSqlDb retrieves and returns a underlying database connection object.
// The parameter <master> specifies whether retrieves master node connection if
// master-slave nodes are configured.
func (bs *dbBase) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) {
func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) {
// Load balance.
node, err := getConfigNodeByGroup(bs.group, master)
node, err := getConfigNodeByGroup(c.group, master)
if err != nil {
return nil, err
}
@ -332,7 +346,7 @@ func (bs *dbBase) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err er
node.Charset = "utf8"
}
// Changes the schema.
nodeSchema := bs.schema.Val()
nodeSchema := c.schema.Val()
if len(schema) > 0 && schema[0] != "" {
nodeSchema = schema[0]
}
@ -343,25 +357,25 @@ func (bs *dbBase) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err er
node = &n
}
// Cache the underlying connection object by node.
v := bs.cache.GetOrSetFuncLock(node.String(), func() interface{} {
sqlDb, err = bs.db.Open(node)
v := c.cache.GetOrSetFuncLock(node.String(), func() interface{} {
sqlDb, err = c.DB.Open(node)
if err != nil {
return nil
}
if bs.maxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(bs.maxIdleConnCount)
if c.maxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(c.maxIdleConnCount)
} else if node.MaxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(node.MaxIdleConnCount)
}
if bs.maxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(bs.maxOpenConnCount)
if c.maxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(c.maxOpenConnCount)
} else if node.MaxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(node.MaxOpenConnCount)
}
if bs.maxConnLifetime > 0 {
sqlDb.SetConnMaxLifetime(bs.maxConnLifetime * time.Second)
if c.maxConnLifetime > 0 {
sqlDb.SetConnMaxLifetime(c.maxConnLifetime * time.Second)
} else if node.MaxConnLifetime > 0 {
sqlDb.SetConnMaxLifetime(node.MaxConnLifetime * time.Second)
}
@ -371,40 +385,7 @@ func (bs *dbBase) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err er
sqlDb = v.(*sql.DB)
}
if node.Debug {
bs.db.SetDebug(node.Debug)
c.DB.SetDebug(node.Debug)
}
return
}
// 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.
func (bs *dbBase) SetSchema(schema string) {
bs.schema.Set(schema)
}
// Master creates and returns a connection from master node if master-slave configured.
// It returns the default connection if master-slave not configured.
func (bs *dbBase) Master() (*sql.DB, error) {
return bs.getSqlDb(true, bs.schema.Val())
}
// Slave creates and returns a connection from slave node if master-slave configured.
// It returns the default connection if master-slave not configured.
func (bs *dbBase) Slave() (*sql.DB, error) {
return bs.getSqlDb(false, bs.schema.Val())
}
// getMaster acts like function Master but with additional <schema> parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Master.
func (bs *dbBase) getMaster(schema ...string) (*sql.DB, error) {
return bs.getSqlDb(true, schema...)
}
// getSlave acts like function Slave but with additional <schema> parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Slave.
func (bs *dbBase) getSlave(schema ...string) (*sql.DB, error) {
return bs.getSqlDb(false, schema...)
}

View File

@ -16,7 +16,6 @@ import (
"strings"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/os/gcache"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/util/gconv"
@ -32,22 +31,34 @@ var (
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`)
)
// Master creates and returns a connection from master node if master-slave configured.
// It returns the default connection if master-slave not configured.
func (c *Core) Master() (*sql.DB, error) {
return c.getSqlDb(true, c.schema.Val())
}
// Slave creates and returns a connection from slave node if master-slave configured.
// It returns the default connection if master-slave not configured.
func (c *Core) Slave() (*sql.DB, error) {
return c.getSqlDb(false, c.schema.Val())
}
// Query commits one query SQL to underlying driver and returns the execution result.
// It is most commonly used for data querying.
func (bs *dbBase) Query(query string, args ...interface{}) (rows *sql.Rows, err error) {
link, err := bs.db.Slave()
func (c *Core) Query(query string, args ...interface{}) (rows *sql.Rows, err error) {
link, err := c.DB.Slave()
if err != nil {
return nil, err
}
return bs.db.doQuery(link, query, args...)
return c.DB.DoQuery(link, query, args...)
}
// doQuery commits the query string and its arguments to underlying driver
// through given link object and returns the execution result.
func (bs *dbBase) doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error) {
func (c *Core) DoQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error) {
query, args = formatQuery(query, args)
query = bs.db.handleSqlBeforeExec(query)
if bs.db.getDebug() {
query = c.DB.HandleSqlBeforeExec(query)
if c.DB.GetDebug() {
mTime1 := gtime.TimestampMilli()
rows, err = link.Query(query, args...)
mTime2 := gtime.TimestampMilli()
@ -59,7 +70,7 @@ func (bs *dbBase) doQuery(link dbLink, query string, args ...interface{}) (rows
Start: mTime1,
End: mTime2,
}
bs.printSql(s)
c.printSql(s)
} else {
rows, err = link.Query(query, args...)
}
@ -73,20 +84,20 @@ func (bs *dbBase) doQuery(link dbLink, query string, args ...interface{}) (rows
// Exec commits one query SQL to underlying driver and returns the execution result.
// It is most commonly used for data inserting and updating.
func (bs *dbBase) Exec(query string, args ...interface{}) (result sql.Result, err error) {
link, err := bs.db.Master()
func (c *Core) Exec(query string, args ...interface{}) (result sql.Result, err error) {
link, err := c.DB.Master()
if err != nil {
return nil, err
}
return bs.db.doExec(link, query, args...)
return c.DB.DoExec(link, query, args...)
}
// doExec commits the query string and its arguments to underlying driver
// through given link object and returns the execution result.
func (bs *dbBase) doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error) {
func (c *Core) DoExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error) {
query, args = formatQuery(query, args)
query = bs.db.handleSqlBeforeExec(query)
if bs.db.getDebug() {
query = c.DB.HandleSqlBeforeExec(query)
if c.DB.GetDebug() {
mTime1 := gtime.TimestampMilli()
result, err = link.Exec(query, args...)
mTime2 := gtime.TimestampMilli()
@ -98,7 +109,7 @@ func (bs *dbBase) doExec(link dbLink, query string, args ...interface{}) (result
Start: mTime1,
End: mTime2,
}
bs.printSql(s)
c.printSql(s)
} else {
result, err = link.Exec(query, args...)
}
@ -113,50 +124,50 @@ func (bs *dbBase) doExec(link dbLink, query string, args ...interface{}) (result
//
// The parameter <execOnMaster> specifies whether executing the sql on master node,
// or else it executes the sql on slave node if master-slave configured.
func (bs *dbBase) Prepare(query string, execOnMaster ...bool) (*sql.Stmt, error) {
func (c *Core) Prepare(query string, execOnMaster ...bool) (*sql.Stmt, error) {
err := (error)(nil)
link := (dbLink)(nil)
if len(execOnMaster) > 0 && execOnMaster[0] {
if link, err = bs.db.Master(); err != nil {
if link, err = c.DB.Master(); err != nil {
return nil, err
}
} else {
if link, err = bs.db.Slave(); err != nil {
if link, err = c.DB.Slave(); err != nil {
return nil, err
}
}
return bs.db.doPrepare(link, query)
return c.DB.DoPrepare(link, query)
}
// doPrepare calls prepare function on given link object and returns the statement object.
func (bs *dbBase) doPrepare(link dbLink, query string) (*sql.Stmt, error) {
func (c *Core) DoPrepare(link dbLink, query string) (*sql.Stmt, error) {
return link.Prepare(query)
}
// GetAll queries and returns data records from database.
func (bs *dbBase) GetAll(query string, args ...interface{}) (Result, error) {
return bs.db.doGetAll(nil, query, args...)
func (c *Core) GetAll(query string, args ...interface{}) (Result, error) {
return c.DB.DoGetAll(nil, query, args...)
}
// doGetAll queries and returns data records from database.
func (bs *dbBase) doGetAll(link dbLink, query string, args ...interface{}) (result Result, err error) {
func (c *Core) DoGetAll(link dbLink, query string, args ...interface{}) (result Result, err error) {
if link == nil {
link, err = bs.db.Slave()
link, err = c.DB.Slave()
if err != nil {
return nil, err
}
}
rows, err := bs.doQuery(link, query, args...)
rows, err := c.DB.DoQuery(link, query, args...)
if err != nil || rows == nil {
return nil, err
}
defer rows.Close()
return bs.db.rowsToResult(rows)
return c.DB.rowsToResult(rows)
}
// GetOne queries and returns one record from database.
func (bs *dbBase) GetOne(query string, args ...interface{}) (Record, error) {
list, err := bs.GetAll(query, args...)
func (c *Core) GetOne(query string, args ...interface{}) (Record, error) {
list, err := c.DB.GetAll(query, args...)
if err != nil {
return nil, err
}
@ -168,8 +179,8 @@ func (bs *dbBase) GetOne(query string, args ...interface{}) (Record, error) {
// GetStruct queries one record from database and converts it to given struct.
// The parameter <pointer> should be a pointer to struct.
func (bs *dbBase) GetStruct(pointer interface{}, query string, args ...interface{}) error {
one, err := bs.GetOne(query, args...)
func (c *Core) GetStruct(pointer interface{}, query string, args ...interface{}) error {
one, err := c.DB.GetOne(query, args...)
if err != nil {
return err
}
@ -181,8 +192,8 @@ func (bs *dbBase) GetStruct(pointer interface{}, query string, args ...interface
// GetStructs queries records from database and converts them to given struct.
// The parameter <pointer> should be type of struct slice: []struct/[]*struct.
func (bs *dbBase) GetStructs(pointer interface{}, query string, args ...interface{}) error {
all, err := bs.GetAll(query, args...)
func (c *Core) GetStructs(pointer interface{}, query string, args ...interface{}) error {
all, err := c.DB.GetAll(query, args...)
if err != nil {
return err
}
@ -198,7 +209,7 @@ func (bs *dbBase) GetStructs(pointer interface{}, query string, args ...interfac
// If parameter <pointer> is type of struct pointer, it calls GetStruct internally for
// the conversion. If parameter <pointer> is type of slice, it calls GetStructs internally
// for conversion.
func (bs *dbBase) GetScan(pointer interface{}, query string, args ...interface{}) error {
func (c *Core) GetScan(pointer interface{}, query string, args ...interface{}) error {
t := reflect.TypeOf(pointer)
k := t.Kind()
if k != reflect.Ptr {
@ -207,9 +218,9 @@ func (bs *dbBase) GetScan(pointer interface{}, query string, args ...interface{}
k = t.Elem().Kind()
switch k {
case reflect.Array, reflect.Slice:
return bs.db.GetStructs(pointer, query, args...)
return c.DB.GetStructs(pointer, query, args...)
case reflect.Struct:
return bs.db.GetStruct(pointer, query, args...)
return c.DB.GetStruct(pointer, query, args...)
}
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
}
@ -217,8 +228,8 @@ func (bs *dbBase) GetScan(pointer interface{}, query string, args ...interface{}
// GetValue queries and returns the field value from database.
// The sql should queries only one field from database, or else it returns only one
// field of the result.
func (bs *dbBase) GetValue(query string, args ...interface{}) (Value, error) {
one, err := bs.GetOne(query, args...)
func (c *Core) GetValue(query string, args ...interface{}) (Value, error) {
one, err := c.DB.GetOne(query, args...)
if err != nil {
return nil, err
}
@ -229,13 +240,13 @@ func (bs *dbBase) GetValue(query string, args ...interface{}) (Value, error) {
}
// GetCount queries and returns the count from database.
func (bs *dbBase) GetCount(query string, args ...interface{}) (int, error) {
func (c *Core) GetCount(query string, args ...interface{}) (int, error) {
// If the query fields do not contains function "COUNT",
// it replaces the query string and adds the "COUNT" function to the fields.
if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, query) {
query, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, query)
}
value, err := bs.GetValue(query, args...)
value, err := c.DB.GetValue(query, args...)
if err != nil {
return 0, err
}
@ -243,8 +254,8 @@ func (bs *dbBase) GetCount(query string, args ...interface{}) (int, error) {
}
// PingMaster pings the master node to check authentication or keeps the connection alive.
func (bs *dbBase) PingMaster() error {
if master, err := bs.db.Master(); err != nil {
func (c *Core) PingMaster() error {
if master, err := c.DB.Master(); err != nil {
return err
} else {
return master.Ping()
@ -252,8 +263,8 @@ func (bs *dbBase) PingMaster() error {
}
// PingSlave pings the slave node to check authentication or keeps the connection alive.
func (bs *dbBase) PingSlave() error {
if slave, err := bs.db.Slave(); err != nil {
func (c *Core) PingSlave() error {
if slave, err := c.DB.Slave(); err != nil {
return err
} else {
return slave.Ping()
@ -264,13 +275,13 @@ func (bs *dbBase) PingSlave() error {
// You should call Commit or Rollback functions of the transaction object
// if you no longer use the transaction. Commit or Rollback functions will also
// close the transaction automatically.
func (bs *dbBase) Begin() (*TX, error) {
if master, err := bs.db.Master(); err != nil {
func (c *Core) Begin() (*TX, error) {
if master, err := c.DB.Master(); err != nil {
return nil, err
} else {
if tx, err := master.Begin(); err == nil {
return &TX{
db: bs.db,
db: c.DB,
tx: tx,
master: master,
}, nil
@ -289,8 +300,8 @@ func (bs *dbBase) Begin() (*TX, error) {
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
//
// The parameter <batch> specifies the batch operation count when given data is slice.
func (bs *dbBase) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
return bs.db.doInsert(nil, table, data, gINSERT_OPTION_DEFAULT, batch...)
func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoInsert(nil, table, data, gINSERT_OPTION_DEFAULT, batch...)
}
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the table.
@ -302,8 +313,8 @@ func (bs *dbBase) Insert(table string, data interface{}, batch ...int) (sql.Resu
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
//
// The parameter <batch> specifies the batch operation count when given data is slice.
func (bs *dbBase) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) {
return bs.db.doInsert(nil, table, data, gINSERT_OPTION_IGNORE, batch...)
func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoInsert(nil, table, data, gINSERT_OPTION_IGNORE, batch...)
}
// Replace does "REPLACE INTO ..." statement for the table.
@ -318,8 +329,8 @@ func (bs *dbBase) InsertIgnore(table string, data interface{}, batch ...int) (sq
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// If given data is type of slice, it then does batch replacing, and the optional parameter
// <batch> specifies the batch operation count.
func (bs *dbBase) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
return bs.db.doInsert(nil, table, data, gINSERT_OPTION_REPLACE, batch...)
func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoInsert(nil, table, data, gINSERT_OPTION_REPLACE, batch...)
}
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the table.
@ -333,8 +344,8 @@ func (bs *dbBase) Replace(table string, data interface{}, batch ...int) (sql.Res
//
// If given data is type of slice, it then does batch saving, and the optional parameter
// <batch> specifies the batch operation count.
func (bs *dbBase) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
return bs.db.doInsert(nil, table, data, gINSERT_OPTION_SAVE, batch...)
func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoInsert(nil, table, data, gINSERT_OPTION_SAVE, batch...)
}
// doInsert inserts or updates data for given table.
@ -344,12 +355,12 @@ func (bs *dbBase) Save(table string, data interface{}, batch ...int) (sql.Result
// 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 (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
func (c *Core) DoInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
var fields []string
var values []string
var params []interface{}
var dataMap Map
table = bs.db.handleTableName(table)
table = c.DB.handleTableName(table)
rv := reflect.ValueOf(data)
kind := rv.Kind()
if kind == reflect.Ptr {
@ -358,7 +369,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
}
switch kind {
case reflect.Slice, reflect.Array:
return bs.db.doBatchInsert(link, table, data, option, batch...)
return c.DB.DoBatchInsert(link, table, data, option, batch...)
case reflect.Map, reflect.Struct:
dataMap = varToMapDeep(data)
default:
@ -367,7 +378,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
if len(dataMap) == 0 {
return nil, errors.New("data cannot be empty")
}
charL, charR := bs.db.getChars()
charL, charR := c.DB.GetChars()
for k, v := range dataMap {
fields = append(fields, charL+k+charR)
values = append(values, "?")
@ -388,11 +399,11 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr)
}
if link == nil {
if link, err = bs.db.Master(); err != nil {
if link, err = c.DB.Master(); err != nil {
return nil, err
}
}
return bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES(%s) %s",
return c.DB.DoExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES(%s) %s",
operation, table, strings.Join(fields, ","),
strings.Join(values, ","), updateStr),
params...)
@ -400,33 +411,33 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
// BatchInsert batch inserts data.
// The parameter <list> must be type of slice of map or struct.
func (bs *dbBase) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
return bs.db.doBatchInsert(nil, table, list, gINSERT_OPTION_DEFAULT, batch...)
func (c *Core) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_DEFAULT, batch...)
}
// BatchInsert batch inserts data with ignore option.
// The parameter <list> must be type of slice of map or struct.
func (bs *dbBase) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
return bs.db.doBatchInsert(nil, table, list, gINSERT_OPTION_IGNORE, batch...)
func (c *Core) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_IGNORE, batch...)
}
// BatchReplace batch replaces data.
// The parameter <list> must be type of slice of map or struct.
func (bs *dbBase) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
return bs.db.doBatchInsert(nil, table, list, gINSERT_OPTION_REPLACE, batch...)
func (c *Core) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_REPLACE, batch...)
}
// BatchSave batch replaces data.
// The parameter <list> must be type of slice of map or struct.
func (bs *dbBase) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
return bs.db.doBatchInsert(nil, table, list, gINSERT_OPTION_SAVE, batch...)
func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_SAVE, batch...)
}
// doBatchInsert batch inserts/replaces/saves data.
func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
func (c *Core) DoBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
var keys, values []string
var params []interface{}
table = bs.db.handleTableName(table)
table = c.DB.handleTableName(table)
listMap := (List)(nil)
switch v := list.(type) {
case Result:
@ -461,7 +472,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
return result, errors.New("data list cannot be empty")
}
if link == nil {
if link, err = bs.db.Master(); err != nil {
if link, err = c.DB.Master(); err != nil {
return
}
}
@ -473,7 +484,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
}
// Prepare the result pointer.
batchResult := new(batchSqlResult)
charL, charR := bs.db.getChars()
charL, charR := c.DB.GetChars()
keysStr := charL + strings.Join(keys, charR+","+charL) + charR
valueHolderStr := "(" + strings.Join(holders, ",") + ")"
@ -504,7 +515,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
}
values = append(values, valueHolderStr)
if len(values) == batchNum || (i == listMapLen-1 && len(values) > 0) {
r, err := bs.db.doExec(
r, err := c.DB.DoExec(
link,
fmt.Sprintf(
"%s INTO %s(%s) VALUES%s %s",
@ -546,18 +557,18 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
// "status IN (?)", g.Slice{1,2,3}
// "age IN(?,?)", 18, 50
// User{ Id : 1, UserName : "john"}
func (bs *dbBase) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := formatWhere(bs.db, condition, args, false)
func (c *Core) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := formatWhere(c.DB, condition, args, false)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
return bs.db.doUpdate(nil, table, data, newWhere, newArgs...)
return c.DB.DoUpdate(nil, table, data, newWhere, newArgs...)
}
// doUpdate does "UPDATE ... " statement for the table.
// Also see Update.
func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
table = bs.db.handleTableName(table)
func (c *Core) DoUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
table = c.DB.handleTableName(table)
updates := ""
rv := reflect.ValueOf(data)
kind := rv.Kind()
@ -570,7 +581,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
case reflect.Map, reflect.Struct:
var fields []string
for k, v := range varToMapDeep(data) {
fields = append(fields, bs.db.quoteWord(k)+"=?")
fields = append(fields, c.DB.QuoteWord(k)+"=?")
params = append(params, v)
}
updates = strings.Join(fields, ",")
@ -585,11 +596,11 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
}
// If no link passed, it then uses the master link.
if link == nil {
if link, err = bs.db.Master(); err != nil {
if link, err = c.DB.Master(); err != nil {
return nil, err
}
}
return bs.db.doExec(link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...)
return c.DB.DoExec(link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...)
}
// Delete does "DELETE FROM ... " statement for the table.
@ -603,38 +614,28 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
// "status IN (?)", g.Slice{1,2,3}
// "age IN(?,?)", 18, 50
// User{ Id : 1, UserName : "john"}
func (bs *dbBase) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
newWhere, newArgs := formatWhere(bs.db, condition, args, false)
func (c *Core) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
newWhere, newArgs := formatWhere(c.DB, condition, args, false)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
return bs.db.doDelete(nil, table, newWhere, newArgs...)
return c.DB.DoDelete(nil, table, newWhere, newArgs...)
}
// doDelete does "DELETE FROM ... " statement for the table.
// Also see Delete.
func (bs *dbBase) doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error) {
func (c *Core) DoDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error) {
if link == nil {
if link, err = bs.db.Master(); err != nil {
if link, err = c.DB.Master(); err != nil {
return nil, err
}
}
table = bs.db.handleTableName(table)
return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
}
// getCache returns the internal cache object.
func (bs *dbBase) getCache() *gcache.Cache {
return bs.cache
}
// getPrefix returns the table prefix string configured.
func (bs *dbBase) getPrefix() string {
return bs.prefix
table = c.DB.handleTableName(table)
return c.DB.DoExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
}
// rowsToResult converts underlying data record type sql.Rows to Result type.
func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
func (c *Core) rowsToResult(rows *sql.Rows) (Result, error) {
if !rows.Next() {
return nil, nil
}
@ -671,7 +672,7 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
// it should do a copy of it.
v := make([]byte, len(value))
copy(v, value)
row[columnNames[i]] = gvar.New(bs.db.convertValue(v, columnTypes[i]))
row[columnNames[i]] = gvar.New(c.DB.convertValue(v, columnTypes[i]))
}
}
records = append(records, row)
@ -687,34 +688,20 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
//
// Note that, this will automatically checks the table prefix whether already added, if true it does
// nothing to the table name, or else adds the prefix to the table name.
func (bs *dbBase) handleTableName(table string) string {
charLeft, charRight := bs.db.getChars()
prefix := bs.db.getPrefix()
func (c *Core) handleTableName(table string) string {
charLeft, charRight := c.DB.GetChars()
prefix := c.DB.GetPrefix()
return doHandleTableName(table, prefix, charLeft, charRight)
}
// quoteWord checks given string <s> a word, if true quotes it with security chars of the database
// and returns the quoted string; or else return <s> without any change.
func (bs *dbBase) quoteWord(s string) string {
charLeft, charRight := bs.db.getChars()
return doQuoteWord(s, charLeft, charRight)
}
// quoteString quotes string with quote chars. Strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
func (bs *dbBase) quoteString(s string) string {
charLeft, charRight := bs.db.getChars()
return doQuoteString(s, charLeft, charRight)
}
// printSql outputs the sql object to logger.
// It is enabled when configuration "debug" is true.
func (bs *dbBase) printSql(v *Sql) {
func (c *Core) printSql(v *Sql) {
s := fmt.Sprintf("[%d ms] %s", v.End-v.Start, v.Format)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
bs.logger.StackWithFilter(gPATH_FILTER_KEY).Error(s)
c.logger.StackWithFilter(gPATH_FILTER_KEY).Error(s)
} else {
bs.logger.StackWithFilter(gPATH_FILTER_KEY).Debug(s)
c.logger.StackWithFilter(gPATH_FILTER_KEY).Debug(s)
}
}

View File

@ -8,6 +8,7 @@ package gdb
import (
"fmt"
"github.com/gogf/gf/os/gcache"
"sync"
"time"
@ -114,29 +115,29 @@ func GetDefaultGroup() string {
}
// SetLogger sets the logger for orm.
func (bs *dbBase) SetLogger(logger *glog.Logger) {
bs.logger = logger
func (c *Core) SetLogger(logger *glog.Logger) {
c.logger = logger
}
// GetLogger returns the logger of the orm.
func (bs *dbBase) GetLogger() *glog.Logger {
return bs.logger
func (c *Core) GetLogger() *glog.Logger {
return c.logger
}
// SetMaxIdleConnCount sets the max idle connection count for underlying connection pool.
func (bs *dbBase) SetMaxIdleConnCount(n int) {
bs.maxIdleConnCount = n
func (c *Core) SetMaxIdleConnCount(n int) {
c.maxIdleConnCount = n
}
// SetMaxOpenConnCount sets the max open connection count for underlying connection pool.
func (bs *dbBase) SetMaxOpenConnCount(n int) {
bs.maxOpenConnCount = n
func (c *Core) SetMaxOpenConnCount(n int) {
c.maxOpenConnCount = n
}
// SetMaxConnLifetime sets the connection TTL for underlying connection pool.
// If parameter <d> <= 0, it means the connection never expires.
func (bs *dbBase) SetMaxConnLifetime(d time.Duration) {
bs.maxConnLifetime = d
func (c *Core) SetMaxConnLifetime(d time.Duration) {
c.maxConnLifetime = d
}
// String returns the node as string.
@ -155,14 +156,36 @@ func (node *ConfigNode) String() string {
}
// SetDebug enables/disables the debug mode.
func (bs *dbBase) SetDebug(debug bool) {
if bs.debug.Val() == debug {
func (c *Core) SetDebug(debug bool) {
if c.debug.Val() == debug {
return
}
bs.debug.Set(debug)
c.debug.Set(debug)
}
// getDebug returns the debug value.
func (bs *dbBase) getDebug() bool {
return bs.debug.Val()
// GetDebug returns the debug value.
func (c *Core) GetDebug() bool {
return c.debug.Val()
}
// GetCache returns the internal cache object.
func (c *Core) GetCache() *gcache.Cache {
return c.cache
}
// GetPrefix returns the table prefix string configured.
func (c *Core) GetPrefix() string {
return c.prefix
}
// 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.
func (c *Core) SetSchema(schema string) {
c.schema.Set(schema)
}
// GetSchema returns the schema configured.
func (c *Core) GetSchema() string {
return c.schema.Val()
}

View File

@ -0,0 +1,50 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). 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 "database/sql"
// GetMaster acts like function Master but with additional <schema> parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Master.
func (c *Core) GetMaster(schema ...string) (*sql.DB, error) {
return c.getSqlDb(true, schema...)
}
// GetSlave acts like function Slave but with additional <schema> parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Slave.
func (c *Core) GetSlave(schema ...string) (*sql.DB, error) {
return c.getSqlDb(false, schema...)
}
// QuoteWord checks given string <s> a word, if true quotes it with security chars of the database
// and returns the quoted string; or else return <s> without any change.
func (c *Core) QuoteWord(s string) string {
charLeft, charRight := c.DB.GetChars()
return doQuoteWord(s, charLeft, charRight)
}
// QuoteString quotes string with quote chars. Strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
func (c *Core) QuoteString(s string) string {
charLeft, charRight := c.DB.GetChars()
return doQuoteString(s, charLeft, charRight)
}
// GetChars returns the security char for current database.
// It does nothing in default.
func (c *Core) GetChars() (charLeft string, charRight string) {
return "", ""
}
// HandleSqlBeforeExec handles the sql before posts it to database.
// It does nothing in default.
func (c *Core) HandleSqlBeforeExec(sql string) string {
return sql
}

View File

@ -5,7 +5,7 @@
// You can obtain one at https://github.com/gogf/gf.
//
// Note:
// 1. It needs manually import: _ "github.com/lib/pq"
// 1. It needs manually import: _ "github.com/denisenkom/go-mssqldb"
// 2. It does not support Save/Replace features.
// 3. It does not support LastInsertId.
@ -22,12 +22,20 @@ import (
"github.com/gogf/gf/text/gregex"
)
type dbMssql struct {
*dbBase
// DriverMssql is the driver for SQL server database.
type DriverMssql struct {
*Core
}
// New creates and returns a database object for SQL server.
func (d *DriverMssql) New(core *Core, node *ConfigNode) (DB, error) {
return &DriverMssql{
Core: core,
}, nil
}
// Open creates and returns a underlying sql.DB object for mssql.
func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
func (d *DriverMssql) Open(config *ConfigNode) (*sql.DB, error) {
source := ""
if config.LinkInfo != "" {
source = config.LinkInfo
@ -45,13 +53,13 @@ func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
}
}
// getChars returns the security char for this type of database.
func (db *dbMssql) getChars() (charLeft string, charRight string) {
// GetChars returns the security char for this type of database.
func (d *DriverMssql) GetChars() (charLeft string, charRight string) {
return "\"", "\""
}
// handleSqlBeforeExec deals with the sql string before commits it to underlying sql driver.
func (db *dbMssql) handleSqlBeforeExec(query string) string {
// HandleSqlBeforeExec deals with the sql string before commits it to underlying sql driver.
func (d *DriverMssql) HandleSqlBeforeExec(query string) string {
var index int
// Convert place holder char '?' to string "@px".
str, _ := gregex.ReplaceStringFunc("\\?", query, func(s string) string {
@ -59,10 +67,10 @@ func (db *dbMssql) handleSqlBeforeExec(query string) string {
return fmt.Sprintf("@p%d", index)
})
str, _ = gregex.ReplaceString("\"", "", str)
return db.parseSql(str)
return d.parseSql(str)
}
func (db *dbMssql) parseSql(sql string) string {
func (d *DriverMssql) parseSql(sql string) string {
// SELECT * FROM USER WHERE ID=1 LIMIT 1
if m, _ := gregex.MatchString(`^SELECT(.+)LIMIT 1$`, sql); len(m) > 1 {
return fmt.Sprintf(`SELECT TOP 1 %s`, m[1])
@ -163,14 +171,14 @@ func (db *dbMssql) parseSql(sql string) string {
}
// Tables retrieves and returns the tables of current schema.
func (db *dbMssql) Tables(schema ...string) (tables []string, err error) {
func (d *DriverMssql) Tables(schema ...string) (tables []string, err error) {
var result Result
link, err := db.getSlave(schema...)
link, err := d.DB.GetSlave(schema...)
if err != nil {
return nil, err
}
result, err = db.doGetAll(link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`)
result, err = d.DB.DoGetAll(link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`)
if err != nil {
return
}
@ -183,24 +191,24 @@ func (db *dbMssql) Tables(schema ...string) (tables []string, err error) {
}
// TableFields retrieves and returns the fields information of specified table of current schema.
func (db *dbMssql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
checkSchema := db.schema.Val()
checkSchema := d.DB.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v := db.cache.GetOrSetFunc(
v := d.DB.GetCache().GetOrSetFunc(
fmt.Sprintf(`mssql_table_fields_%s_%s`, table, checkSchema), func() interface{} {
var result Result
var link *sql.DB
link, err = db.getSlave(checkSchema)
link, err = d.DB.GetSlave(checkSchema)
if err != nil {
return nil
}
result, err = db.doGetAll(link, fmt.Sprintf(`
result, err = d.DB.DoGetAll(link, fmt.Sprintf(`
SELECT c.name as FIELD, CASE t.name
WHEN 'numeric' THEN t.name + '(' + convert(varchar(20),c.xprec) + ',' + convert(varchar(20),c.xscale) + ')'
WHEN 'char' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'

View File

@ -12,15 +12,23 @@ import (
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gstr"
_ "github.com/gf-third/mysql"
_ "github.com/go-sql-driver/mysql"
)
type dbMysql struct {
*dbBase
// DriverMysql is the driver for mysql database.
type DriverMysql struct {
*Core
}
// New creates and returns a database object for mysql.
func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) {
return &DriverMysql{
Core: core,
}, nil
}
// Open creates and returns a underlying sql.DB object for mysql.
func (db *dbMysql) Open(config *ConfigNode) (*sql.DB, error) {
func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
@ -31,31 +39,31 @@ func (db *dbMysql) Open(config *ConfigNode) (*sql.DB, error) {
)
}
intlog.Printf("Open: %s", source)
if db, err := sql.Open("gf-mysql", source); err == nil {
if db, err := sql.Open("mysql", source); err == nil {
return db, nil
} else {
return nil, err
}
}
// getChars returns the security char for this type of database.
func (db *dbMysql) getChars() (charLeft string, charRight string) {
// GetChars returns the security char for this type of database.
func (d *DriverMysql) GetChars() (charLeft string, charRight string) {
return "`", "`"
}
// handleSqlBeforeExec handles the sql before posts it to database.
func (db *dbMysql) handleSqlBeforeExec(sql string) string {
// HandleSqlBeforeExec handles the sql before posts it to database.
func (d *DriverMysql) HandleSqlBeforeExec(sql string) string {
return sql
}
// Tables retrieves and returns the tables of current schema.
func (bs *dbBase) Tables(schema ...string) (tables []string, err error) {
func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) {
var result Result
link, err := bs.db.getSlave(schema...)
link, err := d.DB.GetSlave(schema...)
if err != nil {
return nil, err
}
result, err = bs.db.doGetAll(link, `SHOW TABLES`)
result, err = d.DB.DoGetAll(link, `SHOW TABLES`)
if err != nil {
return
}
@ -73,27 +81,27 @@ func (bs *dbBase) Tables(schema ...string) (tables []string, err error) {
// As a map is unsorted, the TableField struct has a "Index" field marks its sequence in the fields.
//
// It's using cache feature to enhance the performance, which is never expired util the process restarts.
func (bs *dbBase) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
checkSchema := bs.schema.Val()
checkSchema := d.schema.Val()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v := bs.cache.GetOrSetFunc(
v := d.cache.GetOrSetFunc(
fmt.Sprintf(`mysql_table_fields_%s_%s`, table, checkSchema),
func() interface{} {
var result Result
var link *sql.DB
link, err = bs.db.getSlave(checkSchema)
link, err = d.DB.GetSlave(checkSchema)
if err != nil {
return nil
}
result, err = bs.doGetAll(
result, err = d.DB.DoGetAll(
link,
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, bs.db.quoteWord(table)),
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.DB.QuoteWord(table)),
)
if err != nil {
return nil

View File

@ -24,8 +24,9 @@ import (
"github.com/gogf/gf/text/gregex"
)
type dbOracle struct {
*dbBase
// DriverOracle is the driver for oracle database.
type DriverOracle struct {
*Core
}
const (
@ -33,8 +34,15 @@ const (
tableAlias2 = "GFORM2"
)
// New creates and returns a database object for oracle.
func (d *DriverOracle) New(core *Core, node *ConfigNode) (DB, error) {
return &DriverOracle{
Core: core,
}, nil
}
// Open creates and returns a underlying sql.DB object for oracle.
func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
@ -49,13 +57,13 @@ func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
}
}
// getChars returns the security char for this type of database.
func (db *dbOracle) getChars() (charLeft string, charRight string) {
// GetChars returns the security char for this type of database.
func (d *DriverOracle) GetChars() (charLeft string, charRight string) {
return "\"", "\""
}
// handleSqlBeforeExec deals with the sql string before commits it to underlying sql driver.
func (db *dbOracle) handleSqlBeforeExec(query string) string {
// HandleSqlBeforeExec deals with the sql string before commits it to underlying sql driver.
func (d *DriverOracle) HandleSqlBeforeExec(query string) string {
var index int
// Convert place holder char '?' to string ":x".
str, _ := gregex.ReplaceStringFunc("\\?", query, func(s string) string {
@ -63,10 +71,10 @@ func (db *dbOracle) handleSqlBeforeExec(query string) string {
return fmt.Sprintf(":%d", index)
})
str, _ = gregex.ReplaceString("\"", "", str)
return db.parseSql(str)
return d.parseSql(str)
}
func (db *dbOracle) parseSql(sql string) string {
func (d *DriverOracle) parseSql(sql string) string {
patten := `^\s*(?i)(SELECT)|(LIMIT\s*(\d+)\s*,\s*(\d+))`
if gregex.IsMatchString(patten, sql) == false {
return sql
@ -124,9 +132,9 @@ func (db *dbOracle) parseSql(sql string) string {
// Tables retrieves and returns the tables of current schema.
// Note that it ignores the parameter <schema> in oracle database, as it is not necessary.
func (db *dbOracle) Tables(schema ...string) (tables []string, err error) {
func (d *DriverOracle) Tables(schema ...string) (tables []string, err error) {
var result Result
result, err = db.doGetAll(nil, "SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME")
result, err = d.DB.DoGetAll(nil, "SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME")
if err != nil {
return
}
@ -139,20 +147,20 @@ func (db *dbOracle) Tables(schema ...string) (tables []string, err error) {
}
// TableFields retrieves and returns the fields information of specified table of current schema.
func (db *dbOracle) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
func (d *DriverOracle) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
checkSchema := db.schema.Val()
checkSchema := d.DB.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v := db.cache.GetOrSetFunc(
v := d.DB.GetCache().GetOrSetFunc(
fmt.Sprintf(`oracle_table_fields_%s_%s`, table, checkSchema),
func() interface{} {
result := (Result)(nil)
result, err = db.GetAll(fmt.Sprintf(`
result, err = d.DB.GetAll(fmt.Sprintf(`
SELECT COLUMN_NAME AS FIELD, CASE DATA_TYPE
WHEN 'NUMBER' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
WHEN 'FLOAT' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
@ -177,11 +185,11 @@ func (db *dbOracle) TableFields(table string, schema ...string) (fields map[stri
return
}
func (db *dbOracle) getTableUniqueIndex(table string) (fields map[string]map[string]string, err error) {
func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[string]string, err error) {
table = strings.ToUpper(table)
v := db.cache.GetOrSetFunc("table_unique_index_"+table, func() interface{} {
v := d.DB.GetCache().GetOrSetFunc("table_unique_index_"+table, func() interface{} {
res := (Result)(nil)
res, err = db.GetAll(fmt.Sprintf(`
res, err = d.DB.GetAll(fmt.Sprintf(`
SELECT INDEX_NAME,COLUMN_NAME,CHAR_LENGTH FROM USER_IND_COLUMNS
WHERE TABLE_NAME = '%s'
AND INDEX_NAME IN(SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME='%s' AND UNIQUENESS='UNIQUE')
@ -203,7 +211,7 @@ func (db *dbOracle) getTableUniqueIndex(table string) (fields map[string]map[str
return
}
func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
func (d *DriverOracle) DoInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
var fields []string
var values []string
var params []interface{}
@ -218,7 +226,7 @@ func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option
case reflect.Slice:
fallthrough
case reflect.Array:
return db.db.doBatchInsert(link, table, data, option, batch...)
return d.DB.DoBatchInsert(link, table, data, option, batch...)
case reflect.Map:
fallthrough
case reflect.Struct:
@ -231,7 +239,7 @@ func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option
indexMap := make(map[string]string)
indexExists := false
if option != gINSERT_OPTION_DEFAULT {
index, err := db.getTableUniqueIndex(table)
index, err := d.getTableUniqueIndex(table)
if err != nil {
return nil, err
}
@ -253,7 +261,7 @@ func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option
onStr := make([]string, 0)
updateStr := make([]string, 0)
charL, charR := db.db.getChars()
charL, charR := d.DB.GetChars()
for k, v := range dataMap {
k = strings.ToUpper(k)
@ -279,7 +287,7 @@ func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option
}
if link == nil {
if link, err = db.db.Master(); err != nil {
if link, err = d.DB.Master(); err != nil {
return nil, err
}
}
@ -294,9 +302,9 @@ func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option
table, tableAlias1, strings.Join(subSqlStr, ","), tableAlias2,
strings.Join(onStr, "AND"), strings.Join(updateStr, ","), strings.Join(fields, ","), strings.Join(values, ","),
)
return db.db.doExec(link, tmp, params...)
return d.DB.DoExec(link, tmp, params...)
case gINSERT_OPTION_IGNORE:
return db.db.doExec(link,
return d.DB.DoExec(link,
fmt.Sprintf(
"INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(%s(%s)) */ INTO %s(%s) VALUES(%s)",
table, strings.Join(indexs, ","), table, strings.Join(fields, ","), strings.Join(values, ","),
@ -305,7 +313,7 @@ func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option
}
}
return db.db.doExec(
return d.DB.DoExec(
link,
fmt.Sprintf(
"INSERT INTO %s(%s) VALUES(%s)",
@ -314,7 +322,7 @@ func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option
params...)
}
func (db *dbOracle) doBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
func (d *DriverOracle) DoBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
var keys []string
var values []string
var params []interface{}
@ -357,7 +365,7 @@ func (db *dbOracle) doBatchInsert(link dbLink, table string, list interface{}, o
return result, errors.New("empty data list")
}
if link == nil {
if link, err = db.db.Master(); err != nil {
if link, err = d.DB.Master(); err != nil {
return
}
}
@ -368,14 +376,14 @@ func (db *dbOracle) doBatchInsert(link dbLink, table string, list interface{}, o
holders = append(holders, "?")
}
batchResult := new(batchSqlResult)
charL, charR := db.db.getChars()
charL, charR := d.DB.GetChars()
keyStr := charL + strings.Join(keys, charL+","+charR) + charR
valueHolderStr := strings.Join(holders, ",")
// 当操作类型非insert时调用单笔的insert功能
if option != gINSERT_OPTION_DEFAULT {
for _, v := range listMap {
r, err := db.doInsert(link, table, v, option, 1)
r, err := d.DB.DoInsert(link, table, v, option, 1)
if err != nil {
return r, err
}
@ -402,10 +410,9 @@ func (db *dbOracle) doBatchInsert(link dbLink, table string, list interface{}, o
params = append(params, listMap[i][k])
}
values = append(values, valueHolderStr)
intoStr = append(intoStr, fmt.Sprintf(" INTO %s(%s) VALUES(%s) ", table, keyStr, valueHolderStr))
if len(intoStr) == batchNum {
r, err := db.db.doExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
r, err := d.DB.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
if err != nil {
return r, err
}
@ -421,7 +428,7 @@ func (db *dbOracle) doBatchInsert(link dbLink, table string, list interface{}, o
}
// 处理最后不构成指定批量的数据
if len(intoStr) > 0 {
r, err := db.db.doExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
r, err := d.DB.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
if err != nil {
return r, err
}

View File

@ -21,12 +21,20 @@ import (
"github.com/gogf/gf/text/gregex"
)
type dbPgsql struct {
*dbBase
// DriverPgsql is the driver for postgresql database.
type DriverPgsql struct {
*Core
}
// New creates and returns a database object for postgresql.
func (d *DriverPgsql) New(core *Core, node *ConfigNode) (DB, error) {
return &DriverPgsql{
Core: core,
}, nil
}
// Open creates and returns a underlying sql.DB object for pgsql.
func (db *dbPgsql) Open(config *ConfigNode) (*sql.DB, error) {
func (d *DriverPgsql) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
@ -44,13 +52,13 @@ func (db *dbPgsql) Open(config *ConfigNode) (*sql.DB, error) {
}
}
// getChars returns the security char for this type of database.
func (db *dbPgsql) getChars() (charLeft string, charRight string) {
// GetChars returns the security char for this type of database.
func (d *DriverPgsql) GetChars() (charLeft string, charRight string) {
return "\"", "\""
}
// handleSqlBeforeExec deals with the sql string before commits it to underlying sql driver.
func (db *dbPgsql) handleSqlBeforeExec(sql string) string {
// HandleSqlBeforeExec deals with the sql string before commits it to underlying sql driver.
func (d *DriverPgsql) HandleSqlBeforeExec(sql string) string {
var index int
// Convert place holder char '?' to string "$x".
sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string {
@ -62,9 +70,9 @@ func (db *dbPgsql) handleSqlBeforeExec(sql string) string {
}
// Tables retrieves and returns the tables of current schema.
func (db *dbPgsql) Tables(schema ...string) (tables []string, err error) {
func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) {
var result Result
link, err := db.getSlave(schema...)
link, err := d.DB.GetSlave(schema...)
if err != nil {
return nil, err
}
@ -73,7 +81,7 @@ func (db *dbPgsql) Tables(schema ...string) (tables []string, err error) {
if len(schema) > 0 && schema[0] != "" {
query = fmt.Sprintf("SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = '%s' ORDER BY TABLENAME", schema[0])
}
result, err = db.doGetAll(link, query)
result, err = d.DB.DoGetAll(link, query)
if err != nil {
return
}
@ -86,25 +94,25 @@ func (db *dbPgsql) Tables(schema ...string) (tables []string, err error) {
}
// TableFields retrieves and returns the fields information of specified table of current schema.
func (db *dbPgsql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
}
table, _ = gregex.ReplaceString("\"", "", table)
checkSchema := db.schema.Val()
checkSchema := d.DB.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v := db.cache.GetOrSetFunc(
v := d.DB.GetCache().GetOrSetFunc(
fmt.Sprintf(`pgsql_table_fields_%s_%s`, table, checkSchema), func() interface{} {
var result Result
var link *sql.DB
link, err = db.getSlave(checkSchema)
link, err = d.DB.GetSlave(checkSchema)
if err != nil {
return nil
}
result, err = db.doGetAll(link, fmt.Sprintf(`
result, err = d.DB.DoGetAll(link, 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

View File

@ -16,12 +16,20 @@ import (
"github.com/gogf/gf/text/gstr"
)
type dbSqlite struct {
*dbBase
// DriverSqlite is the driver for sqlite database.
type DriverSqlite struct {
*Core
}
// New creates and returns a database object for sqlite.
func (d *DriverSqlite) New(core *Core, node *ConfigNode) (DB, error) {
return &DriverSqlite{
Core: core,
}, nil
}
// Open creates and returns a underlying sql.DB object for sqlite.
func (db *dbSqlite) Open(config *ConfigNode) (*sql.DB, error) {
func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
@ -36,20 +44,20 @@ func (db *dbSqlite) Open(config *ConfigNode) (*sql.DB, error) {
}
}
// getChars returns the security char for this type of database.
func (db *dbSqlite) getChars() (charLeft string, charRight string) {
// GetChars returns the security char for this type of database.
func (d *DriverSqlite) GetChars() (charLeft string, charRight string) {
return "`", "`"
}
// Tables retrieves and returns the tables of current schema.
// TODO
func (db *dbSqlite) Tables(schema ...string) (tables []string, err error) {
func (d *DriverSqlite) Tables(schema ...string) (tables []string, err error) {
return
}
// TableFields retrieves and returns the fields information of specified table of current schema.
// TODO
func (db *dbSqlite) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
@ -57,9 +65,9 @@ func (db *dbSqlite) TableFields(table string, schema ...string) (fields map[stri
return
}
// handleSqlBeforeExec deals with the sql string before commits it to underlying sql driver.
// HandleSqlBeforeExec 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 (db *dbSqlite) handleSqlBeforeExec(sql string) string {
func (d *DriverSqlite) HandleSqlBeforeExec(sql string) string {
return sql
}

View File

@ -269,7 +269,7 @@ func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, new
// formatWhereKeyValue handles each key-value pair of the parameter map.
func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key string, value interface{}) []interface{} {
key = db.quoteWord(key)
key = db.QuoteWord(key)
if buffer.Len() > 0 {
buffer.WriteString(" AND ")
}

View File

@ -68,10 +68,10 @@ const (
// Table creates and returns a new ORM model from given schema.
// The parameter <tables> can be more than one table names, like :
// "user", "user u", "user, user_detail", "user u, user_detail ud"
func (bs *dbBase) Table(table string) *Model {
table = bs.db.handleTableName(table)
func (c *Core) Table(table string) *Model {
table = c.DB.handleTableName(table)
return &Model{
db: bs.db,
db: c.DB,
tablesInit: table,
tables: table,
fields: "*",
@ -82,21 +82,21 @@ func (bs *dbBase) Table(table string) *Model {
}
}
// Model is alias of dbBase.Table.
// See dbBase.Table.
func (bs *dbBase) Model(table string) *Model {
return bs.db.Table(table)
// Model is alias of Core.Table.
// See Core.Table.
func (c *Core) Model(table string) *Model {
return c.DB.Table(table)
}
// From is alias of dbBase.Table.
// See dbBase.Table.
// From is alias of Core.Table.
// See Core.Table.
// Deprecated.
func (bs *dbBase) From(table string) *Model {
return bs.db.Table(table)
func (c *Core) From(table string) *Model {
return c.DB.Table(table)
}
// Table acts like dbBase.Table except it operates on transaction.
// See dbBase.Table.
// Table acts like Core.Table except it operates on transaction.
// See Core.Table.
func (tx *TX) Table(table string) *Model {
table = tx.db.handleTableName(table)
return &Model{
@ -403,7 +403,7 @@ func (m *Model) Or(where interface{}, args ...interface{}) *Model {
// Group sets the "GROUP BY" statement for the model.
func (m *Model) Group(groupBy string) *Model {
model := m.getModel()
model.groupBy = m.db.quoteString(groupBy)
model.groupBy = m.db.QuoteString(groupBy)
return model
}
@ -417,7 +417,7 @@ func (m *Model) GroupBy(groupBy string) *Model {
// Order sets the "ORDER BY" statement for the model.
func (m *Model) Order(orderBy string) *Model {
model := m.getModel()
model.orderBy = m.db.quoteString(orderBy)
model.orderBy = m.db.QuoteString(orderBy)
return model
}
@ -586,7 +586,7 @@ func (m *Model) doInsertWithOption(option int, data ...interface{}) (result sql.
if m.batch > 0 {
batch = m.batch
}
return m.db.doBatchInsert(
return m.db.DoBatchInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
@ -595,7 +595,7 @@ func (m *Model) doInsertWithOption(option int, data ...interface{}) (result sql.
)
} else if data, ok := m.data.(Map); ok {
// Single insert.
return m.db.doInsert(
return m.db.DoInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
@ -626,7 +626,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) {
if m.batch > 0 {
batch = m.batch
}
return m.db.doBatchInsert(
return m.db.DoBatchInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
@ -635,7 +635,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) {
)
} else if data, ok := m.data.(Map); ok {
// Single insert.
return m.db.doInsert(
return m.db.DoInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
@ -669,7 +669,7 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) {
if m.batch > 0 {
batch = m.batch
}
return m.db.doBatchInsert(
return m.db.DoBatchInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
@ -678,7 +678,7 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) {
)
} else if data, ok := m.data.(Map); ok {
// Single save.
return m.db.doInsert(
return m.db.DoInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
@ -712,7 +712,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
return nil, errors.New("updating table with empty data")
}
condition, conditionArgs := m.formatCondition(false)
return m.db.doUpdate(
return m.db.DoUpdate(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(m.data),
@ -734,7 +734,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
}
}()
condition, conditionArgs := m.formatCondition(false)
return m.db.doDelete(m.getLink(true), m.tables, condition, conditionArgs...)
return m.db.DoDelete(m.getLink(true), m.tables, condition, conditionArgs...)
}
// Select is alias of Model.All.
@ -1059,10 +1059,16 @@ func (m *Model) getLink(master bool) dbLink {
}
switch linkType {
case gLINK_TYPE_MASTER:
link, _ := m.db.getMaster(m.schema)
link, err := m.db.GetMaster(m.schema)
if err != nil {
panic(err)
}
return link
case gLINK_TYPE_SLAVE:
link, _ := m.db.getSlave(m.schema)
link, err := m.db.GetSlave(m.schema)
if err != nil {
panic(err)
}
return link
}
return nil
@ -1077,17 +1083,17 @@ func (m *Model) getAll(query string, args ...interface{}) (result Result, err er
if len(cacheKey) == 0 {
cacheKey = query + "/" + gconv.String(args)
}
if v := m.db.getCache().Get(cacheKey); v != nil {
if v := m.db.GetCache().Get(cacheKey); v != nil {
return v.(Result), nil
}
}
result, err = m.db.doGetAll(m.getLink(false), query, args...)
result, err = m.db.DoGetAll(m.getLink(false), query, args...)
// Cache the result.
if len(cacheKey) > 0 && err == nil {
if m.cacheDuration < 0 {
m.db.getCache().Remove(cacheKey)
m.db.GetCache().Remove(cacheKey)
} else {
m.db.getCache().Set(cacheKey, result, m.cacheDuration)
m.db.GetCache().Set(cacheKey, result, m.cacheDuration)
}
}
return result, err
@ -1113,7 +1119,7 @@ func (m *Model) getPrimaryKey() string {
// checkAndRemoveCache checks and remove the cache if necessary.
func (m *Model) checkAndRemoveCache() {
if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 {
m.db.getCache().Remove(m.cacheName)
m.db.GetCache().Remove(m.cacheName)
}
}

View File

@ -14,9 +14,9 @@ type Schema struct {
}
// Schema creates and returns a schema.
func (bs *dbBase) Schema(schema string) *Schema {
func (c *Core) Schema(schema string) *Schema {
return &Schema{
db: bs.db,
db: c.DB,
schema: schema,
}
}
@ -44,8 +44,8 @@ func (s *Schema) Table(table string) *Model {
return m
}
// Model is alias of dbBase.Table.
// See dbBase.Table.
// Model is alias of Core.Table.
// See Core.Table.
func (s *Schema) Model(table string) *Model {
return s.Table(table)
}

View File

@ -21,7 +21,7 @@ import (
// convertValue automatically checks and converts field value from database type
// to golang variable type.
func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{} {
func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
t = strings.ToLower(t)
switch t {
@ -106,10 +106,10 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{}
}
// filterFields removes all key-value pairs which are not the field of given table.
func (bs *dbBase) filterFields(schema, table string, data map[string]interface{}) map[string]interface{} {
func (c *Core) filterFields(schema, table string, data map[string]interface{}) map[string]interface{} {
// It must use data copy here to avoid its changing the origin data map.
newDataMap := make(map[string]interface{}, len(data))
if fields, err := bs.db.TableFields(table, schema); err == nil {
if fields, err := c.DB.TableFields(table, schema); err == nil {
for k, v := range data {
if _, ok := fields[k]; ok {
newDataMap[k] = v

View File

@ -32,15 +32,15 @@ func (tx *TX) Rollback() error {
}
// Query does query operation on transaction.
// See dbBase.Query.
// See Core.Query.
func (tx *TX) Query(query string, args ...interface{}) (rows *sql.Rows, err error) {
return tx.db.doQuery(tx.tx, query, args...)
return tx.db.DoQuery(tx.tx, query, args...)
}
// Exec does none query operation on transaction.
// See dbBase.Exec.
// See Core.Exec.
func (tx *TX) Exec(query string, args ...interface{}) (sql.Result, error) {
return tx.db.doExec(tx.tx, query, args...)
return tx.db.DoExec(tx.tx, query, args...)
}
// Prepare creates a prepared statement for later queries or executions.
@ -49,7 +49,7 @@ func (tx *TX) Exec(query string, args ...interface{}) (sql.Result, error) {
// The caller must call the statement's Close method
// when the statement is no longer needed.
func (tx *TX) Prepare(query string) (*sql.Stmt, error) {
return tx.db.doPrepare(tx.tx, query)
return tx.db.DoPrepare(tx.tx, query)
}
// GetAll queries and returns data records from database.
@ -154,7 +154,7 @@ func (tx *TX) GetCount(query string, args ...interface{}) (int, error) {
//
// The parameter <batch> specifies the batch operation count when given data is slice.
func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, gINSERT_OPTION_DEFAULT, batch...)
return tx.db.DoInsert(tx.tx, table, data, gINSERT_OPTION_DEFAULT, batch...)
}
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the table.
@ -167,7 +167,7 @@ func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result,
//
// The parameter <batch> specifies the batch operation count when given data is slice.
func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, gINSERT_OPTION_IGNORE, batch...)
return tx.db.DoInsert(tx.tx, table, data, gINSERT_OPTION_IGNORE, batch...)
}
// Replace does "REPLACE INTO ..." statement for the table.
@ -183,7 +183,7 @@ func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Re
// If given data is type of slice, it then does batch replacing, and the optional parameter
// <batch> specifies the batch operation count.
func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, gINSERT_OPTION_REPLACE, batch...)
return tx.db.DoInsert(tx.tx, table, data, gINSERT_OPTION_REPLACE, batch...)
}
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the table.
@ -198,31 +198,31 @@ func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result,
// If given data is type of slice, it then does batch saving, and the optional parameter
// <batch> specifies the batch operation count.
func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, gINSERT_OPTION_SAVE, batch...)
return tx.db.DoInsert(tx.tx, table, data, gINSERT_OPTION_SAVE, batch...)
}
// BatchInsert batch inserts data.
// The parameter <list> must be type of slice of map or struct.
func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, gINSERT_OPTION_DEFAULT, batch...)
return tx.db.DoBatchInsert(tx.tx, table, list, gINSERT_OPTION_DEFAULT, batch...)
}
// BatchInsert batch inserts data with ignore option.
// The parameter <list> must be type of slice of map or struct.
func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, gINSERT_OPTION_IGNORE, batch...)
return tx.db.DoBatchInsert(tx.tx, table, list, gINSERT_OPTION_IGNORE, batch...)
}
// BatchReplace batch replaces data.
// The parameter <list> must be type of slice of map or struct.
func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, gINSERT_OPTION_REPLACE, batch...)
return tx.db.DoBatchInsert(tx.tx, table, list, gINSERT_OPTION_REPLACE, batch...)
}
// BatchSave batch replaces data.
// The parameter <list> must be type of slice of map or struct.
func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, gINSERT_OPTION_SAVE, batch...)
return tx.db.DoBatchInsert(tx.tx, table, list, gINSERT_OPTION_SAVE, batch...)
}
// Update does "UPDATE ... " statement for the table.
@ -244,7 +244,7 @@ func (tx *TX) Update(table string, data interface{}, condition interface{}, args
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
return tx.db.doUpdate(tx.tx, table, data, newWhere, newArgs...)
return tx.db.DoUpdate(tx.tx, table, data, newWhere, newArgs...)
}
// Delete does "DELETE FROM ... " statement for the table.
@ -263,5 +263,5 @@ func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
return tx.db.doDelete(tx.tx, table, newWhere, newArgs...)
return tx.db.DoDelete(tx.tx, table, newWhere, newArgs...)
}

View File

@ -54,9 +54,9 @@ type PoolStats struct {
}
const (
gDEFAULT_POOL_IDLE_TIMEOUT = 60 * time.Second
gDEFAULT_POOL_IDLE_TIMEOUT = 30 * time.Second
gDEFAULT_POOL_CONN_TIMEOUT = 10 * time.Second
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
gDEFAULT_POOL_MAX_LIFE_TIME = 30 * time.Second
)
var (
@ -80,6 +80,7 @@ func New(config Config) *Redis {
config: config,
pool: pools.GetOrSetFuncLock(fmt.Sprintf("%v", config), func() interface{} {
return &redis.Pool{
Wait: true,
IdleTimeout: config.IdleTimeout,
MaxActive: config.MaxActive,
MaxIdle: config.MaxIdle,

3
go.mod
View File

@ -7,8 +7,8 @@ require (
github.com/clbanning/mxj v1.8.4
github.com/fatih/structs v1.1.0
github.com/fsnotify/fsnotify v1.4.7
github.com/gf-third/mysql v1.4.2
github.com/gf-third/yaml v1.0.1
github.com/go-sql-driver/mysql v1.5.0
github.com/gomodule/redigo v2.0.0+incompatible
github.com/google/uuid v1.1.1
github.com/gorilla/websocket v1.4.1
@ -17,5 +17,4 @@ require (
github.com/olekukonko/tablewriter v0.0.1
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e // indirect
golang.org/x/text v0.3.2
google.golang.org/appengine v1.6.5 // indirect
)