[gogf/gf]pgsql数组类型字段维护问题

2024-06-25 143 views
2
问题

go v1.18 gf v2.1.2

pgsql数据库的int8[]类型字段,gen dao后生成entity结构对应为[]int64 目前查询DB时数据转换没有问题(现有contrib已支持),但是在insert和update时,执行sql会报解析错误。

原因是slice类型的数据,会经过json.Marshal后再拼在sql中(code),但pgsql的int8[]类型字段,接受的是 {1,2,3} 大括号包着的数据,而json编码后是[1,2,3]中括号

暂时解决办法是自定义结构重写生成的entity中的[]int64类型字段,自定义结构实现driver.Valuer接口

@gqcn 帮忙看看框架是否能支持这个场景?

复现 pgsql
CREATE TABLE public.test_slice (
    ids _int8 NULL
);

INSERT INTO public.test_slice (ids) VALUES ('{1,2}');
go
package main

import (
    _ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
    _ "github.com/lib/pq"

    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/os/gctx"
    "github.com/gogf/gf/v2/util/gutil"
)

type Result struct {
    IDs []int64
}

func main() {
    ctx := gctx.New()

    var res Result
    _ = g.DB().Model("test_slice").Ctx(ctx).Scan(&res)
    gutil.DumpWithType(res)

    data := Result{
        IDs: []int64{3, 4},
    }
    if _, err := g.DB().Model("test_slice").Ctx(ctx).Insert(data); err != nil {
        gutil.DumpWithType(err)
    }
}
output
2022-08-01 17:55:27.296 [DEBU] {e04c685cf72f07178b559a6801f35276} [ 25 ms] [default] [rows:1  ] SELECT "ids" FROM "test_slice" LIMIT 1
main.Result(1) {
    IDs: []int64(2) [
        int64(1),
        int64(2),
    ],
}
2022-08-01 17:55:27.325 [ERRO] {e04c685cf72f07178b559a6801f35276} [ 29 ms] [default] [rows:0  ] INSERT INTO "test_slice"("ids") VALUES('[3,4]')
Error: pq: malformed array literal: "[3,4]"

回答

7

@qinyuguang 你好,感谢提问。

  1. 通过ORM操作不同数据库类型时,针对于不同数据库一些特定类型的支持,可以修改这里:https://github.com/gogf/gf/blob/f580b7a4883465b3bcfac8caa69eacf4ef4ceb3b/contrib/drivers/pgsql/pgsql.go#L124 增加对应数据类型case即可。

  2. 通过CLI工具针对数据库不同类型生成特定的Golang类型代码,可以修改这里:https://github.com/gogf/gf/blob/f580b7a4883465b3bcfac8caa69eacf4ef4ceb3b/database/gdb/gdb_func_structure.go#L37

6

db的类型转换成local的[]int64,目前通过ConvertValueForLocal做的是没有问题的

有问题的是[]int64往db插入的数据时,ConvertDataForRecordValue方法只根据value的类型推断,slice时会直接json.Marshal

此时插入数据库的数据就会是 [1,2,3]这种中括号的json字符串,这个内容插入到DB的数组类型字段上,sql会解析错误

1

这种场景不要直接将变量转换为[]int64类型,而转换为gdb.Raw("{1,2,3}")这种变量返回,这样就会讲数据直接原模原样提交给底层数据库引擎。

4

生成的do对应字段是[]int64,所以没法直接以do结构传gdb.Raw

// =================================================================================
// Code generated by GoFrame CLI tool. DO NOT EDIT. 
// =================================================================================

package do

import(
"github.com/gogf/gf/v2/frame/g"
)

// TestSlice is the golang structure of table test_slice for DAO operations like Where/Data.
type TestSlice struct {
g.Meta `orm:"table:test_slice, do:true"`
    Ids []int64 //   
}

倒是可以用map来进行字段维护

比如

    data := g.Map{
        "ids": gdb.Raw("'{1,2,3}'"),
    }
    if _, err := g.DB().Model("test_slice").Ctx(ctx).Insert(data); err != nil {
        gutil.DumpWithType(err)
    }

这样可以成功插入数据库...

8

可以自定义一个数据结构来实现driver.Valuer接口里的 Value()方法 返回{1,2,3}

1

嗯,自定义结构倒没问题,就是得重写下gf gen生成的entity字段类型

github.com/lib/pq 中有一些比如pq.Int64Array类型,封装好了Scan和Value方法的。目前我是用的这个

gf gen生成的entity如果是pq.Int64Array类型可能会更方便一些

7

@qinyuguang 我记得这个支持好像已经PR实现了,该issue我就关闭了,有问题请再提。