From 0e52d467d37ee8c78669aed7dcc6c599bc5d7179 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 8 Mar 2020 00:17:42 +0800 Subject: [PATCH] improving package gdb --- .example/database/gdb/driver/driver.go | 121 ++++++++++ .example/database/gdb/mysql/gdb_all.go | 6 +- .example/database/gdb/mysql/gdb_value.go | 1 - database/gdb/gdb.go | 177 ++++++-------- database/gdb/{gdb_base.go => gdb_core.go} | 227 +++++++++--------- .../gdb/{gdb_config.go => gdb_core_config.go} | 55 +++-- database/gdb/gdb_core_utility.go | 50 ++++ .../gdb/{gdb_mssql.go => gdb_driver_mssql.go} | 44 ++-- .../gdb/{gdb_mysql.go => gdb_driver_mysql.go} | 44 ++-- .../{gdb_oracle.go => gdb_driver_oracle.go} | 73 +++--- .../gdb/{gdb_pgsql.go => gdb_driver_pgsql.go} | 38 +-- .../{gdb_sqlite.go => gdb_driver_sqlite.go} | 26 +- database/gdb/gdb_func.go | 2 +- database/gdb/gdb_model.go | 66 ++--- database/gdb/gdb_schema.go | 8 +- database/gdb/gdb_structure.go | 6 +- database/gdb/gdb_transaction.go | 30 +-- database/gredis/gredis.go | 5 +- go.mod | 3 +- 19 files changed, 594 insertions(+), 388 deletions(-) create mode 100644 .example/database/gdb/driver/driver.go rename database/gdb/{gdb_base.go => gdb_core.go} (70%) rename database/gdb/{gdb_config.go => gdb_core_config.go} (80%) create mode 100644 database/gdb/gdb_core_utility.go rename database/gdb/{gdb_mssql.go => gdb_driver_mssql.go} (82%) rename database/gdb/{gdb_mysql.go => gdb_driver_mysql.go} (67%) rename database/gdb/{gdb_oracle.go => gdb_driver_oracle.go} (82%) rename database/gdb/{gdb_pgsql.go => gdb_driver_pgsql.go} (74%) rename database/gdb/{gdb_sqlite.go => gdb_driver_sqlite.go} (64%) diff --git a/.example/database/gdb/driver/driver.go b/.example/database/gdb/driver/driver.go new file mode 100644 index 000000000..0d0599c9c --- /dev/null +++ b/.example/database/gdb/driver/driver.go @@ -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 +} diff --git a/.example/database/gdb/mysql/gdb_all.go b/.example/database/gdb/mysql/gdb_all.go index 681277607..30161552f 100644 --- a/.example/database/gdb/mysql/gdb_all.go +++ b/.example/database/gdb/mysql/gdb_all.go @@ -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()) } } diff --git a/.example/database/gdb/mysql/gdb_value.go b/.example/database/gdb/mysql/gdb_value.go index 400323a16..c0e325189 100644 --- a/.example/database/gdb/mysql/gdb_value.go +++ b/.example/database/gdb/mysql/gdb_value.go @@ -9,5 +9,4 @@ func main() { db.SetDebug(true) db.Table("user").Fields("DISTINCT id,nickname").Filter().All() - } diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index b804ad059..cc3e1b429 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -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 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 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 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 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...) -} diff --git a/database/gdb/gdb_base.go b/database/gdb/gdb_core.go similarity index 70% rename from database/gdb/gdb_base.go rename to database/gdb/gdb_core.go index cbefda532..599d12051 100644 --- a/database/gdb/gdb_base.go +++ b/database/gdb/gdb_core.go @@ -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 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 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 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 is type of struct pointer, it calls GetStruct internally for // the conversion. If parameter 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 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 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 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 // 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 // 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 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 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 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 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 a word, if true quotes it with security chars of the database -// and returns the quoted string; or else return 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) } } diff --git a/database/gdb/gdb_config.go b/database/gdb/gdb_core_config.go similarity index 80% rename from database/gdb/gdb_config.go rename to database/gdb/gdb_core_config.go index 74523b220..46e52ef54 100644 --- a/database/gdb/gdb_config.go +++ b/database/gdb/gdb_core_config.go @@ -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 <= 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() } diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go new file mode 100644 index 000000000..b024fe28d --- /dev/null +++ b/database/gdb/gdb_core_utility.go @@ -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 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 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 a word, if true quotes it with security chars of the database +// and returns the quoted string; or else return 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 +} diff --git a/database/gdb/gdb_mssql.go b/database/gdb/gdb_driver_mssql.go similarity index 82% rename from database/gdb/gdb_mssql.go rename to database/gdb/gdb_driver_mssql.go index f3d27bb25..441f65da8 100644 --- a/database/gdb/gdb_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -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)+ ')' diff --git a/database/gdb/gdb_mysql.go b/database/gdb/gdb_driver_mysql.go similarity index 67% rename from database/gdb/gdb_mysql.go rename to database/gdb/gdb_driver_mysql.go index f4cfd45db..de5f0b44c 100644 --- a/database/gdb/gdb_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -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 diff --git a/database/gdb/gdb_oracle.go b/database/gdb/gdb_driver_oracle.go similarity index 82% rename from database/gdb/gdb_oracle.go rename to database/gdb/gdb_driver_oracle.go index 686dce1fa..de8936322 100644 --- a/database/gdb/gdb_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -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 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 } diff --git a/database/gdb/gdb_pgsql.go b/database/gdb/gdb_driver_pgsql.go similarity index 74% rename from database/gdb/gdb_pgsql.go rename to database/gdb/gdb_driver_pgsql.go index 24b9b3e38..61e15094f 100644 --- a/database/gdb/gdb_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -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 diff --git a/database/gdb/gdb_sqlite.go b/database/gdb/gdb_driver_sqlite.go similarity index 64% rename from database/gdb/gdb_sqlite.go rename to database/gdb/gdb_driver_sqlite.go index 62dd3a4a2..659ae47d3 100644 --- a/database/gdb/gdb_sqlite.go +++ b/database/gdb/gdb_driver_sqlite.go @@ -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 } diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index fa9ad177d..deb94c8be 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -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 ") } diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index aa7bf739c..c5612f57f 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -68,10 +68,10 @@ const ( // Table creates and returns a new ORM model from given schema. // The parameter 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) } } diff --git a/database/gdb/gdb_schema.go b/database/gdb/gdb_schema.go index 9d4631168..875869a55 100644 --- a/database/gdb/gdb_schema.go +++ b/database/gdb/gdb_schema.go @@ -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) } diff --git a/database/gdb/gdb_structure.go b/database/gdb/gdb_structure.go index 3a251fa97..dc7c23154 100644 --- a/database/gdb/gdb_structure.go +++ b/database/gdb/gdb_structure.go @@ -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 diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index d270ab108..c717d2d91 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -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 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 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 // 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 // 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 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 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 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 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...) } diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index 02c75cf4d..6bd31378a 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -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, diff --git a/go.mod b/go.mod index 4c37a2c14..d82b1ab16 100644 --- a/go.mod +++ b/go.mod @@ -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 )