backup.go 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // Copyright 2023 Ross Light
  2. // SPDX-License-Identifier: ISC
  3. package sqlite
  4. import (
  5. "fmt"
  6. "modernc.org/libc"
  7. lib "modernc.org/sqlite/lib"
  8. )
  9. // A Backup represents a copy operation between two database connections.
  10. // See https://www.sqlite.org/backup.html for more details.
  11. type Backup struct {
  12. tls *libc.TLS
  13. ptr uintptr
  14. }
  15. // NewBackup creates a [Backup] that copies from one connection to another.
  16. // The database name is "main" or "" for the main database,
  17. // "temp" for the temporary database,
  18. // or the name specified after the AS keyword in an ATTACH statement for an attached database.
  19. // If src and dst are the same connection, NewBackup will return an error.
  20. // It is the caller's responsibility to call [Backup.Close] on the returned Backup object.
  21. func NewBackup(dst *Conn, dstName string, src *Conn, srcName string) (*Backup, error) {
  22. cDstName, freeCDstName, err := cDBName(dstName)
  23. if err != nil {
  24. return nil, fmt.Errorf("sqlite: new backup: %w", err)
  25. }
  26. defer freeCDstName()
  27. cSrcName, freeCSrcName, err := cDBName(srcName)
  28. if err != nil {
  29. return nil, fmt.Errorf("sqlite: new backup: %w", err)
  30. }
  31. defer freeCSrcName()
  32. backupPtr := lib.Xsqlite3_backup_init(dst.tls, dst.conn, cDstName, src.conn, cSrcName)
  33. if backupPtr == 0 {
  34. res := ResultCode(lib.Xsqlite3_errcode(dst.tls, dst.conn))
  35. return nil, fmt.Errorf("sqlite: new backup: %w", dst.extreserr(res))
  36. }
  37. return &Backup{dst.tls, backupPtr}, nil
  38. }
  39. // Step copies up to n pages from the source database to the destination database.
  40. // If n is negative, all remaining source pages are copied.
  41. // more will be true if there are pages still remaining to be copied.
  42. // Step may return both an error and that more pages are still remaining:
  43. // this indicates the error is temporary and that Step can be retried.
  44. func (b *Backup) Step(n int) (more bool, err error) {
  45. res := ResultCode(lib.Xsqlite3_backup_step(b.tls, b.ptr, int32(n)))
  46. switch res {
  47. case ResultOK:
  48. return true, nil
  49. case ResultDone:
  50. return false, nil
  51. case ResultBusy, ResultLocked:
  52. // SQLITE_BUSY and SQLITE_LOCKED are retriable errors.
  53. return true, fmt.Errorf("sqlite: backup step: %w", res.ToError())
  54. default:
  55. return false, fmt.Errorf("sqlite: backup step: %w", res.ToError())
  56. }
  57. }
  58. // Remaining returns the number of pages still to be backed up
  59. // at the conclusion of the most recent call to [Backup.Step].
  60. // The return value of Remaining before calling [Backup.Step] is undefined.
  61. // If the source database is modified in a way that changes the number of pages remaining,
  62. // that change is not reflected in the output until after the next call to [Backup.Step].
  63. func (b *Backup) Remaining() int {
  64. return int(lib.Xsqlite3_backup_remaining(b.tls, b.ptr))
  65. }
  66. // PageCount returns the total number of pages in the source database
  67. // at the conclusion of the most recent call to [Backup.Step].
  68. // The return value of PageCount before calling [Backup.Step] is undefined.
  69. // If the source database is modified in a way that changes the size of the source database,
  70. // that change is not reflected in the output until after the next call to [Backup.Step].
  71. func (b *Backup) PageCount() int {
  72. return int(lib.Xsqlite3_backup_pagecount(b.tls, b.ptr))
  73. }
  74. // Close releases all resources associated with the backup.
  75. // If [Backup.Step] has not yet returned (false, nil),
  76. // then any active write transaction on the destination database is rolled back.
  77. func (b *Backup) Close() error {
  78. // The error from sqlite3_backup_finish indicates whether
  79. // a previous call to sqlite3_backup_step returned an error.
  80. // Since we're assuming that the caller will handle errors as they arise,
  81. // we always return nil from Close.
  82. // However, I'm not fully confident that Close will always be infallible,
  83. // so I'm not documenting this as part of the API.
  84. lib.Xsqlite3_backup_finish(b.tls, b.ptr)
  85. return nil
  86. }