Postgresql 转到插入复合类型数组不支持的类型

Postgresql 转到插入复合类型数组不支持的类型,postgresql,go,Postgresql,Go,sql 插入复合类型数组 CREATE 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); 错误 sql:转换参数$1类型:不支持的类型parsetest.u

sql

插入复合类型数组

CREATE 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);
错误

sql:转换参数$1类型:不支持的类型parsetest.url,结构

图书馆

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)
    }

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

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

https://godoc.org/github.com/lib/pq
有关这方面的更多信息,请访问:

请注意,我没有在数组中尝试过这一点,只是在切片中尝试过,因此您可能必须从使用数组切换到使用切片,即使用
var-url[1]url
而不是
var-url=make([]url,1)


如果您还希望能够从db检索URL数组,那么您必须实现
sql.Scanner
接口,但是这里的
pq.array
不是很可靠,您必须在切片类型上实现Scanner,并自己进行所有解析

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

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

因此,您可以看到,复合类型中的数据越复杂,解析也就越复杂

下面是一个粗略的示例(不处理引用的值):

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

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
}


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

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

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)
}

您需要告诉人们您正在使用的数据库库。如果它支持PostgreSQL更复杂的数据类型,那么它可能需要您显式定义映射。@RichardHuxton谢谢,我添加了库描述您有一个包含多个未导出字段的结构。如何将其序列化到查询中?我不是一个真正的go用户,从我了解PostgreSQL驱动程序的状态到现在已经有一年左右了。我必须承认,我对当时所有选择的不同弱点感到失望。尤其是在编写自己的解析器时,go看起来非常笨拙。
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
}
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)
}
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                
}