Files
gf/contrib/drivers/gaussdb/gaussdb_table_fields.go
John Guo bf6238e178 feat(contrib/drivers/gaussdb): add gaussdb driver support (#4563)
This pull request introduces a new database driver for openGauss
(GaussDB), integrating it into the GoFrame framework. The implementation
includes connection handling, SQL execution, type conversion, and other
driver-specific logic. Additionally, the CI workflow is updated to
include an openGauss server for testing. The main themes are: new driver
implementation, SQL and type handling, and CI integration.

**GaussDB Driver Implementation:**

* Added a new driver in `contrib/drivers/gaussdb` to support
openGauss/GaussDB databases, including initialization, connection
handling, and registration with GoFrame's database abstraction.
(`gaussdb.go`, `gaussdb_open.go`)
[[1]](diffhunk://#diff-4f0d2a9160a039ccdf1dc98205ed7cd9f3bb8d606fed57c5a4813937eecca81fL11-R49)
[[2]](diffhunk://#diff-a0534a00c87159a3a3d2ea20a9779ead115cc7e38ab274484cfd4b2aa86b6055R1-R69)
* Implemented custom SQL execution and result handling to support
GaussDB's PostgreSQL-based features, including primary key handling on
insert and custom result types. (`gaussdb_do_exec.go`,
`gaussdb_result.go`)
[[1]](diffhunk://#diff-528b2ec06651f4af022e0550526794a606bf257d59bc18b6bce58373c784a2f2R1-R110)
[[2]](diffhunk://#diff-ad33dffe3bbccae20b113e3865aa491ef3b54c68ef586a89cf09a581a1c2abedR1-R24)

**SQL and Type Handling:**

* Added SQL filtering and placeholder conversion to support
PostgreSQL-style parameterization and GaussDB-specific SQL quirks, such
as handling `INSERT IGNORE` and JSONB syntax. (`gaussdb_do_filter.go`)
* Implemented comprehensive type conversion logic for mapping
PostgreSQL/GaussDB types to Go types, including arrays, UUIDs, and
custom handling for JSON and numeric types. (`gaussdb_convert.go`)
* Provided a function for random ordering (`ORDER BY RANDOM()`) and
explicitly disabled upsert/ON CONFLICT support, as GaussDB does not
support this feature. (`gaussdb_order.go`, `gaussdb_format_upsert.go`)
[[1]](diffhunk://#diff-510fc9393899057fddacc7dd6d14f0ca2fff145b52341dd3cfa5db48c960e5c1R1-R12)
[[2]](diffhunk://#diff-c89496520a15032be867e26861b248f11557cc45d683b5216ca1756949a7b9adR1-R94)

**CI Integration:**

* Updated the CI workflow to start an openGauss server in Docker,
enabling automated tests against the new driver.
(`.github/workflows/ci-main.yml`)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-16 21:42:29 +08:00

109 lines
3.5 KiB
Go

// 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 gaussdb
import (
"context"
"fmt"
"github.com/gogf/gf/v2/database/gdb"
)
var (
tableFieldsSqlTmp = `
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
FROM pg_attribute a
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
ORDER BY a.attnum`
)
func init() {
var err error
tableFieldsSqlTmp, err = gdb.FormatMultiLineSqlToSingle(tableFieldsSqlTmp)
if err != nil {
panic(err)
}
}
// 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
structureSql = fmt.Sprintf(tableFieldsSqlTmp, table)
)
// Schema parameter is not used for SlaveLink as it would attempt to switch database
// In GaussDB/PostgreSQL, schema is handled via search_path or table qualification
if link, err = d.SlaveLink(); 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 (
index = 0
name string
ok bool
existingField *gdb.TableField
)
for _, m := range result {
name = m["field"].String()
// 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
}
continue
}
var (
fieldType string
dataType = m["type"].String()
dataLength = m["length"].Int()
)
if dataLength > 0 {
fieldType = fmt.Sprintf("%s(%d)", dataType, dataLength)
} else {
fieldType = dataType
}
fields[name] = &gdb.TableField{
Index: index,
Name: name,
Type: fieldType,
Null: !m["null"].Bool(),
Key: m["key"].String(),
Default: m["default_value"].Val(),
Comment: m["comment"].String(),
}
index++
}
return fields, nil
}