db_indexid.go 4.5 KB

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