请注意,自定义复合类型在
lib/pq
中并没有得到完全支持。
如果您只是想能够存储URL,则最简单的方法是在
url
类型上实现
driver.Valuer
接口,然后像使用
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 = make([]url, 1)
代替var urls [1]url
。
如果您还想能够从数据库中检索出urls数组,则必须实现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
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]
for i := 0; i < len(a); i++ {
if a[i] == '"' && (len(a) > (i+1) && a[i+1] == '(') {
i += 2
j := i
u := url{}
for ; i < len(a) && a[i] != ','; i++ {
}
u.url = string(a[j:i])
i += 1
j = i
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
}
}
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)
}
请记住,上述两个示例只应被视为解决此问题方向的提示。这两个示例不仅在处理引用值时不完整,而且它们的实现也不够优雅。