diff --git a/.example/database/gdb/driver/driver/driver.go b/.example/database/gdb/driver/driver/driver.go index e253f2bff..acb2bca93 100644 --- a/.example/database/gdb/driver/driver/driver.go +++ b/.example/database/gdb/driver/driver/driver.go @@ -8,112 +8,72 @@ 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/gogf/gf/frame/g" + "github.com/gogf/gf/os/gtime" ) +// MyDriver is a custom database driver, which is used for testing only. +// For simplifying the unit testing case purpose, MyDriver struct inherits the mysql driver +// gdb.DriverMysql and overwrites its function HandleSqlBeforeCommit. +// So if there's any sql execution, it goes through MyDriver.HandleSqlBeforeCommit firstly and +// then gdb.DriverMysql.HandleSqlBeforeCommit. +// You can call it sql "HOOK" or "HiJack" as your will. type MyDriver struct { - *gdb.Core + *gdb.DriverMysql } -// 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("mysql", source); err == nil { - return db, nil - } else { - return nil, err +var ( + // customDriverName is my driver name, which is used for registering. + customDriverName = "MyDriver" +) + +func init() { + // It here registers my custom driver in package initialization function "init". + // You can later use this type in the database configuration. + if err := gdb.Register(customDriverName, &MyDriver{}); err != nil { + panic(err) } } -// getChars returns the security char for this type of database. -func (d *MyDriver) GetChars() (charLeft string, charRight string) { - return "`", "`" +// New creates and returns a database object for mysql. +// It implements the interface of gdb.Driver for extra database driver installation. +func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { + return &MyDriver{ + &gdb.DriverMysql{ + Core: core, + }, + }, nil } -// 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()) - } +// DoQuery commits the sql string and its arguments to underlying driver +// through given link object and returns the execution result. +func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { + tsMilli := gtime.TimestampMilli() + rows, err = d.DriverMysql.DoQuery(link, sql, args...) + if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{ + "sql": gdb.FormatSqlWithArgs(sql, args), + "cost": gtime.TimestampMilli() - tsMilli, + "time": gtime.Now(), + "error": err.Error(), + }); err != nil { + panic(err) } 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) +// DoExec commits the query string and its arguments to underlying driver +// through given link object and returns the execution result. +func (d *MyDriver) DoExec(link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) { + tsMilli := gtime.TimestampMilli() + result, err = d.DriverMysql.DoExec(link, sql, args...) + if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{ + "sql": gdb.FormatSqlWithArgs(sql, args), + "cost": gtime.TimestampMilli() - tsMilli, + "time": gtime.Now(), + "error": err.Error(), + }); err != nil { + panic(err) } return } diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index fc5125530..ad37c2c5d 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -30,29 +30,29 @@ type DB interface { Open(config *ConfigNode) (*sql.DB, error) // Query APIs. - Query(query string, args ...interface{}) (*sql.Rows, error) + Query(sql string, args ...interface{}) (*sql.Rows, error) Exec(sql string, args ...interface{}) (sql.Result, error) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) // Internal APIs for CURD, which can be overwrote for custom CURD implements. - DoQuery(link Link, query string, args ...interface{}) (rows *sql.Rows, err error) - DoGetAll(link Link, query string, args ...interface{}) (result Result, err error) - DoExec(link Link, query string, args ...interface{}) (result sql.Result, err error) - DoPrepare(link Link, query string) (*sql.Stmt, error) + DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) + DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) + DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) + DoPrepare(link Link, sql string) (*sql.Stmt, error) DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // Query APIs for convenience purpose. - GetAll(query string, args ...interface{}) (Result, error) - GetOne(query string, args ...interface{}) (Record, error) - GetValue(query string, args ...interface{}) (Value, error) - GetArray(query string, args ...interface{}) ([]Value, error) - GetCount(query string, args ...interface{}) (int, error) - GetStruct(objPointer interface{}, query string, args ...interface{}) error - GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error - GetScan(objPointer interface{}, query string, args ...interface{}) error + GetAll(sql string, args ...interface{}) (Result, error) + GetOne(sql string, args ...interface{}) (Record, error) + GetValue(sql string, args ...interface{}) (Value, error) + GetArray(sql string, args ...interface{}) ([]Value, error) + GetCount(sql string, args ...interface{}) (int, error) + GetStruct(objPointer interface{}, sql string, args ...interface{}) error + GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error + GetScan(objPointer interface{}, sql string, args ...interface{}) error // Master/Slave specification support. Master() (*sql.DB, error) @@ -107,9 +107,9 @@ type DB interface { // HandleSqlBeforeCommit is a hook function, which deals with the sql string before // it's committed to underlying driver. The parameter specifies the current - // database connection operation object. You can modify the sql string and its + // database connection operation object. You can modify the sql string and its // arguments as you wish before they're committed to driver. - HandleSqlBeforeCommit(link Link, query string, args []interface{}) (string, []interface{}) + HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) // Internal methods. filterFields(schema, table string, data map[string]interface{}) map[string]interface{} @@ -161,7 +161,7 @@ type TableField struct { // Link is a common database function wrapper interface. type Link interface { - Query(query string, args ...interface{}) (*sql.Rows, error) + Query(sql string, args ...interface{}) (*sql.Rows, error) Exec(sql string, args ...interface{}) (sql.Result, error) Prepare(sql string) (*sql.Stmt, error) } @@ -193,6 +193,8 @@ const ( ) var ( + // ErrNoRows is alias of sql.ErrNoRows. + ErrNoRows = sql.ErrNoRows // instances is the management map for instances. instances = gmap.NewStrAnyMap(true) // driverMap manages all custom registered driver. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 7770f2cdd..43aaa4b6b 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -45,75 +45,75 @@ func (c *Core) Slave() (*sql.DB, error) { // Query commits one query SQL to underlying driver and returns the execution result. // It is most commonly used for data querying. -func (c *Core) Query(query string, args ...interface{}) (rows *sql.Rows, err error) { +func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { link, err := c.DB.Slave() if err != nil { return nil, err } - return c.DB.DoQuery(link, query, args...) + return c.DB.DoQuery(link, sql, args...) } -// doQuery commits the query string and its arguments to underlying driver +// DoQuery commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. -func (c *Core) DoQuery(link Link, query string, args ...interface{}) (rows *sql.Rows, err error) { - query, args = formatQuery(query, args) - query, args = c.DB.HandleSqlBeforeCommit(link, query, args) +func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { + sql, args = formatSql(sql, args) + sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) if c.DB.GetDebug() { mTime1 := gtime.TimestampMilli() - rows, err = link.Query(query, args...) + rows, err = link.Query(sql, args...) mTime2 := gtime.TimestampMilli() s := &Sql{ - Sql: query, + Sql: sql, Args: args, - Format: bindArgsToQuery(query, args), + Format: FormatSqlWithArgs(sql, args), Error: err, Start: mTime1, End: mTime2, } c.writeSqlToLogger(s) } else { - rows, err = link.Query(query, args...) + rows, err = link.Query(sql, args...) } if err == nil { return rows, nil } else { - err = formatError(err, query, args...) + err = formatError(err, sql, args...) } return nil, err } // Exec commits one query SQL to underlying driver and returns the execution result. // It is most commonly used for data inserting and updating. -func (c *Core) Exec(query string, args ...interface{}) (result sql.Result, err error) { +func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) { link, err := c.DB.Master() if err != nil { return nil, err } - return c.DB.DoExec(link, query, args...) + return c.DB.DoExec(link, sql, args...) } -// doExec commits the query string and its arguments to underlying driver +// DoExec commits the sql string and its arguments to underlying driver // through given link object and returns the execution result. -func (c *Core) DoExec(link Link, query string, args ...interface{}) (result sql.Result, err error) { - query, args = formatQuery(query, args) - query, args = c.DB.HandleSqlBeforeCommit(link, query, args) +func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) { + sql, args = formatSql(sql, args) + sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args) if c.DB.GetDebug() { mTime1 := gtime.TimestampMilli() - result, err = link.Exec(query, args...) + result, err = link.Exec(sql, args...) mTime2 := gtime.TimestampMilli() s := &Sql{ - Sql: query, + Sql: sql, Args: args, - Format: bindArgsToQuery(query, args), + Format: FormatSqlWithArgs(sql, args), Error: err, Start: mTime1, End: mTime2, } c.writeSqlToLogger(s) } else { - result, err = link.Exec(query, args...) + result, err = link.Exec(sql, args...) } - return result, formatError(err, query, args...) + return result, formatError(err, sql, args...) } // Prepare creates a prepared statement for later queries or executions. @@ -124,7 +124,7 @@ func (c *Core) DoExec(link Link, query string, args ...interface{}) (result sql. // // The parameter specifies whether executing the sql on master node, // or else it executes the sql on slave node if master-slave configured. -func (c *Core) Prepare(query string, execOnMaster ...bool) (*sql.Stmt, error) { +func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) { err := (error)(nil) link := (Link)(nil) if len(execOnMaster) > 0 && execOnMaster[0] { @@ -136,28 +136,28 @@ func (c *Core) Prepare(query string, execOnMaster ...bool) (*sql.Stmt, error) { return nil, err } } - return c.DB.DoPrepare(link, query) + return c.DB.DoPrepare(link, sql) } // doPrepare calls prepare function on given link object and returns the statement object. -func (c *Core) DoPrepare(link Link, query string) (*sql.Stmt, error) { - return link.Prepare(query) +func (c *Core) DoPrepare(link Link, sql string) (*sql.Stmt, error) { + return link.Prepare(sql) } // GetAll queries and returns data records from database. -func (c *Core) GetAll(query string, args ...interface{}) (Result, error) { - return c.DB.DoGetAll(nil, query, args...) +func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) { + return c.DB.DoGetAll(nil, sql, args...) } // doGetAll queries and returns data records from database. -func (c *Core) DoGetAll(link Link, query string, args ...interface{}) (result Result, err error) { +func (c *Core) DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) { if link == nil { link, err = c.DB.Slave() if err != nil { return nil, err } } - rows, err := c.DB.DoQuery(link, query, args...) + rows, err := c.DB.DoQuery(link, sql, args...) if err != nil || rows == nil { return nil, err } @@ -166,8 +166,8 @@ func (c *Core) DoGetAll(link Link, query string, args ...interface{}) (result Re } // GetOne queries and returns one record from database. -func (c *Core) GetOne(query string, args ...interface{}) (Record, error) { - list, err := c.DB.GetAll(query, args...) +func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) { + list, err := c.DB.GetAll(sql, args...) if err != nil { return nil, err } @@ -179,8 +179,8 @@ func (c *Core) GetOne(query string, args ...interface{}) (Record, error) { // GetArray queries and returns data values as slice from database. // Note that if there're multiple columns in the result, it returns just one column values randomly. -func (c *Core) GetArray(query string, args ...interface{}) ([]Value, error) { - all, err := c.DB.DoGetAll(nil, query, args...) +func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) { + all, err := c.DB.DoGetAll(nil, sql, args...) if err != nil { return nil, err } @@ -189,26 +189,26 @@ func (c *Core) GetArray(query string, args ...interface{}) ([]Value, error) { // GetStruct queries one record from database and converts it to given struct. // The parameter should be a pointer to struct. -func (c *Core) GetStruct(pointer interface{}, query string, args ...interface{}) error { - one, err := c.DB.GetOne(query, args...) +func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) error { + one, err := c.DB.GetOne(sql, args...) if err != nil { return err } if len(one) == 0 { - return sql.ErrNoRows + return ErrNoRows } return one.Struct(pointer) } // GetStructs queries records from database and converts them to given struct. // The parameter should be type of struct slice: []struct/[]*struct. -func (c *Core) GetStructs(pointer interface{}, query string, args ...interface{}) error { - all, err := c.DB.GetAll(query, args...) +func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{}) error { + all, err := c.DB.GetAll(sql, args...) if err != nil { return err } if len(all) == 0 { - return sql.ErrNoRows + return ErrNoRows } return all.Structs(pointer) } @@ -219,7 +219,7 @@ func (c *Core) GetStructs(pointer interface{}, query string, args ...interface{} // 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 (c *Core) GetScan(pointer interface{}, query string, args ...interface{}) error { +func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) error { t := reflect.TypeOf(pointer) k := t.Kind() if k != reflect.Ptr { @@ -228,9 +228,9 @@ func (c *Core) GetScan(pointer interface{}, query string, args ...interface{}) e k = t.Elem().Kind() switch k { case reflect.Array, reflect.Slice: - return c.DB.GetStructs(pointer, query, args...) + return c.DB.GetStructs(pointer, sql, args...) case reflect.Struct: - return c.DB.GetStruct(pointer, query, args...) + return c.DB.GetStruct(pointer, sql, args...) } return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) } @@ -238,8 +238,8 @@ func (c *Core) GetScan(pointer interface{}, query string, args ...interface{}) e // 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 (c *Core) GetValue(query string, args ...interface{}) (Value, error) { - one, err := c.DB.GetOne(query, args...) +func (c *Core) GetValue(sql string, args ...interface{}) (Value, error) { + one, err := c.DB.GetOne(sql, args...) if err != nil { return nil, err } @@ -250,13 +250,13 @@ func (c *Core) GetValue(query string, args ...interface{}) (Value, error) { } // GetCount queries and returns the count from database. -func (c *Core) GetCount(query string, args ...interface{}) (int, error) { +func (c *Core) GetCount(sql 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) + // it replaces the sql string and adds the "COUNT" function to the fields. + if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, sql) { + sql, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, sql) } - value, err := c.DB.GetValue(query, args...) + value, err := c.DB.GetValue(sql, args...) if err != nil { return 0, err } diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index a6253caf3..d90ac1f4d 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -60,10 +60,10 @@ func (d *DriverMssql) GetChars() (charLeft string, charRight string) { } // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverMssql) HandleSqlBeforeCommit(link Link, query string, args []interface{}) (string, []interface{}) { +func (d *DriverMssql) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { var index int // Convert place holder char '?' to string "@px". - str, _ := gregex.ReplaceStringFunc("\\?", query, func(s string) string { + str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ return fmt.Sprintf("@p%d", index) }) diff --git a/database/gdb/gdb_driver_oracle.go b/database/gdb/gdb_driver_oracle.go index 374abffc3..72eaa5603 100644 --- a/database/gdb/gdb_driver_oracle.go +++ b/database/gdb/gdb_driver_oracle.go @@ -64,10 +64,10 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) { } // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. -func (d *DriverOracle) HandleSqlBeforeCommit(link Link, query string, args []interface{}) (string, []interface{}) { +func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { var index int // Convert place holder char '?' to string ":x". - str, _ := gregex.ReplaceStringFunc("\\?", query, func(s string) string { + str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ return fmt.Sprintf(":%d", index) }) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index cead92cc0..d5bfad270 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -8,7 +8,6 @@ package gdb import ( "bytes" - "database/sql" "errors" "fmt" "github.com/gogf/gf/internal/empty" @@ -222,11 +221,11 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi return where } -// formatQuery formats the query string and its arguments before executing. +// formatSql formats the sql string and its arguments before executing. // The internal handleArguments function might be called twice during the SQL procedure, // but do not worry about it, it's safe and efficient. -func formatQuery(query string, args []interface{}) (newQuery string, newArgs []interface{}) { - return handleArguments(query, args) +func formatSql(sql string, args []interface{}) (newQuery string, newArgs []interface{}) { + return handleArguments(sql, args) } // formatWhere formats where statement and its arguments. @@ -384,8 +383,8 @@ func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key // handleArguments is a nice function which handles the query and its arguments before committing to // underlying driver. -func handleArguments(query string, args []interface{}) (newQuery string, newArgs []interface{}) { - newQuery = query +func handleArguments(sql string, args []interface{}) (newSql string, newArgs []interface{}) { + newSql = sql // Handles the slice arguments. if len(args) > 0 { for index, arg := range args { @@ -409,12 +408,12 @@ func handleArguments(query string, args []interface{}) (newQuery string, newArgs // It the '?' holder count equals the length of the slice, // it does not implement the arguments splitting logic. // Eg: db.Query("SELECT ?+?", g.Slice{1, 2}) - if len(args) == 1 && gstr.Count(newQuery, "?") == rv.Len() { + if len(args) == 1 && gstr.Count(newSql, "?") == rv.Len() { break } // counter is used to finding the inserting position for the '?' holder. counter := 0 - newQuery, _ = gregex.ReplaceStringFunc(`\?`, newQuery, func(s string) string { + newSql, _ = gregex.ReplaceStringFunc(`\?`, newSql, func(s string) string { counter++ if counter == index+1 { return "?" + strings.Repeat(",?", rv.Len()-1) @@ -450,19 +449,19 @@ func handleArguments(query string, args []interface{}) (newQuery string, newArgs } // formatError customizes and returns the SQL error. -func formatError(err error, query string, args ...interface{}) error { - if err != nil && err != sql.ErrNoRows { - return errors.New(fmt.Sprintf("%s, %s\n", err.Error(), bindArgsToQuery(query, args))) +func formatError(err error, sql string, args ...interface{}) error { + if err != nil && err != ErrNoRows { + return errors.New(fmt.Sprintf("%s, %s\n", err.Error(), FormatSqlWithArgs(sql, args))) } return err } -// bindArgsToQuery binds the arguments to the query string and returns a complete +// FormatSqlWithArgs binds the arguments to the sql string and returns a complete // sql string, just for debugging. -func bindArgsToQuery(query string, args []interface{}) string { +func FormatSqlWithArgs(sql string, args []interface{}) string { index := -1 newQuery, _ := gregex.ReplaceStringFunc( - `(\?|:\d+|\$\d+|@p\d+)`, query, func(s string) string { + `(\?|:\d+|\$\d+|@p\d+)`, sql, func(s string) string { index++ if len(args) > index { if args[index] == nil { diff --git a/database/gdb/gdb_model_condition.go b/database/gdb/gdb_model_condition.go index 66705a25b..cee015c08 100644 --- a/database/gdb/gdb_model_condition.go +++ b/database/gdb/gdb_model_condition.go @@ -145,19 +145,19 @@ func (m *Model) ForPage(page, limit int) *Model { } // getAll does the query from database. -func (m *Model) getAll(query string, args ...interface{}) (result Result, err error) { +func (m *Model) getAll(sql string, args ...interface{}) (result Result, err error) { cacheKey := "" // Retrieve from cache. if m.cacheEnabled { cacheKey = m.cacheName if len(cacheKey) == 0 { - cacheKey = query + "/" + gconv.String(args) + cacheKey = sql + "/" + gconv.String(args) } if v := m.db.GetCache().Get(cacheKey); v != nil { return v.(Result), nil } } - result, err = m.db.DoGetAll(m.getLink(false), query, m.mergeArguments(args)...) + result, err = m.db.DoGetAll(m.getLink(false), sql, m.mergeArguments(args)...) // Cache the result. if len(cacheKey) > 0 && err == nil { if m.cacheDuration < 0 { diff --git a/database/gdb/gdb_transaction.go b/database/gdb/gdb_transaction.go index c717d2d91..cdae88386 100644 --- a/database/gdb/gdb_transaction.go +++ b/database/gdb/gdb_transaction.go @@ -33,14 +33,14 @@ func (tx *TX) Rollback() error { // Query does query operation on transaction. // See Core.Query. -func (tx *TX) Query(query string, args ...interface{}) (rows *sql.Rows, err error) { - return tx.db.DoQuery(tx.tx, query, args...) +func (tx *TX) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) { + return tx.db.DoQuery(tx.tx, sql, args...) } // Exec does none query operation on transaction. // See Core.Exec. -func (tx *TX) Exec(query string, args ...interface{}) (sql.Result, error) { - return tx.db.DoExec(tx.tx, query, args...) +func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) { + return tx.db.DoExec(tx.tx, sql, args...) } // Prepare creates a prepared statement for later queries or executions. @@ -48,13 +48,13 @@ func (tx *TX) Exec(query string, args ...interface{}) (sql.Result, error) { // returned statement. // 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) +func (tx *TX) Prepare(sql string) (*sql.Stmt, error) { + return tx.db.DoPrepare(tx.tx, sql) } // GetAll queries and returns data records from database. -func (tx *TX) GetAll(query string, args ...interface{}) (Result, error) { - rows, err := tx.Query(query, args...) +func (tx *TX) GetAll(sql string, args ...interface{}) (Result, error) { + rows, err := tx.Query(sql, args...) if err != nil || rows == nil { return nil, err } @@ -63,8 +63,8 @@ func (tx *TX) GetAll(query string, args ...interface{}) (Result, error) { } // GetOne queries and returns one record from database. -func (tx *TX) GetOne(query string, args ...interface{}) (Record, error) { - list, err := tx.GetAll(query, args...) +func (tx *TX) GetOne(sql string, args ...interface{}) (Record, error) { + list, err := tx.GetAll(sql, args...) if err != nil { return nil, err } @@ -76,8 +76,8 @@ func (tx *TX) 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 (tx *TX) GetStruct(obj interface{}, query string, args ...interface{}) error { - one, err := tx.GetOne(query, args...) +func (tx *TX) GetStruct(obj interface{}, sql string, args ...interface{}) error { + one, err := tx.GetOne(sql, args...) if err != nil { return err } @@ -86,8 +86,8 @@ func (tx *TX) GetStruct(obj interface{}, query string, args ...interface{}) erro // GetStructs queries records from database and converts them to given struct. // The parameter should be type of struct slice: []struct/[]*struct. -func (tx *TX) GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error { - all, err := tx.GetAll(query, args...) +func (tx *TX) GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error { + all, err := tx.GetAll(sql, args...) if err != nil { return err } @@ -100,7 +100,7 @@ func (tx *TX) GetStructs(objPointerSlice interface{}, query string, args ...inte // 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 (tx *TX) GetScan(objPointer interface{}, query string, args ...interface{}) error { +func (tx *TX) GetScan(objPointer interface{}, sql string, args ...interface{}) error { t := reflect.TypeOf(objPointer) k := t.Kind() if k != reflect.Ptr { @@ -109,9 +109,9 @@ func (tx *TX) GetScan(objPointer interface{}, query string, args ...interface{}) k = t.Elem().Kind() switch k { case reflect.Array, reflect.Slice: - return tx.db.GetStructs(objPointer, query, args...) + return tx.db.GetStructs(objPointer, sql, args...) case reflect.Struct: - return tx.db.GetStruct(objPointer, query, args...) + return tx.db.GetStruct(objPointer, sql, args...) default: return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) } @@ -121,8 +121,8 @@ func (tx *TX) GetScan(objPointer 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 (tx *TX) GetValue(query string, args ...interface{}) (Value, error) { - one, err := tx.GetOne(query, args...) +func (tx *TX) GetValue(sql string, args ...interface{}) (Value, error) { + one, err := tx.GetOne(sql, args...) if err != nil { return nil, err } @@ -133,11 +133,11 @@ func (tx *TX) GetValue(query string, args ...interface{}) (Value, error) { } // GetCount queries and returns the count from database. -func (tx *TX) GetCount(query string, args ...interface{}) (int, error) { - if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, query) { - query, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, query) +func (tx *TX) GetCount(sql string, args ...interface{}) (int, error) { + if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, sql) { + sql, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, sql) } - value, err := tx.GetValue(query, args...) + value, err := tx.GetValue(sql, args...) if err != nil { return 0, err } diff --git a/database/gdb/gdb_unit_z_func_test.go b/database/gdb/gdb_unit_z_func_test.go index ba311c23c..fa6453fe5 100644 --- a/database/gdb/gdb_unit_z_func_test.go +++ b/database/gdb/gdb_unit_z_func_test.go @@ -11,29 +11,29 @@ import ( "testing" ) -func Test_Func_bindArgsToQuery(t *testing.T) { +func Test_Func_FormatSqlWithArgs(t *testing.T) { // mysql gtest.C(t, func(t *gtest.T) { var s string - s = bindArgsToQuery("select * from table where id>=? and sex=?", []interface{}{100, 1}) + s = FormatSqlWithArgs("select * from table where id>=? and sex=?", []interface{}{100, 1}) t.Assert(s, "select * from table where id>=100 and sex=1") }) // mssql gtest.C(t, func(t *gtest.T) { var s string - s = bindArgsToQuery("select * from table where id>=@p1 and sex=@p2", []interface{}{100, 1}) + s = FormatSqlWithArgs("select * from table where id>=@p1 and sex=@p2", []interface{}{100, 1}) t.Assert(s, "select * from table where id>=100 and sex=1") }) // pgsql gtest.C(t, func(t *gtest.T) { var s string - s = bindArgsToQuery("select * from table where id>=$1 and sex=$2", []interface{}{100, 1}) + s = FormatSqlWithArgs("select * from table where id>=$1 and sex=$2", []interface{}{100, 1}) t.Assert(s, "select * from table where id>=100 and sex=1") }) // oracle gtest.C(t, func(t *gtest.T) { var s string - s = bindArgsToQuery("select * from table where id>=:1 and sex=:2", []interface{}{100, 1}) + s = FormatSqlWithArgs("select * from table where id>=:1 and sex=:2", []interface{}{100, 1}) t.Assert(s, "select * from table where id>=100 and sex=1") }) }