为了账号安全,请及时绑定邮箱和手机立即绑定

go 插入复合类型数组不支持的类型

go 插入复合类型数组不支持的类型

Go
牛魔王的故事 2023-08-14 15:10:07
sqlCREATE TABLE public.tiantang_page (    href varchar NOT NULL,    status int4 NOT NULL,    description varchar NOT NULL,    urls url[] NULL);CREATE TYPE url AS (    url varchar,    status int4);插入复合类型数组type url  struct {    url string    status int }    var urls [1]url    urls[0] = url{        url:    "",        status: 0,    }    update := "UPDATE \"public\".\"tiantang_page\" SET \"urls\"=$1 where \"href\"=$2;"    r, err := db.Exec(update, pq.Array(urls),href)    if err != nil {        log.Fatal(err)    }错误sql:转换参数 $1 类型:不支持的类型 parsetest.url,一个结构图书馆https://godoc.org/github.com/lib/pq
查看完整描述

2 回答

?
鸿蒙传说

TA贡献1865条经验 获得超7个赞

请注意,. 不完全支持自定义复合类型lib/pq。


如果您想要的只是能够存储 url,那么最简单的方法是driver.Valuer在url类型上实现接口,然后像使用它一样使用它pq.Array:


func (u url) Value() (driver.Value, error) {

    return fmt.Sprintf("(%s,%d)", u.url, u.status), nil

}


// ...

r, err := db.Exec(update, pq.Array(urls), href)

有关更多信息,请访问:https://github.com/lib/pq/issues/544


请注意,我没有尝试过使用数组,仅使用切片,因此您可能必须从使用数组切换到使用切片,即而不是var urls [1]url使用var urls = make([]url, 1).


如果您还希望能够从数据库检索 url 数组,那么您必须实现该sql.Scanner接口,但是这里pq.Array不太可靠,您必须在切片类型上实现扫描仪并执行全部自己解析。


请注意,复合类型的一般格式是(val1, val2, ...),必须在包含逗号或括号的值两边加上双引号。例如,要构造 url 类型的值,您可以使用文字表达式:(http://example.com,4)。文档中的更多信息。


复合类型数组的格式为,请{"(val1, val2, ...)" [, ...]}注意,在这种情况下,如果您需要在需要转义它们的值两边加上双引号。例如{"(http://example.com,4)","(\"http://example.com/?list=foo,bar,baz\",3)"}


正如您所看到的,复合类型中的数据越复杂,解析也就越复杂。


这是一个粗略的示例(不处理带引号的值):

type urlslice []url


func (s *urlslice) Scan(src interface{}) error {

    var a []byte // the pq array as bytes

    switch v := src.(type) {

    case []byte:

        a = v

    case string:

        a = []byte(v)

    case nil:

        *s = nil

        return nil

    default:

        return fmt.Errorf("urlslice.Scan unexpected src type %T", src)

    }


    a = a[1 : len(a)-1] // drop curly braces

    for i := 0; i < len(a); i++ {

        if a[i] == '"' && (len(a) > (i+1) && a[i+1] == '(') { // element start?

            i += 2 // move past `"(`

            j := i // start of url.url

            u := url{}


            for ; i < len(a) && a[i] != ','; i++ {

            }

            u.url = string(a[j:i])


            i += 1 // move past `,`

            j = i  // start of url.status

            for ; i < len(a) && a[i] != ')'; i++ {

            }

            i64, err := strconv.ParseInt(string(a[j:i]), 10, 64)

            if err != nil {

                return err

            }

            u.status = int(i64)

            *s = append(*s, u)


            i += 2 // move past `)",`

        }

    }

    return nil

}

为了完整起见,这里是切片类型实现的 Valuer 接口,同样不处理可能需要它的值的正确引用:


func (s urlslice) Value() (driver.Value, error) {

    data := []byte{'{'}

    for _, url := range s {

        data = append(data, '"', '(')

        data = append(data, []byte(url.url)...)

        data = append(data, ',')

        data = strconv.AppendInt(data, int64(url.status), 10)

        data = append(data, ')', '"', ',')

    }

    data[len(data)-1] = '}' // replace last ',' with '}' to close the array

    return data, nil

}

通过urlslice直接实现这两个接口,您可以停止使用pq.Array.


var urls = urlslice{{

    url:    "http://example.com",

    status: 4,

}}

update := `UPDATE "public"."tiantang_page" SET "urls"=$1 where "href"=$2`

r, err := db.Exec(update, urls, href)

if err != nil {

    log.Fatal(err)

}


var urls2 urlslice

selurls := `SELECT "urls" FROM "public"."tiantang_page" where "href" = $1`

if err := db.QueryRow(selurls, href).Scan(&urls2); err != nil {

     log.Fatal(err)

}

请记住,上述两个示例仅应被视为解决此问题的方向的提示。这两个示例不仅不完整,因为它们不处理带引号的值,而且它们也不是非常优雅的实现。


查看完整回答
反对 回复 2023-08-14
?
心有法竹

TA贡献1866条经验 获得超5个赞

相当完整的复合文字解析器:


type parseState int     


const ( 

        state_initial     parseState = iota // start

        state_value_start                   // no bytes read from value yet

        state_value                         // unquoted value

        state_quoted                        // inside quote

        state_value_end                     // after a close quote

        state_end                           // after close paren

)


func parseComposite(in []byte) ([]string, error) {

        state := state_initial   

        ret := []string{}

        val := []byte{}      


        for _, b := range in {

                switch state {                       


                case state_initial:               

                        if b != '(' {

                                return nil, fmt.Errorf("initial character not ')': %v", in)

                        } else {

                                state = state_value_start

                        }


                case state_value_start:

                        if b == '"' {

                                state = state_quoted       

                                continue

                        } 

                        fallthrough


                case state_value:

                        if b == ',' {       

                                ret = append(ret, string(val))

                                val = nil

                                state = state_value_start    

                        } else if b == ')' {

                                ret = append(ret, string(val))

                                val = nil

                                state = state_end       

                        } else {                                                                                                                      

                                val = append(val, b) 

                        }


                case state_quoted:

                        if b == '"' {      

                                ret = append(ret, string(val))

                                val = nil

                                state = state_value_end

                        } else {  

                                val = append(val, b)          

                        }   


                case state_value_end:

                        if b == ',' {

                                state = state_value_start

                        } else if b == ')' {    

                                state = state_end

                        } else {          

                                return nil, fmt.Errorf("invalid delimiter after closing quote: %v", in)

                        }


                case state_end:

                        return nil, fmt.Errorf("trailing bytes: %v", in)

                }        

        }                                             


        if state != state_end {                

                return nil, fmt.Errorf("unterminated value: %v", in)

        }                                   


        return ret, nil                

}    


查看完整回答
反对 回复 2023-08-14
  • 2 回答
  • 0 关注
  • 76 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信