folderdb_indexid.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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. "database/sql"
  9. "encoding/hex"
  10. "errors"
  11. "fmt"
  12. "github.com/syncthing/syncthing/internal/itererr"
  13. "github.com/syncthing/syncthing/lib/protocol"
  14. )
  15. func (s *folderDB) GetIndexID(device protocol.DeviceID) (protocol.IndexID, error) {
  16. // Try a fast read-only query to begin with. If it does not find the ID
  17. // we'll do the full thing under a lock.
  18. var indexID string
  19. if err := s.stmt(`
  20. SELECT i.index_id FROM indexids i
  21. INNER JOIN devices d ON d.idx = i.device_idx
  22. WHERE d.device_id = ?
  23. `).Get(&indexID, device.String()); err == nil && indexID != "" {
  24. idx, err := indexIDFromHex(indexID)
  25. return idx, wrap(err, "select")
  26. }
  27. if device != protocol.LocalDeviceID {
  28. // For non-local devices we do not create the index ID, so return
  29. // zero anyway if we don't have one.
  30. return 0, nil
  31. }
  32. s.updateLock.Lock()
  33. defer s.updateLock.Unlock()
  34. // We are now operating only for the local device ID
  35. if err := s.stmt(`
  36. SELECT index_id FROM indexids WHERE device_idx = {{.LocalDeviceIdx}}
  37. `).Get(&indexID); err != nil && !errors.Is(err, sql.ErrNoRows) {
  38. return 0, wrap(err, "select local")
  39. }
  40. if indexID == "" {
  41. // Generate a new index ID. Some trickiness in the query as we need
  42. // to find the max sequence of local files if there already exist
  43. // any.
  44. id := protocol.NewIndexID()
  45. if _, err := s.stmt(`
  46. INSERT INTO indexids (device_idx, index_id, sequence)
  47. SELECT {{.LocalDeviceIdx}}, ?, COALESCE(MAX(sequence), 0) FROM files
  48. WHERE device_idx = {{.LocalDeviceIdx}}
  49. ON CONFLICT DO UPDATE SET index_id = ?
  50. `).Exec(indexIDToHex(id), indexIDToHex(id)); err != nil {
  51. return 0, wrap(err, "insert")
  52. }
  53. return id, nil
  54. }
  55. return indexIDFromHex(indexID)
  56. }
  57. func (s *folderDB) SetIndexID(device protocol.DeviceID, id protocol.IndexID) error {
  58. s.updateLock.Lock()
  59. defer s.updateLock.Unlock()
  60. deviceIdx, err := s.deviceIdxLocked(device)
  61. if err != nil {
  62. return wrap(err, "device idx")
  63. }
  64. if _, err := s.stmt(`
  65. INSERT OR REPLACE INTO indexids (device_idx, index_id, sequence) values (?, ?, 0)
  66. `).Exec(deviceIdx, indexIDToHex(id)); err != nil {
  67. return wrap(err, "insert")
  68. }
  69. return nil
  70. }
  71. func (s *folderDB) DropAllIndexIDs() error {
  72. s.updateLock.Lock()
  73. defer s.updateLock.Unlock()
  74. _, err := s.stmt(`DELETE FROM indexids`).Exec()
  75. return wrap(err)
  76. }
  77. func (s *folderDB) GetDeviceSequence(device protocol.DeviceID) (int64, error) {
  78. var res sql.NullInt64
  79. err := s.stmt(`
  80. SELECT sequence FROM indexids i
  81. INNER JOIN devices d ON d.idx = i.device_idx
  82. WHERE d.device_id = ?
  83. `).Get(&res, device.String())
  84. if errors.Is(err, sql.ErrNoRows) {
  85. return 0, nil
  86. }
  87. if err != nil {
  88. return 0, wrap(err)
  89. }
  90. if !res.Valid {
  91. return 0, nil
  92. }
  93. return res.Int64, nil
  94. }
  95. func (s *folderDB) RemoteSequences() (map[protocol.DeviceID]int64, error) {
  96. type row struct {
  97. Device string
  98. Seq int64
  99. }
  100. it, errFn := iterStructs[row](s.stmt(`
  101. SELECT d.device_id AS device, i.sequence AS seq FROM indexids i
  102. INNER JOIN devices d ON d.idx = i.device_idx
  103. WHERE i.device_idx != {{.LocalDeviceIdx}}
  104. `).Queryx())
  105. res := make(map[protocol.DeviceID]int64)
  106. for row, err := range itererr.Zip(it, errFn) {
  107. if err != nil {
  108. return nil, wrap(err)
  109. }
  110. dev, err := protocol.DeviceIDFromString(row.Device)
  111. if err != nil {
  112. return nil, wrap(err, "device ID")
  113. }
  114. res[dev] = row.Seq
  115. }
  116. return res, nil
  117. }
  118. func indexIDFromHex(s string) (protocol.IndexID, error) {
  119. bs, err := hex.DecodeString(s)
  120. if err != nil {
  121. return 0, fmt.Errorf("indexIDFromHex: %q: %w", s, err)
  122. }
  123. var id protocol.IndexID
  124. if err := id.Unmarshal(bs); err != nil {
  125. return 0, fmt.Errorf("indexIDFromHex: %q: %w", s, err)
  126. }
  127. return id, nil
  128. }
  129. func indexIDToHex(i protocol.IndexID) string {
  130. bs, _ := i.Marshal()
  131. return hex.EncodeToString(bs)
  132. }