folderdb_counts.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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 int64 `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. // Exclude ignored and receive-only changed files from the global count
  37. // (legacy expectation? it's a bit weird since those files can in fact
  38. // be global and you can get them with GetGlobal etc.)
  39. var res []countsRow
  40. err := s.stmt(`
  41. SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
  42. WHERE s.local_flags & {{.FlagLocalGlobal}} != 0 AND s.local_flags & {{or .FlagLocalReceiveOnly .FlagLocalIgnored}} = 0
  43. `).Select(&res)
  44. if err != nil {
  45. return db.Counts{}, wrap(err)
  46. }
  47. return summarizeCounts(res), nil
  48. }
  49. func (s *folderDB) CountReceiveOnlyChanged() (db.Counts, error) {
  50. var res []countsRow
  51. err := s.stmt(`
  52. SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
  53. WHERE local_flags & {{.FlagLocalReceiveOnly}} != 0
  54. `).Select(&res)
  55. if err != nil {
  56. return db.Counts{}, wrap(err)
  57. }
  58. return summarizeCounts(res), nil
  59. }
  60. func (s *folderDB) needSizeLocal() (db.Counts, error) {
  61. // The need size for the local device is the sum of entries with the
  62. // need bit set.
  63. var res []countsRow
  64. err := s.stmt(`
  65. SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
  66. WHERE s.local_flags & {{.FlagLocalNeeded}} != 0
  67. `).Select(&res)
  68. if err != nil {
  69. return db.Counts{}, wrap(err)
  70. }
  71. return summarizeCounts(res), nil
  72. }
  73. func (s *folderDB) needSizeRemote(device protocol.DeviceID) (db.Counts, error) {
  74. var res []countsRow
  75. // See neededGlobalFilesRemote for commentary as that is the same query without summing
  76. if err := s.stmt(`
  77. SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g
  78. WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND NOT g.invalid AND NOT EXISTS (
  79. SELECT 1 FROM FILES f
  80. INNER JOIN devices d ON d.idx = f.device_idx
  81. WHERE f.name = g.name AND f.version = g.version AND d.device_id = ?
  82. )
  83. GROUP BY g.type, g.local_flags, g.deleted
  84. UNION ALL
  85. SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g
  86. WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND NOT g.invalid AND EXISTS (
  87. SELECT 1 FROM FILES f
  88. INNER JOIN devices d ON d.idx = f.device_idx
  89. WHERE f.name = g.name AND d.device_id = ? AND NOT f.deleted AND NOT f.invalid
  90. )
  91. GROUP BY g.type, g.local_flags, g.deleted
  92. `).Select(&res, device.String(),
  93. device.String()); err != nil {
  94. return db.Counts{}, wrap(err)
  95. }
  96. return summarizeCounts(res), nil
  97. }
  98. func summarizeCounts(res []countsRow) db.Counts {
  99. c := db.Counts{
  100. DeviceID: protocol.LocalDeviceID,
  101. }
  102. for _, r := range res {
  103. switch {
  104. case r.Deleted:
  105. c.Deleted += r.Count
  106. case r.Type == protocol.FileInfoTypeFile:
  107. c.Files += r.Count
  108. c.Bytes += r.Size
  109. case r.Type == protocol.FileInfoTypeDirectory:
  110. c.Directories += r.Count
  111. c.Bytes += r.Size
  112. case r.Type == protocol.FileInfoTypeSymlink:
  113. c.Symlinks += r.Count
  114. c.Bytes += r.Size
  115. }
  116. }
  117. return c
  118. }