去插入复合类型数组不支持的类型
问题:去插入复合类型数组不支持的类型 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
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);
插入复合类型数组
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
解答
请注意,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 [1]url
代替var urls = make([]url, 1)
。
如果您还希望能够从数据库中检索 url 数组,那么您必须实现sql.Scanner
接口,但是这里pq.Array
不是很可靠,您必须在切片类型上实现扫描器并自己进行所有解析。
复合类型的一般格式是(val1, val2, ...)
请注意,您必须在包含逗号或括号的值周围加上双引号。例如,要构造一个 url 类型的值,您可以使用文字表达式:(http://example.com,4)
。文档中的更多信息。
复合类型的_array_的格式是{"(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)
}
请记住,以上两个示例都应仅被视为解决此问题的方向的_提示_。这两个示例不仅不完整,因为它们不处理引用值,而且它们也不是非常优雅的实现。
更多推荐
所有评论(0)