mirror of
https://gitee.com/johng/gf
synced 2026-07-06 13:42:46 +08:00
add soft deleting feature, improve chars handling for package gdb
This commit is contained in:
@ -1,47 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
type A struct {
|
||||
F string
|
||||
G string
|
||||
}
|
||||
|
||||
type B struct {
|
||||
A
|
||||
H string
|
||||
}
|
||||
|
||||
type C struct {
|
||||
A A
|
||||
F string
|
||||
}
|
||||
|
||||
type D struct {
|
||||
I A
|
||||
F string
|
||||
}
|
||||
|
||||
func SystemJsonEncode(a interface{}) string {
|
||||
js, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
return "{}"
|
||||
} else {
|
||||
return fmt.Sprintf("%s", js)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("encoding/json", SystemJsonEncode(B{}))
|
||||
fmt.Println("gjson", gjson.New(B{}).MustToJsonString())
|
||||
fmt.Println()
|
||||
fmt.Println("encoding/json", SystemJsonEncode(C{}))
|
||||
fmt.Println("gjson", gjson.New(C{}).MustToJsonString())
|
||||
fmt.Println()
|
||||
fmt.Println("encoding/json", SystemJsonEncode(D{}))
|
||||
fmt.Println("gjson", gjson.New(D{}).MustToJsonString())
|
||||
s := `user u LEFT JOIN user_detail ud ON(ud.id=u.id) LEFT JOIN user_stats us ON(us.id=u.id)`
|
||||
split := " JOIN "
|
||||
as := "my-as"
|
||||
if gstr.Contains(s, split) {
|
||||
// For join table.
|
||||
array := gstr.Split(s, split)
|
||||
array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])
|
||||
s = gstr.Join(array, split)
|
||||
} else {
|
||||
// For base table.
|
||||
s = gstr.TrimRight(s) + " AS " + as
|
||||
}
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
@ -78,8 +78,8 @@ type DB interface {
|
||||
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
|
||||
|
||||
// Model creation.
|
||||
Table(tables string) *Model
|
||||
Model(tables string) *Model
|
||||
Table(table ...string) *Model
|
||||
Model(table ...string) *Model
|
||||
Schema(schema string) *Schema
|
||||
|
||||
// Configuration methods.
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
@ -417,7 +418,7 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
|
||||
for k, _ := range dataMap {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
if k == gSOFT_FIELD_NAME_CREATE {
|
||||
if utils.EqualFoldWithoutChars(k, gSOFT_FIELD_NAME_CREATE) {
|
||||
continue
|
||||
}
|
||||
if len(updateStr) > 0 {
|
||||
@ -538,7 +539,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
for _, k := range keys {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
if k == gSOFT_FIELD_NAME_CREATE {
|
||||
if utils.EqualFoldWithoutChars(k, gSOFT_FIELD_NAME_CREATE) {
|
||||
continue
|
||||
}
|
||||
if len(updateStr) > 0 {
|
||||
|
||||
@ -147,8 +147,13 @@ func doQuoteWord(s, charLeft, charRight string) string {
|
||||
}
|
||||
|
||||
// doQuoteString quotes string with quote chars. It handles strings like:
|
||||
// "user", "user u", "user,user_detail", "user u, user_detail ut",
|
||||
// "user.user u, user.user_detail ut", "u.id asc".
|
||||
// "user",
|
||||
// "user u",
|
||||
// "user,user_detail",
|
||||
// "user u, user_detail ut",
|
||||
// "user.user u, user.user_detail ut",
|
||||
// "u.id, u.name, u.age",
|
||||
// "u.id asc".
|
||||
func doQuoteString(s, charLeft, charRight string) string {
|
||||
array1 := gstr.SplitAndTrim(s, ",")
|
||||
for k1, v1 := range array1 {
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
@ -59,14 +61,28 @@ const (
|
||||
)
|
||||
|
||||
// Table creates and returns a new ORM model from given schema.
|
||||
// The parameter <tables> can be more than one table names, like :
|
||||
// "user", "user u", "user, user_detail", "user u, user_detail ud"
|
||||
func (c *Core) Table(table string) *Model {
|
||||
table = c.DB.QuotePrefixTableName(table)
|
||||
// The parameter <table> can be more than one table names, and also alias name, like:
|
||||
// 1. Table names:
|
||||
// Table("user")
|
||||
// Table("user u")
|
||||
// Table("user, user_detail")
|
||||
// Table("user u, user_detail ud")
|
||||
// 2. Table name with alias: Table("user", "u")
|
||||
func (c *Core) Table(table ...string) *Model {
|
||||
tables := ""
|
||||
if len(table) > 1 {
|
||||
tables = fmt.Sprintf(
|
||||
`%s AS %s`, c.DB.QuotePrefixTableName(table[0]), c.DB.QuoteWord(table[1]),
|
||||
)
|
||||
} else if len(table) == 1 {
|
||||
tables = c.DB.QuotePrefixTableName(table[0])
|
||||
} else {
|
||||
panic("table cannot be empty")
|
||||
}
|
||||
return &Model{
|
||||
db: c.DB,
|
||||
tablesInit: table,
|
||||
tables: table,
|
||||
tablesInit: tables,
|
||||
tables: tables,
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
@ -76,37 +92,39 @@ func (c *Core) Table(table string) *Model {
|
||||
|
||||
// Model is alias of Core.Table.
|
||||
// See Core.Table.
|
||||
func (c *Core) Model(table string) *Model {
|
||||
return c.DB.Table(table)
|
||||
func (c *Core) Model(table ...string) *Model {
|
||||
return c.DB.Table(table...)
|
||||
}
|
||||
|
||||
// Table acts like Core.Table except it operates on transaction.
|
||||
// See Core.Table.
|
||||
func (tx *TX) Table(table string) *Model {
|
||||
table = tx.db.QuotePrefixTableName(table)
|
||||
return &Model{
|
||||
db: tx.db,
|
||||
tx: tx,
|
||||
tablesInit: table,
|
||||
tables: table,
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
option: OPTION_ALLOWEMPTY,
|
||||
}
|
||||
func (tx *TX) Table(table ...string) *Model {
|
||||
model := tx.db.Table(table...)
|
||||
model.db = tx.db
|
||||
model.tx = tx
|
||||
return model
|
||||
}
|
||||
|
||||
// Model is alias of tx.Table.
|
||||
// See tx.Table.
|
||||
func (tx *TX) Model(table string) *Model {
|
||||
return tx.Table(table)
|
||||
func (tx *TX) Model(table ...string) *Model {
|
||||
return tx.Table(table...)
|
||||
}
|
||||
|
||||
// As sets an alias name for current table.
|
||||
func (m *Model) As(as string) *Model {
|
||||
if m.tables != "" {
|
||||
model := m.getModel()
|
||||
model.tables = gstr.TrimRight(model.tables) + " AS " + as
|
||||
split := " JOIN "
|
||||
if gstr.Contains(model.tables, split) {
|
||||
// For join table.
|
||||
array := gstr.Split(model.tables, split)
|
||||
array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])
|
||||
model.tables = gstr.Join(array, split)
|
||||
} else {
|
||||
// For base table.
|
||||
model.tables = gstr.TrimRight(model.tables) + " AS " + as
|
||||
}
|
||||
return model
|
||||
}
|
||||
return m
|
||||
|
||||
@ -44,7 +44,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
|
||||
return m.db.DoUpdate(
|
||||
m.getLink(true),
|
||||
m.tables,
|
||||
fmt.Sprintf(`%s='%s'`, m.db.QuoteWord(fieldNameDelete), gtime.Now().String()),
|
||||
fmt.Sprintf(`%s='%s'`, m.db.QuoteString(fieldNameDelete), gtime.Now().String()),
|
||||
conditionWhere+conditionExtra,
|
||||
conditionArgs...,
|
||||
)
|
||||
|
||||
@ -9,22 +9,70 @@ package gdb
|
||||
import "fmt"
|
||||
|
||||
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
|
||||
func (m *Model) LeftJoin(table string, on string) *Model {
|
||||
// The parameter <table> can be joined table and its joined condition,
|
||||
// and also with its alias name, like:
|
||||
// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
func (m *Model) LeftJoin(table ...string) *Model {
|
||||
model := m.getModel()
|
||||
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", m.db.QuotePrefixTableName(table), on)
|
||||
if len(table) > 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" LEFT JOIN %s AS %s ON (%s)",
|
||||
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
|
||||
)
|
||||
} else if len(table) == 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" LEFT JOIN %s ON (%s)",
|
||||
m.db.QuotePrefixTableName(table[0]), table[1],
|
||||
)
|
||||
} else {
|
||||
panic("invalid join table parameter")
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
|
||||
func (m *Model) RightJoin(table string, on string) *Model {
|
||||
// The parameter <table> can be joined table and its joined condition,
|
||||
// and also with its alias name, like:
|
||||
// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
func (m *Model) RightJoin(table ...string) *Model {
|
||||
model := m.getModel()
|
||||
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", m.db.QuotePrefixTableName(table), on)
|
||||
if len(table) > 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" RIGHT JOIN %s AS %s ON (%s)",
|
||||
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
|
||||
)
|
||||
} else if len(table) == 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" RIGHT JOIN %s ON (%s)",
|
||||
m.db.QuotePrefixTableName(table[0]), table[1],
|
||||
)
|
||||
} else {
|
||||
panic("invalid join table parameter")
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
|
||||
func (m *Model) InnerJoin(table string, on string) *Model {
|
||||
// The parameter <table> can be joined table and its joined condition,
|
||||
// and also with its alias name, like:
|
||||
// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
func (m *Model) InnerJoin(table ...string) *Model {
|
||||
model := m.getModel()
|
||||
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", m.db.QuotePrefixTableName(table), on)
|
||||
if len(table) > 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" INNER JOIN %s AS %s ON (%s)",
|
||||
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
|
||||
)
|
||||
} else if len(table) == 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" INNER JOIN %s ON (%s)",
|
||||
m.db.QuotePrefixTableName(table[0]), table[1],
|
||||
)
|
||||
} else {
|
||||
panic("invalid join table parameter")
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
@ -31,19 +31,24 @@ func (m *Model) All(where ...interface{}) (Result, error) {
|
||||
return m.Where(where[0], where[1:]...).All()
|
||||
}
|
||||
var (
|
||||
fieldNameDelete = m.getSoftFieldNameDelete()
|
||||
softDeletingCondition = m.getConditionForSoftDeleting()
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
|
||||
)
|
||||
if !m.unscoped && fieldNameDelete != "" {
|
||||
if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
conditionWhere = " WHERE"
|
||||
conditionWhere = " WHERE "
|
||||
} else {
|
||||
conditionWhere += " AND"
|
||||
conditionWhere += " AND "
|
||||
}
|
||||
conditionWhere += fmt.Sprintf(` %s IS NULL`, m.db.QuoteWord(fieldNameDelete))
|
||||
conditionWhere += softDeletingCondition
|
||||
}
|
||||
return m.getAll(
|
||||
fmt.Sprintf("SELECT %s FROM %s%s", m.fields, m.tables, conditionWhere+conditionExtra),
|
||||
fmt.Sprintf(
|
||||
"SELECT %s FROM %s%s",
|
||||
m.db.QuoteString(m.fields),
|
||||
m.tables,
|
||||
conditionWhere+conditionExtra,
|
||||
),
|
||||
conditionArgs...,
|
||||
)
|
||||
}
|
||||
@ -244,19 +249,19 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
}
|
||||
countFields := "COUNT(1)"
|
||||
if m.fields != "" && m.fields != "*" {
|
||||
countFields = fmt.Sprintf(`COUNT(%s)`, m.fields)
|
||||
countFields = fmt.Sprintf(`COUNT(%s)`, m.db.QuoteString(m.fields))
|
||||
}
|
||||
var (
|
||||
fieldNameDelete = m.getSoftFieldNameDelete()
|
||||
softDeletingCondition = m.getConditionForSoftDeleting()
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
|
||||
)
|
||||
if !m.unscoped && fieldNameDelete != "" {
|
||||
if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
conditionWhere = " WHERE"
|
||||
conditionWhere = " WHERE "
|
||||
} else {
|
||||
conditionWhere += " AND"
|
||||
conditionWhere += " AND "
|
||||
}
|
||||
conditionWhere += fmt.Sprintf(` %s IS NULL`, m.db.QuoteWord(fieldNameDelete))
|
||||
conditionWhere += softDeletingCondition
|
||||
}
|
||||
|
||||
s := fmt.Sprintf("SELECT %s FROM %s %s", countFields, m.tables, conditionWhere+conditionExtra)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -7,6 +7,10 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
@ -20,38 +24,121 @@ const (
|
||||
// getSoftFieldNameCreate checks and returns the field name for record creating time.
|
||||
// If there's no field name for storing creating time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameCreate() (field string) {
|
||||
fieldsMap, _ := m.db.TableFields(m.tables)
|
||||
if len(fieldsMap) > 0 {
|
||||
field, _ = gutil.MapPossibleItemByKey(
|
||||
gconv.Map(fieldsMap), gSOFT_FIELD_NAME_CREATE,
|
||||
)
|
||||
func (m *Model) getSoftFieldNameCreate(table ...string) string {
|
||||
name := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
}
|
||||
return
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_CREATE)
|
||||
}
|
||||
|
||||
// getSoftFieldNameUpdate checks and returns the field name for record updating time.
|
||||
// If there's no field name for storing updating time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameUpdate() (field string) {
|
||||
fieldsMap, _ := m.db.TableFields(m.tables)
|
||||
if len(fieldsMap) > 0 {
|
||||
field, _ = gutil.MapPossibleItemByKey(
|
||||
gconv.Map(fieldsMap), gSOFT_FIELD_NAME_UPDATE,
|
||||
)
|
||||
func (m *Model) getSoftFieldNameUpdate(table ...string) (field string) {
|
||||
name := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
}
|
||||
return
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_UPDATE)
|
||||
}
|
||||
|
||||
// getSoftFieldNameDelete checks and returns the field name for record deleting time.
|
||||
// If there's no field name for storing deleting time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameDelete() (field string) {
|
||||
fieldsMap, _ := m.db.TableFields(m.tables)
|
||||
func (m *Model) getSoftFieldNameDelete(table ...string) (field string) {
|
||||
name := ""
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
} else {
|
||||
name = m.getPrimaryTableName()
|
||||
}
|
||||
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_DELETE)
|
||||
}
|
||||
|
||||
// getSoftFieldName retrieves and returns the field name of the table for possible key.
|
||||
func (m *Model) getSoftFieldName(table string, key string) (field string) {
|
||||
fieldsMap, _ := m.db.TableFields(table)
|
||||
if len(fieldsMap) > 0 {
|
||||
field, _ = gutil.MapPossibleItemByKey(
|
||||
gconv.Map(fieldsMap), gSOFT_FIELD_NAME_DELETE,
|
||||
gconv.Map(fieldsMap), key,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getConditionForSoftDeleting retrieves and returns the condition string for soft deleting.
|
||||
// It supports multiple tables string like:
|
||||
// "user u, user_detail ud"
|
||||
// "user u LEFT JOIN user_detail ud ON(ud.uid=u.uid)"
|
||||
// "user LEFT JOIN user_detail ON(user_detail.uid=user.uid)"
|
||||
// "user u LEFT JOIN user_detail ud ON(ud.uid=u.uid) LEFT JOIN user_stats us ON(us.uid=u.uid)"
|
||||
func (m *Model) getConditionForSoftDeleting() string {
|
||||
conditionArray := garray.NewStrArray()
|
||||
if gstr.Contains(m.tables, " JOIN ") {
|
||||
// Base table.
|
||||
match, _ := gregex.MatchString(`(.+?) [A-Z]+ JOIN`, m.tables)
|
||||
conditionArray.Append(m.getConditionOfTableStringForSoftDeleting(match[1]))
|
||||
// Multiple joined tables.
|
||||
matches, _ := gregex.MatchAllString(`JOIN (.+?) ON`, m.tables)
|
||||
for _, match := range matches {
|
||||
conditionArray.Append(m.getConditionOfTableStringForSoftDeleting(match[1]))
|
||||
}
|
||||
}
|
||||
if conditionArray.Len() == 0 && gstr.Contains(m.tables, ",") {
|
||||
// Multiple base tables.
|
||||
for _, s := range gstr.SplitAndTrim(m.tables, ",") {
|
||||
conditionArray.Append(m.getConditionOfTableStringForSoftDeleting(s))
|
||||
}
|
||||
}
|
||||
conditionArray.FilterEmpty()
|
||||
if conditionArray.Len() > 0 {
|
||||
return conditionArray.Join(" AND ")
|
||||
}
|
||||
// Only one table.
|
||||
if fieldName := m.getSoftFieldNameDelete(); fieldName != "" {
|
||||
return fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(fieldName))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getConditionOfTableStringForSoftDeleting does something as its name describes.
|
||||
func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string {
|
||||
var (
|
||||
field = ""
|
||||
table = ""
|
||||
array1 = gstr.SplitAndTrim(s, " ")
|
||||
array2 = gstr.SplitAndTrim(array1[0], ".")
|
||||
)
|
||||
if len(array2) >= 2 {
|
||||
table = array2[1]
|
||||
} else {
|
||||
table = array2[0]
|
||||
}
|
||||
field = m.getSoftFieldNameDelete(table)
|
||||
if field == "" {
|
||||
return ""
|
||||
}
|
||||
if len(array1) >= 3 {
|
||||
return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(array1[2]), m.db.QuoteWord(field))
|
||||
}
|
||||
if len(array1) >= 2 {
|
||||
return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(array1[1]), m.db.QuoteWord(field))
|
||||
}
|
||||
return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(table), m.db.QuoteWord(field))
|
||||
}
|
||||
|
||||
// getPrimaryTableName parses and returns the primary table name.
|
||||
func (m *Model) getPrimaryTableName() string {
|
||||
array1 := gstr.SplitAndTrim(m.tables, ",")
|
||||
array2 := gstr.SplitAndTrim(array1[0], " ")
|
||||
array3 := gstr.SplitAndTrim(array2[0], ".")
|
||||
if len(array3) >= 2 {
|
||||
return array3[1]
|
||||
}
|
||||
return array3[0]
|
||||
}
|
||||
|
||||
@ -74,8 +74,13 @@ func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
|
||||
|
||||
if len(m.fields) > 0 && m.fields != "*" {
|
||||
// Keep specified fields.
|
||||
set := gset.NewStrSetFrom(gstr.SplitAndTrim(m.fields, ","))
|
||||
var (
|
||||
set = gset.NewStrSetFrom(gstr.SplitAndTrim(m.fields, ","))
|
||||
charL, charR = m.db.GetChars()
|
||||
chars = charL + charR
|
||||
)
|
||||
for k := range data {
|
||||
k = gstr.Trim(k, chars)
|
||||
if !set.Contains(k) {
|
||||
delete(data, k)
|
||||
}
|
||||
|
||||
@ -1,117 +0,0 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Func_FormatSqlWithArgs(t *testing.T) {
|
||||
// mysql
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var s string
|
||||
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 = 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 = 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 = 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")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Func_doQuoteWord(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := map[string]string{
|
||||
"user": "`user`",
|
||||
"user u": "user u",
|
||||
"user_detail": "`user_detail`",
|
||||
"user,user_detail": "user,user_detail",
|
||||
"user u, user_detail ut": "user u, user_detail ut",
|
||||
"u.id asc": "u.id asc",
|
||||
"u.id asc, ut.uid desc": "u.id asc, ut.uid desc",
|
||||
}
|
||||
for k, v := range array {
|
||||
t.Assert(doQuoteWord(k, "`", "`"), v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Func_doQuoteString(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
|
||||
array := map[string]string{
|
||||
"user": "`user`",
|
||||
"user u": "`user` u",
|
||||
"user,user_detail": "`user`,`user_detail`",
|
||||
"user u, user_detail ut": "`user` u,`user_detail` ut",
|
||||
"u.id asc": "`u`.`id` asc",
|
||||
"u.id asc, ut.uid desc": "`u`.`id` asc,`ut`.`uid` desc",
|
||||
"user.user u, user.user_detail ut": "`user`.`user` u,`user`.`user_detail` ut",
|
||||
// mssql global schema access with double dots.
|
||||
"user..user u, user.user_detail ut": "`user`..`user` u,`user`.`user_detail` ut",
|
||||
}
|
||||
for k, v := range array {
|
||||
t.Assert(doQuoteString(k, "`", "`"), v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Func_addTablePrefix(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := ""
|
||||
array := map[string]string{
|
||||
"user": "`user`",
|
||||
"user u": "`user` u",
|
||||
"user as u": "`user` as u",
|
||||
"user,user_detail": "`user`,`user_detail`",
|
||||
"user u, user_detail ut": "`user` u,`user_detail` ut",
|
||||
"`user`.user_detail": "`user`.`user_detail`",
|
||||
"`user`.`user_detail`": "`user`.`user_detail`",
|
||||
"user as u, user_detail as ut": "`user` as u,`user_detail` as ut",
|
||||
"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`user` as u,`UserCenter`.`user_detail` as ut",
|
||||
// mssql global schema access with double dots.
|
||||
"UserCenter..user as u, user_detail as ut": "`UserCenter`..`user` as u,`user_detail` as ut",
|
||||
}
|
||||
for k, v := range array {
|
||||
t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := "gf_"
|
||||
array := map[string]string{
|
||||
"user": "`gf_user`",
|
||||
"user u": "`gf_user` u",
|
||||
"user as u": "`gf_user` as u",
|
||||
"user,user_detail": "`gf_user`,`gf_user_detail`",
|
||||
"user u, user_detail ut": "`gf_user` u,`gf_user_detail` ut",
|
||||
"`user`.user_detail": "`user`.`gf_user_detail`",
|
||||
"`user`.`user_detail`": "`user`.`gf_user_detail`",
|
||||
"user as u, user_detail as ut": "`gf_user` as u,`gf_user_detail` as ut",
|
||||
"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`gf_user` as u,`UserCenter`.`gf_user_detail` as ut",
|
||||
// mssql global schema access with double dots.
|
||||
"UserCenter..user as u, user_detail as ut": "`UserCenter`..`gf_user` as u,`gf_user_detail` as ut",
|
||||
}
|
||||
for k, v := range array {
|
||||
t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
|
||||
}
|
||||
})
|
||||
}
|
||||
289
database/gdb/gdb_unit_z_mysql_internal_test.go
Normal file
289
database/gdb/gdb_unit_z_mysql_internal_test.go
Normal file
@ -0,0 +1,289 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
SCHEMA = "test_internal"
|
||||
)
|
||||
|
||||
var (
|
||||
db DB
|
||||
configNode ConfigNode
|
||||
)
|
||||
|
||||
func init() {
|
||||
parser, err := gcmd.Parse(map[string]bool{
|
||||
"name": true,
|
||||
"type": true,
|
||||
}, false)
|
||||
gtest.Assert(err, nil)
|
||||
configNode = ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: "root",
|
||||
Pass: "12345678",
|
||||
Name: parser.GetOpt("name", ""),
|
||||
Type: parser.GetOpt("type", "mysql"),
|
||||
Role: "master",
|
||||
Charset: "utf8",
|
||||
Weight: 1,
|
||||
MaxIdleConnCount: 10,
|
||||
MaxOpenConnCount: 10,
|
||||
MaxConnLifetime: 600,
|
||||
}
|
||||
AddConfigNode(DEFAULT_GROUP_NAME, configNode)
|
||||
// Default db.
|
||||
if r, err := New(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
db = r
|
||||
}
|
||||
schemaTemplate := "CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET UTF8"
|
||||
if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
db.SetSchema(SCHEMA)
|
||||
}
|
||||
|
||||
func dropTable(table string) {
|
||||
if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Func_FormatSqlWithArgs(t *testing.T) {
|
||||
// mysql
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var s string
|
||||
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 = 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 = 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 = 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")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Func_doQuoteWord(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := map[string]string{
|
||||
"user": "`user`",
|
||||
"user u": "user u",
|
||||
"user_detail": "`user_detail`",
|
||||
"user,user_detail": "user,user_detail",
|
||||
"user u, user_detail ut": "user u, user_detail ut",
|
||||
"u.id asc": "u.id asc",
|
||||
"u.id asc, ut.uid desc": "u.id asc, ut.uid desc",
|
||||
}
|
||||
for k, v := range array {
|
||||
t.Assert(doQuoteWord(k, "`", "`"), v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Func_doQuoteString(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := map[string]string{
|
||||
"user": "`user`",
|
||||
"user u": "`user` u",
|
||||
"user,user_detail": "`user`,`user_detail`",
|
||||
"user u, user_detail ut": "`user` u,`user_detail` ut",
|
||||
"u.id, u.name, u.age": "`u`.`id`,`u`.`name`,`u`.`age`",
|
||||
"u.id asc": "`u`.`id` asc",
|
||||
"u.id asc, ut.uid desc": "`u`.`id` asc,`ut`.`uid` desc",
|
||||
"user.user u, user.user_detail ut": "`user`.`user` u,`user`.`user_detail` ut",
|
||||
// mssql global schema access with double dots.
|
||||
"user..user u, user.user_detail ut": "`user`..`user` u,`user`.`user_detail` ut",
|
||||
}
|
||||
for k, v := range array {
|
||||
t.Assert(doQuoteString(k, "`", "`"), v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Func_addTablePrefix(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := ""
|
||||
array := map[string]string{
|
||||
"user": "`user`",
|
||||
"user u": "`user` u",
|
||||
"user as u": "`user` as u",
|
||||
"user,user_detail": "`user`,`user_detail`",
|
||||
"user u, user_detail ut": "`user` u,`user_detail` ut",
|
||||
"`user`.user_detail": "`user`.`user_detail`",
|
||||
"`user`.`user_detail`": "`user`.`user_detail`",
|
||||
"user as u, user_detail as ut": "`user` as u,`user_detail` as ut",
|
||||
"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`user` as u,`UserCenter`.`user_detail` as ut",
|
||||
// mssql global schema access with double dots.
|
||||
"UserCenter..user as u, user_detail as ut": "`UserCenter`..`user` as u,`user_detail` as ut",
|
||||
}
|
||||
for k, v := range array {
|
||||
t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := "gf_"
|
||||
array := map[string]string{
|
||||
"user": "`gf_user`",
|
||||
"user u": "`gf_user` u",
|
||||
"user as u": "`gf_user` as u",
|
||||
"user,user_detail": "`gf_user`,`gf_user_detail`",
|
||||
"user u, user_detail ut": "`gf_user` u,`gf_user_detail` ut",
|
||||
"`user`.user_detail": "`user`.`gf_user_detail`",
|
||||
"`user`.`user_detail`": "`user`.`gf_user_detail`",
|
||||
"user as u, user_detail as ut": "`gf_user` as u,`gf_user_detail` as ut",
|
||||
"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`gf_user` as u,`UserCenter`.`gf_user_detail` as ut",
|
||||
// mssql global schema access with double dots.
|
||||
"UserCenter..user as u, user_detail as ut": "`UserCenter`..`gf_user` as u,`gf_user_detail` as ut",
|
||||
}
|
||||
for k, v := range array {
|
||||
t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_getSoftFieldName(t *testing.T) {
|
||||
table1 := "soft_deleting_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
name varchar(45) DEFAULT NULL,
|
||||
create_at datetime DEFAULT NULL,
|
||||
update_at datetime DEFAULT NULL,
|
||||
delete_at datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table1)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table1)
|
||||
|
||||
table2 := "soft_deleting_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
name varchar(45) DEFAULT NULL,
|
||||
createat datetime DEFAULT NULL,
|
||||
updateat datetime DEFAULT NULL,
|
||||
deleteat datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table2)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table2)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(table1)
|
||||
gtest.Assert(model.getSoftFieldNameCreate(table2), "createat")
|
||||
gtest.Assert(model.getSoftFieldNameUpdate(table2), "updateat")
|
||||
gtest.Assert(model.getSoftFieldNameDelete(table2), "deleteat")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_getConditionForSoftDeleting(t *testing.T) {
|
||||
table1 := "soft_deleting_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id1 int(11) NOT NULL,
|
||||
name1 varchar(45) DEFAULT NULL,
|
||||
create_at datetime DEFAULT NULL,
|
||||
update_at datetime DEFAULT NULL,
|
||||
delete_at datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id1)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table1)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table1)
|
||||
|
||||
table2 := "soft_deleting_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id2 int(11) NOT NULL,
|
||||
name2 varchar(45) DEFAULT NULL,
|
||||
createat datetime DEFAULT NULL,
|
||||
updateat datetime DEFAULT NULL,
|
||||
deleteat datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id2)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table2)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table2)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(table1)
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s as t`, table1))
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s, %s`, table1, table2))
|
||||
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
|
||||
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL",
|
||||
table1, table2,
|
||||
))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s t1, %s as t2`, table1, table2))
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s as t1, %s as t2`, table1, table2))
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s as t1`, table1)).LeftJoin(table2+" t2", "t2.id2=t1.id1")
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1")
|
||||
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
|
||||
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL",
|
||||
table1, table2,
|
||||
))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1").RightJoin(table2, "t2.id2=t1.id1")
|
||||
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
|
||||
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL AND `%s`.`deleteat` IS NULL",
|
||||
table1, table2, table2,
|
||||
))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(table1+" as t1").LeftJoin(table2+" as t2", "t2.id2=t1.id1").RightJoin(table2+" as t3 ", "t2.id2=t1.id1")
|
||||
t.Assert(
|
||||
model.getConditionForSoftDeleting(),
|
||||
"`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL AND `t3`.`deleteat` IS NULL",
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -147,3 +147,74 @@ CREATE TABLE %s (
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SoftDelete_Join(t *testing.T) {
|
||||
table1 := "time_test_table1"
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
name varchar(45) DEFAULT NULL,
|
||||
create_at datetime DEFAULT NULL,
|
||||
update_at datetime DEFAULT NULL,
|
||||
delete_at datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table1)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table1)
|
||||
|
||||
table2 := "time_test_table2"
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
name varchar(45) DEFAULT NULL,
|
||||
createat datetime DEFAULT NULL,
|
||||
updateat datetime DEFAULT NULL,
|
||||
deleteat datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table2)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table2)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//db.SetDebug(true)
|
||||
dataInsert1 := g.Map{
|
||||
"id": 1,
|
||||
"name": "name_1",
|
||||
}
|
||||
r, err := db.Table(table1).Data(dataInsert1).Insert()
|
||||
t.Assert(err, nil)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
dataInsert2 := g.Map{
|
||||
"id": 1,
|
||||
"name": "name_2",
|
||||
}
|
||||
r, err = db.Table(table2).Data(dataInsert2).Insert()
|
||||
t.Assert(err, nil)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["name"], "name_1")
|
||||
|
||||
// Soft deleting.
|
||||
r, err = db.Table(table1).Delete()
|
||||
t.Assert(err, nil)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err = db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one.IsEmpty(), true)
|
||||
|
||||
one, err = db.Table(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -6,7 +6,16 @@
|
||||
|
||||
package utils
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// replaceCharReg is the regular expression object for replacing chars in key.
|
||||
// It is used for function EqualFoldWithoutChars.
|
||||
replaceCharReg, _ = regexp.Compile(`[\-\.\_\s]+`)
|
||||
)
|
||||
|
||||
// IsLetterUpper checks whether the given byte b is in upper case.
|
||||
func IsLetterUpper(b byte) bool {
|
||||
@ -73,3 +82,12 @@ func ReplaceByMap(origin string, replaces map[string]string) string {
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// EqualFoldWithoutChars checks string <s1> and <s2> equal case-insensitively,
|
||||
// with/without chars '-'/'_'/'.'/' '.
|
||||
func EqualFoldWithoutChars(s1, s2 string) bool {
|
||||
return strings.EqualFold(
|
||||
replaceCharReg.ReplaceAllString(s1, ""),
|
||||
replaceCharReg.ReplaceAllString(s2, ""),
|
||||
)
|
||||
}
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
package gutil
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -40,14 +40,9 @@ func MapPossibleItemByKey(data map[string]interface{}, key string) (foundKey str
|
||||
if v, ok := data[key]; ok {
|
||||
return key, v
|
||||
}
|
||||
replacedKey := replaceCharReg.ReplaceAllString(key, "")
|
||||
if v, ok := data[replacedKey]; ok {
|
||||
return replacedKey, v
|
||||
}
|
||||
// Loop for check.
|
||||
// Loop checking.
|
||||
for k, v := range data {
|
||||
// Remove all special chars and compare with case insensitive.
|
||||
if strings.EqualFold(replaceCharReg.ReplaceAllString(k, ""), replacedKey) {
|
||||
if utils.EqualFoldWithoutChars(k, key) {
|
||||
return k, v
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user