Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed:mssqlserver_insert_and_get #4051

Open
wants to merge 52 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
5f6843c
提交go.mod
Jul 31, 2024
999f822
session增加重置方法
Aug 13, 2024
5c0a9ce
session增加重置方法
Aug 14, 2024
8f7f13a
session增加重置方法
Aug 14, 2024
f26b5c7
session增加重置方法
Aug 15, 2024
44da506
Merge branch 'master' into master
lxy1151 Aug 15, 2024
d132f4e
session增加重置方法
Aug 15, 2024
e2f4563
Merge branch 'master' of https://github.com/lxy1151/gf_session
Aug 15, 2024
0435e2a
session增加重置方法
Aug 15, 2024
373b052
session增加重置方法
Aug 16, 2024
8f7c1f4
撤销对多语言翻译的修改
lxy1151 Sep 26, 2024
660e874
提交备注文件
lxy1151 Dec 9, 2024
4168226
撤销对多语言翻译的修改
lxy1151 Dec 9, 2024
29230a3
Merge branch 'master' of https://github.com/lxy1151/gf_session
lxy1151 Dec 16, 2024
988dd05
master保持和gf的master一致
lxy1151 Dec 16, 2024
5339459
master保持和gf的master一致
lxy1151 Dec 16, 2024
cd2c6e4
master保持和gf的master一致
lxy1151 Dec 16, 2024
e978c48
master保持和gf的master一致
lxy1151 Dec 16, 2024
9d0aae6
master保持和gf的master一致
lxy1151 Dec 16, 2024
0a487b9
master保持和gf的master一致
lxy1151 Dec 16, 2024
843876a
master保持和gf的master一致
lxy1151 Dec 16, 2024
6e77f60
修复mssql的InsertAndGetId方法
lxy1151 Dec 17, 2024
701fb15
Merge branch 'master' into fixed_mssqlserver_insert_and_get_id
lxy1151 Dec 17, 2024
f8fe824
修复mssql的InsertAndGetId方法
lxy1151 Dec 17, 2024
a9b92b4
Update mssql_do_insert.go
houseme Dec 17, 2024
cc68d11
Update mssql_z_unit_basic_test.go
houseme Dec 17, 2024
a409642
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
4916d29
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
fa39047
Merge remote-tracking branch 'origin/fixed_mssqlserver_insert_and_get…
lxy1151 Dec 18, 2024
017e6c4
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
7d6b181
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
13d1670
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
8bb5c9b
Update mssql_do_insert.go
houseme Dec 18, 2024
ee51d56
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
7c16184
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
bcd54f8
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
3ec1866
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
b9ef721
Merge branch 'master' into fixed_mssqlserver_insert_and_get_id
lxy1151 Dec 18, 2024
ee463d4
修复mssql的InsertAndGetId方法
lxy1151 Dec 18, 2024
1b623ec
Merge remote-tracking branch 'origin/fixed_mssqlserver_insert_and_get…
lxy1151 Dec 18, 2024
5beb48c
修复mssql的InsertAndGetId方法
lxy1151 Dec 19, 2024
bf43307
修复mssql的InsertAndGetId方法
lxy1151 Dec 19, 2024
405c531
修复mssql的InsertAndGetId方法
lxy1151 Dec 19, 2024
7248bb5
修复mssql的InsertAndGetId方法
lxy1151 Dec 19, 2024
c84d8b4
Merge branch 'master' into fixed_mssqlserver_insert_and_get_id
lxy1151 Dec 19, 2024
8cccf2d
修复mssql的InsertAndGetId方法
lxy1151 Dec 20, 2024
a57d9f6
Merge remote-tracking branch 'origin/fixed_mssqlserver_insert_and_get…
lxy1151 Dec 20, 2024
868b9a7
Merge branch 'master' into fixed_mssqlserver_insert_and_get_id
lxy1151 Dec 20, 2024
282c169
修复mssql的InsertAndGetId方法
lxy1151 Dec 20, 2024
8164522
修复mssql的InsertAndGetId方法
lxy1151 Dec 20, 2024
9def00d
Merge remote-tracking branch 'origin/fixed_mssqlserver_insert_and_get…
lxy1151 Dec 20, 2024
4536b67
修复mssql的InsertAndGetId方法
lxy1151 Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 266 additions & 3 deletions contrib/drivers/mssql/mssql_do_insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"context"
"database/sql"
"fmt"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/util/gconv"
houseme marked this conversation as resolved.
Show resolved Hide resolved
"strings"

"github.com/gogf/gf/v2/container/gset"
Expand All @@ -19,7 +21,21 @@ import (
"github.com/gogf/gf/v2/text/gstr"
)

// DoInsert inserts or updates data for given table.
var (
createdFiledNames = []string{"created_at", "create_at"}
)

const (
autoIncrementName = "auto_increment"
mssqlOutPutKey = "OUTPUT"
mssqlInsertedObjName = "INSERTED"
mssqlAffectFd = " 1 as AffectCount"
affectCountFieldName = "AffectCount"
mssqlPrimaryKeyName = "PRI"
fdId = "ID"
)

