| 446 | } |
| 447 | |
| 448 | func (s *folderDB) recalcGlobalForFile(txp *txPreparedStmts, file string) error { |
| 449 | //nolint:sqlclosecheck |
| 450 | selStmt, err := txp.Preparex(` |
| 451 | SELECT n.name, f.device_idx, f.sequence, f.modified, v.version, f.deleted, f.local_flags FROM files f |
| 452 | INNER JOIN file_versions v ON v.idx = f.version_idx |
| 453 | INNER JOIN file_names n ON n.idx = f.name_idx |
| 454 | WHERE n.name = ? |
| 455 | `) |
| 456 | if err != nil { |
| 457 | return wrap(err, "prepare select") |
| 458 | } |
| 459 | es, err := itererr.Collect(iterStructs[fileRow](selStmt.Queryx(file))) |
| 460 | if err != nil { |
| 461 | return wrap(err) |
| 462 | } |
| 463 | if len(es) == 0 { |
| 464 | // shouldn't happen |
| 465 | return nil |
| 466 | } |
| 467 | |
| 468 | // Sort the entries; the global entry is at the head of the list |
| 469 | slices.SortFunc(es, fileRow.Compare) |
| 470 | |
| 471 | // The global version is the first one in the list that is not invalid, |
| 472 | // or just the first one in the list if all are invalid. |
| 473 | var global fileRow |
| 474 | globIdx := slices.IndexFunc(es, func(e fileRow) bool { return !e.IsInvalid() }) |
| 475 | if globIdx < 0 { |
| 476 | globIdx = 0 |
| 477 | } |
| 478 | global = es[globIdx] |
| 479 | |
| 480 | // We "have" the file if the position in the list of versions is at the |
| 481 | // global version or better, or if the version is the same as the global |
| 482 | // file (we might be further down the list due to invalid flags), or if |
| 483 | // the global is deleted and we don't have it at all... |
| 484 | localIdx := slices.IndexFunc(es, func(e fileRow) bool { return e.DeviceIdx == s.localDeviceIdx }) |
| 485 | hasLocal := localIdx >= 0 && localIdx <= globIdx || // have a better or equal version |
| 486 | localIdx >= 0 && es[localIdx].Version.Equal(global.Version.Vector) || // have an equal version but invalid/ignored |
| 487 | localIdx < 0 && global.Deleted // missing it, but the global is also deleted |
| 488 | |
| 489 | // Set the global flag on the global entry. Set the need flag if the |
| 490 | // local device needs this file, unless it's invalid. |
| 491 | global.LocalFlags |= protocol.FlagLocalGlobal |
| 492 | if hasLocal || global.IsInvalid() { |
| 493 | global.LocalFlags &= ^protocol.FlagLocalNeeded |
| 494 | } else { |
| 495 | global.LocalFlags |= protocol.FlagLocalNeeded |
| 496 | } |
| 497 | //nolint:sqlclosecheck |
| 498 | upStmt, err := txp.Preparex(` |
| 499 | UPDATE files SET local_flags = ? |
| 500 | WHERE device_idx = ? AND sequence = ? |
| 501 | `) |
| 502 | if err != nil { |
| 503 | return wrap(err) |
| 504 | } |
| 505 | if _, err := upStmt.Exec(global.LocalFlags, global.DeviceIdx, global.Sequence); err != nil { |