【问题标题】:Read a Postgresql array directly into a Golang Slice将 Postgresql 数组直接读入 Golang Slice
【发布时间】:2015-08-09 22:25:39
【问题描述】:

我有一个查询,它返回一行,其中包含一个字符串数组 (character varying[]):

{http://wp.me/p62MJv-Jc,http://tyrant.click/1LGBoD6}

是否有任何简单的方法可以将其直接读入 Golang 切片?例如

var arr []string

for rows.Next() {
    rows.Scan(&arr)
    fmt.Println(len(arr))
}

生产:

0

【问题讨论】:

  • 您使用的是哪个 PostgreSQL 数据库驱动程序?您是否阅读了它的文档(驱动程序会提供这样的功能)?如果做不到这一点,您可能只需要创建一个实现 sql.Scanner 的切片类型。
  • 使用godoc.org/github.com/lib/pq 好的,谢谢 我会调查一下,有点失望这不是开箱即用的
  • 顺便说一句,您甚至不应该在不检查 Scan 的错误返回的情况下查看 len(arr)(可能类似于:“reflect.Set: value of type []uint8 is不可分配给类型 []string",即数据库驱动程序将列作为 []byte)。
  • 快速搜索issues for that package 出现:Support Postgresql array types #49Array Support #327。您可以评论或跟踪其中之一。
  • 检查这个opsdash.com/blog/postgres-arrays-golang.html,按照本教程我能够克服切片(go)和数组(postgres)之间的映射

标签: postgresql go


【解决方案1】:

我认为这应该可以完成这项工作。在 sql 中使用 array_to_json。然后解组 json-string 到 golang 切片

sql-> select array_to_json(arr) from ....

var arrStr string
var arr []string

for rows.Next() {
    rows.Scan(&arrStr)
    json.Unmarshal([]byte(arrStr), &arr)
    fmt.Println(len(arr))
}

【讨论】:

    【解决方案2】:

    正如 Victor 在原帖的 cmets 中提到的,this 帖子很好地回答了这个问题,并解释了 pq.Array()

    直接取自链接:

    要将 Postgres 数组值读入 Go 切片,请使用:

    func getTags(db *sql.DB, title string) (tags []string) {
        // the select query, returning 1 column of array type
        sel := "SELECT tags FROM posts WHERE title=$1"
    
        // wrap the output parameter in pq.Array for receiving into it
        if err := db.QueryRow(sel, title).Scan(pq.Array(&tags)); err != nil {
            log.Fatal(err)
        }
    
        return
    }
    

    我也刚刚在自己的项目中使用了这个功能,所以可以确认它是否有效。

    【讨论】:

    • 请注意,pq.Array() 仅适用于 []bool[]float64[]int64[]string。这很容易通过代码源检查。
    • 使用pq.Array()有没有副作用。喜欢任何性能影响?
    【解决方案3】:

    目前,没有直接的方法可以使用 lib/pq 库将 PostgreSQL 数组加载到 Go 切片中。可能是在某个时候,但对于此类事情是否应该由图书馆本身透明地处理存在一些争论。

    但是,一种选择是将结果加载到字符串中(看起来像 {item1,item2,"comma,item"}),然后使用正则表达式将该字符串拆分为字符串切片,如下面的代码中所做的那样(部分取自 @ 987654321@ 安德鲁哈里斯):

    import (
        "regexp"
        "strings"
    )
    
    var (
        // unquoted array values must not contain: (" , \ { } whitespace NULL)
        // and must be at least one char
        unquotedChar  = `[^",\\{}\s(NULL)]`
        unquotedValue = fmt.Sprintf("(%s)+", unquotedChar)
    
        // quoted array values are surrounded by double quotes, can be any
        // character except " or \, which must be backslash escaped:
        quotedChar  = `[^"\\]|\\"|\\\\`
        quotedValue = fmt.Sprintf("\"(%s)*\"", quotedChar)
    
        // an array value may be either quoted or unquoted:
        arrayValue = fmt.Sprintf("(?P<value>(%s|%s))", unquotedValue, quotedValue)
    
        // Array values are separated with a comma IF there is more than one value:
        arrayExp = regexp.MustCompile(fmt.Sprintf("((%s)(,)?)", arrayValue))
    )
    
    // Parse the output string from the array type.
    // Regex used: (((?P<value>(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?)
    func pgArrayToSlice(array string) []string {
        var valueIndex int
        results := make([]string, 0)
        matches := arrayExp.FindAllStringSubmatch(array, -1)
        for _, match := range matches {
            s := match[valueIndex]
            // the string _might_ be wrapped in quotes, so trim them:
            s = strings.Trim(s, "\"")
            results = append(results, s)
        }
        return results
    }
    

    以下是它的使用方法:

    rows, err := db.Query("SELECT link FROM links")
    if err != nil {
        panic(err)
    }
    var tmp string
    for rows.Next() {
        rows.Scan(&tmp)
        links := pgArrayToSlice(tmp)
        fmt.Println(len(links), links)
    }
    

    在数据库中有以下内容:

    # \d links
        Table "public.links"
     Column |  Type  | Modifiers 
    --------+--------+-----------
     link   | text[] | 
    
    # select * from links;
                 link             
    ------------------------------
     {this,that}
     {another,thing}
    
     {}
     {"test,123","one,two,three"}
    (5 rows)
    

    这是上面的 Go 代码输出的内容:

    2 []string{"this,", "that"}
    2 []string{"another,", "thing"}
    2 []string{"another,", "thing"}
    0 []string{}
    2 []string{"test,123\",", "one,two,three"}
    

    【讨论】:

      【解决方案4】:

      我到处都看到过这段代码的变体,但它不适用于某些测试集。

      这是我写的东西,它处理了我扔给它的所有测试值(下面是测试用例)。它也快了大约 80%。

      func ParsePGArray(array string) ([]string, error) {
        var out []string
        var arrayOpened,quoteOpened,escapeOpened bool
        item := &bytes.Buffer{}
        for _, r := range array {
          switch {
          case !arrayOpened:
            if r != '{' {
              return nil, errors.New("Doesn't appear to be a postgres array.  Doesn't start with an opening curly brace.")
            }
            arrayOpened = true
          case escapeOpened:
            item.WriteRune(r)
            escapeOpened = false
          case quoteOpened:
            switch r {
            case '\\':
              escapeOpened = true
            case '"':
              quoteOpened = false
              if item.String() == "NULL" {
                item.Reset()
              }
            default:
              item.WriteRune(r)
            }
          case r == '}':
            // done
            out = append(out, item.String())
            return out, nil
          case r == '"':
            quoteOpened = true
          case r == ',':
            // end of item
            out = append(out, item.String())
            item.Reset()
          default:
            item.WriteRune(r)
          }
        }
        return nil, errors.New("Doesn't appear to be a postgres array.  Premature end of string.")
      }
      

      这里是测试用例:

      scanTests := []struct {
        in   string
        out  []string
      }{
        {"{one,two}", []string{"one", "two"}},
        {`{"one, sdf",two}`, []string{"one, sdf", "two"}},
        {`{"\"one\"",two}`, []string{`"one"`, "two"}},
        {`{"\\one\\",two}`, []string{`\one\`, "two"}},
        {`{"{one}",two}`, []string{`{one}`, "two"}},
        {`{"one two"}`, []string{`one two`}},
        {`{"one,two"}`, []string{`one,two`}},
        {`{abcdef:83bf98cc-fec9-4e77-b4cf-99f9fb6655fa-0NH:zxcvzxc:wers:vxdfw-asdf-asdf}`, []string{"abcdef:83bf98cc-fec9-4e77-b4cf-99f9fb6655fa-0NH:zxcvzxc:wers:vxdfw-asdf-asdf"}},
        {`{"",two}`, []string{"","two"}},
        {`{" ","NULL"}`, []string{" ",""}},
      }
      

      【讨论】:

        猜你喜欢
        • 2020-12-31
        • 1970-01-01
        • 1970-01-01
        • 2020-06-22
        • 1970-01-01
        • 1970-01-01
        • 2019-05-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多