数据库/SQL中的json.RawMessage被覆盖了。

huangapple go评论130阅读模式
英文:

json.RawMessage from database/sql json column getting overwritten

问题

使用嵌入的JSON结构体时出现奇怪的行为。

  1. package main
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "fmt"
  6. _ "github.com/lib/pq"
  7. )
  8. type Article struct {
  9. Id int
  10. Doc *json.RawMessage
  11. }
  12. func main() {
  13. db, err := sql.Open("postgres", "postgres://localhost/json_test?sslmode=disable")
  14. if err != nil {
  15. panic(err)
  16. }
  17. _, err = db.Query(`create table if not exists articles (id serial primary key, doc json)`)
  18. if err != nil {
  19. panic(err)
  20. }
  21. _, err = db.Query(`truncate articles`)
  22. if err != nil {
  23. panic(err)
  24. }
  25. docs := []string{
  26. `{"type":"event1"}`,
  27. `{"type":"event2"}`,
  28. }
  29. for _, doc := range docs {
  30. _, err = db.Query(`insert into articles ("doc") values ($1)`, doc)
  31. if err != nil {
  32. panic(err)
  33. }
  34. }
  35. rows, err := db.Query(`select id, doc from articles`)
  36. if err != nil {
  37. panic(err)
  38. }
  39. articles := make([]Article, 0)
  40. for rows.Next() {
  41. var a Article
  42. err := rows.Scan(
  43. &a.Id,
  44. &a.Doc,
  45. )
  46. if err != nil {
  47. panic(err)
  48. }
  49. articles = append(articles, a)
  50. fmt.Println("scan", string(*a.Doc), len(*a.Doc))
  51. }
  52. fmt.Println()
  53. for _, a := range articles {
  54. fmt.Println("loop", string(*a.Doc), len(*a.Doc))
  55. }
  56. }

输出:

  1. scan {"type":"event1"} 17
  2. scan {"type":"event2"} 17
  3. loop {"type":"event2"} 17
  4. loop {"type":"event2"} 17

所以文章最终指向相同的JSON。

我做错了什么吗?

更新

编辑为可运行的示例。我正在使用Postgres和lib/pq

英文:

Getting strange behaviour with a struct with embedded json.

  1. package main
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "fmt"
  6. _ "github.com/lib/pq"
  7. )
  8. type Article struct {
  9. Id int
  10. Doc *json.RawMessage
  11. }
  12. func main() {
  13. db, err := sql.Open("postgres", "postgres://localhost/json_test?sslmode=disable")
  14. if err != nil {
  15. panic(err)
  16. }
  17. _, err = db.Query(`create table if not exists articles (id serial primary key, doc json)`)
  18. if err != nil {
  19. panic(err)
  20. }
  21. _, err = db.Query(`truncate articles`)
  22. if err != nil {
  23. panic(err)
  24. }
  25. docs := []string{
  26. `{"type":"event1"}`,
  27. `{"type":"event2"}`,
  28. }
  29. for _, doc := range docs {
  30. _, err = db.Query(`insert into articles ("doc") values ($1)`, doc)
  31. if err != nil {
  32. panic(err)
  33. }
  34. }
  35. rows, err := db.Query(`select id, doc from articles`)
  36. if err != nil {
  37. panic(err)
  38. }
  39. articles := make([]Article, 0)
  40. for rows.Next() {
  41. var a Article
  42. err := rows.Scan(
  43. &a.Id,
  44. &a.Doc,
  45. )
  46. if err != nil {
  47. panic(err)
  48. }
  49. articles = append(articles, a)
  50. fmt.Println("scan", string(*a.Doc), len(*a.Doc))
  51. }
  52. fmt.Println()
  53. for _, a := range articles {
  54. fmt.Println("loop", string(*a.Doc), len(*a.Doc))
  55. }
  56. }

Output:

  1. scan {"type":"event1"} 17
  2. scan {"type":"event2"} 17
  3. loop {"type":"event2"} 17
  4. loop {"type":"event2"} 17

So the articles end up pointing to the same json.

Am I doing something wrong?

UPDATE

Edited to a runnable example. I'm using Postgres and lib/pq.

答案1

得分: 5

我遇到了同样的问题,经过长时间的研究后,我阅读了关于Scan的文档,文档中写道:

> 如果一个参数的类型是*[]byte,Scan会将相应的数据复制到该参数中。复制的数据由调用者拥有,可以进行修改并持有时间不确定。如果想避免复制,可以使用类型为*RawBytes的参数;请参阅RawBytes的文档以了解其使用限制。

我认为的问题是,如果你使用json.RawMessage,那么Scan不会将其视为[]byte,并且不会将值复制到其中。因此,在下一次循环中,你会得到一个内部切片,Scan会覆盖它。

将你的Scan更改为将json.RawMessage强制转换为[]byte,这样Scan就会将值复制到其中。

  1. err := rows.Scan(
  2. &a.Id,
  3. (*[]byte)(a.Doc),
  4. )
英文:

I ran into this same issue and after looking at if for a long time I read the doc on Scan and it says

> If an argument has type *[]byte, Scan saves in that argument a copy of the corresponding data. The copy is owned by the caller and can be modified and held indefinitely. The copy can be avoided by using an argument of type *RawBytes instead; see the documentation for RawBytes for restrictions on its use.

What I think is happening if you use *json.RawMessage then Scan does not see it as a *[]byte and does not copy into it. So you get in internal slice on the next loop Scan overwrites.

Change your Scan to cast the *json.RawMessage to a *[]byte so Scan will copy the values to it.

  1. err := rows.Scan(
  2. &a.Id,
  3. (*[]byte)(a.Doc),
  4. )

答案2

得分: 0

如果有帮助的话:

我使用了masebase的答案,将我的结构体中的json.RawMessage属性插入到一个具有jsonb列类型的PostgreSQL数据库列中。

你只需要在插入绑定方法中进行类型转换:([]byte)(a.Doc)(在我的情况下不需要*)。

英文:

In case that helps anyone :

I used masebase anwser to INSERT a json.RawMessage property of my struct in a postgresql db column having jsonb column type.

All you need to do is cast : ([]byte)(a.Doc) in the insert binding method (without the * in my case).

huangapple
  • 本文由 发表于 2014年6月6日 04:15:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/24069459.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定