调整目录结构,更适合拆分驱动

This commit is contained in:
飞哥(fizzday)
2024-04-03 14:25:18 +08:00
parent 1881c4220f
commit 2485391ce2
24 changed files with 337 additions and 466 deletions

40
builder/builder.go Normal file
View File

@@ -0,0 +1,40 @@
package builder
type IBuilder interface {
ToSql() (sql4prepare string, binds []any, err error)
ToSqlSelect() (sql4prepare string, binds []any)
ToSqlTable() (sql4prepare string, values []any, err error)
ToSqlJoin() (sql4prepare string, binds []any, err error)
ToSqlWhere() (sql4prepare string, values []any, err error)
ToSqlOrderBy() (sql4prepare string)
ToSqlLimitOffset() (sqlSegment string, binds []any)
ToSqlInsert(obj any, args ...TypeToSqlInsertCase) (sqlSegment string, binds []any, err error)
ToSqlDelete(obj any, mustColumn ...string) (sqlSegment string, binds []any, err error)
ToSqlUpdate(obj any, mustColumn ...string) (sqlSegment string, binds []any, err error)
ToSqlIncDec(symbol string, data map[string]any) (sql4prepare string, values []any, err error)
}
// LimitOffsetClause 存储LIMIT和OFFSET信息。
type LimitOffsetClause struct {
Limit int
Offset int
Page int
}
type TypeToSqlUpdateCase struct {
BindOrData any
MustColumn []string
}
type TypeToSqlIncDecCase struct {
Symbol string
Data map[string]any
}
type TypeToSqlInsertCase struct {
IsReplace bool
IsIgnoreCase bool
OnDuplicateKeys []string
UpdateFields []string
MustColumn []string
}

View File

