Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/ci-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@ jobs:
ports:
- 5236:5236

# openGauss server
# docker run --privileged=true -e GS_PASSWORD=UTpass@1234 -p 9950:5432 opengauss/opengauss:7.0.0-RC1.B023
gaussdb:
image: opengauss/opengauss:7.0.0-RC1.B023
env:
GS_PASSWORD: UTpass@1234
TZ: Asia/Shanghai
ports:
- 9950:5432


zookeeper:
image: zookeeper:3.8
ports:
Expand Down
50 changes: 26 additions & 24 deletions contrib/drivers/gaussdb/gaussdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,43 @@
package gaussdb

import (
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
_ "gitee.com/opengauss/openGauss-connector-go-pq"

"github.com/gogf/gf/contrib/drivers/mysql/v2"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/os/gctx"
)

// Driver is the driver for GaussDB database.
//
// GaussDB is an enterprise-level distributed database developed by Huawei. GaussDB for MySQL is a cloud-native
// database that is fully compatible with MySQL protocol.
//
// Although GaussDB is compatible with MySQL protocol, it is packaged as a separate driver component
// rather than reusing the mysql adapter directly. This design allows for future extensibility,
// such as implementing GaussDB-specific features or optimizations for cloud-native scenarios.
type Driver struct {
*mysql.Driver
*gdb.Core
}

const (
internalPrimaryKeyInCtx gctx.StrKey = "primary_key"
defaultSchema string = "public"
quoteChar string = `"`
)

func init() {
var (
err error
driverObj = New()
driverNames = g.SliceStr{"gaussdb"}
)
for _, driverName := range driverNames {
if err = gdb.Register(driverName, driverObj); err != nil {
panic(err)
}
if err := gdb.Register(`gaussdb`, New()); err != nil {
panic(err)
}
}

// New creates and returns a driver that implements gdb.Driver, which supports operations for GaussDB.
// New create and returns a driver that implements gdb.Driver, which supports operations for PostgreSql.
func New() gdb.Driver {
mysqlDriver := mysql.New().(*mysql.Driver)
return &Driver{}
}

// New creates and returns a database object for postgresql.
// It implements the interface of gdb.Driver for extra database driver installation.
func (d *Driver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
return &Driver{
Driver: mysqlDriver,
}
Core: core,
}, nil
}

// GetChars returns the security char for this type of database.
func (d *Driver) GetChars() (charLeft string, charRight string) {
return quoteChar, quoteChar
}
257 changes: 257 additions & 0 deletions contrib/drivers/gaussdb/gaussdb_convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
// 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"
"reflect"
"strings"

"github.com/google/uuid"
"github.com/lib/pq"

"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)

// ConvertValueForField converts value to database acceptable value.
func (d *Driver) ConvertValueForField(ctx context.Context, fieldType string, fieldValue any) (any, error) {
if g.IsNil(fieldValue) {
return d.Core.ConvertValueForField(ctx, fieldType, fieldValue)
}

var fieldValueKind = reflect.TypeOf(fieldValue).Kind()

if fieldValueKind == reflect.Slice {
// For pgsql, json or jsonb require '[]'
if !gstr.Contains(fieldType, "json") {
fieldValue = gstr.ReplaceByMap(gconv.String(fieldValue),
map[string]string{
"[": "{",
"]": "}",
},
)
}
}
return d.Core.ConvertValueForField(ctx, fieldType, fieldValue)
}

// CheckLocalTypeForField checks and returns corresponding local golang type for given db type.
// The parameter `fieldType` is in lower case, like:
// `int2`, `int4`, `int8`, `_int2`, `_int4`, `_int8`, `_float4`, `_float8`, etc.
//
// PostgreSQL type mapping:
//
// | PostgreSQL Type | Local Go Type |
// |------------------------------|---------------|
// | int2, int4 | int |
// | int8 | int64 |
// | uuid | uuid.UUID |
// | _int2, _int4 | []int32 | // Note: pq package does not provide Int16Array; int32 is used for compatibility
// | _int8 | []int64 |
// | _float4 | []float32 |
// | _float8 | []float64 |
// | _bool | []bool |
// | _varchar, _text | []string |
// | _char, _bpchar | []string |
// | _numeric, _decimal, _money | []float64 |
// | _bytea | [][]byte |
// | _uuid | []uuid.UUID |
func (d *Driver) CheckLocalTypeForField(ctx context.Context, fieldType string, fieldValue any) (gdb.LocalType, error) {
var typeName string
match, _ := gregex.MatchString(`(.+?)\((.+)\)`, fieldType)
if len(match) == 3 {
typeName = gstr.Trim(match[1])
} else {
typeName = fieldType
}
typeName = strings.ToLower(typeName)
switch typeName {
case "int2", "int4":
return gdb.LocalTypeInt, nil

case "int8":
return gdb.LocalTypeInt64, nil

case "uuid":
return gdb.LocalTypeUUID, nil

case "_int2", "_int4":
return gdb.LocalTypeInt32Slice, nil

case "_int8":
return gdb.LocalTypeInt64Slice, nil

case "_float4":
return gdb.LocalTypeFloat32Slice, nil

case "_float8":
return gdb.LocalTypeFloat64Slice, nil

case "_bool":
return gdb.LocalTypeBoolSlice, nil

case "_varchar", "_text", "_char", "_bpchar":
return gdb.LocalTypeStringSlice, nil

case "_uuid":
return gdb.LocalTypeUUIDSlice, nil

case "_numeric", "_decimal", "_money":
return gdb.LocalTypeFloat64Slice, nil

case "_bytea":
return gdb.LocalTypeBytesSlice, nil

default:
return d.Core.CheckLocalTypeForField(ctx, fieldType, fieldValue)
}
}

