2021-01-17 21:46:25 +08:00
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2020-03-13 17:21:30 +08:00
//
// 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 (
2022-03-24 15:33:30 +08:00
"context"
2020-03-13 17:21:30 +08:00
"fmt"
2021-05-21 13:25:53 +08:00
"reflect"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/container/gset"
2021-11-13 23:23:55 +08:00
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
2022-03-11 10:24:42 +08:00
"github.com/gogf/gf/v2/internal/reflection"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
2020-03-13 17:21:30 +08:00
)
// All does "SELECT FROM ..." statement for the model.
// It retrieves the records from table and returns the result as slice type.
// It returns nil if there's no record retrieved with the given conditions from table.
//
2021-02-08 17:57:21 +08:00
// The optional parameter `where` is the same as the parameter of Model.Where function,
2020-03-13 17:21:30 +08:00
// see Model.Where.
func ( m * Model ) All ( where ... interface { } ) ( Result , error ) {
2022-03-24 17:51:49 +08:00
var ctx = m . GetCtx ( )
return m . doGetAll ( ctx , false , where ... )
2020-07-15 09:15:03 +08:00
}
2023-06-01 21:33:40 +08:00
// AllAndCount retrieves all records and the total count of records from the model.
// If useFieldForCount is true, it will use the fields specified in the model for counting;
// otherwise, it will use a constant value of 1 for counting.
// It returns the result as a slice of records, the total count of records, and an error if any.
// The where parameter is an optional list of conditions to use when retrieving records.
//
// Example:
//
// var model Model
// var result Result
// var count int
// where := []interface{}{"name = ?", "John"}
// result, count, err := model.AllAndCount(true)
// if err != nil {
// // Handle error.
// }
// fmt.Println(result, count)
func ( m * Model ) AllAndCount ( useFieldForCount bool ) ( result Result , totalCount int , err error ) {
// Clone the model for counting
countModel := m . Clone ( )
// If useFieldForCount is false, set the fields to a constant value of 1 for counting
if ! useFieldForCount {
countModel . fields = "1"
}
// Get the total count of records
totalCount , err = countModel . Count ( )
if err != nil {
return
}
// If the total count is 0, there are no records to retrieve, so return early
if totalCount == 0 {
return
}
// Retrieve all records
result , err = m . doGetAll ( m . GetCtx ( ) , false )
return
}
2021-08-01 10:33:33 +08:00
// Chunk iterates the query result with given `size` and `handler` function.
func ( m * Model ) Chunk ( size int , handler ChunkHandler ) {
2020-03-13 17:21:30 +08:00
page := m . start
2020-11-26 14:26:32 +08:00
if page <= 0 {
2020-03-13 17:21:30 +08:00
page = 1
}
model := m
for {
2021-08-01 10:33:33 +08:00
model = model . Page ( page , size )
2020-03-13 17:21:30 +08:00
data , err := model . All ( )
if err != nil {
2021-08-01 10:33:33 +08:00
handler ( nil , err )
2020-03-13 17:21:30 +08:00
break
}
if len ( data ) == 0 {
break
}
2022-11-01 20:12:21 +08:00
if ! handler ( data , err ) {
2020-03-13 17:21:30 +08:00
break
}
2021-08-01 10:33:33 +08:00
if len ( data ) < size {
2020-03-13 17:21:30 +08:00
break
}
page ++
}
}
// One retrieves one record from table and returns the result as map type.
// It returns nil if there's no record retrieved with the given conditions from table.
//
2021-02-08 17:57:21 +08:00
// The optional parameter `where` is the same as the parameter of Model.Where function,
2020-03-13 17:21:30 +08:00
// see Model.Where.
func ( m * Model ) One ( where ... interface { } ) ( Record , error ) {
2022-03-24 17:51:49 +08:00
var ctx = m . GetCtx ( )
2020-03-13 17:21:30 +08:00
if len ( where ) > 0 {
return m . Where ( where [ 0 ] , where [ 1 : ] ... ) . One ( )
}
2022-03-24 17:51:49 +08:00
all , err := m . doGetAll ( ctx , true )
2020-03-13 17:21:30 +08:00
if err != nil {
return nil , err
}
if len ( all ) > 0 {
return all [ 0 ] , nil
}
return nil , nil
}
// Array queries and returns data values as slice from database.
2021-05-02 12:17:06 +08:00
// Note that if there are multiple columns in the result, it returns just one column values randomly.
2020-03-13 17:21:30 +08:00
//
2021-02-08 17:57:21 +08:00
// If the optional parameter `fieldsAndWhere` is given, the fieldsAndWhere[0] is the selected fields
2020-03-13 17:21:30 +08:00
// and fieldsAndWhere[1:] is treated as where condition fields.
// Also see Model.Fields and Model.Where functions.
func ( m * Model ) Array ( fieldsAndWhere ... interface { } ) ( [ ] Value , error ) {
if len ( fieldsAndWhere ) > 0 {
if len ( fieldsAndWhere ) > 2 {
return m . Fields ( gconv . String ( fieldsAndWhere [ 0 ] ) ) . Where ( fieldsAndWhere [ 1 ] , fieldsAndWhere [ 2 : ] ... ) . Array ( )
} else if len ( fieldsAndWhere ) == 2 {
return m . Fields ( gconv . String ( fieldsAndWhere [ 0 ] ) ) . Where ( fieldsAndWhere [ 1 ] ) . Array ( )
} else {
return m . Fields ( gconv . String ( fieldsAndWhere [ 0 ] ) ) . Array ( )
}
}
all , err := m . All ( )
if err != nil {
return nil , err
}
2023-04-28 14:55:37 +08:00
var field string
if len ( all ) > 0 {
if internalData := m . db . GetCore ( ) . GetInternalCtxDataFromCtx ( m . GetCtx ( ) ) ; internalData != nil {
field = internalData . FirstResultColumn
} else {
return nil , gerror . NewCode (
gcode . CodeInternalError ,
` query array error: the internal context data is missing. there's internal issue should be fixed ` ,
)
}
}
return all . Array ( field ) , nil
2020-03-13 17:21:30 +08:00
}
// Struct retrieves one record from table and converts it into given struct.
2021-02-08 17:57:21 +08:00
// The parameter `pointer` should be type of *struct/**struct. If type **struct is given,
2020-03-13 17:21:30 +08:00
// it can create the struct internally during converting.
//
2021-02-08 17:57:21 +08:00
// The optional parameter `where` is the same as the parameter of Model.Where function,
2020-03-13 17:21:30 +08:00
// see Model.Where.
//
2021-06-22 21:48:56 +08:00
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
// default value and there's no record retrieved with the given conditions from table.
2020-03-13 17:21:30 +08:00
//
2021-06-22 21:48:56 +08:00
// Example:
2020-03-13 17:21:30 +08:00
// user := new(User)
2021-06-22 21:48:56 +08:00
// err := db.Model("user").Where("id", 1).Scan(user)
2020-03-13 17:21:30 +08:00
//
// user := (*User)(nil)
2021-11-16 17:21:13 +08:00
// err := db.Model("user").Where("id", 1).Scan(&user).
2021-06-22 21:48:56 +08:00
func ( m * Model ) doStruct ( pointer interface { } , where ... interface { } ) error {
model := m
// Auto selecting fields by struct attributes.
if model . fieldsEx == "" && ( model . fields == "" || model . fields == "*" ) {
2021-09-15 22:28:26 +08:00
if v , ok := pointer . ( reflect . Value ) ; ok {
model = m . Fields ( v . Interface ( ) )
} else {
model = m . Fields ( pointer )
}
2021-06-22 21:48:56 +08:00
}
one , err := model . One ( where ... )
2020-03-13 17:21:30 +08:00
if err != nil {
return err
}
2021-02-08 17:57:21 +08:00
if err = one . Struct ( pointer ) ; err != nil {
return err
}
2021-06-22 21:48:56 +08:00
return model . doWithScanStruct ( pointer )
}
2020-03-13 17:21:30 +08:00
// Structs retrieves records from table and converts them into given struct slice.
2021-02-08 17:57:21 +08:00
// The parameter `pointer` should be type of *[]struct/*[]*struct. It can create and fill the struct
2020-03-13 17:21:30 +08:00
// slice internally during converting.
//
2021-02-08 17:57:21 +08:00
// The optional parameter `where` is the same as the parameter of Model.Where function,
2020-03-13 17:21:30 +08:00
// see Model.Where.
//
2021-06-22 21:48:56 +08:00
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
// default value and there's no record retrieved with the given conditions from table.
2020-03-13 17:21:30 +08:00
//
2021-06-22 21:48:56 +08:00
// Example:
2020-03-13 17:21:30 +08:00
// users := ([]User)(nil)
2021-06-22 21:48:56 +08:00
// err := db.Model("user").Scan(&users)
2020-03-13 17:21:30 +08:00
//
// users := ([]*User)(nil)
2021-11-16 17:21:13 +08:00
// err := db.Model("user").Scan(&users).
2021-06-22 21:48:56 +08:00
func ( m * Model ) doStructs ( pointer interface { } , where ... interface { } ) error {
model := m
// Auto selecting fields by struct attributes.
if model . fieldsEx == "" && ( model . fields == "" || model . fields == "*" ) {
2021-09-15 22:28:26 +08:00
if v , ok := pointer . ( reflect . Value ) ; ok {
model = m . Fields (
reflect . New (
v . Type ( ) . Elem ( ) ,
) . Interface ( ) ,
)
} else {
model = m . Fields (
reflect . New (
reflect . ValueOf ( pointer ) . Elem ( ) . Type ( ) . Elem ( ) ,
) . Interface ( ) ,
)
}
2021-06-22 21:48:56 +08:00
}
all , err := model . All ( where ... )
2020-03-13 17:21:30 +08:00
if err != nil {
return err
}
2021-02-09 18:00:43 +08:00
if err = all . Structs ( pointer ) ; err != nil {
return err
}
2021-06-22 21:48:56 +08:00
return model . doWithScanStructs ( pointer )
2020-03-13 17:21:30 +08:00
}
2021-02-08 17:57:21 +08:00
// Scan automatically calls Struct or Structs function according to the type of parameter `pointer`.
2021-06-22 21:48:56 +08:00
// It calls function doStruct if `pointer` is type of *struct/**struct.
// It calls function doStructs if `pointer` is type of *[]struct/*[]*struct.
2020-03-13 17:21:30 +08:00
//
2021-06-22 21:48:56 +08:00
// The optional parameter `where` is the same as the parameter of Model.Where function, see Model.Where.
2020-03-13 17:21:30 +08:00
//
2021-06-22 21:48:56 +08:00
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
// default value and there's no record retrieved with the given conditions from table.
2020-03-13 17:21:30 +08:00
//
2021-06-22 21:48:56 +08:00
// Example:
2020-03-13 17:21:30 +08:00
// user := new(User)
2020-12-29 13:30:15 +08:00
// err := db.Model("user").Where("id", 1).Scan(user)
2020-03-13 17:21:30 +08:00
//
// user := (*User)(nil)
2020-12-29 13:30:15 +08:00
// err := db.Model("user").Where("id", 1).Scan(&user)
2020-03-13 17:21:30 +08:00
//
// users := ([]User)(nil)
2020-12-29 13:30:15 +08:00
// err := db.Model("user").Scan(&users)
2020-03-13 17:21:30 +08:00
//
// users := ([]*User)(nil)
2021-11-16 17:21:13 +08:00
// err := db.Model("user").Scan(&users).
2020-03-13 17:21:30 +08:00
func ( m * Model ) Scan ( pointer interface { } , where ... interface { } ) error {
2022-03-11 10:24:42 +08:00
reflectInfo := reflection . OriginTypeAndKind ( pointer )
2021-10-28 23:18:23 +08:00
if reflectInfo . InputKind != reflect . Ptr {
return gerror . NewCode (
gcode . CodeInvalidParameter ,
` the parameter "pointer" for function Scan should type of pointer ` ,
)
2021-06-22 21:48:56 +08:00
}
2021-10-28 23:18:23 +08:00
switch reflectInfo . OriginKind {
2021-06-22 21:48:56 +08:00
case reflect . Slice , reflect . Array :
return m . doStructs ( pointer , where ... )
case reflect . Struct , reflect . Invalid :
return m . doStruct ( pointer , where ... )
default :
2021-07-20 23:02:02 +08:00
return gerror . NewCode (
2021-08-24 21:18:59 +08:00
gcode . CodeInvalidParameter ,
2021-07-20 23:02:02 +08:00
` element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct ` ,
)
2020-03-13 17:21:30 +08:00
}
}
2023-06-01 21:33:40 +08:00
// ScanAndCount scans a single record or record array that matches the given conditions and counts the total number of records that match those conditions.
// If useFieldForCount is true, it will use the fields specified in the model for counting;
// The pointer parameter is a pointer to a struct that the scanned data will be stored in.
// The pointerCount parameter is a pointer to an integer that will be set to the total number of records that match the given conditions.
// The where parameter is an optional list of conditions to use when retrieving records.
//
// Example:
//
// var count int
// user := new(User)
// err := db.Model("user").Where("id", 1).ScanAndCount(user,&count,true)
// fmt.Println(user, count)
//
// Example Join:
//
// type User struct {
// Id int
// Passport string
// Name string
// Age int
// }
// var users []User
// var count int
// db.Model(table).As("u1").
// LeftJoin(tableName2, "u2", "u2.id=u1.id").
// Fields("u1.passport,u1.id,u2.name,u2.age").
// Where("u1.id<2").
// ScanAndCount(&users, &count, false)
func ( m * Model ) ScanAndCount ( pointer interface { } , totalCount * int , useFieldForCount bool ) ( err error ) {
// support Fileds with *, example: .Fileds("a.*, b.name"). Count sql is select count(1) from xxx
countModel := m . Clone ( )
// If useFieldForCount is false, set the fields to a constant value of 1 for counting
if ! useFieldForCount {
countModel . fields = "1"
}
// Get the total count of records
* totalCount , err = countModel . Count ( )
if err != nil {
return err
}
// If the total count is 0, there are no records to retrieve, so return early
if * totalCount == 0 {
return
}
err = m . Scan ( pointer )
return
}
2020-03-13 17:21:30 +08:00
2021-02-08 17:57:21 +08:00
// ScanList converts `r` to struct slice which contains other complex struct attributes.
// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct.
2020-07-05 11:54:37 +08:00
//
2021-11-05 01:07:06 +08:00
// See Result.ScanList.
func ( m * Model ) ScanList ( structSlicePointer interface { } , bindToAttrName string , relationAttrNameAndFields ... string ) ( err error ) {
2022-01-15 21:38:15 +08:00
var result Result
2021-11-23 15:28:40 +08:00
out , err := checkGetSliceElementInfoForScanList ( structSlicePointer , bindToAttrName )
if err != nil {
return err
}
2022-01-15 21:38:15 +08:00
if m . fields != defaultFields || m . fieldsEx != "" {
// There are custom fields.
result , err = m . All ( )
} else {
// Filter fields using temporary created struct using reflect.New.
result , err = m . Fields ( reflect . New ( out . BindToAttrType ) . Interface ( ) ) . All ( )
}
2020-07-05 11:54:37 +08:00
if err != nil {
return err
}
2021-11-05 01:07:06 +08:00
var (
relationAttrName string
relationFields string
)
switch len ( relationAttrNameAndFields ) {
case 2 :
relationAttrName = relationAttrNameAndFields [ 0 ]
relationFields = relationAttrNameAndFields [ 1 ]
case 1 :
relationFields = relationAttrNameAndFields [ 0 ]
}
2021-11-23 15:28:40 +08:00
return doScanList ( doScanListInput {
Model : m ,
Result : result ,
StructSlicePointer : structSlicePointer ,
StructSliceValue : out . SliceReflectValue ,
BindToAttrName : bindToAttrName ,
RelationAttrName : relationAttrName ,
RelationFields : relationFields ,
} )
2020-07-05 11:54:37 +08:00
}
2022-11-16 10:04:49 +08:00
// Value retrieves a specified record value from table and returns the result as interface type.
// It returns nil if there's no record found with the given conditions from table.
//
// If the optional parameter `fieldsAndWhere` is given, the fieldsAndWhere[0] is the selected fields
// and fieldsAndWhere[1:] is treated as where condition fields.
// Also see Model.Fields and Model.Where functions.
func ( m * Model ) Value ( fieldsAndWhere ... interface { } ) ( Value , error ) {
var ctx = m . GetCtx ( )
if len ( fieldsAndWhere ) > 0 {
if len ( fieldsAndWhere ) > 2 {
return m . Fields ( gconv . String ( fieldsAndWhere [ 0 ] ) ) . Where ( fieldsAndWhere [ 1 ] , fieldsAndWhere [ 2 : ] ... ) . Value ( )
} else if len ( fieldsAndWhere ) == 2 {
return m . Fields ( gconv . String ( fieldsAndWhere [ 0 ] ) ) . Where ( fieldsAndWhere [ 1 ] ) . Value ( )
} else {
return m . Fields ( gconv . String ( fieldsAndWhere [ 0 ] ) ) . Value ( )
}
}
var (
sqlWithHolder , holderArgs = m . getFormattedSqlAndArgs ( ctx , queryTypeValue , true )
all , err = m . doGetAllBySql ( ctx , queryTypeValue , sqlWithHolder , holderArgs ... )
)
if err != nil {
return nil , err
}
if len ( all ) > 0 {
if internalData := m . db . GetCore ( ) . GetInternalCtxDataFromCtx ( ctx ) ; internalData != nil {
2023-04-28 14:55:37 +08:00
if v , ok := all [ 0 ] [ internalData . FirstResultColumn ] ; ok {
2022-11-16 10:04:49 +08:00
return v , nil
}
2023-04-28 14:55:37 +08:00
} else {
return nil , gerror . NewCode (
gcode . CodeInternalError ,
` query value error: the internal context data is missing. there's internal issue should be fixed ` ,
)
2022-11-16 10:04:49 +08:00
}
}
return nil , nil
}
2020-03-13 17:21:30 +08:00
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
2021-02-08 17:57:21 +08:00
// The optional parameter `where` is the same as the parameter of Model.Where function,
2020-03-13 17:21:30 +08:00
// see Model.Where.
2022-12-30 16:54:43 +08:00
func ( m * Model ) Count ( where ... interface { } ) ( int , error ) {
2022-03-24 17:51:49 +08:00
var ctx = m . GetCtx ( )
2020-03-13 17:21:30 +08:00
if len ( where ) > 0 {
return m . Where ( where [ 0 ] , where [ 1 : ] ... ) . Count ( )
}
2021-05-27 22:18:16 +08:00
var (
2022-03-24 15:33:30 +08:00
sqlWithHolder , holderArgs = m . getFormattedSqlAndArgs ( ctx , queryTypeCount , false )
all , err = m . doGetAllBySql ( ctx , queryTypeCount , sqlWithHolder , holderArgs ... )
2021-05-27 22:18:16 +08:00
)
2020-03-13 17:21:30 +08:00
if err != nil {
return 0 , err
}
2022-03-24 15:33:30 +08:00
if len ( all ) > 0 {
2022-04-12 21:31:51 +08:00
if internalData := m . db . GetCore ( ) . GetInternalCtxDataFromCtx ( ctx ) ; internalData != nil {
2023-04-28 14:55:37 +08:00
if v , ok := all [ 0 ] [ internalData . FirstResultColumn ] ; ok {
2022-12-30 16:54:43 +08:00
return v . Int ( ) , nil
2022-03-24 15:33:30 +08:00
}
2023-04-28 14:55:37 +08:00
} else {
return 0 , gerror . NewCode (
gcode . CodeInternalError ,
` query count error: the internal context data is missing. there's internal issue should be fixed ` ,
)
2020-03-13 17:21:30 +08:00
}
}
return 0 , nil
}
2021-05-02 12:17:06 +08:00
// CountColumn does "SELECT COUNT(x) FROM ..." statement for the model.
2022-12-30 16:54:43 +08:00
func ( m * Model ) CountColumn ( column string ) ( int , error ) {
2021-05-02 12:17:06 +08:00
if len ( column ) == 0 {
return 0 , nil
}
return m . Fields ( column ) . Count ( )
}
// Min does "SELECT MIN(x) FROM ..." statement for the model.
func ( m * Model ) Min ( column string ) ( float64 , error ) {
if len ( column ) == 0 {
return 0 , nil
}
2021-11-17 21:29:46 +08:00
value , err := m . Fields ( fmt . Sprintf ( ` MIN(%s) ` , m . QuoteWord ( column ) ) ) . Value ( )
2021-05-02 12:17:06 +08:00
if err != nil {
return 0 , err
}
return value . Float64 ( ) , err
}
// Max does "SELECT MAX(x) FROM ..." statement for the model.
func ( m * Model ) Max ( column string ) ( float64 , error ) {
if len ( column ) == 0 {
return 0 , nil
}
2021-11-17 21:29:46 +08:00
value , err := m . Fields ( fmt . Sprintf ( ` MAX(%s) ` , m . QuoteWord ( column ) ) ) . Value ( )
2021-05-02 12:17:06 +08:00
if err != nil {
return 0 , err
}
return value . Float64 ( ) , err
}
// Avg does "SELECT AVG(x) FROM ..." statement for the model.
func ( m * Model ) Avg ( column string ) ( float64 , error ) {
if len ( column ) == 0 {
return 0 , nil
}
2021-11-17 21:29:46 +08:00
value , err := m . Fields ( fmt . Sprintf ( ` AVG(%s) ` , m . QuoteWord ( column ) ) ) . Value ( )
2021-05-02 12:17:06 +08:00
if err != nil {
return 0 , err
}
return value . Float64 ( ) , err
}
// Sum does "SELECT SUM(x) FROM ..." statement for the model.
func ( m * Model ) Sum ( column string ) ( float64 , error ) {
if len ( column ) == 0 {
return 0 , nil
}
2021-11-17 21:29:46 +08:00
value , err := m . Fields ( fmt . Sprintf ( ` SUM(%s) ` , m . QuoteWord ( column ) ) ) . Value ( )
2021-05-02 12:17:06 +08:00
if err != nil {
return 0 , err
}
return value . Float64 ( ) , err
}
2021-06-06 23:06:39 +08:00
// Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement for the model.
func ( m * Model ) Union ( unions ... * Model ) * Model {
return m . db . Union ( unions ... )
}
// UnionAll does "(SELECT xxx FROM xxx) UNION ALL (SELECT xxx FROM xxx) ..." statement for the model.
func ( m * Model ) UnionAll ( unions ... * Model ) * Model {
return m . db . UnionAll ( unions ... )
}
2021-11-17 22:01:17 +08:00
// Limit sets the "LIMIT" statement for the model.
// The parameter `limit` can be either one or two number, if passed two number is passed,
// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
// statement.
func ( m * Model ) Limit ( limit ... int ) * Model {
model := m . getModel ( )
switch len ( limit ) {
case 1 :
model . limit = limit [ 0 ]
case 2 :
model . start = limit [ 0 ]
model . limit = limit [ 1 ]
}
return model
}
// Offset sets the "OFFSET" statement for the model.
// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
func ( m * Model ) Offset ( offset int ) * Model {
model := m . getModel ( )
model . offset = offset
return model
}
// Distinct forces the query to only return distinct results.
func ( m * Model ) Distinct ( ) * Model {
model := m . getModel ( )
model . distinct = "DISTINCT "
return model
}
// Page sets the paging number for the model.
// The parameter `page` is started from 1 for paging.
// Note that, it differs that the Limit function starts from 0 for "LIMIT" statement.
func ( m * Model ) Page ( page , limit int ) * Model {
model := m . getModel ( )
if page <= 0 {
page = 1
}
model . start = ( page - 1 ) * limit
model . limit = limit
return model
}
// Having sets the having statement for the model.
// The parameters of this function usage are as the same as function Where.
// See Where.
func ( m * Model ) Having ( having interface { } , args ... interface { } ) * Model {
model := m . getModel ( )
model . having = [ ] interface { } {
having , args ,
}
return model
}
2022-11-16 10:04:49 +08:00
// doGetAll does "SELECT FROM ..." statement for the model.
// It retrieves the records from table and returns the result as slice type.
// It returns nil if there's no record retrieved with the given conditions from table.
//
// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set.
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
func ( m * Model ) doGetAll ( ctx context . Context , limit1 bool , where ... interface { } ) ( Result , error ) {
if len ( where ) > 0 {
return m . Where ( where [ 0 ] , where [ 1 : ] ... ) . All ( )
}
sqlWithHolder , holderArgs := m . getFormattedSqlAndArgs ( ctx , queryTypeNormal , limit1 )
return m . doGetAllBySql ( ctx , queryTypeNormal , sqlWithHolder , holderArgs ... )
}
2020-07-15 09:15:03 +08:00
// doGetAllBySql does the select statement on the database.
2022-11-16 10:04:49 +08:00
func ( m * Model ) doGetAllBySql ( ctx context . Context , queryType queryType , sql string , args ... interface { } ) ( result Result , err error ) {
2022-03-31 15:42:12 +08:00
if result , err = m . getSelectResultFromCache ( ctx , sql , args ... ) ; err != nil || result != nil {
return
2020-04-26 21:31:55 +08:00
}
2022-03-14 23:47:55 +08:00
in := & HookSelectInput {
internalParamHookSelect : internalParamHookSelect {
internalParamHook : internalParamHook {
2022-05-09 14:22:28 +08:00
link : m . getLink ( false ) ,
2022-03-14 23:47:55 +08:00
} ,
2022-03-24 15:33:30 +08:00
handler : m . hookHandler . Select ,
2022-03-14 23:47:55 +08:00
} ,
2022-05-09 14:22:28 +08:00
Model : m ,
2022-03-23 16:23:33 +08:00
Table : m . tables ,
Sql : sql ,
Args : m . mergeArguments ( args ) ,
2022-03-14 23:47:55 +08:00
}
2022-03-31 15:42:12 +08:00
if result , err = in . Next ( ctx ) ; err != nil {
return
2020-04-26 21:31:55 +08:00
}
2022-03-31 15:42:12 +08:00
2022-11-16 10:04:49 +08:00
err = m . saveSelectResultToCache ( ctx , queryType , result , sql , args ... )
2022-03-31 15:42:12 +08:00
return
2020-04-26 21:31:55 +08:00
}
2021-05-27 22:18:16 +08:00
2023-02-08 19:08:10 +08:00
func ( m * Model ) getFormattedSqlAndArgs (
ctx context . Context , queryType queryType , limit1 bool ,
) ( sqlWithHolder string , holderArgs [ ] interface { } ) {
2021-05-27 22:18:16 +08:00
switch queryType {
case queryTypeCount :
2022-03-24 15:33:30 +08:00
queryFields := "COUNT(1)"
2021-05-27 22:18:16 +08:00
if m . fields != "" && m . fields != "*" {
// DO NOT quote the m.fields here, in case of fields like:
// DISTINCT t.user_id uid
2022-03-24 15:33:30 +08:00
queryFields = fmt . Sprintf ( ` COUNT(%s%s) ` , m . distinct , m . fields )
2021-05-27 22:18:16 +08:00
}
2021-06-21 19:21:38 +08:00
// Raw SQL Model.
if m . rawSql != "" {
2022-03-24 15:33:30 +08:00
sqlWithHolder = fmt . Sprintf ( "SELECT %s FROM (%s) AS T" , queryFields , m . rawSql )
2021-06-21 19:21:38 +08:00
return sqlWithHolder , nil
}
2022-03-24 17:51:49 +08:00
conditionWhere , conditionExtra , conditionArgs := m . formatCondition ( ctx , false , true )
2022-03-24 15:33:30 +08:00
sqlWithHolder = fmt . Sprintf ( "SELECT %s FROM %s%s" , queryFields , m . tables , conditionWhere + conditionExtra )
2021-05-27 22:18:16 +08:00
if len ( m . groupBy ) > 0 {
sqlWithHolder = fmt . Sprintf ( "SELECT COUNT(1) FROM (%s) count_alias" , sqlWithHolder )
}
return sqlWithHolder , conditionArgs
default :
2022-03-24 17:51:49 +08:00
conditionWhere , conditionExtra , conditionArgs := m . formatCondition ( ctx , limit1 , false )
2021-06-06 23:06:39 +08:00
// Raw SQL Model, especially for UNION/UNION ALL featured SQL.
if m . rawSql != "" {
sqlWithHolder = fmt . Sprintf (
"%s%s" ,
m . rawSql ,
conditionWhere + conditionExtra ,
)
return sqlWithHolder , conditionArgs
}
2021-05-27 22:18:16 +08:00
// DO NOT quote the m.fields where, in case of fields like:
// DISTINCT t.user_id uid
sqlWithHolder = fmt . Sprintf (
"SELECT %s%s FROM %s%s" ,
2022-03-24 15:33:30 +08:00
m . distinct , m . getFieldsFiltered ( ) , m . tables , conditionWhere + conditionExtra ,
2021-05-27 22:18:16 +08:00
)
return sqlWithHolder , conditionArgs
}
}
2021-11-17 22:01:17 +08:00
2023-02-08 19:08:10 +08:00
func ( m * Model ) getHolderAndArgsAsSubModel ( ctx context . Context ) ( holder string , args [ ] interface { } ) {
holder , args = m . getFormattedSqlAndArgs (
ctx , queryTypeNormal , false ,
)
args = m . mergeArguments ( args )
return
}
2022-04-30 15:53:56 +08:00
func ( m * Model ) getAutoPrefix ( ) string {
2021-11-17 22:01:17 +08:00
autoPrefix := ""
if gstr . Contains ( m . tables , " JOIN " ) {
autoPrefix = m . db . GetCore ( ) . QuoteWord (
m . db . GetCore ( ) . guessPrimaryTableName ( m . tablesInit ) ,
)
}
2022-04-30 15:53:56 +08:00
return autoPrefix
}
2021-11-17 22:01:17 +08:00
2022-11-16 10:04:49 +08:00
// getFieldsFiltered checks the fields and fieldsEx attributes, filters and returns the fields that will
// really be committed to underlying database driver.
func ( m * Model ) getFieldsFiltered ( ) string {
if m . fieldsEx == "" {
// No filtering, containing special chars.
if gstr . ContainsAny ( m . fields , "()" ) {
return m . fields
}
// No filtering.
if ! gstr . ContainsAny ( m . fields , ". " ) {
return m . db . GetCore ( ) . QuoteString ( m . fields )
}
return m . fields
}
var (
fieldsArray [ ] string
fieldsExSet = gset . NewStrSetFrom ( gstr . SplitAndTrim ( m . fieldsEx , "," ) )
)
if m . fields != "*" {
// Filter custom fields with fieldEx.
fieldsArray = make ( [ ] string , 0 , 8 )
for _ , v := range gstr . SplitAndTrim ( m . fields , "," ) {
fieldsArray = append ( fieldsArray , v [ gstr . PosR ( v , "-" ) + 1 : ] )
}
} else {
if gstr . Contains ( m . tables , " " ) {
panic ( "function FieldsEx supports only single table operations" )
}
// Filter table fields with fieldEx.
tableFields , err := m . TableFields ( m . tablesInit )
if err != nil {
panic ( err )
}
if len ( tableFields ) == 0 {
panic ( fmt . Sprintf ( ` empty table fields for table "%s" ` , m . tables ) )
}
fieldsArray = make ( [ ] string , len ( tableFields ) )
for k , v := range tableFields {
fieldsArray [ v . Index ] = k
}
}
newFields := ""
for _ , k := range fieldsArray {
if fieldsExSet . Contains ( k ) {
continue
}
if len ( newFields ) > 0 {
newFields += ","
}
newFields += m . db . GetCore ( ) . QuoteWord ( k )
}
return newFields
}
2022-04-30 15:53:56 +08:00
// formatCondition formats where arguments of the model and returns a new condition sql and its arguments.
// Note that this function does not change any attribute value of the `m`.
//
// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set.
2023-02-15 09:45:40 +08:00
func ( m * Model ) formatCondition (
ctx context . Context , limit1 bool , isCountStatement bool ,
) ( conditionWhere string , conditionExtra string , conditionArgs [ ] interface { } ) {
2022-04-30 15:53:56 +08:00
var autoPrefix = m . getAutoPrefix ( )
2021-11-17 22:01:17 +08:00
// GROUP BY.
if m . groupBy != "" {
conditionExtra += " GROUP BY " + m . groupBy
}
2022-04-30 15:53:56 +08:00
// WHERE
conditionWhere , conditionArgs = m . whereBuilder . Build ( )
2022-05-06 22:21:43 +08:00
softDeletingCondition := m . getConditionForSoftDeleting ( )
if m . rawSql != "" && conditionWhere != "" {
if gstr . ContainsI ( m . rawSql , " WHERE " ) {
conditionWhere = " AND " + conditionWhere
} else {
conditionWhere = " WHERE " + conditionWhere
}
} else if ! m . unscoped && softDeletingCondition != "" {
if conditionWhere == "" {
conditionWhere = fmt . Sprintf ( ` WHERE %s ` , softDeletingCondition )
} else {
conditionWhere = fmt . Sprintf ( ` WHERE (%s) AND %s ` , conditionWhere , softDeletingCondition )
}
} else {
if conditionWhere != "" {
conditionWhere = " WHERE " + conditionWhere
}
}
2021-11-17 22:01:17 +08:00
// HAVING.
if len ( m . having ) > 0 {
2022-04-30 15:53:56 +08:00
havingHolder := WhereHolder {
2021-12-28 17:15:01 +08:00
Where : m . having [ 0 ] ,
Args : gconv . Interfaces ( m . having [ 1 ] ) ,
Prefix : autoPrefix ,
}
2022-03-24 17:51:49 +08:00
havingStr , havingArgs := formatWhereHolder ( ctx , m . db , formatWhereHolderInput {
2022-04-30 15:53:56 +08:00
WhereHolder : havingHolder ,
OmitNil : m . option & optionOmitNilWhere > 0 ,
OmitEmpty : m . option & optionOmitEmptyWhere > 0 ,
Schema : m . schema ,
Table : m . tables ,
2021-11-17 22:01:17 +08:00
} )
if len ( havingStr ) > 0 {
conditionExtra += " HAVING " + havingStr
conditionArgs = append ( conditionArgs , havingArgs ... )
}
}
// ORDER BY.
if m . orderBy != "" {
conditionExtra += " ORDER BY " + m . orderBy
}
// LIMIT.
if ! isCountStatement {
if m . limit != 0 {
if m . start >= 0 {
conditionExtra += fmt . Sprintf ( " LIMIT %d,%d" , m . start , m . limit )
} else {
conditionExtra += fmt . Sprintf ( " LIMIT %d" , m . limit )
}
} else if limit1 {
conditionExtra += " LIMIT 1"
}
if m . offset >= 0 {
conditionExtra += fmt . Sprintf ( " OFFSET %d" , m . offset )
}
}
if m . lockInfo != "" {
conditionExtra += " " + m . lockInfo
}
return
}