2024-01-30 20:03:58 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). 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 pgsql
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"database/sql"
|
2024-12-07 14:17:33 +08:00
|
|
|
|
2024-01-30 20:03:58 +08:00
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
|
|
|
"github.com/gogf/gf/v2/errors/gcode"
|
|
|
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
2025-12-04 20:33:08 +08:00
|
|
|
"github.com/gogf/gf/v2/text/gstr"
|
2024-01-30 20:03:58 +08:00
|
|
|
)
|
|
|
|
|
|
2024-03-12 20:40:20 +08:00
|
|
|
// DoInsert inserts or updates data for given table.
|
2025-12-08 14:37:35 +08:00
|
|
|
// The list parameter must contain at least one record, which was previously validated.
|
|
|
|
|
func (d *Driver) DoInsert(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption,
|
|
|
|
|
) (result sql.Result, err error) {
|
2024-01-30 20:03:58 +08:00
|
|
|
switch option.InsertOption {
|
2025-12-08 14:37:35 +08:00
|
|
|
case
|
|
|
|
|
gdb.InsertOptionReplace,
|
|
|
|
|
gdb.InsertOptionSave:
|
2025-12-04 20:33:08 +08:00
|
|
|
// PostgreSQL does not support REPLACE INTO syntax, use Save (ON CONFLICT ... DO UPDATE) instead.
|
|
|
|
|
// Automatically detect primary keys if OnConflict is not specified.
|
|
|
|
|
if len(option.OnConflict) == 0 {
|
|
|
|
|
primaryKeys, err := d.getPrimaryKeys(ctx, table)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, gerror.WrapCode(
|
|
|
|
|
gcode.CodeInternalError,
|
|
|
|
|
err,
|
2025-12-08 14:54:26 +08:00
|
|
|
`failed to get primary keys for Save/Replace operation`,
|
2025-12-04 20:33:08 +08:00
|
|
|
)
|
|
|
|
|
}
|
2025-12-08 14:37:35 +08:00
|
|
|
foundPrimaryKey := false
|
|
|
|
|
for _, conflictKey := range primaryKeys {
|
|
|
|
|
if _, ok := list[0][conflictKey]; ok {
|
|
|
|
|
foundPrimaryKey = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !foundPrimaryKey {
|
2025-12-04 20:33:08 +08:00
|
|
|
return nil, gerror.NewCode(
|
|
|
|
|
gcode.CodeMissingParameter,
|
2025-12-08 14:37:35 +08:00
|
|
|
`Please specify conflict columns or ensure the record has a primary key for Save/Replace operation`,
|
2025-12-04 20:33:08 +08:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
option.OnConflict = primaryKeys
|
|
|
|
|
}
|
|
|
|
|
// Treat Replace as Save operation
|
|
|
|
|
option.InsertOption = gdb.InsertOptionSave
|
2024-01-30 20:03:58 +08:00
|
|
|
|
|
|
|
|
case gdb.InsertOptionDefault:
|
|
|
|
|
tableFields, err := d.GetCore().GetDB().TableFields(ctx, table)
|
|
|
|
|
if err == nil {
|
|
|
|
|
for _, field := range tableFields {
|
2025-12-08 15:01:01 +08:00
|
|
|
if gstr.Equal(field.Key, "pri") {
|
2024-01-30 20:03:58 +08:00
|
|
|
pkField := *field
|
|
|
|
|
ctx = context.WithValue(ctx, internalPrimaryKeyInCtx, pkField)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-04 20:33:08 +08:00
|
|
|
|
|
|
|
|
default:
|
2024-01-30 20:03:58 +08:00
|
|
|
}
|
|
|
|
|
return d.Core.DoInsert(ctx, link, table, list, option)
|
|
|
|
|
}
|
2025-12-04 20:33:08 +08:00
|
|
|
|
|
|
|
|
// getPrimaryKeys retrieves the primary key field list of the table.
|
|
|
|
|
// This method extracts primary key information from TableFields.
|
|
|
|
|
func (d *Driver) getPrimaryKeys(ctx context.Context, table string) ([]string, error) {
|
2025-12-08 16:27:17 +08:00
|
|
|
tableFields, err := d.GetCore().GetDB().TableFields(ctx, table)
|
2025-12-04 20:33:08 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var primaryKeys []string
|
|
|
|
|
for _, field := range tableFields {
|
2025-12-08 15:00:52 +08:00
|
|
|
if gstr.Equal(field.Key, "pri") {
|
2025-12-04 20:33:08 +08:00
|
|
|
primaryKeys = append(primaryKeys, field.Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return primaryKeys, nil
|
|
|
|
|
}
|