mirror of
https://gitee.com/johng/gf
synced 2026-07-06 05:42:20 +08:00
enhance: improve Save feature for drivers oracle and dm (#3426)
This commit is contained in:
@ -17,7 +17,6 @@ import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// DoInsert inserts or updates data for given table.
|
||||
@ -25,15 +24,16 @@ 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:
|
||||
return d.doSave(ctx, link, table, list, option)
|
||||
|
||||
case gdb.InsertOptionReplace:
|
||||
// TODO:: Should be Supported
|
||||
return nil, gerror.NewCode(
|
||||
gcode.CodeNotSupported, `Replace operation is not supported by dm driver`,
|
||||
)
|
||||
|
||||
case gdb.InsertOptionSave:
|
||||
return d.doSave(ctx, link, table, list, option)
|
||||
}
|
||||
|
||||
return d.Core.DoInsert(ctx, link, table, list, option)
|
||||
}
|
||||
|
||||
@ -54,18 +54,23 @@ func (d *Driver) doSave(ctx context.Context,
|
||||
}
|
||||
|
||||
var (
|
||||
one = list[0]
|
||||
charL, charR = d.GetChars()
|
||||
valueCharL, valueCharR = "'", "'"
|
||||
one = list[0]
|
||||
oneLen = len(one)
|
||||
charL, charR = d.GetChars()
|
||||
|
||||
conflictKeys = option.OnConflict
|
||||
conflictKeySet = gset.New(false)
|
||||
|
||||
// insertKeys: Handle valid keys that need to be inserted
|
||||
// insertValues: Handle values that need to be inserted
|
||||
// updateValues: Handle values that need to be updated
|
||||
// queryValues: Handle data that need to be upsert
|
||||
queryValues, insertKeys, insertValues, updateValues []string
|
||||
// queryHolders: Handle data with Holder that need to be upsert
|
||||
// queryValues: Handle data that need to be upsert
|
||||
// insertKeys: Handle valid keys that need to be inserted
|
||||
// insertValues: Handle values that need to be inserted
|
||||
// updateValues: Handle values that need to be updated
|
||||
queryHolders = make([]string, oneLen)
|
||||
queryValues = make([]interface{}, oneLen)
|
||||
insertKeys = make([]string, oneLen)
|
||||
insertValues = make([]string, oneLen)
|
||||
updateValues []string
|
||||
)
|
||||
|
||||
// conflictKeys slice type conv to set type
|
||||
@ -73,31 +78,28 @@ func (d *Driver) doSave(ctx context.Context,
|
||||
conflictKeySet.Add(gstr.ToUpper(conflictKey))
|
||||
}
|
||||
|
||||
index := 0
|
||||
for key, value := range one {
|
||||
saveValue := gconv.String(value)
|
||||
queryValues = append(
|
||||
queryValues,
|
||||
fmt.Sprintf(
|
||||
valueCharL+"%s"+valueCharR+" AS "+charL+"%s"+charR,
|
||||
saveValue, key,
|
||||
),
|
||||
)
|
||||
keyWithChar := charL + key + charR
|
||||
queryHolders[index] = fmt.Sprintf("? AS %s", keyWithChar)
|
||||
queryValues[index] = value
|
||||
insertKeys[index] = keyWithChar
|
||||
insertValues[index] = fmt.Sprintf("T2.%s", keyWithChar)
|
||||
|
||||
insertKeys = append(insertKeys, charL+key+charR)
|
||||
insertValues = append(insertValues, "T2."+charL+key+charR)
|
||||
|
||||
// filter conflict keys in updateValues
|
||||
if !conflictKeySet.Contains(key) {
|
||||
// filter conflict keys in updateValues.
|
||||
// And the key is not a soft created field.
|
||||
if !(conflictKeySet.Contains(key) || d.Core.IsSoftCreatedFieldName(key)) {
|
||||
updateValues = append(
|
||||
updateValues,
|
||||
fmt.Sprintf(`T1.%s = T2.%s`, charL+key+charR, charL+key+charR),
|
||||
fmt.Sprintf(`T1.%s = T2.%s`, keyWithChar, keyWithChar),
|
||||
)
|
||||
}
|
||||
index++
|
||||
}
|
||||
|
||||
batchResult := new(gdb.SqlResult)
|
||||
sqlStr := parseSqlForUpsert(table, queryValues, insertKeys, insertValues, updateValues, conflictKeys)
|
||||
r, err := d.DoExec(ctx, link, sqlStr)
|
||||
sqlStr := parseSqlForUpsert(table, queryHolders, insertKeys, insertValues, updateValues, conflictKeys)
|
||||
r, err := d.DoExec(ctx, link, sqlStr, queryValues...)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
@ -112,17 +114,17 @@ func (d *Driver) doSave(ctx context.Context,
|
||||
|
||||
// parseSqlForUpsert
|
||||
// MERGE INTO {{table}} T1
|
||||
// USING ( SELECT {{queryValues}} FROM DUAL T2
|
||||
// USING ( SELECT {{queryHolders}} FROM DUAL T2
|
||||
// ON (T1.{{duplicateKey}} = T2.{{duplicateKey}} AND ...)
|
||||
// WHEN NOT MATCHED THEN
|
||||
// INSERT {{insertKeys}} VALUES {{insertValues}}
|
||||
// WHEN MATCHED THEN
|
||||
// UPDATE SET {{updateValues}}
|
||||
func parseSqlForUpsert(table string,
|
||||
queryValues, insertKeys, insertValues, updateValues, duplicateKey []string,
|
||||
queryHolders, insertKeys, insertValues, updateValues, duplicateKey []string,
|
||||
) (sqlStr string) {
|
||||
var (
|
||||
queryValueStr = strings.Join(queryValues, ",")
|
||||
queryHolderStr = strings.Join(queryHolders, ",")
|
||||
insertKeyStr = strings.Join(insertKeys, ",")
|
||||
insertValueStr = strings.Join(insertValues, ",")
|
||||
updateValueStr = strings.Join(updateValues, ",")
|
||||
@ -140,7 +142,7 @@ func parseSqlForUpsert(table string,
|
||||
|
||||
return fmt.Sprintf(pattern,
|
||||
table,
|
||||
queryValueStr,
|
||||
queryHolderStr,
|
||||
duplicateKeyStr,
|
||||
insertKeyStr,
|
||||
insertValueStr,
|
||||
|
||||
@ -10,9 +10,10 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
@ -110,18 +111,23 @@ func (d *Driver) doSave(ctx context.Context,
|
||||
}
|
||||
|
||||
var (
|
||||
one = list[0]
|
||||
charL, charR = d.GetChars()
|
||||
valueCharL, valueCharR = "'", "'"
|
||||
one = list[0]
|
||||
oneLen = len(one)
|
||||
charL, charR = d.GetChars()
|
||||
|
||||
conflictKeys = option.OnConflict
|
||||
conflictKeySet = gset.New(false)
|
||||
|
||||
// insertKeys: Handle valid keys that need to be inserted
|
||||
// insertValues: Handle values that need to be inserted
|
||||
// updateValues: Handle values that need to be updated
|
||||
// queryValues: Handle data that need to be upsert
|
||||
queryValues, insertKeys, insertValues, updateValues []string
|
||||
// queryHolders: Handle data with Holder that need to be upsert
|
||||
// queryValues: Handle data that need to be upsert
|
||||
// insertKeys: Handle valid keys that need to be inserted
|
||||
// insertValues: Handle values that need to be inserted
|
||||
// updateValues: Handle values that need to be updated
|
||||
queryHolders = make([]string, oneLen)
|
||||
queryValues = make([]interface{}, oneLen)
|
||||
insertKeys = make([]string, oneLen)
|
||||
insertValues = make([]string, oneLen)
|
||||
updateValues []string
|
||||
)
|
||||
|
||||
// conflictKeys slice type conv to set type
|
||||
@ -129,31 +135,28 @@ func (d *Driver) doSave(ctx context.Context,
|
||||
conflictKeySet.Add(gstr.ToUpper(conflictKey))
|
||||
}
|
||||
|
||||
index := 0
|
||||
for key, value := range one {
|
||||
saveValue := gconv.String(value)
|
||||
queryValues = append(
|
||||
queryValues,
|
||||
fmt.Sprintf(
|
||||
valueCharL+"%s"+valueCharR+" AS "+charL+"%s"+charR,
|
||||
saveValue, key,
|
||||
),
|
||||
)
|
||||
keyWithChar := charL + key + charR
|
||||
queryHolders[index] = fmt.Sprintf("? AS %s", keyWithChar)
|
||||
queryValues[index] = value
|
||||
insertKeys[index] = keyWithChar
|
||||
insertValues[index] = fmt.Sprintf("T2.%s", keyWithChar)
|
||||
|
||||
insertKeys = append(insertKeys, charL+key+charR)
|
||||
insertValues = append(insertValues, "T2."+charL+key+charR)
|
||||
|
||||
// filter conflict keys in updateValues
|
||||
if !conflictKeySet.Contains(key) {
|
||||
// filter conflict keys in updateValues.
|
||||
// And the key is not a soft created field.
|
||||
if !(conflictKeySet.Contains(key) || d.Core.IsSoftCreatedFieldName(key)) {
|
||||
updateValues = append(
|
||||
updateValues,
|
||||
fmt.Sprintf(`T1.%s = T2.%s`, charL+key+charR, charL+key+charR),
|
||||
fmt.Sprintf(`T1.%s = T2.%s`, keyWithChar, keyWithChar),
|
||||
)
|
||||
}
|
||||
index++
|
||||
}
|
||||
|
||||
batchResult := new(gdb.SqlResult)
|
||||
sqlStr := parseSqlForUpsert(table, queryValues, insertKeys, insertValues, updateValues, conflictKeys)
|
||||
r, err := d.DoExec(ctx, link, sqlStr)
|
||||
sqlStr := parseSqlForUpsert(table, queryHolders, insertKeys, insertValues, updateValues, conflictKeys)
|
||||
r, err := d.DoExec(ctx, link, sqlStr, queryValues...)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
@ -168,17 +171,17 @@ func (d *Driver) doSave(ctx context.Context,
|
||||
|
||||
// parseSqlForUpsert
|
||||
// MERGE INTO {{table}} T1
|
||||
// USING ( SELECT {{queryValues}} FROM DUAL T2
|
||||
// USING ( SELECT {{queryHolders}} FROM DUAL T2
|
||||
// ON (T1.{{duplicateKey}} = T2.{{duplicateKey}} AND ...)
|
||||
// WHEN NOT MATCHED THEN
|
||||
// INSERT {{insertKeys}} VALUES {{insertValues}}
|
||||
// WHEN MATCHED THEN
|
||||
// UPDATE SET {{updateValues}}
|
||||
func parseSqlForUpsert(table string,
|
||||
queryValues, insertKeys, insertValues, updateValues, duplicateKey []string,
|
||||
queryHolders, insertKeys, insertValues, updateValues, duplicateKey []string,
|
||||
) (sqlStr string) {
|
||||
var (
|
||||
queryValueStr = strings.Join(queryValues, ",")
|
||||
queryHolderStr = strings.Join(queryHolders, ",")
|
||||
insertKeyStr = strings.Join(insertKeys, ",")
|
||||
insertValueStr = strings.Join(insertValues, ",")
|
||||
updateValueStr = strings.Join(updateValues, ",")
|
||||
@ -196,7 +199,7 @@ func parseSqlForUpsert(table string,
|
||||
|
||||
return fmt.Sprintf(pattern,
|
||||
table,
|
||||
queryValueStr,
|
||||
queryHolderStr,
|
||||
duplicateKeyStr,
|
||||
insertKeyStr,
|
||||
insertValueStr,
|
||||
|
||||
@ -126,7 +126,7 @@ func createTable(table ...string) (name string) {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
//db.Schema("test")
|
||||
// db.Schema("test")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user