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 all 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
191 changes: 191 additions & 0 deletions contrib/drivers/mssql/mssql_do_exec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package mssql

import (
"context"
"database/sql"
"fmt"
"regexp"
"strings"

"github.com/gogf/gf/v2/database/gdb"
lxy1151 marked this conversation as resolved.
Show resolved Hide resolved
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)

const (
backIdInsertHeadDefault = "INSERT INTO"
backIdInsertHeadInsertIgnore = "INSERT IGNORE INTO"

autoIncrementName = "auto_increment"
mssqlOutPutKey = "OUTPUT"
mssqlInsertedObjName = "INSERTED"
mssqlAffectFd = " 1 as AffectCount"
affectCountFieldName = "AffectCount"
mssqlPrimaryKeyName = "PRI"
fdId = "ID"
positionInsertValues = ") VALUES" // find the position of the string "VALUES" in the INSERT SQL statement to embed output code for retrieving the last inserted ID
)

// DoExec commits the sql string and its arguments to underlying driver
// through given link object and returns the execution result.
func (d *Driver) DoExec(ctx context.Context, link gdb.Link, sqlStr string, args ...interface{}) (result sql.Result, err error) {
lxy1151 marked this conversation as resolved.
Show resolved Hide resolved
// Transaction checks.
if link == nil {
if tx := gdb.TXFromCtx(ctx, d.GetGroup()); tx != nil {
// Firstly, check and retrieve transaction link from context.
link = &txLinkMssql{tx.GetSqlTX()}
} else if link, err = d.Core.MasterLink(); err != nil {
// Or else it creates one from master node.
return nil, err
}
} else if !link.IsTransaction() {
// If current link is not transaction link, it checks and retrieves transaction from context.
if tx := gdb.TXFromCtx(ctx, d.GetGroup()); tx != nil {
link = &txLinkMssql{tx.GetSqlTX()}
}
}

// SQL filtering.
sqlStr, args = d.Core.FormatSqlBeforeExecuting(sqlStr, args)
sqlStr, args, err = d.DoFilter(ctx, link, sqlStr, args)
if err != nil {
return nil, err
}

if !(strings.HasPrefix(sqlStr, backIdInsertHeadDefault) || strings.HasPrefix(sqlStr, backIdInsertHeadInsertIgnore)) {
return d.Core.DoExec(ctx, link, sqlStr, args)
}
// find the first pos
pos := strings.Index(sqlStr, positionInsertValues)

table := d.GetTableNameFromSql(sqlStr)
outPutSql := d.GetInsertOutputSql(ctx, table)
// rebuild sql add output
var (
sqlValueBefore = sqlStr[:pos+1]
sqlValueAfter = sqlStr[pos+1:]
)

sqlStr = fmt.Sprintf("%s%s%s", sqlValueBefore, outPutSql, sqlValueAfter)

// fmt.Println("sql str:", sqlStr)
// Link execution.
var out gdb.DoCommitOutput
out, err = d.DoCommit(ctx, gdb.DoCommitInput{
Link: link,
Sql: sqlStr,
Args: args,
Stmt: nil,
Type: gdb.SqlTypeQueryContext,
IsTransaction: link.IsTransaction(),
})
if err != nil {
return &InsertResult{lastInsertId: 0, rowsAffected: 0, err: err}, err
}
var (
aCount int64 // affect count
lId int64 // last insert id
)
stdSqlResult := out.Records
if len(stdSqlResult) == 0 {
err = gerror.WrapCode(gcode.CodeDbOperationError, gerror.New("affectcount is zero"), `sql.Result.RowsAffected failed`)
return &InsertResult{lastInsertId: 0, rowsAffected: 0, err: err}, err
}
// get affect count
aCount = stdSqlResult[0].GMap().GetVar(affectCountFieldName).Int64()
// get last_insert_id
lId = stdSqlResult[0].GMap().GetVar(fdId).Int64()

return &InsertResult{lastInsertId: lId, rowsAffected: aCount}, err
}

// GetTableNameFromSql get table name from sql statement
// It handles table string like:
// "user"
// "user u"
// "DbLog.dbo.user",
// "user as u".
func (d *Driver) GetTableNameFromSql(sqlStr string) (table string) {
// INSERT INTO "ip_to_id"("ip") OUTPUT 1 as AffectCount,INSERTED.id as ID VALUES(?)
leftChars, rightChars := d.GetChars()
trimStr := leftChars + rightChars + "[] "
pattern := "INTO(.+?)\\("
regCompile := regexp.MustCompile(pattern)
tableInfo := regCompile.FindStringSubmatch(sqlStr)
//get the first one. after the first it may be content of the value, it's not table name.
table = tableInfo[1]
table = strings.Trim(table, " ")
if strings.Contains(table, ".") {
tmpAry := strings.Split(table, ".")
// the last one is tablename
table = tmpAry[len(tmpAry)-1]
} else if strings.Contains(table, "as") || strings.Contains(table, " ") {
tmpAry := strings.Split(table, "as")
if len(tmpAry) < 2 {
tmpAry = strings.Split(table, " ")
}
// get the first one
table = tmpAry[0]
}
table = strings.Trim(table, trimStr)
return table
}