// ConvertValueForLocal converts value to local Golang type of value according field type name from database.
// The parameter `fieldType` is in lower case, like:
// `int2`, `int4`, `int8`, `_int2`, `_int4`, `_int8`, `uuid`, `_uuid`, etc.
//
// See: https://www.postgresql.org/docs/current/datatype.html
//
// PostgreSQL type mapping:
//
// | PostgreSQL Type | SQL Type | pq Type | Go Type |
// |-----------------|--------------------------------|-----------------|-------------|
// | int2 | int2, smallint | - | int |
// | int4 | int4, integer | - | int |
// | int8 | int8, bigint, bigserial | - | int64 |
// | uuid | uuid | - | uuid.UUID |
// | _int2 | int2[], smallint[] | pq.Int32Array | []int32 |
// | _int4 | int4[], integer[] | pq.Int32Array | []int32 |
// | _int8 | int8[], bigint[] | pq.Int64Array | []int64 |
// | _float4 | float4[], real[] | pq.Float32Array | []float32 |
// | _float8 | float8[], double precision[] | pq.Float64Array | []float64 |
// | _bool | boolean[], bool[] | pq.BoolArray | []bool |
// | _varchar | varchar[], character varying[] | pq.StringArray | []string |
// | _text | text[] | pq.StringArray | []string |
// | _char, _bpchar | char[], character[] | pq.StringArray | []string |
// | _numeric | numeric[] | pq.Float64Array | []float64 |
// | _decimal | decimal[] | pq.Float64Array | []float64 |
// | _money | money[] | pq.Float64Array | []float64 |
// | _bytea | bytea[] | pq.ByteaArray | [][]byte |
// | _uuid | uuid[] | pq.StringArray | []uuid.UUID |
//
// Note: PostgreSQL also supports these array types but they are not yet mapped:
// - _date (date[]), _timestamp (timestamp[]), _timestamptz (timestamptz[])
// - _jsonb (jsonb[]), _json (json[])
func (d *Driver) ConvertValueForLocal(ctx context.Context, fieldType string, fieldValue any) (any, error) {
typeName, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
typeName = strings.ToLower(typeName)

// Basic types are mostly handled by Core layer, only handle array types here
switch typeName {

// []int32
case "_int2", "_int4":
var result pq.Int32Array
if err := result.Scan(fieldValue); err != nil {
return nil, err
}
return []int32(result), nil

// []int64
case "_int8":
var result pq.Int64Array
if err := result.Scan(fieldValue); err != nil {
return nil, err
}
return []int64(result), nil

// []float32
case "_float4":
var result pq.Float32Array
if err := result.Scan(fieldValue); err != nil {
return nil, err
}
return []float32(result), nil

// []float64
case "_float8":
var result pq.Float64Array
if err := result.Scan(fieldValue); err != nil {
return nil, err
}
return []float64(result), nil

// []bool
case "_bool":
var result pq.BoolArray
if err := result.Scan(fieldValue); err != nil {
return nil, err
}
return []bool(result), nil

// []string
case "_varchar", "_text", "_char", "_bpchar":
var result pq.StringArray
if err := result.Scan(fieldValue); err != nil {
return nil, err
}
return []string(result), nil

// uuid.UUID
case "uuid":
var uuidStr string
switch v := fieldValue.(type) {
case []byte:
uuidStr = string(v)
case string:
uuidStr = v
default:
uuidStr = gconv.String(fieldValue)
}
result, err := uuid.Parse(uuidStr)
if err != nil {
return nil, err
}
return result, nil

// []uuid.UUID
case "_uuid":
var strArray pq.StringArray
if err := strArray.Scan(fieldValue); err != nil {
return nil, err
}
result := make([]uuid.UUID, len(strArray))
for i, s := range strArray {
parsed, err := uuid.Parse(s)
if err != nil {
return nil, err
}
result[i] = parsed
}
return result, nil

// []float64
case "_numeric", "_decimal", "_money":
var result pq.Float64Array
if err := result.Scan(fieldValue); err != nil {
return nil, err
}
return []float64(result), nil

// [][]byte
case "_bytea":
var result pq.ByteaArray
if err := result.Scan(fieldValue); err != nil {
return nil, err
}
return [][]byte(result), nil

default:
return d.Core.ConvertValueForLocal(ctx, fieldType, fieldValue)
}
}
Loading
Loading