folderdb_counts.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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. "github.com/syncthing/syncthing/internal/db"
  9. "github.com/syncthing/syncthing/lib/protocol"
  10. )
  11. type countsRow struct {
  12. Type protocol.FileInfoType
  13. Count int
  14. Size int64
  15. Deleted bool
  16. LocalFlags protocol.FlagLocal `db:"local_flags"`
  17. }
  18. func (s *folderDB) CountLocal(device protocol.DeviceID) (db.Counts, error) {
  19. var res []countsRow
  20. if err := s.stmt(`
  21. SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
  22. INNER JOIN devices d ON d.idx = s.device_idx
  23. WHERE d.device_id = ? AND s.local_flags & {{.FlagLocalIgnored}} = 0
  24. `).Select(&res, device.String()); err != nil {
  25. return db.Counts{}, wrap(err)
  26. }
  27. return summarizeCounts(res), nil
  28. }
  29. func (s *folderDB) CountNeed(device protocol.DeviceID) (db.Counts, error) {
  30. if device == protocol.LocalDeviceID {
  31. return s.needSizeLocal()
  32. }
  33. return s.needSizeRemote(device)
  34. }
  35. func (s *folderDB) CountGlobal() (db.Counts, error) {
  36. var res []countsRow
  37. err := s.stmt(`
  38. SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
  39. WHERE s.local_flags & {{.FlagLocalGlobal}} != 0 AND s.local_flags & {{.LocalInvalidFlags}} = 0
  40. `).Select(&res)
  41. if err != nil {
  42. return db.Counts{}, wrap(err)
  43. }
  44. return summarizeCounts(res), nil
  45. }
  46. func (s *folderDB) CountReceiveOnlyChanged() (db.Counts, error) {
  47. var res []countsRow
  48. err := s.stmt(`
  49. SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
  50. WHERE local_flags & {{.FlagLocalReceiveOnly}} != 0
  51. `).Select(&res)
  52. if err != nil {
  53. return db.Counts{}, wrap(err)
  54. }
  55. return summarizeCounts(res), nil
  56. }
  57. func (s *folderDB) needSizeLocal() (db.Counts, error) {
  58. // The need size for the local device is the sum of entries with the
  59. // need bit set.
  60. var res []countsRow
  61. err := s.stmt(`
  62. SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
  63. WHERE s.local_flags & {{.FlagLocalNeeded}} != 0
  64. `).Select(&res)
  65. if err != nil {
  66. return db.Counts{}, wrap(err)
  67. }
  68. return summarizeCounts(res), nil
  69. }
  70. func (s *folderDB) needSizeRemote(device protocol.DeviceID) (db.Counts, error) {
  71. var res []countsRow
  72. // See neededGlobalFilesRemote for commentary as that is the same query without summing
  73. if err := s.stmt(`
  74. SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g
  75. WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND NOT EXISTS (
  76. SELECT 1 FROM FILES f
  77. INNER JOIN devices d ON d.idx = f.device_idx
  78. WHERE f.name_idx = g.name_idx AND f.version_idx = g.version_idx AND d.device_id = ?
  79. )
  80. GROUP BY g.type, g.local_flags, g.deleted
  81. UNION ALL
  82. SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g
  83. WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND EXISTS (
  84. SELECT 1 FROM FILES f
  85. INNER JOIN devices d ON d.idx = f.device_idx
  86. WHERE f.name_idx = g.name_idx AND d.device_id = ? AND NOT f.deleted AND f.local_flags & {{.LocalInvalidFlags}} = 0
  87. )
  88. GROUP BY g.type, g.local_flags, g.deleted
  89. `).Select(&res, device.String(),
  90. device.String()); err != nil {
  91. return db.Counts{}, wrap(err)
  92. }
  93. return summarizeCounts(res), nil
  94. }
  95. func summarizeCounts(res []countsRow) db.Counts {
  96. c := db.Counts{
  97. DeviceID: protocol.LocalDeviceID,
  98. }
  99. for _, r := range res {
  100. switch {
  101. case r.Deleted:
  102. c.Deleted += r.Count
  103. case r.Type == protocol.FileInfoTypeFile:
  104. c.Files += r.Count
  105. c.Bytes += r.Size
  106. case r.Type == protocol.FileInfoTypeDirectory:
  107. c.Directories += r.Count
  108. c.Bytes += r.Size
  109. case r.Type == protocol.FileInfoTypeSymlink:
  110. c.Symlinks += r.Count
  111. c.Bytes += r.Size
  112. }
  113. }
  114. return c
  115. }