2022-09-26 22:11:13 +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 gdb
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"database/sql"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
2024-05-22 21:14:43 +08:00
|
|
|
"github.com/gogf/gf/v2/container/gvar"
|
2022-10-17 15:43:22 +08:00
|
|
|
"github.com/gogf/gf/v2/encoding/gjson"
|
2022-09-26 22:11:13 +08:00
|
|
|
"github.com/gogf/gf/v2/errors/gcode"
|
|
|
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
2022-10-17 15:43:22 +08:00
|
|
|
"github.com/gogf/gf/v2/internal/intlog"
|
2024-05-22 21:14:43 +08:00
|
|
|
"github.com/gogf/gf/v2/os/gcache"
|
2022-09-26 22:11:13 +08:00
|
|
|
"github.com/gogf/gf/v2/text/gstr"
|
|
|
|
|
"github.com/gogf/gf/v2/util/gutil"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// DriverWrapperDB is a DB wrapper for extending features with embedded DB.
|
|
|
|
|
type DriverWrapperDB struct {
|
|
|
|
|
DB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Open creates and returns an underlying sql.DB object for pgsql.
|
|
|
|
|
// https://pkg.go.dev/github.com/lib/pq
|
2022-10-17 15:43:22 +08:00
|
|
|
func (d *DriverWrapperDB) Open(node *ConfigNode) (db *sql.DB, err error) {
|
|
|
|
|
var ctx = d.GetCtx()
|
|
|
|
|
intlog.PrintFunc(ctx, func() string {
|
|
|
|
|
return fmt.Sprintf(`open new connection:%s`, gjson.MustEncode(node))
|
|
|
|
|
})
|
|
|
|
|
return d.DB.Open(node)
|
2022-09-26 22:11:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tables retrieves and returns the tables of current schema.
|
|
|
|
|
// It's mainly used in cli tool chain for automatically generating the models.
|
|
|
|
|
func (d *DriverWrapperDB) Tables(ctx context.Context, schema ...string) (tables []string, err error) {
|
|
|
|
|
ctx = context.WithValue(ctx, ctxKeyInternalProducedSQL, struct{}{})
|
|
|
|
|
return d.DB.Tables(ctx, schema...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TableFields retrieves and returns the fields' information of specified table of current
|
|
|
|
|
// schema.
|
|
|
|
|
//
|
|
|
|
|
// The parameter `link` is optional, if given nil it automatically retrieves a raw sql connection
|
|
|
|
|
// as its link to proceed necessary sql query.
|
|
|
|
|
//
|
|
|
|
|
// Note that it returns a map containing the field name and its corresponding fields.
|
|
|
|
|
// As a map is unsorted, the TableField struct has an "Index" field marks its sequence in
|
|
|
|
|
// the fields.
|
|
|
|
|
//
|
|
|
|
|
// It's using cache feature to enhance the performance, which is never expired util the
|
|
|
|
|
// process restarts.
|
2022-10-08 21:42:30 +08:00
|
|
|
func (d *DriverWrapperDB) TableFields(
|
|
|
|
|
ctx context.Context, table string, schema ...string,
|
|
|
|
|
) (fields map[string]*TableField, err error) {
|
|
|
|
|
if table == "" {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
2022-09-26 22:11:13 +08:00
|
|
|
charL, charR := d.GetChars()
|
|
|
|
|
table = gstr.Trim(table, charL+charR)
|
|
|
|
|
if gstr.Contains(table, " ") {
|
|
|
|
|
return nil, gerror.NewCode(
|
|
|
|
|
gcode.CodeInvalidParameter,
|
|
|
|
|
"function TableFields supports only single table operations",
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
var (
|
2024-05-22 21:14:43 +08:00
|
|
|
innerMemCache = d.GetCore().GetInnerMemCache()
|
2024-02-05 10:29:43 +08:00
|
|
|
// prefix:group@schema#table
|
2024-05-22 21:14:43 +08:00
|
|
|
cacheKey = genTableFieldsCacheKey(
|
2022-09-26 22:11:13 +08:00
|
|
|
d.GetGroup(),
|
|
|
|
|
gutil.GetOrDefaultStr(d.GetSchema(), schema...),
|
|
|
|
|
table,
|
|
|
|
|
)
|
refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395)
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.
**Type alias migration to `any`:**
* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)
**Generated code and templates:**
* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
**Container and utility updates:**
* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)
These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
2025-08-28 16:53:19 +08:00
|
|
|
cacheFunc = func(ctx context.Context) (any, error) {
|
2024-05-22 21:14:43 +08:00
|
|
|
return d.DB.TableFields(
|
|
|
|
|
context.WithValue(ctx, ctxKeyInternalProducedSQL, struct{}{}),
|
|
|
|
|
table, schema...,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
value *gvar.Var
|
2022-09-26 22:11:13 +08:00
|
|
|
)
|
2024-05-22 21:14:43 +08:00
|
|
|
value, err = innerMemCache.GetOrSetFuncLock(
|
|
|
|
|
ctx, cacheKey, cacheFunc, gcache.DurationNoExpire,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if !value.IsNil() {
|
|
|
|
|
fields = value.Val().(map[string]*TableField)
|
2022-09-26 22:11:13 +08:00
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-08-07 21:03:56 +08:00
|
|
|
|
2024-03-12 20:40:20 +08:00
|
|
|
// DoInsert inserts or updates data for given table.
|
2023-08-07 21:03:56 +08:00
|
|
|
// This function is usually used for custom interface definition, you do not need call it manually.
|
|
|
|
|
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
|
|
|
|
|
// Eg:
|
|
|
|
|
// Data(g.Map{"uid": 10000, "name":"john"})
|
|
|
|
|
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
|
|
|
|
|
//
|
|
|
|
|
// The parameter `option` values are as follows:
|
2023-09-04 21:23:54 +08:00
|
|
|
// InsertOptionDefault: just insert, if there's unique/primary key in the data, it returns error;
|
|
|
|
|
// InsertOptionReplace: if there's unique/primary key in the data, it deletes it from table and inserts a new one;
|
|
|
|
|
// InsertOptionSave: if there's unique/primary key in the data, it updates it or else inserts a new one;
|
|
|
|
|
// InsertOptionIgnore: if there's unique/primary key in the data, it ignores the inserting;
|
feat(contrib/drivers/dm&pgsql&mssql&oracle): add Replace/LastInsertId features support for dm/pgsql/mssql/oracle (#4547)
This pull request introduces significant improvements to the handling of
the `Replace` and `Save` operations for multiple database drivers,
especially for MSSQL and PostgreSQL. The changes ensure that these
operations now auto-detect primary keys when conflict columns are not
explicitly provided, improving usability and aligning behavior across
drivers. Additionally, the pull request updates related tests to reflect
these enhancements and includes some minor documentation and code
cleanup.
**Key changes:**
### Enhanced Replace/Save Logic for Database Drivers
* **MSSQL Driver:**
- `Replace` and `Save` operations now auto-detect primary keys if
`OnConflict` is not specified, using the `MERGE` statement for upsert
functionality. If no primary key is found in the data, a detailed error
is returned.
[[1]](diffhunk://#diff-87815aa559a927e2de09bd05148f9841dfc06a1b5f3ecc5e3d5fcb80323a87f8L23-R61)
[[2]](diffhunk://#diff-87815aa559a927e2de09bd05148f9841dfc06a1b5f3ecc5e3d5fcb80323a87f8L43-L59)
- Updated tests to verify that `Replace` correctly updates or inserts
records, and that missing conflict columns are properly handled.
[[1]](diffhunk://#diff-bdbde9d7d6ee14c795343767b414740c4396f4dd3e97788b1f9d4e615405a42dL141-R151)
[[2]](diffhunk://#diff-26338e93e473300b1313936eb0f6826546473793442f24715fa294b595f7a805L2661-R2707)
* **PostgreSQL Driver:**
- Similar to MSSQL, `Replace` and `Save` now auto-detect primary keys
for conflict resolution if `OnConflict` is not set, and treat `Replace`
as a `Save` operation.
- Adjusted tests to ensure `Save` and `Replace` work as expected,
including verifying data replacement and insertion.
[[1]](diffhunk://#diff-c22703c37ebb6836c332f7cd2ada570577ba4564fe39886db02f7c2d0e7a2048L93-R93)
[[2]](diffhunk://#diff-c22703c37ebb6836c332f7cd2ada570577ba4564fe39886db02f7c2d0e7a2048R102)
[[3]](diffhunk://#diff-c22703c37ebb6836c332f7cd2ada570577ba4564fe39886db02f7c2d0e7a2048L110-R130)
* **DM Driver:**
- Improved conflict detection: now checks that at least one primary key
exists in the provided data when `OnConflict` is not specified, and
provides clearer error messages.
- Refactored to use the core method for primary key detection and
removed redundant code.
### Minor Improvements and Documentation
* Added clarifying comments to `DoInsert` methods for ClickHouse, DM,
MSSQL, Oracle, and PostgreSQL drivers, specifying that the input list
must have at least one validated record.
[[1]](diffhunk://#diff-f2e003895041ed3c52b91bb8c270696adc3528d77c39d2f7137af3396267444cR19)
[[2]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eR23)
[[3]](diffhunk://#diff-87815aa559a927e2de09bd05148f9841dfc06a1b5f3ecc5e3d5fcb80323a87f8L23-R61)
[[4]](diffhunk://#diff-f61dac3fcfd5df4a3936cd8743499c8c0fc45f4f5d0f5398ed84a0cb1603202cR24)
[[5]](diffhunk://#diff-c1dfed79aaa3a432057d2bd74d270e4b4094ebcf72984f1161d4972bea009410R16-R72)
* Minor code and comment cleanups, including improved formatting and
error handling.
[[1]](diffhunk://#diff-f61dac3fcfd5df4a3936cd8743499c8c0fc45f4f5d0f5398ed84a0cb1603202cR37)
[[2]](diffhunk://#diff-f61dac3fcfd5df4a3936cd8743499c8c0fc45f4f5d0f5398ed84a0cb1603202cL96-R98)
[[3]](diffhunk://#diff-f61dac3fcfd5df4a3936cd8743499c8c0fc45f4f5d0f5398ed84a0cb1603202cL106-L116)
[[4]](diffhunk://#diff-a17b44c76aaac53d1f164a2bb9440a5531659f4355e7ccfabdadff8dc8633c09L170-R171)
[[5]](diffhunk://#diff-56189fa9ae1df51716b50d34d7fe56bfe67a330e8ac2c6b0de7b958db6817ed5R83-R98)
### Workflow and Documentation Updates
* Updated example Docker commands in the CI workflow for consistency and
clarity.
[[1]](diffhunk://#diff-a1a3cb9bdeb5541d148091d973cf266aa3b317e6415a86630e816cbe27cf8b9cL57-R57)
[[2]](diffhunk://#diff-a1a3cb9bdeb5541d148091d973cf266aa3b317e6415a86630e816cbe27cf8b9cL78-R78)
[[3]](diffhunk://#diff-a1a3cb9bdeb5541d148091d973cf266aa3b317e6415a86630e816cbe27cf8b9cL92-R92)
[[4]](diffhunk://#diff-a1a3cb9bdeb5541d148091d973cf266aa3b317e6415a86630e816cbe27cf8b9cL106-R106)
[[5]](diffhunk://#diff-a1a3cb9bdeb5541d148091d973cf266aa3b317e6415a86630e816cbe27cf8b9cL153-R153)
[[6]](diffhunk://#diff-a1a3cb9bdeb5541d148091d973cf266aa3b317e6415a86630e816cbe27cf8b9cL164-R164)
* Removed outdated note about `Replace` support from the SQLite driver
documentation.
These changes improve the consistency, reliability, and developer
experience when performing upsert operations across different database
backends.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Lance Add <1196661499@qq.com>
2025-12-09 15:46:41 +08:00
|
|
|
func (d *DriverWrapperDB) DoInsert(
|
|
|
|
|
ctx context.Context, link Link, table string, list List, option DoInsertOption,
|
|
|
|
|
) (result sql.Result, err error) {
|
|
|
|
|
if len(list) == 0 {
|
|
|
|
|
return nil, gerror.NewCodef(
|
|
|
|
|
gcode.CodeInvalidRequest,
|
|
|
|
|
`data list is empty for %s operation`,
|
|
|
|
|
GetInsertOperationByOption(option.InsertOption),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-07 21:03:56 +08:00
|
|
|
// Convert data type before commit it to underlying db driver.
|
|
|
|
|
for i, item := range list {
|
2023-09-04 21:23:54 +08:00
|
|
|
list[i], err = d.GetCore().ConvertDataForRecord(ctx, item, table)
|
2023-08-07 21:03:56 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return d.DB.DoInsert(ctx, link, table, list, option)
|
|
|
|
|
}
|