// DoInsert inserts or updates data for given table. rewrite db.core.DoInsert
func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case gdb.InsertOptionSave:
Expand All @@ -30,10 +46,257 @@ func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list
gcode.CodeNotSupported,
`Replace operation is not supported by mssql driver`,
)
//default:
// return d.Core.DoInsert(ctx, link, table, list, option)
}
var (
keys []string // Field names.
values []string // Value holder string array, like: (?,?,?)
params []interface{} // Values that will be committed to underlying database driver.
onDuplicateStr string // onDuplicateStr is used in "ON DUPLICATE KEY UPDATE" statement.
)
// Group the list by fields. Different fields to different list.
// It here uses ListMap to keep sequence for data inserting.
var keyListMap = gmap.NewListMap()
for _, item := range list {
var (
tmpKeys = make([]string, 0)
tmpKeysInSequenceStr string
)
for k := range item {
tmpKeys = append(tmpKeys, k)
}
keys, err = d.fieldsToSequence(ctx, table, tmpKeys)
if err != nil {
return nil, err
}
tmpKeysInSequenceStr = gstr.Join(keys, ",")

if !keyListMap.Contains(tmpKeysInSequenceStr) {
keyListMap.Set(tmpKeysInSequenceStr, make(gdb.List, 0))
}
tmpKeysInSequenceList := keyListMap.Get(tmpKeysInSequenceStr).(gdb.List)
tmpKeysInSequenceList = append(tmpKeysInSequenceList, item)
keyListMap.Set(tmpKeysInSequenceStr, tmpKeysInSequenceList)
}
if keyListMap.Size() > 1 {
var (
tmpResult sql.Result
sqlResult gdb.SqlResult
rowsAffected int64
)
keyListMap.Iterator(func(key, value interface{}) bool {
tmpResult, err = d.DoInsert(ctx, link, table, value.(gdb.List), option)
if err != nil {
return false
}
rowsAffected, err = tmpResult.RowsAffected()
if err != nil {
return false
}
sqlResult.Result = tmpResult
sqlResult.Affected += rowsAffected
return true
})
return &sqlResult, nil
}
// Prepare the batch result pointer.
var (
charL, charR = d.GetDB().GetChars()
batchResult = new(gdb.SqlResult)
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
operation = gdb.GetInsertOperationByOption(option.InsertOption)
)
if option.InsertOption == gdb.InsertOptionSave {
onDuplicateStr = d.formatOnDuplicate(keys, option)
}
var (
listLength = len(list)
valueHolder = make([]string, 0)
)
for i := 0; i < listLength; i++ {
values = values[:0]
// Note that the map type is unordered,
// so it should use slice+key to retrieve the value.
for _, k := range keys {
if s, ok := list[i][k].(gdb.Raw); ok {
values = append(values, gconv.String(s))
} else {
values = append(values, "?")
params = append(params, list[i][k])
}
}
valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")")
// Batch package checks: It meets the batch number, or it is the last element.
if len(valueHolder) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) {
var (
stdSqlResult gdb.Result
retResult interface{}
)

stdSqlResult, err = d.GetDB().DoQuery(ctx, link, fmt.Sprintf(
"%s INTO %s(%s) %s VALUES%s %s ",
operation, d.QuotePrefixTableName(table), keysStr,
d.GetInsertOutputSql(ctx, table),
gstr.Join(valueHolder, ","),
onDuplicateStr,
), params...)
if err != nil {
retResult = &InsertResult{lastInsertId: 0, rowsAffected: 0, err: err}
return retResult.(sql.Result), err
}
var (
aCount int64 // affect count
lId int64 // last insert id
)
if len(stdSqlResult) == 0 {
err = gerror.WrapCode(gcode.CodeDbOperationError, gerror.New("affectcount is zero"), `sql.Result.RowsAffected failed`)
retResult = &InsertResult{lastInsertId: 0, rowsAffected: 0, err: err}
return retResult.(sql.Result), err
}
// get affect count
aCount = stdSqlResult[0].GMap().GetVar(affectCountFieldName).Int64()
// get last_insert_id
lId = stdSqlResult[0].GMap().GetVar(fdId).Int64()

default:
return d.Core.DoInsert(ctx, link, table, list, option)
retResult = &InsertResult{lastInsertId: lId, rowsAffected: aCount}

batchResult.Result = retResult.(sql.Result)
batchResult.Affected += aCount

params = params[:0]
valueHolder = valueHolder[:0]
}
}
lxy1151 marked this conversation as resolved.
Show resolved Hide resolved
return batchResult, nil
}

// InsertResult instance of sql.Result
type InsertResult struct {
lastInsertId int64
rowsAffected int64
err error
}

func (r *InsertResult) LastInsertId() (int64, error) {
return r.lastInsertId, r.err
}

func (r *InsertResult) RowsAffected() (int64, error) {
return r.rowsAffected, r.err
}

