mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
feat(contrib/drivers/pgsql): more field types converting support (#3737)
This pull request significantly improves PostgreSQL array type handling and conversion in the `pgsql` driver, providing more accurate type mapping and conversion logic, especially for array types. It introduces comprehensive documentation, refactors conversion logic to use the `pq` package for array types, and adds extensive unit tests to ensure correctness and error handling. Additionally, minor enhancements and clarifications are made to upsert formatting and table field queries. ### PostgreSQL Array Type Handling and Conversion * Refactored `CheckLocalTypeForField` and `ConvertValueForLocal` methods in `contrib/drivers/pgsql/pgsql_convert.go` to accurately map PostgreSQL array types (such as `_int2`, `_int4`, `_int8`, `_float4`, `_float8`, `_bool`, `_varchar`, `_text`, `_char`, `_bpchar`, `_numeric`, `_decimal`, `_money`, `_bytea`) to their corresponding Go types, using the `pq` package for conversion. Added detailed documentation and mapping tables for supported types. [[1]](diffhunk://#diff-a3b1e68bfa29fbcfda7c703bbe875fa82e958f6c3ad942ef82193a9dd8ad67e2R46-R63) [[2]](diffhunk://#diff-a3b1e68bfa29fbcfda7c703bbe875fa82e958f6c3ad942ef82193a9dd8ad67e2L56-R103) [[3]](diffhunk://#diff-a3b1e68bfa29fbcfda7c703bbe875fa82e958f6c3ad942ef82193a9dd8ad67e2R112-R209) * Added comprehensive unit tests in `contrib/drivers/pgsql/pgsql_z_unit_convert_test.go` to verify type mapping and conversion for all supported array types, including error cases for invalid input. ### Utility and API Improvements * Added a new `Bools()` method to the `gvar.Var` type in `container/gvar/gvar_slice.go` for converting values to `[]bool`, with corresponding unit tests in `container/gvar/gvar_z_unit_slice_test.go`. [[1]](diffhunk://#diff-32e887e540e0170f785508d105cb794e4d54d854b53b6950973c80022973c490R11-R15) [[2]](diffhunk://#diff-01453eca4d4b3e35d07ca105cb924c6441d0cd9df6cbcc337a89832c8d53057fR24-R41) ### SQL Formatting and Documentation * Improved documentation and formatting in the upsert logic of `contrib/drivers/pgsql/pgsql_format_upsert.go` to clarify the use of `EXCLUDED` in PostgreSQL's `ON CONFLICT DO UPDATE`. * Enhanced readability of the table field query in `contrib/drivers/pgsql/pgsql_table_fields.go` by reformatting SQL and clarifying field extraction. --------- Co-authored-by: hailaz <739476267@qq.com> Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
@ -794,22 +794,32 @@ const (
|
||||
LocalTypeDatetime LocalType = "datetime"
|
||||
LocalTypeInt LocalType = "int"
|
||||
LocalTypeUint LocalType = "uint"
|
||||
LocalTypeInt32 LocalType = "int32"
|
||||
LocalTypeUint32 LocalType = "uint32"
|
||||
LocalTypeInt64 LocalType = "int64"
|
||||
LocalTypeUint64 LocalType = "uint64"
|
||||
LocalTypeBigInt LocalType = "bigint"
|
||||
LocalTypeIntSlice LocalType = "[]int"
|
||||
LocalTypeUintSlice LocalType = "[]uint"
|
||||
LocalTypeInt32Slice LocalType = "[]int32"
|
||||
LocalTypeUint32Slice LocalType = "[]uint32"
|
||||
LocalTypeInt64Slice LocalType = "[]int64"
|
||||
LocalTypeUint64Slice LocalType = "[]uint64"
|
||||
LocalTypeStringSlice LocalType = "[]string"
|
||||
LocalTypeFloat64Slice LocalType = "[]float64"
|
||||
LocalTypeInt64Bytes LocalType = "int64-bytes"
|
||||
LocalTypeUint64Bytes LocalType = "uint64-bytes"
|
||||
LocalTypeFloat32 LocalType = "float32"
|
||||
LocalTypeFloat64 LocalType = "float64"
|
||||
LocalTypeFloat32Slice LocalType = "[]float32"
|
||||
LocalTypeFloat64Slice LocalType = "[]float64"
|
||||
LocalTypeBytes LocalType = "[]byte"
|
||||
LocalTypeBytesSlice LocalType = "[][]byte"
|
||||
LocalTypeBool LocalType = "bool"
|
||||
LocalTypeBoolSlice LocalType = "[]bool"
|
||||
LocalTypeJson LocalType = "json"
|
||||
LocalTypeJsonb LocalType = "jsonb"
|
||||
LocalTypeUUID LocalType = "uuid.UUID"
|
||||
LocalTypeUUIDSlice LocalType = "[]uuid.UUID"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -501,9 +501,7 @@ func (c *Core) OrderRandomFunction() string {
|
||||
return "RAND()"
|
||||
}
|
||||
|
||||
func (c *Core) columnValueToLocalValue(
|
||||
ctx context.Context, value any, columnType *sql.ColumnType,
|
||||
) (any, error) {
|
||||
func (c *Core) columnValueToLocalValue(ctx context.Context, value any, columnType *sql.ColumnType) (any, error) {
|
||||
var scanType = columnType.ScanType()
|
||||
if scanType != nil {
|
||||
// Common basic builtin types.
|
||||
@ -513,10 +511,7 @@ func (c *Core) columnValueToLocalValue(
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64:
|
||||
return gconv.Convert(
|
||||
gconv.String(value),
|
||||
columnType.ScanType().String(),
|
||||
), nil
|
||||
return gconv.Convert(gconv.String(value), scanType.String()), nil
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@ -719,6 +719,14 @@ func formatWhereKeyValue(in formatWhereKeyValueInput) (newArgs []any) {
|
||||
reflectValue = reflect.ValueOf(in.Value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
// Check if the value implements iString interface (like uuid.UUID).
|
||||
// These types should be treated as single values, not arrays.
|
||||
if reflectKind == reflect.Array {
|
||||
if v, ok := in.Value.(iString); ok {
|
||||
in.Value = v.String()
|
||||
reflectKind = reflect.String
|
||||
}
|
||||
}
|
||||
switch reflectKind {
|
||||
// Slice argument.
|
||||
case reflect.Slice, reflect.Array:
|
||||
@ -780,9 +788,7 @@ func formatWhereKeyValue(in formatWhereKeyValueInput) (newArgs []any) {
|
||||
|
||||
// handleSliceAndStructArgsForSql is an important function, which handles the sql and all its arguments
|
||||
// before committing them to underlying driver.
|
||||
func handleSliceAndStructArgsForSql(
|
||||
oldSql string, oldArgs []any,
|
||||
) (newSql string, newArgs []any) {
|
||||
func handleSliceAndStructArgsForSql(oldSql string, oldArgs []any) (newSql string, newArgs []any) {
|
||||
newSql = oldSql
|
||||
if len(oldArgs) == 0 {
|
||||
return
|
||||
@ -800,6 +806,13 @@ func handleSliceAndStructArgsForSql(
|
||||
newArgs = append(newArgs, oldArg)
|
||||
continue
|
||||
}
|
||||
// It does not split types that implement fmt.Stringer interface (like uuid.UUID).
|
||||
// These types should be converted to string instead of being expanded as arrays.
|
||||
// Eg: table.Where("uuid = ?", uuid.UUID{...})
|
||||
if v, ok := oldArg.(iString); ok {
|
||||
newArgs = append(newArgs, v.String())
|
||||
continue
|
||||
}
|
||||
var (
|
||||
valueHolderCount = gstr.Count(newSql, "?")
|
||||
argSliceLength = argReflectInfo.OriginValue.Len()
|
||||
|
||||
Reference in New Issue
Block a user