鸿蒙传说
请注意,. 不完全支持自定义复合类型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 []urlfunc (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 urlsliceselurls := `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 }