// GetInsertOutputSql gen get last_insert_id code
func (m *Driver) GetInsertOutputSql(ctx context.Context, table string) string {
fds, errFd := m.GetDB().TableFields(ctx, table)
if errFd != nil {
return ""
}
extraSqlAry := make([]string, 0)
extraSqlAry = append(extraSqlAry, fmt.Sprintf("%s %s", mssqlOutPutKey, mssqlAffectFd))
incrNo := 0
if len(fds) > 0 {
for _, fd := range fds {
// has primary key and is auto-incement
if fd.Extra == autoIncrementName && fd.Key == mssqlPrimaryKeyName && !fd.Null {
incrNoStr := ""
if incrNo == 0 { //fixed first field named id, convenient to get
incrNoStr = fmt.Sprintf(" as %s", fdId)
}

extraSqlAry = append(extraSqlAry, fmt.Sprintf("%s.%s%s", mssqlInsertedObjName, fd.Name, incrNoStr))
incrNo++
}
//fmt.Printf("null:%t name:%s key:%s k:%s \n", fd.Null, fd.Name, fd.Key, k)
}
}
//fmt.Println(extraSqlAry)
return strings.Join(extraSqlAry, ",")
//";select ID = convert(bigint, SCOPE_IDENTITY()), AffectCount = @@ROWCOUNT;"
}

func (d *Driver) fieldsToSequence(ctx context.Context, table string, fields []string) ([]string, error) {
var (
fieldSet = gset.NewStrSetFrom(fields)
fieldsResultInSequence = make([]string, 0)
tableFields, err = d.GetDB().TableFields(ctx, table)
)
if err != nil {
return nil, err
}
// Sort the fields in order.
var fieldsOfTableInSequence = make([]string, len(tableFields))
for _, field := range tableFields {
fieldsOfTableInSequence[field.Index] = field.Name
}
// Sort the input fields.
for _, fieldName := range fieldsOfTableInSequence {
if fieldSet.Contains(fieldName) {
fieldsResultInSequence = append(fieldsResultInSequence, fieldName)
}
}
return fieldsResultInSequence, nil
}

func (d *Driver) formatOnDuplicate(columns []string, option gdb.DoInsertOption) string {
var onDuplicateStr string
if option.OnDuplicateStr != "" {
onDuplicateStr = option.OnDuplicateStr
} else if len(option.OnDuplicateMap) > 0 {
for k, v := range option.OnDuplicateMap {
if len(onDuplicateStr) > 0 {
onDuplicateStr += ","
}
switch v.(type) {
case gdb.Raw, *gdb.Raw:
onDuplicateStr += fmt.Sprintf(
"%s=%s",
d.QuoteWord(k),
v,
)
default:
onDuplicateStr += fmt.Sprintf(
"%s=VALUES(%s)",
d.QuoteWord(k),
d.QuoteWord(gconv.String(v)),
)
}
}
} else {
for _, column := range columns {
// If it's SAVE operation, do not automatically update the creating time.
if d.isSoftCreatedFieldName(column) {
continue
}
if len(onDuplicateStr) > 0 {
onDuplicateStr += ","
}
onDuplicateStr += fmt.Sprintf(
"%s=VALUES(%s)",
d.QuoteWord(column),
d.QuoteWord(column),
)
}
}
return fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", onDuplicateStr)
}

func (d *Driver) isSoftCreatedFieldName(fieldName string) bool {
if fieldName == "" {
return false
}
if config := d.GetDB().GetConfig(); config.CreatedAt != "" {
if equalFoldWithoutChars(fieldName, config.CreatedAt) {
return true
}
return gstr.InArray(append([]string{config.CreatedAt}, createdFiledNames...), fieldName)
}
for _, v := range createdFiledNames {
if equalFoldWithoutChars(fieldName, v) {
return true
}
}
return false
lxy1151 marked this conversation as resolved.
Show resolved Hide resolved
}

// doSave support upsert for SQL server
Expand Down
22 changes: 22 additions & 0 deletions contrib/drivers/mssql/mssql_tools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package mssql

import "strings"

// RemoveSymbols removes all symbols from string and lefts only numbers and letters.
func removeSymbols(s string) string {
var b = make([]rune, 0, len(s))
for _, c := range s {
if c > 127 {
b = append(b, c)
} else if (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') {
b = append(b, c)
}
}
return string(b)
}

// equalFoldWithoutChars checks string `s1` and `s2` equal case-insensitively,
// with/without chars '-'/'_'/'.'/' '.
func equalFoldWithoutChars(s1, s2 string) bool {
return strings.EqualFold(removeSymbols(s1), removeSymbols(s2))
}
16 changes: 16 additions & 0 deletions contrib/drivers/mssql/mssql_z_unit_basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/encoding/gxml"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
)
Expand Down Expand Up @@ -148,6 +149,21 @@ func TestDoInsert(t *testing.T) {
})
}

func TestDoInsertGetId(t *testing.T) {
//先创建表
createInsertAndGetIdTableForTest()
gtest.C(t, func(t *gtest.T) {
table := "ip_to_id"
data := map[string]interface{}{
"ip": "192.168.179.1",
}
id, err := db.InsertAndGetId(gctx.New(), table, data)
t.AssertNil(err)
t.AssertGT(id, 0)
//fmt.Println("id:", id)
})
}

func Test_DB_Ping(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
err1 := db.PingMaster()
Expand Down
Loading
Loading