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"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
|
|
|
"github.com/gogf/gf/v2/util/gutil"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
tableFieldsSqlTmp = `
|
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>
2025-12-08 11:18:45 +08:00
|
|
|
SELECT
|
|
|
|
|
a.attname AS field,
|
|
|
|
|
t.typname AS type,
|
|
|
|
|
a.attnotnull AS null,
|
|
|
|
|
(CASE WHEN d.contype = 'p' THEN 'pri' WHEN d.contype = 'u' THEN 'uni' ELSE '' END) AS key,
|
|
|
|
|
ic.column_default AS default_value,
|
|
|
|
|
b.description AS comment,
|
|
|
|
|
COALESCE(character_maximum_length, numeric_precision, -1) AS length,
|
|
|
|
|
numeric_scale AS scale
|
2024-01-30 20:03:58 +08:00
|
|
|
FROM pg_attribute a
|
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>
2025-12-08 11:18:45 +08:00
|
|
|
LEFT JOIN pg_class c ON a.attrelid = c.oid
|
|
|
|
|
LEFT JOIN pg_constraint d ON d.conrelid = c.oid AND a.attnum = d.conkey[1]
|
|
|
|
|
LEFT JOIN pg_description b ON a.attrelid = b.objoid AND a.attnum = b.objsubid
|
|
|
|
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
|
|
|
|
LEFT JOIN information_schema.columns ic ON ic.column_name = a.attname AND ic.table_name = c.relname
|
|
|
|
|
WHERE c.oid = '%s'::regclass
|
|
|
|
|
AND a.attisdropped IS FALSE
|
|
|
|
|
AND a.attnum > 0
|
2024-01-30 20:03:58 +08:00
|
|
|
ORDER BY a.attnum`
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func init() {
|
2024-03-13 19:22:17 +08:00
|
|
|
var err error
|
|
|
|
|
tableFieldsSqlTmp, err = gdb.FormatMultiLineSqlToSingle(tableFieldsSqlTmp)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2024-01-30 20:03:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TableFields retrieves and returns the fields' information of specified table of current schema.
|
|
|
|
|
func (d *Driver) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*gdb.TableField, err error) {
|
|
|
|
|
var (
|
|
|
|
|
result gdb.Result
|
|
|
|
|
link gdb.Link
|
|
|
|
|
usedSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...)
|
|
|
|
|
// TODO duplicated `id` result?
|
|
|
|
|
structureSql = fmt.Sprintf(tableFieldsSqlTmp, table)
|
|
|
|
|
)
|
|
|
|
|
if link, err = d.SlaveLink(usedSchema); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
result, err = d.DoSelect(ctx, link, structureSql)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
fields = make(map[string]*gdb.TableField)
|
|
|
|
|
var (
|
2025-10-13 18:16:09 +08:00
|
|
|
index = 0
|
|
|
|
|
name string
|
|
|
|
|
ok bool
|
|
|
|
|
existingField *gdb.TableField
|
2024-01-30 20:03:58 +08:00
|
|
|
)
|
|
|
|
|
for _, m := range result {
|
|
|
|
|
name = m["field"].String()
|
2025-10-13 18:16:09 +08:00
|
|
|
// Merge duplicated fields, especially for key constraints.
|
|
|
|
|
// Priority: pri > uni > others
|
|
|
|
|
if existingField, ok = fields[name]; ok {
|
|
|
|
|
currentKey := m["key"].String()
|
|
|
|
|
// Merge key information with priority: pri > uni
|
|
|
|
|
if currentKey == "pri" || (currentKey == "uni" && existingField.Key != "pri") {
|
|
|
|
|
existingField.Key = currentKey
|
|
|
|
|
}
|
2024-01-30 20:03:58 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
fields[name] = &gdb.TableField{
|
|
|
|
|
Index: index,
|
|
|
|
|
Name: name,
|
|
|
|
|
Type: m["type"].String(),
|
|
|
|
|
Null: !m["null"].Bool(),
|
|
|
|
|
Key: m["key"].String(),
|
|
|
|
|
Default: m["default_value"].Val(),
|
|
|
|
|
Comment: m["comment"].String(),
|
|
|
|
|
}
|
|
|
|
|
index++
|
|
|
|
|
}
|
|
|
|
|
return fields, nil
|
|
|
|
|
}
|