util.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. // Copyright (C) 2025 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package sqlite
  7. import (
  8. "cmp"
  9. "database/sql/driver"
  10. "errors"
  11. "iter"
  12. "slices"
  13. "github.com/jmoiron/sqlx"
  14. "github.com/syncthing/syncthing/internal/gen/bep"
  15. "github.com/syncthing/syncthing/internal/gen/dbproto"
  16. "github.com/syncthing/syncthing/lib/osutil"
  17. "github.com/syncthing/syncthing/lib/protocol"
  18. "google.golang.org/protobuf/proto"
  19. )
  20. // iterStructs returns an iterator over the given struct type by scanning
  21. // the SQL rows. `rows` is closed when the iterator exits.
  22. func iterStructs[T any](rows *sqlx.Rows, err error) (iter.Seq[T], func() error) {
  23. if err != nil {
  24. return func(_ func(T) bool) {}, func() error { return err }
  25. }
  26. var retErr error
  27. return func(yield func(T) bool) {
  28. defer rows.Close()
  29. for rows.Next() {
  30. v := new(T)
  31. if err := rows.StructScan(v); err != nil {
  32. retErr = err
  33. break
  34. }
  35. if cleanuper, ok := any(v).(interface{ cleanup() }); ok {
  36. cleanuper.cleanup()
  37. }
  38. if !yield(*v) {
  39. return
  40. }
  41. }
  42. if err := rows.Err(); err != nil && retErr == nil {
  43. retErr = err
  44. }
  45. }, func() error { return retErr }
  46. }
  47. // dbVector is a wrapper that allows protocol.Vector values to be serialized
  48. // to and from the database.
  49. type dbVector struct { //nolint:recvcheck
  50. protocol.Vector
  51. }
  52. func (v dbVector) Value() (driver.Value, error) {
  53. return v.String(), nil
  54. }
  55. func (v *dbVector) Scan(value any) error {
  56. str, ok := value.(string)
  57. if !ok {
  58. return errors.New("not a string")
  59. }
  60. if str == "" {
  61. v.Vector = protocol.Vector{}
  62. return nil
  63. }
  64. vec, err := protocol.VectorFromString(str)
  65. if err != nil {
  66. return wrap(err)
  67. }
  68. // This is only necessary because I messed up counter serialisation and
  69. // thereby ordering in 2.0.0 betas, and can be removed in the future.
  70. slices.SortFunc(vec.Counters, func(a, b protocol.Counter) int { return cmp.Compare(a.ID, b.ID) })
  71. v.Vector = vec
  72. return nil
  73. }
  74. // indirectFI constructs a FileInfo from separate marshalled FileInfo and
  75. // BlockList bytes.
  76. type indirectFI struct {
  77. Name string // not used, must be present as dest for Need iterator
  78. FiProtobuf []byte
  79. BlProtobuf []byte
  80. Size int64 // not used
  81. Modified int64 // not used
  82. }
  83. func (i indirectFI) FileInfo() (protocol.FileInfo, error) {
  84. var fi bep.FileInfo
  85. if err := proto.Unmarshal(i.FiProtobuf, &fi); err != nil {
  86. return protocol.FileInfo{}, wrap(err, "unmarshal fileinfo")
  87. }
  88. if len(i.BlProtobuf) > 0 {
  89. var bl dbproto.BlockList
  90. if err := proto.Unmarshal(i.BlProtobuf, &bl); err != nil {
  91. return protocol.FileInfo{}, wrap(err, "unmarshal blocklist")
  92. }
  93. fi.Blocks = bl.Blocks
  94. }
  95. fi.Name = osutil.NativeFilename(fi.Name)
  96. return protocol.FileInfoFromDB(&fi), nil
  97. }
  98. func prefixEnd(s string) string {
  99. if s == "" {
  100. panic("bug: cannot represent end prefix for empty string")
  101. }
  102. bs := []byte(s)
  103. for i := len(bs) - 1; i >= 0; i-- {
  104. if bs[i] < 0xff {
  105. bs[i]++
  106. break
  107. }
  108. }
  109. return string(bs)
  110. }