// txLink is used to implement interface Link for TX.
type txLinkMssql struct {
*sql.Tx
}

// IsTransaction returns if current Link is a transaction.
func (l *txLinkMssql) IsTransaction() bool {
return true
}

// IsOnMaster checks and returns whether current link is operated on master node.
// Note that, transaction operation is always operated on master node.
func (l *txLinkMssql) IsOnMaster() bool {
return true
}

// 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)
}
}
return strings.Join(extraSqlAry, ",")
// sql example:INSERT INTO "ip_to_id"("ip") OUTPUT 1 as AffectCount,INSERTED.id as ID VALUES(?)
}
52 changes: 52 additions & 0 deletions contrib/drivers/mssql/mssql_z_unit_basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ import (
"testing"
"time"

"github.com/gogf/gf/v2/database/gdb"
"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"

"github.com/gogf/gf/contrib/drivers/mssql/v2"
)

func TestTables(t *testing.T) {
Expand Down Expand Up @@ -148,6 +152,54 @@ func TestDoInsert(t *testing.T) {
})
}

func TestDoInsertGetId(t *testing.T) {
// create test table
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)

// multiple insert test
dataAry := []map[string]interface{}{{"ip": "192.168.5.9"}, {"ip": "192.168.5.10"}}
id1, err1 := db.InsertAndGetId(gctx.New(), table, dataAry)
t.AssertNil(err1)
t.AssertGT(id1, 0)
})
}

func TestGetTableFromSql(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
okTable := "ip_to_id"
sqlStr := "INSERT INTO \"ip_to_id\"(\"ip\") VALUES(?)"
dbMssql, _ := db.GetCore().GetDB().(*gdb.DriverWrapperDB).DB.(*mssql.Driver)
//fmt.Println("db:", fmt.Sprintf("%T", dbMssql), " ok:", ok)
table := dbMssql.GetTableNameFromSql(sqlStr)
// fmt.Println("default table:", table)
t.Assert(table, okTable)

sqlStr = "INSERT INTO \"MyLogDb\".\"dbo\".\"ip_to_id\"(\"ip\") VALUES(?)"
table = dbMssql.GetTableNameFromSql(sqlStr)
// fmt.Println("MyLogDb.dbo.ip_to_id table:", table)
t.Assert(table, okTable)

sqlStr = "INSERT INTO \"ip_to_id\" as \"tt\" (\"ip\") VALUES(?)"
table = dbMssql.GetTableNameFromSql(sqlStr)
// fmt.Println("ip_to_id as tt table:", table)
t.Assert(table, okTable)

sqlStr = "INSERT INTO \"ip_to_id\" \"tt\" (\"ip\") VALUES(?)"
table = dbMssql.GetTableNameFromSql(sqlStr)
// fmt.Println("ip_to_id tt table:", table)
t.Assert(table, okTable)
})
}

func Test_DB_Ping(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
err1 := db.PingMaster()
Expand Down
38 changes: 33 additions & 5 deletions contrib/drivers/mssql/mssql_z_unit_init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,23 @@ var (
)

const (
TableSize = 10
TestDbUser = "sa"
TestDbPass = "LoremIpsum86"
TableSize = 10
TableName = "t_user"
TestSchema1 = "test1"
TestSchema2 = "test2"
TableNamePrefix1 = "gf_"
TestDbUser = "sa"
TestDbPass = "LoremIpsum86" // "theone@123"
CreateTime = "2018-10-24 10:00:00"
)

func init() {
node := gdb.ConfigNode{
Host: "127.0.0.1",
Host: "127.0.0.1", // 192.168.5.72 127.0.0.1
Port: "1433",
User: TestDbUser,
Pass: TestDbPass,
Name: "master",
Name: "test", // "QPLogDB",
Type: "mssql",
Role: "master",
Charset: "utf8",
Expand Down Expand Up @@ -142,3 +147,26 @@ func dropTable(table string) {
gtest.Fatal(err)
}
}

// createInsertAndGetIdTableForTest test for InsertAndGetId
func createInsertAndGetIdTableForTest() (name string) {

if _, err := db.Exec(context.Background(), `
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='ip_to_id' and xtype='U')
begin
CREATE TABLE [ip_to_id](
[id] [int] IDENTITY(1,1) NOT NULL,
[ip] [varchar](128) NULL,
CONSTRAINT [PK_ip_to_id] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
end
`); err != nil {
gtest.Fatal(err)
}

db.Schema(db.GetConfig().Name)
return
}
Loading