2 回答

TA貢獻1865條經(jīng)驗 獲得超7個贊
請注意,. 不完全支持自定義復合類型lib/pq。
如果您想要的只是能夠存儲 url,那么最簡單的方法是driver.Valuer在url類型上實現(xiàn)接口,然后像使用它一樣使用它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
請注意,我沒有嘗試過使用數(shù)組,僅使用切片,因此您可能必須從使用數(shù)組切換到使用切片,即而不是var urls [1]url使用var urls = make([]url, 1).
如果您還希望能夠從數(shù)據(jù)庫檢索 url 數(shù)組,那么您必須實現(xiàn)該sql.Scanner接口,但是這里pq.Array不太可靠,您必須在切片類型上實現(xiàn)掃描儀并執(zhí)行全部自己解析。
請注意,復合類型的一般格式是(val1, val2, ...),必須在包含逗號或括號的值兩邊加上雙引號。例如,要構造 url 類型的值,您可以使用文字表達式:(http://example.com,4)。文檔中的更多信息。
復合類型數(shù)組的格式為,請{"(val1, val2, ...)" [, ...]}注意,在這種情況下,如果您需要在需要轉義它們的值兩邊加上雙引號。例如{"(http://example.com,4)","(\"http://example.com/?list=foo,bar,baz\",3)"}
正如您所看到的,復合類型中的數(shù)據(jù)越復雜,解析也就越復雜。
這是一個粗略的示例(不處理帶引號的值):
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
}
為了完整起見,這里是切片類型實現(xiàn)的 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直接實現(xiàn)這兩個接口,您可以停止使用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)
}
請記住,上述兩個示例僅應被視為解決此問題的方向的提示。這兩個示例不僅不完整,因為它們不處理帶引號的值,而且它們也不是非常優(yōu)雅的實現(xiàn)。

TA貢獻1866條經(jīng)驗 獲得超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
}
- 2 回答
- 0 關注
- 140 瀏覽
添加回答
舉報