@@ -1,4 +1,4 @@
package gorose
package builder
type Context struct {
TableClause TableClause

View File

@@ -1,4 +1,4 @@
package gorose
package builder
type TypeGroupItem struct {
Column string

View File

@@ -1,4 +1,4 @@
package gorose
package builder
// HavingClause 类似于WhereClause但应用于HAVING子句。
type HavingClause struct {

30
builder/helper.go Normal file
View File

@@ -0,0 +1,30 @@
package builder
import (
"reflect"
"strings"
)
func IsExpression(obj any) (b bool) {
rfv := reflect.Indirect(reflect.ValueOf(obj))
if rfv.Kind() == reflect.String && strings.Contains(rfv.String(), "?") {
b = true
}
return
}
func ToSlice(arg any) []any {
ref := reflect.Indirect(reflect.ValueOf(arg))
var res []any
switch ref.Kind() {
case reflect.Slice:
l := ref.Len()
v := ref.Slice(0, l)
for i := 0; i < l; i++ {
res = append(res, v.Index(i).Interface())
}
default:
res = append(res, ref.Interface())
}
return res
}

View File

@@ -8,9 +8,11 @@
// db.xxx.LeftJoin(xxx, <same as before>)
// db.Table(gorose.As("users", "a")).Join(gorose.As("card", "b"), "a.id", "b.uid")
package gorose
package builder
import "errors"
import (
"errors"
)
type IJoinOn interface {
On(column string, args ...string) IJoinOn

View File

@@ -1,4 +1,4 @@
package gorose
package builder
type OrderByItem struct {
Column string
@@ -22,6 +22,8 @@ func (db *OrderByClause) OrderBy(column string, directions ...string) {
Direction: direction,
})
}
// OrderByRaw adds a Raw ORDER BY clause to the query.
func (db *OrderByClause) OrderByRaw(column string) {
db.Columns = append(db.Columns, OrderByItem{
Column: column,

View File

@@ -1,4 +1,4 @@
package gorose
package builder
import "strings"

View File

@@ -1,4 +1,4 @@
package gorose
package builder
// TableClause table clause
type TableClause struct {
@@ -6,13 +6,6 @@ type TableClause struct {
Alias string
}
func As(table any, alias string) TableClause {
return TableClause{
Tables: table,
Alias: alias,
}
}
// Table sets the table name for the query.
func (db *TableClause) Table(table any, alias ...string) {
var as string

View File

@@ -1,4 +1,4 @@
package gorose
package builder
import (
"errors"
@@ -389,6 +389,10 @@ func (w *WhereClause) WhereNot(column any, args ...any) IWhere {
w.Not = true
return w.Where(column, args...)
}
func (w *WhereClause) OrWhereNot(column any, args ...any) IWhere {
w.Not = true
return w.OrWhere(column, args...)
}
func (w *WhereClause) addTypeWhereRaw(boolean string, value string, bindings []any) *WhereClause {
w.Conditions = append(w.Conditions, TypeWhereRaw{LogicalOp: boolean, Column: value, Bindings: bindings})

View File

@@ -3,34 +3,22 @@ package gorose
import (
"database/sql"
"fmt"
"github.com/gohouse/gorose/v3/builder"
"github.com/gohouse/gorose/v3/driver"
"reflect"
)
type IBuilder interface {
ToSql() (sql4prepare string, binds []any, err error)
ToSqlSelect() (sql4prepare string, binds []any)
ToSqlTable() (sql4prepare string, values []any, err error)
ToSqlJoin() (sql4prepare string, binds []any, err error)
ToSqlWhere() (sql4prepare string, values []any, err error)
ToSqlOrderBy() (sql4prepare string)
ToSqlLimitOffset() (sqlSegment string, binds []any)
ToSqlInsert(obj any, args ...TypeToSqlInsertCase) (sqlSegment string, binds []any, err error)
ToSqlDelete(obj any, mustColumn ...string) (sqlSegment string, binds []any, err error)
ToSqlUpdate(obj any, mustColumn ...string) (sqlSegment string, binds []any, err error)
ToSqlIncDec(symbol string, data map[string]any) (sql4prepare string, values []any, err error)
}
type Database struct {
*Engin
Driver IDriver
Context *Context
Driver driver.IDriver
Context *builder.Context
}
func NewDatabase(g *GoRose) *Database {
return &Database{
Driver: GetDriver(g.driver),
Driver: driver.GetDriver(g.driver),
Engin: NewEngin(g),
Context: NewContext(g.prefix),
Context: builder.NewContext(g.prefix),
}
}
func (db *Database) Table(table any, alias ...string) *Database {
@@ -228,7 +216,7 @@ func (db *Database) queryToBindResult(bind any, query string, args ...any) (err
return db.Engin.QueryTo(bind, query, args...)
}
func (db *Database) insert(obj any, arg TypeToSqlInsertCase) (res sql.Result, err error) {
func (db *Database) insert(obj any, arg builder.TypeToSqlInsertCase) (res sql.Result, err error) {
//segment, binds, err := db.ToSqlInsert(obj, ignoreCase, onDuplicateKeys, mustColumn...)
segment, binds, err := db.ToSqlInsert(obj, arg)
if err != nil {
@@ -237,7 +225,7 @@ func (db *Database) insert(obj any, arg TypeToSqlInsertCase) (res sql.Result, er
return db.Engin.Exec(segment, binds...)
}
func (db *Database) Insert(obj any, mustColumn ...string) (affectedRows int64, err error) {
result, err := db.insert(obj, TypeToSqlInsertCase{MustColumn: mustColumn})
result, err := db.insert(obj, builder.TypeToSqlInsertCase{MustColumn: mustColumn})
if err != nil {
return affectedRows, err
}
@@ -248,7 +236,7 @@ func (db *Database) Insert(obj any, mustColumn ...string) (affectedRows int64, e
//
// 参考 https://laravel.com/docs/10.x/queries#auto-incrementing-ids
func (db *Database) InsertGetId(obj any, mustColumn ...string) (lastInsertId int64, err error) {
result, err := db.insert(obj, TypeToSqlInsertCase{MustColumn: mustColumn})
result, err := db.insert(obj, builder.TypeToSqlInsertCase{MustColumn: mustColumn})
if err != nil {
return lastInsertId, err
}
@@ -259,7 +247,7 @@ func (db *Database) InsertGetId(obj any, mustColumn ...string) (lastInsertId int
//
// 参考 https://laravel.com/docs/10.x/queries#insert-statements
func (db *Database) InsertOrIgnore(obj any, mustColumn ...string) (affectedRows int64, err error) {
result, err := db.insert(obj, TypeToSqlInsertCase{IsIgnoreCase: true, MustColumn: mustColumn})
result, err := db.insert(obj, builder.TypeToSqlInsertCase{IsIgnoreCase: true, MustColumn: mustColumn})
if err != nil {
return affectedRows, err
}
@@ -273,7 +261,7 @@ func (db *Database) InsertOrIgnore(obj any, mustColumn ...string) (affectedRows
//
// eg: Upsert(obj, []string{"id"}, []string{"age"}, "id", "name")
func (db *Database) Upsert(obj any, onDuplicateKeys, updateFields []string, mustColumn ...string) (affectedRows int64, err error) {
result, err := db.insert(obj, TypeToSqlInsertCase{OnDuplicateKeys: onDuplicateKeys, UpdateFields: updateFields, MustColumn: mustColumn})
result, err := db.insert(obj, builder.TypeToSqlInsertCase{OnDuplicateKeys: onDuplicateKeys, UpdateFields: updateFields, MustColumn: mustColumn})
if err != nil {
return affectedRows, err
}
@@ -284,7 +272,7 @@ func (db *Database) Upsert(obj any, onDuplicateKeys, updateFields []string, must
//
// 参考 mysql replace into 用法
func (db *Database) Replace(obj any, mustColumn ...string) (affectedRows int64, err error) {
result, err := db.insert(obj, TypeToSqlInsertCase{IsReplace: true, MustColumn: mustColumn})
result, err := db.insert(obj, builder.TypeToSqlInsertCase{IsReplace: true, MustColumn: mustColumn})
if err != nil {
return affectedRows, err
}
@@ -421,7 +409,7 @@ func (db *Database) DoesntExist(bind ...any) (b bool, err error) {
b, err = db.Exists(bind...)
return !b, err
}
func (db *Database) Union(b IBuilder, unionAll ...bool) (res []map[string]any, err error) {
func (db *Database) Union(b builder.IBuilder, unionAll ...bool) (res []map[string]any, err error) {
prepare, values, err := db.ToSql()
if err != nil {
return res, err
@@ -437,7 +425,7 @@ func (db *Database) Union(b IBuilder, unionAll ...bool) (res []map[string]any, e
err = db.queryToBindResult(&res, fmt.Sprintf("%s %s %s", prepare, union, sql4prepare), append(values, binds...))
return
}
func (db *Database) UnionAll(b IBuilder) (res []map[string]any, err error) {
func (db *Database) UnionAll(b builder.IBuilder) (res []map[string]any, err error) {
return db.Union(b, true)
}
@@ -451,14 +439,14 @@ func (db *Database) Truncate(obj ...any) (affectedRows int64, err error) {
if err != nil {
return
}
return db.Engin.execute(fmt.Sprintf("TRUNCATE TABLE %s", BackQuotes(table)))
return db.Engin.execute(fmt.Sprintf("TRUNCATE TABLE %s", table))
}
type TxHandler func() *Database
func (db *Database) Begin() (tx TxHandler, err error) {
return func() *Database {
db.Context = NewContext(db.prefix)
db.Context = builder.NewContext(db.prefix)
return db
}, db.Engin.Begin()
}

View File

@@ -1,43 +0,0 @@
package gorose
import (
"sync"
)
type IDriver interface {
ToSqlSelect(c *Context) (sql4prepare string, binds []any)
ToSqlTable(c *Context) (sql4prepare string, values []any, err error)
ToSqlJoin(c *Context) (sql4prepare string, binds []any, err error)
ToSqlWhere(c *Context) (sql4prepare string, values []any, err error)
ToSqlOrderBy(c *Context) (sql4prepare string)
ToSqlLimitOffset(c *Context) (sqlSegment string, binds []any)
ToSql(c *Context) (sql4prepare string, binds []any, err error)
ToSqlInsert(c *Context, obj any, args ...TypeToSqlInsertCase) (sqlSegment string, binds []any, err error)
ToSqlUpdate(c *Context, arg any) (sqlSegment string, binds []any, err error)
ToSqlDelete(c *Context, obj any, mustColumn ...string) (sqlSegment string, binds []any, err error)
}
var driverMap = map[string]IDriver{}
var driverLock sync.RWMutex
func Register(driver string, parser IDriver) {
driverLock.Lock()
defer driverLock.Unlock()
driverMap[driver] = parser
}
func GetDriver(driver string) IDriver {
driverLock.RLock()
defer driverLock.RUnlock()
return driverMap[driver]
}
func DriverList() (dr []string) {
driverLock.RLock()
defer driverLock.RUnlock()
for d := range driverMap {
dr = append(dr, d)
}
return
}

44
driver/driver.go Normal file
View File

@@ -0,0 +1,44 @@
package driver
import (
"github.com/gohouse/gorose/v3/builder"
"sync"
)
type IDriver interface {
ToSqlSelect(c *builder.Context) (sql4prepare string, binds []any)
ToSqlTable(c *builder.Context) (sql4prepare string, values []any, err error)
ToSqlJoin(c *builder.Context) (sql4prepare string, binds []any, err error)
ToSqlWhere(c *builder.Context) (sql4prepare string, values []any, err error)
ToSqlOrderBy(c *builder.Context) (sql4prepare string)
ToSqlLimitOffset(c *builder.Context) (sqlSegment string, binds []any)
ToSql(c *builder.Context) (sql4prepare string, binds []any, err error)
ToSqlInsert(c *builder.Context, obj any, args ...builder.TypeToSqlInsertCase) (sqlSegment string, binds []any, err error)
ToSqlUpdate(c *builder.Context, arg any) (sqlSegment string, binds []any, err error)
ToSqlDelete(c *builder.Context, obj any, mustColumn ...string) (sqlSegment string, binds []any, err error)
}
var driverMap = map[string]IDriver{}
var driverLock sync.RWMutex
func Register(driver string, parser IDriver) {
driverLock.Lock()
defer driverLock.Unlock()
driverMap[driver] = parser
}
func GetDriver(driver string) IDriver {
driverLock.RLock()
defer driverLock.RUnlock()
return driverMap[driver]
}
func DriverList() (dr []string) {
driverLock.RLock()
defer driverLock.RUnlock()
for d := range driverMap {
dr = append(dr, d)
}
return
}

View File

@@ -3,8 +3,11 @@ package mysql
import (
"errors"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/gohouse/gorose/v3"
"github.com/gohouse/gorose/v3/builder"
"github.com/gohouse/gorose/v3/parser"
//_ "github.com/go-sql-driver/mysql"
"github.com/gohouse/gorose/v3/driver"
"reflect"
"strings"
)
@@ -16,10 +19,10 @@ type Builder struct {
}
func init() {
gorose.Register(DriverName, &Builder{})
driver.Register(DriverName, &Builder{})
}
func (b Builder) ToSql(c *gorose.Context) (sql4prepare string, binds []any, err error) {
func (b Builder) ToSql(c *builder.Context) (sql4prepare string, binds []any, err error) {
selects, anies := b.ToSqlSelect(c)
table, binds2, err := b.ToSqlTable(c)
if err != nil {
@@ -49,7 +52,7 @@ func (b Builder) ToSql(c *gorose.Context) (sql4prepare string, binds []any, err
return
}
func (Builder) ToSqlSelect(c *gorose.Context) (sql4prepare string, binds []any) {
func (Builder) ToSqlSelect(c *builder.Context) (sql4prepare string, binds []any) {
var cols []string
for _, col := range c.SelectClause.Columns {
if col.IsRaw {
@@ -74,12 +77,12 @@ func (Builder) ToSqlSelect(c *gorose.Context) (sql4prepare string, binds []any)
return
}
func (b Builder) ToSqlTable(c *gorose.Context) (sql4prepare string, binds []any, err error) {
func (b Builder) ToSqlTable(c *builder.Context) (sql4prepare string, binds []any, err error) {
return b.buildSqlTable(c.TableClause, c.Prefix)
}
func (b Builder) buildSqlTable(tab gorose.TableClause, prefix string) (sql4prepare string, binds []any, err error) {
if v, ok := tab.Tables.(gorose.IBuilder); ok {
func (b Builder) buildSqlTable(tab builder.TableClause, prefix string) (sql4prepare string, binds []any, err error) {
if v, ok := tab.Tables.(builder.IBuilder); ok {
sql4prepare, binds, err = v.ToSql()
if tab.Alias != "" {
sql4prepare = fmt.Sprintf("(%s) %s", sql4prepare, BackQuotes(tab.Alias))
@@ -106,34 +109,29 @@ func (b Builder) buildSqlTable(tab gorose.TableClause, prefix string) (sql4prepa
return strings.TrimSpace(fmt.Sprintf("%s %s", sql4prepare, BackQuotes(tab.Alias))), binds, err
}
func (b Builder) toSqlWhere(c *gorose.Context, wc gorose.WhereClause) (sql4prepare string, binds []any, err error) {
func (b Builder) toSqlWhere(c *builder.Context, wc builder.WhereClause) (sql4prepare string, binds []any, err error) {
if len(wc.Conditions) == 0 {
return
}
var sql4prepareArr []string
for _, v := range wc.Conditions {
switch item := v.(type) {
case gorose.TypeWhereRaw:
//item := v.(gorose.TypeWhereRaw)
case builder.TypeWhereRaw:
sql4prepareArr = append(sql4prepareArr, fmt.Sprintf("%s %s", item.LogicalOp, item.Column))
binds = append(binds, item.Bindings...)
case gorose.TypeWhereStandard:
//item := v.(gorose.TypeWhereStandard)
case builder.TypeWhereStandard:
sql4prepareArr = append(sql4prepareArr, fmt.Sprintf("%s %s %s ?", item.LogicalOp, BackQuotes(item.Column), item.Operator))
binds = append(binds, item.Value)
case gorose.TypeWhereIn:
//item := v.(gorose.TypeWhereIn)
case builder.TypeWhereIn:
values := ToSlice(item.Value)
sql4prepareArr = append(sql4prepareArr, fmt.Sprintf("%s %s %s (%s)", item.LogicalOp, BackQuotes(item.Column), item.Operator, strings.Repeat("?,", len(values)-1)+"?"))
binds = append(binds, values...)
case gorose.TypeWhereBetween:
//item := v.(gorose.TypeWhereBetween)
case builder.TypeWhereBetween:
values := ToSlice(item.Value)
sql4prepareArr = append(sql4prepareArr, fmt.Sprintf("%s %s %s ? AND ?", item.LogicalOp, BackQuotes(item.Column), item.Operator))
binds = append(binds, values...)
case gorose.TypeWhereNested:
//item := v.(gorose.TypeWhereNested)
var tmp = gorose.Context{}
case builder.TypeWhereNested:
var tmp = builder.Context{}
item.WhereNested(&tmp.WhereClause)
prepare, anies, err := b.ToSqlWhere(&tmp)
if err != nil {
@@ -141,17 +139,15 @@ func (b Builder) toSqlWhere(c *gorose.Context, wc gorose.WhereClause) (sql4prepa
}
sql4prepareArr = append(sql4prepareArr, fmt.Sprintf("%s (%s)", item.LogicalOp, strings.TrimPrefix(prepare, "WHERE ")))
binds = append(binds, anies...)
case gorose.TypeWhereSubQuery:
//item := v.(gorose.TypeWhereSubQuery)
case builder.TypeWhereSubQuery:
query, anies, err := item.SubQuery.ToSql()
if err != nil {
return sql4prepare, binds, err
}
sql4prepareArr = append(sql4prepareArr, fmt.Sprintf("%s %s %s (%s)", item.LogicalOp, BackQuotes(item.Column), item.Operator, query))
binds = append(binds, anies...)
case gorose.TypeWhereSubHandler:
//item := v.(gorose.TypeWhereSubQuery)
var ctx = gorose.NewContext(c.Prefix)
case builder.TypeWhereSubHandler:
var ctx = builder.NewContext(c.Prefix)
item.Sub(ctx)
query, anies, err := b.ToSql(ctx)
if err != nil {
@@ -166,7 +162,7 @@ func (b Builder) toSqlWhere(c *gorose.Context, wc gorose.WhereClause) (sql4prepa
}
return
}
func (b Builder) ToSqlWhere(c *gorose.Context) (sql4prepare string, binds []any, err error) {
func (b Builder) ToSqlWhere(c *builder.Context) (sql4prepare string, binds []any, err error) {
sql4prepare, binds, err = b.toSqlWhere(c, c.WhereClause)
if sql4prepare != "" {
if c.WhereClause.Not {
@@ -177,7 +173,7 @@ func (b Builder) ToSqlWhere(c *gorose.Context) (sql4prepare string, binds []any,
return
}
func (b Builder) ToSqlJoin(c *gorose.Context) (sql4prepare string, binds []any, err error) {
func (b Builder) ToSqlJoin(c *builder.Context) (sql4prepare string, binds []any, err error) {
if c.JoinClause.Err != nil {
return sql4prepare, binds, c.JoinClause.Err
}
@@ -189,19 +185,19 @@ func (b Builder) ToSqlJoin(c *gorose.Context) (sql4prepare string, binds []any,
var sql4 string
var bind []any
switch item := v.(type) {
case gorose.TypeJoinStandard:
case builder.TypeJoinStandard:
prepare, bind, err = b.buildSqlTable(item.TableClause, c.Prefix)
if err != nil {
return
}
sql4 = fmt.Sprintf("%s %s ON %s %s %s", item.Type, prepare, BackQuotes(item.Column1), item.Operator, BackQuotes(item.Column2))
case gorose.TypeJoinSub:
case builder.TypeJoinSub:
sql4, bind, err = item.ToSql()
if err != nil {
return
}
case gorose.TypeJoinOn:
var tjo gorose.TypeJoinOnCondition
case builder.TypeJoinOn:
var tjo builder.TypeJoinOnCondition
item.OnClause(&tjo)
if len(tjo.Conditions) == 0 {
return
@@ -219,7 +215,7 @@ func (b Builder) ToSqlJoin(c *gorose.Context) (sql4prepare string, binds []any,
return
}
func (b Builder) ToSqlGroupBy(c *gorose.Context) (sql4prepare string) {
func (b Builder) ToSqlGroupBy(c *builder.Context) (sql4prepare string) {
if len(c.GroupClause.Groups) > 0 {
var tmp []string
for _, col := range c.GroupClause.Groups {
@@ -233,14 +229,14 @@ func (b Builder) ToSqlGroupBy(c *gorose.Context) (sql4prepare string) {
}
return
}
func (b Builder) ToSqlHaving(c *gorose.Context) (sql4prepare string, binds []any, err error) {
func (b Builder) ToSqlHaving(c *builder.Context) (sql4prepare string, binds []any, err error) {
sql4prepare, binds, err = b.toSqlWhere(c, c.HavingClause.WhereClause)
if sql4prepare != "" {
sql4prepare = fmt.Sprintf("HAVING %s", sql4prepare)
}
return
}
func (b Builder) ToSqlOrderBy(c *gorose.Context) (sql4prepare string) {
func (b Builder) ToSqlOrderBy(c *builder.Context) (sql4prepare string) {
if len(c.OrderByClause.Columns) == 0 {
return
}
@@ -260,7 +256,7 @@ func (b Builder) ToSqlOrderBy(c *gorose.Context) (sql4prepare string) {
return
}
func (b Builder) ToSqlLimitOffset(c *gorose.Context) (sqlSegment string, binds []any) {
func (b Builder) ToSqlLimitOffset(c *builder.Context) (sqlSegment string, binds []any) {
var offset int
if c.LimitOffsetClause.Offset > 0 {
offset = c.LimitOffsetClause.Offset
@@ -279,11 +275,9 @@ func (b Builder) ToSqlLimitOffset(c *gorose.Context) (sqlSegment string, binds [
return
}
//func (b Builder) ToSqlInsert(c *gorose.Context, obj any, ignoreCase string, onDuplicateKeys []string, mustColumn ...string) (sqlSegment string, binds []any, err error) {
// ToSqlInsert insert
func (b Builder) ToSqlInsert(c *gorose.Context, obj any, args ...gorose.TypeToSqlInsertCase) (sqlSegment string, binds []any, err error) {
var arg gorose.TypeToSqlInsertCase
func (b Builder) ToSqlInsert(c *builder.Context, obj any, args ...builder.TypeToSqlInsertCase) (sqlSegment string, binds []any, err error) {
var arg builder.TypeToSqlInsertCase
if len(args) > 0 {
arg = args[0]
}
@@ -292,7 +286,7 @@ func (b Builder) ToSqlInsert(c *gorose.Context, obj any, args ...gorose.TypeToSq
switch rfv.Kind() {
case reflect.Struct:
var datas []map[string]any
datas, err = gorose.StructsToInsert(obj, arg.MustColumn...)
datas, err = parser.StructsToInsert(obj, arg.MustColumn...)
if err != nil {
return
}
@@ -303,7 +297,7 @@ func (b Builder) ToSqlInsert(c *gorose.Context, obj any, args ...gorose.TypeToSq
case reflect.Struct:
c.TableClause.Table(obj)
var datas []map[string]any
datas, err = gorose.StructsToInsert(obj, arg.MustColumn...)
datas, err = parser.StructsToInsert(obj, arg.MustColumn...)
if err != nil {
return
}
@@ -316,12 +310,12 @@ func (b Builder) ToSqlInsert(c *gorose.Context, obj any, args ...gorose.TypeToSq
}
}
func (b Builder) ToSqlDelete(c *gorose.Context, obj any, mustColumn ...string) (sqlSegment string, binds []any, err error) {
func (b Builder) ToSqlDelete(c *builder.Context, obj any, mustColumn ...string) (sqlSegment string, binds []any, err error) {
var ctx = *c
rfv := reflect.Indirect(reflect.ValueOf(obj))
switch rfv.Kind() {
case reflect.Struct:
data, err := gorose.StructToDelete(obj, mustColumn...)
data, err := parser.StructToDelete(obj, mustColumn...)
if err != nil {
return sqlSegment, binds, err
}
@@ -337,22 +331,22 @@ func (b Builder) ToSqlDelete(c *gorose.Context, obj any, mustColumn ...string) (
return
}
func (b Builder) ToSqlUpdate(c *gorose.Context, arg any) (sqlSegment string, binds []any, err error) {
func (b Builder) ToSqlUpdate(c *builder.Context, arg any) (sqlSegment string, binds []any, err error) {
switch v := arg.(type) {
case gorose.TypeToSqlUpdateCase:
case builder.TypeToSqlUpdateCase:
return b.toSqlUpdate(c, v.BindOrData, v.MustColumn...)
case gorose.TypeToSqlIncDecCase:
case builder.TypeToSqlIncDecCase:
return b.toSqlIncDec(c, v.Symbol, v.Data)
default:
return
}
}
func (b Builder) toSqlUpdate(c *gorose.Context, obj any, mustColumn ...string) (sqlSegment string, binds []any, err error) {
func (b Builder) toSqlUpdate(c *builder.Context, obj any, mustColumn ...string) (sqlSegment string, binds []any, err error) {
rfv := reflect.Indirect(reflect.ValueOf(obj))
switch rfv.Kind() {
case reflect.Struct:
dataMap, pk, pkValue, err := gorose.StructToUpdate(obj, mustColumn...)
dataMap, pk, pkValue, err := parser.StructToUpdate(obj, mustColumn...)
if err != nil {
return sqlSegment, binds, err
}
@@ -370,7 +364,7 @@ func (b Builder) toSqlUpdate(c *gorose.Context, obj any, mustColumn ...string) (
}
}
func (b Builder) toSqlIncDec(c *gorose.Context, symbol string, data map[string]any) (sql4prepare string, values []any, err error) {
func (b Builder) toSqlIncDec(c *builder.Context, symbol string, data map[string]any) (sql4prepare string, values []any, err error) {
prepare, anies, err := b.ToSqlTable(c)
if err != nil {
return sql4prepare, values, err

View File

@@ -3,18 +3,19 @@ package mysql
import (
"errors"
"fmt"
"github.com/gohouse/gorose/v3"
"github.com/gohouse/gorose/v3/builder"
"github.com/gohouse/gorose/v3/parser"
"reflect"
"sort"
"strings"
)
func (b Builder) buildTableName(rft reflect.Type, prefix string) (tab string) {
return BackQuotes(fmt.Sprintf("%s%s", prefix, gorose.StructsToTableName(rft)))
return BackQuotes(fmt.Sprintf("%s%s", prefix, parser.StructsToTableName(rft)))
}
// func (b Builder) toSqlInsert(c *gorose.Context, data any, ignoreCase string, onDuplicateKeys []string) (sql4prepare string, values []any, err error) {
func (b Builder) toSqlInsert(c *gorose.Context, data any, insertCase gorose.TypeToSqlInsertCase) (sql4prepare string, values []any, err error) {
func (b Builder) toSqlInsert(c *builder.Context, data any, insertCase builder.TypeToSqlInsertCase) (sql4prepare string, values []any, err error) {
rfv := reflect.Indirect(reflect.ValueOf(data))
var fields []string
var valuesPlaceholderArr []string
@@ -90,7 +91,7 @@ func (b Builder) toSqlInsert(c *gorose.Context, data any, insertCase gorose.Type
return
}
func (b Builder) toSqlUpdateReal(c *gorose.Context, data any) (sql4prepare string, values []any, err error) {
func (b Builder) toSqlUpdateReal(c *builder.Context, data any) (sql4prepare string, values []any, err error) {
rfv := reflect.Indirect(reflect.ValueOf(data))
var updates []string
switch rfv.Kind() {
@@ -123,7 +124,7 @@ func (b Builder) toSqlUpdateReal(c *gorose.Context, data any) (sql4prepare strin
return
}
func (b Builder) toSqlDelete(c *gorose.Context) (sql4prepare string, values []any, err error) {
func (b Builder) toSqlDelete(c *builder.Context) (sql4prepare string, values []any, err error) {
var tables string
tables, _, err = b.ToSqlTable(c)
if err != nil {

View File

@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"errors"
"github.com/gohouse/gorose/v3/parser"
"log/slog"
"reflect"
)
@@ -154,7 +155,7 @@ func (s *Engin) rowsToBind(rows *sql.Rows, bind any) (err error) {
func (s *Engin) rowsToStruct(rows *sql.Rows, rfv reflect.Value) error {
//FieldTag, FieldStruct, _ := structsParse(rfv)
FieldTag, FieldStruct, _ := structsTypeParse(rfv.Type())
FieldTag, FieldStruct, _ := parser.StructsTypeParse(rfv.Type())
defer rows.Close()
@@ -167,14 +168,6 @@ func (s *Engin) rowsToStruct(rows *sql.Rows, rfv reflect.Value) error {
count := len(columns)
for rows.Next() {
// 要先扫描到map, 再做字段比对, 因为这里不确定具体字段数量
// 主要针对 select * 或者直接sql语句
//todo 如果是由struct转换而来, 可以新开一个方法, 不需要做转换比对过程
//entry, err := s.rowsToMapSingle(rows, columns, count)
//if err != nil {
// return err
//}
if rfv.Kind() == reflect.Slice {
rfvItem := reflect.Indirect(reflect.New(rfv.Type().Elem()))
err = s.scanStructRow(rfvItem, rows, count, FieldTag, FieldStruct, columns)

View File

@@ -2,7 +2,8 @@ package gorose
import (
"database/sql"
"math"
"github.com/gohouse/gorose/v3/builder"
_ "github.com/gohouse/gorose/v3/driver/mysql"
)
type GoRose struct {
@@ -14,10 +15,10 @@ type GoRose struct {
handlers HandlersChain
}
type HandlerFunc func(*Context)
type HandlerFunc func(*builder.Context)
type HandlersChain []HandlerFunc
const abortIndex int8 = math.MaxInt8 >> 1
//const abortIndex int8 = math.MaxInt8 >> 1
//type handlers struct {
// handlers HandlersChain
@@ -44,6 +45,7 @@ func (g *GoRose) Use(h ...HandlerFunc) *GoRose {
// examples
//
// Open("mysql", "root:root@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true")
// Open(&Config{...})
// Open(&ConfigCluster{...})
func Open(conf ...any) *GoRose {
var g = GoRose{}

View File

@@ -1,58 +0,0 @@
package gorose
import "math"
// LimitOffsetClause 存储LIMIT和OFFSET信息。
type LimitOffsetClause struct {
Limit int
Offset int
Page int
}
// Pagination 是用于分页查询结果的结构体,包含当前页数据及分页信息。
type Pagination struct {
Limit int `json:"limit"`
Pages int `json:"pages"`
CurrentPage int `json:"currentPage"`
PrevPage int `json:"prevPage"`
NextPage int `json:"nextPage"`
Total int64 `json:"total"`
Data []map[string]any `json:"data"`
}
func (db *Database) Paginate(obj ...any) (result Pagination, err error) {
if len(obj) > 0 {
db.Table(obj[0])
}
var count int64
count, err = db.Count()
if err != nil || count == 0 {
return
}
if db.Context.LimitOffsetClause.Limit == 0 {
db.Limit(15)
}
if db.Context.LimitOffsetClause.Page == 0 {
db.Page(1)
}
res, err := db.Get()
if err != nil {
return result, err
}
result.Total = count
result.Data = res
result.Limit = db.Context.LimitOffsetClause.Limit
result.Pages = int(math.Ceil(float64(count) / float64(db.Context.LimitOffsetClause.Limit)))
result.CurrentPage = db.Context.LimitOffsetClause.Page
result.PrevPage = db.Context.LimitOffsetClause.Page - 1
result.NextPage = db.Context.LimitOffsetClause.Page + 1
if db.Context.LimitOffsetClause.Page == 1 {
result.PrevPage = 1
}
if db.Context.LimitOffsetClause.Page == result.Pages {
result.NextPage = result.Pages
}
return
}

View File

@@ -1,4 +1,4 @@
package gorose
package parser
import (
"database/sql/driver"
@@ -28,20 +28,20 @@ func StructsParse(obj any) (FieldTag []string, FieldStruct []string, pkField str
rfv := reflect.Indirect(reflect.ValueOf(obj))
switch rfv.Kind() {
case reflect.Struct:
return structsTypeParse(rfv.Type())
return StructsTypeParse(rfv.Type())
case reflect.Slice:
return structsTypeParse(rfv.Type())
return StructsTypeParse(rfv.Type())
default:
return
}
}
func structsTypeParse(rft reflect.Type) (fieldTag []string, fieldStruct []string, pkField string) {
func StructsTypeParse(rft reflect.Type) (fieldTag []string, fieldStruct []string, pkField string) {
//rfv := reflect.Indirect(reflect.ValueOf(obj))
if rft.Kind() == reflect.Slice {
rft2 := rft.Elem()
if rft2.Kind() == reflect.Struct {
return structsTypeParse(rft2)
return StructsTypeParse(rft2)
}
} else {
for i := 0; i < rft.NumField(); i++ {
@@ -89,7 +89,7 @@ func structsTypeParse(rft reflect.Type) (fieldTag []string, fieldStruct []string
// }
//}
func structDataToMap(rfv reflect.Value, tags, fieldStruct []string, mustColumn ...string) (data map[string]any, err error) {
func StructDataToMap(rfv reflect.Value, tags, fieldStruct []string, mustColumn ...string) (data map[string]any, err error) {
data = make(map[string]any)
for i, fieldName := range fieldStruct {
field := rfv.FieldByName(fieldName)
@@ -136,8 +136,8 @@ func structUpdateDataToMap(rfv reflect.Value, tags, fieldStruct []string, pkFiel
func StructToDelete(obj any, mustColumn ...string) (data map[string]any, err error) {
rfv := reflect.Indirect(reflect.ValueOf(obj))
if rfv.Kind() == reflect.Struct {
tag, fieldStruct, _ := structsTypeParse(rfv.Type())
data, err = structDataToMap(rfv, tag, fieldStruct, mustColumn...)
tag, fieldStruct, _ := StructsTypeParse(rfv.Type())
data, err = StructDataToMap(rfv, tag, fieldStruct, mustColumn...)
}
return
}
@@ -146,18 +146,18 @@ func StructsToInsert(obj any, mustColumn ...string) (datas []map[string]any, err
rfv := reflect.Indirect(reflect.ValueOf(obj))
switch rfv.Kind() {
case reflect.Struct:
fieldTag, fieldStruct, _ := structsTypeParse(rfv.Type())
fieldTag, fieldStruct, _ := StructsTypeParse(rfv.Type())
var data = make(map[string]any)
data, err = structDataToMap(rfv, fieldTag, fieldStruct, mustColumn...)
data, err = StructDataToMap(rfv, fieldTag, fieldStruct, mustColumn...)
if err != nil {
return
}
datas = append(datas, data)
case reflect.Slice:
tag, fieldStruct, _ := structsTypeParse(rfv.Type())
tag, fieldStruct, _ := StructsTypeParse(rfv.Type())
for i := 0; i < rfv.Len(); i++ {
var data = make(map[string]any)
data, err = structDataToMap(rfv.Index(i), tag, fieldStruct, mustColumn...)
data, err = StructDataToMap(rfv.Index(i), tag, fieldStruct, mustColumn...)
if err != nil {
return
}

161
sugar.go
View File

@@ -1,108 +1,153 @@
package gorose
//func (db *Database) MustFirst(columns ...string) (res map[string]any) {
// res, db.Context.Err = db.First(columns...)
// return
//}
//func (db *Database) MustGet(columns ...string) (res []map[string]any) {
// res, db.Context.Err = db.Get(columns...)
// return
//}
import (
"github.com/gohouse/gorose/v3/builder"
"math"
)
func (db *Database) WhereSub(column string, operation string, sub WhereSubHandler) *Database {
// Pagination 是用于分页查询结果的结构体,包含当前页数据及分页信息。
type Pagination struct {
Limit int `json:"limit"`
Pages int `json:"pages"`
CurrentPage int `json:"currentPage"`
PrevPage int `json:"prevPage"`
NextPage int `json:"nextPage"`
Total int64 `json:"total"`
Data []map[string]any `json:"data"`
}
func (db *Database) Paginate(obj ...any) (result Pagination, err error) {
if len(obj) > 0 {
db.Table(obj[0])
}
var count int64
count, err = db.Count()
if err != nil || count == 0 {
return
}
if db.Context.LimitOffsetClause.Limit == 0 {
db.Limit(15)
}
if db.Context.LimitOffsetClause.Page == 0 {
db.Page(1)
}
res, err := db.Get()
if err != nil {
return result, err
}
result.Total = count
result.Data = res
result.Limit = db.Context.LimitOffsetClause.Limit
result.Pages = int(math.Ceil(float64(count) / float64(db.Context.LimitOffsetClause.Limit)))
result.CurrentPage = db.Context.LimitOffsetClause.Page
result.PrevPage = db.Context.LimitOffsetClause.Page - 1
result.NextPage = db.Context.LimitOffsetClause.Page + 1
if db.Context.LimitOffsetClause.Page == 1 {
result.PrevPage = 1
}
if db.Context.LimitOffsetClause.Page == result.Pages {
result.NextPage = result.Pages
}
return
}
func (db *Database) WhereSub(column string, operation string, sub builder.WhereSubHandler) *Database {
db.Context.WhereClause.WhereSub(column, operation, sub)
return db
}
func (db *Database) OrWhereSub(column string, operation string, sub WhereSubHandler) *Database {
func (db *Database) OrWhereSub(column string, operation string, sub builder.WhereSubHandler) *Database {
db.Context.WhereClause.OrWhereSub(column, operation, sub)
return db
}
func (db *Database) WhereBuilder(column string, operation string, sub IBuilder) *Database {
func (db *Database) WhereBuilder(column string, operation string, sub builder.IBuilder) *Database {
db.Context.WhereClause.WhereBuilder(column, operation, sub)
return db
}
func (db *Database) OrWhereBuilder(column string, operation string, sub IBuilder) *Database {
func (db *Database) OrWhereBuilder(column string, operation string, sub builder.IBuilder) *Database {
db.Context.WhereClause.OrWhereBuilder(column, operation, sub)
return db
}
func (db *Database) WhereNested(handler WhereNestedHandler) *Database {
func (db *Database) WhereNested(handler builder.WhereNestedHandler) *Database {
db.Context.WhereClause.WhereNested(handler)
return db
}
func (db *Database) OrWhereNested(handler WhereNestedHandler) *Database {
func (db *Database) OrWhereNested(handler builder.WhereNestedHandler) *Database {
db.Context.WhereClause.OrWhereNested(handler)
return db
}
func (db *Database) WhereIn(column string, value any) *Database {
db.Context.WhereClause.whereIn("AND", column, value)
return db
}
func (db *Database) WhereNotIn(column string, value any) *Database {
db.Context.WhereClause.whereIn("AND", column, value, true)
db.Context.WhereClause.WhereIn(column, value)
return db
}
func (db *Database) OrWhereIn(column string, value any) *Database {
db.Context.WhereClause.whereIn("OR", column, value)
return db
}
func (db *Database) OrWhereNotIn(column string, value any) *Database {
db.Context.WhereClause.whereIn("OR", column, value, true)
db.Context.WhereClause.WhereIn(column, value)
return db
}
func (db *Database) WhereNull(column string) *Database {
db.Context.WhereClause.whereNull("AND", column)
return db
}
func (db *Database) WhereNotNull(column string) *Database {
db.Context.WhereClause.whereNull("AND", column, true)
db.Context.WhereClause.WhereNull(column)
return db
}
func (db *Database) OrWhereNull(column string) *Database {
db.Context.WhereClause.whereNull("OR", column)
return db
}
func (db *Database) OrWhereNotNull(column string) *Database {
db.Context.WhereClause.whereNull("OR", column, true)
db.Context.WhereClause.WhereNull(column)
return db
}
func (db *Database) WhereBetween(column string, value any) *Database {
db.Context.WhereClause.whereBetween("AND", column, value)
return db
}
func (db *Database) WhereNotBetween(column string, value any) *Database {
db.Context.WhereClause.whereBetween("AND", column, value, true)
db.Context.WhereClause.WhereBetween(column, value)
return db
}
func (db *Database) OrWhereBetween(column string, value any) *Database {
db.Context.WhereClause.whereBetween("OR", column, value)
db.Context.WhereClause.WhereBetween(column, value)
return db
}
func (db *Database) OrWhereNotBetween(column string, value any) *Database {
db.Context.WhereClause.whereBetween("OR", column, value, true)
return db
}
func (db *Database) WhereExists(clause IBuilder) {
func (db *Database) WhereExists(clause builder.IBuilder) {
db.Context.WhereClause.WhereExists(clause)
}
func (db *Database) WhereNotExists(clause IBuilder) {
db.Context.WhereClause.WhereNotExists(clause)
}
func (db *Database) WhereLike(column, value string) *Database {
db.Context.WhereClause.whereLike("AND", column, value)
return db
}
func (db *Database) WhereNotLike(column, value string) *Database {
db.Context.WhereClause.whereLike("AND", column, value, true)
db.Context.WhereClause.WhereLike(column, value)
return db
}
func (db *Database) OrWhereLike(column, value string) *Database {
db.Context.WhereClause.whereLike("OR", column, value)
return db
}
func (db *Database) OrWhereNotLike(column, value string) *Database {
db.Context.WhereClause.whereLike("OR", column, value, true)
db.Context.WhereClause.OrWhereLike(column, value)
return db
}
//func (db *Database) WhereNotIn(column string, value any) *Database {
// db.Context.WhereClause.WhereNotIn(column, value)
// return db
//}
//func (db *Database) OrWhereNotIn(column string, value any) *Database {
// db.Context.WhereClause.WhereNotIn(column, value)
// return db
//}
//func (db *Database) WhereNotNull(column string) *Database {
// db.Context.WhereClause.whereNull("AND", column, true)
// return db
//}
//func (db *Database) OrWhereNotNull(column string) *Database {
// db.Context.WhereClause.whereNull("OR", column, true)
// return db
//}
//func (db *Database) WhereNotBetween(column string, value any) *Database {
// db.Context.WhereClause.whereBetween("AND", column, value, true)
// return db
//}
//func (db *Database) OrWhereNotBetween(column string, value any) *Database {
// db.Context.WhereClause.whereBetween("OR", column, value, true)
// return db
//}
//func (db *Database) WhereNotExists(clause IBuilder) {
// db.Context.WhereClause.WhereNotExists(clause)
//}
//func (db *Database) WhereNotLike(column, value string) *Database {
// db.Context.WhereClause.whereLike("AND", column, value, true)
// return db
//}
//func (db *Database) OrWhereNotLike(column, value string) *Database {
// db.Context.WhereClause.whereLike("OR", column, value, true)
// return db
//}
func (db *Database) WhereNot(column any, args ...any) *Database {
db.Context.WhereClause.WhereNot(column, args...)
return db

View File

@@ -3,41 +3,11 @@ package gorose
import (
"errors"
"fmt"
"github.com/gohouse/gorose/v3/builder"
"github.com/gohouse/gorose/v3/parser"
"reflect"
)
//type TypeToSqlExists struct {
// Bindings []any
//}
//type TypeToSqlPessimisticLocking struct {
// LockType string
//}
//type TypeToSqlAggregate struct {
// CallFunc, Column string
//}
type TypeToSqlUpdateCase struct {
BindOrData any
MustColumn []string
}
//type TypeToSqlDeleteCase struct {
// Bind any
// MustColumn []string
//}
type TypeToSqlIncDecCase struct {
Symbol string
Data map[string]any
}
type TypeToSqlInsertCase struct {
IsReplace bool
IsIgnoreCase bool
OnDuplicateKeys []string
UpdateFields []string
MustColumn []string
}
func (db *Database) ToSqlSelect() (sql4prepare string, binds []any) {
return db.Driver.ToSqlSelect(db.Context)
}
@@ -79,7 +49,7 @@ func (db *Database) ToSqlExists(bind ...any) (sql4prepare string, values []any,
func (db *Database) ToSqlAggregate(function, column string) (sql4prepare string, values []any, err error) {
var ctx = *db.Context
ctx.SelectClause.Columns = append(ctx.SelectClause.Columns, Column{
ctx.SelectClause.Columns = append(ctx.SelectClause.Columns, builder.Column{
Name: fmt.Sprintf("%s(%s)", function, column),
Alias: function,
IsRaw: true,
@@ -90,11 +60,11 @@ func (db *Database) ToSqlAggregate(function, column string) (sql4prepare string,
func (db *Database) ToSqlTo(obj any, mustColumn ...string) (sql4prepare string, binds []any, err error) {
rfv := reflect.Indirect(reflect.ValueOf(obj))
columns, fieldStruct, _ := StructsParse(obj)
columns, fieldStruct, _ := parser.StructsParse(obj)
switch rfv.Kind() {
case reflect.Struct:
var data = make(map[string]any)
data, err = structDataToMap(rfv, columns, fieldStruct, mustColumn...)
data, err = parser.StructDataToMap(rfv, columns, fieldStruct, mustColumn...)
if err != nil {
return
}
@@ -109,7 +79,7 @@ func (db *Database) ToSqlTo(obj any, mustColumn ...string) (sql4prepare string,
return
}
func (db *Database) ToSqlInsert(obj any, args ...TypeToSqlInsertCase) (sqlSegment string, binds []any, err error) {
func (db *Database) ToSqlInsert(obj any, args ...builder.TypeToSqlInsertCase) (sqlSegment string, binds []any, err error) {
return db.Driver.ToSqlInsert(db.Context, obj, args...)
}
func (db *Database) ToSqlDelete(obj any, mustColumn ...string) (sqlSegment string, binds []any, err error) {
@@ -117,9 +87,9 @@ func (db *Database) ToSqlDelete(obj any, mustColumn ...string) (sqlSegment strin
}
func (db *Database) ToSqlUpdate(obj any, mustColumn ...string) (sqlSegment string, binds []any, err error) {
return db.Driver.ToSqlUpdate(db.Context, TypeToSqlUpdateCase{obj, mustColumn})
return db.Driver.ToSqlUpdate(db.Context, builder.TypeToSqlUpdateCase{obj, mustColumn})
}
func (db *Database) ToSqlIncDec(symbol string, data map[string]any) (sql4prepare string, values []any, err error) {
//return db.Driver.ToSqlIncDec(db.Context, symbol, data)
return db.Driver.ToSqlUpdate(db.Context, TypeToSqlIncDecCase{symbol, data})
return db.Driver.ToSqlUpdate(db.Context, builder.TypeToSqlIncDecCase{symbol, data})
}

158
util.go
View File

@@ -3,10 +3,9 @@ package gorose
import (
"database/sql"
"fmt"
"github.com/gohouse/gorose/v3/builder"
"math/rand"
"reflect"
"regexp"
"sort"
"strings"
"time"
)
@@ -14,6 +13,13 @@ import (
func init() {
rand.New(rand.NewSource(time.Now().UnixNano()))
}
func As(table any, alias string) builder.TableClause {
return builder.TableClause{
Tables: table,
Alias: alias,
}
}
func GetRandomInt(num int) int {
return rand.Intn(num)
}
@@ -46,158 +52,16 @@ func GetRandomWeightedIndex(weights []int) int {
//////////// struct field ptr 4 orm helpers ////////////
// PtrBool helper
func PtrBool(arg bool) *bool {
return &arg
}
// PtrString helper
func PtrString(arg string) *string {
return &arg
}
// PtrInt helper
func PtrInt(arg int) *int {
return &arg
}
// PtrInt8 helper
func PtrInt8(arg int8) *int8 {
return &arg
}
// PtrInt16 helper
func PtrInt16(arg int16) *int16 {
return &arg
}
// PtrInt64 helper
func PtrInt64(arg int64) *int64 {
return &arg
}
// PtrFloat64 helper
func PtrFloat64(arg float64) *float64 {
return &arg
}
// PtrTime helper
func PtrTime(arg time.Time) *time.Time {
func Ptr[T any](arg T) *T {
return &arg
}
//////////// sql.Null* type helpers ////////////
// NullInt64From helper
func NullInt64From(arg int64) sql.NullInt64 { return sql.NullInt64{Int64: arg, Valid: true} }
// NullInt32From helper
func NullInt32From(arg int32) sql.NullInt32 { return sql.NullInt32{Int32: arg, Valid: true} }
// NullInt16From helper
func NullInt16From(arg int16) sql.NullInt16 { return sql.NullInt16{Int16: arg, Valid: true} }
// NullByteFrom helper
func NullByteFrom(arg byte) sql.NullByte { return sql.NullByte{Byte: arg, Valid: true} }
// NullFloat64From helper
func NullFloat64From(arg float64) sql.NullFloat64 { return sql.NullFloat64{Float64: arg, Valid: true} }
// NullBoolFrom helper
func NullBoolFrom(arg bool) sql.NullBool { return sql.NullBool{Bool: arg, Valid: true} }
// NullTimeFrom helper
func NullTimeFrom(arg time.Time) sql.NullTime { return sql.NullTime{Time: arg, Valid: true} }
func ToSlice(arg any) []any {
ref := reflect.Indirect(reflect.ValueOf(arg))
var res []any
switch ref.Kind() {
case reflect.Slice:
l := ref.Len()
v := ref.Slice(0, l)
for i := 0; i < l; i++ {
res = append(res, v.Index(i).Interface())
}
default:
res = append(res, ref.Interface())
}
return res
}
func ToSliceAddressable(arg any) []any {
ref := reflect.Indirect(reflect.ValueOf(arg))
var res []any
switch ref.Kind() {
case reflect.Slice:
l := ref.Len()
v := ref.Slice(0, l)
for i := 0; i < l; i++ {
res = append(res, v.Index(i).Addr().Interface())
}
default:
res = append(res, ref.Addr().Interface())
}
return res
}
func SliceContains(haystack []string, needle string) bool {
for _, v := range haystack {
if v == needle {
return true
}
}
return false
}
func Map[E any, T any](data []E, mapper func(E) T) []T {
results := make([]T, 0, len(data))
for _, v := range data {
results = append(results, mapper(v))
}
return results
func Null[T any](arg T) sql.Null[T] {
return sql.Null[T]{V: arg, Valid: true}
}
func NamedSprintf(format string, a ...any) string {
return strings.TrimSpace(regexp.MustCompile(`\s{2,}`).ReplaceAllString(fmt.Sprintf(regexp.MustCompile(`:\w+`).ReplaceAllString(format, "%s"), a...), " "))
}
func BackQuotes(arg any) string {
var tmp []string
if v, ok := arg.(string); ok {
split := strings.Split(v, " ")
split2 := strings.Split(split[0], ".")
if len(split2) > 1 {
if split2[1] == "*" {
tmp = append(tmp, fmt.Sprintf("`%s`.%s", split2[0], split2[1]))
} else {
tmp = append(tmp, fmt.Sprintf("`%s`.`%s`", split2[0], split2[1]))
}
} else {
tmp = append(tmp, fmt.Sprintf("`%s`", split2[len(split2)-1]))
}
tmp = append(tmp, split[1:]...)
}
return strings.Join(tmp, " ")
}
func SortedMapKeys(data any) (cols []string) {
// 从 map 中获取所有的键,并转换为切片
keys := reflect.ValueOf(data).MapKeys()
// 对切片进行排序
sort.Slice(keys, func(i, j int) bool {
return keys[i].String() < keys[j].String()
})
// 输出排序后的结果
for _, key := range keys {
cols = append(cols, key.String())
}
return
}
func IsExpression(obj any) (b bool) {
rfv := reflect.Indirect(reflect.ValueOf(obj))
if rfv.Kind() == reflect.String && strings.Contains(rfv.String(), "?") {
b = true
}
return
}