WriteTo writes the entire database to a writer. If err == nil then exactly tx.Size() bytes will be written into the writer.
(w io.Writer)
| 389 | // WriteTo writes the entire database to a writer. |
| 390 | // If err == nil then exactly tx.Size() bytes will be written into the writer. |
| 391 | func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) { |
| 392 | var f *os.File |
| 393 | // There is a risk that between the time a read-only transaction |
| 394 | // is created and the time the file is actually opened, the |
| 395 | // underlying db file at tx.db.path may have been replaced |
| 396 | // (e.g. via rename). In that case, opening the file again would |
| 397 | // unexpectedly point to a different file, rather than the one |
| 398 | // the transaction was based on. |
| 399 | // |
| 400 | // To overcome this, we reuse the already opened file handle when |
| 401 | // WriteFlag not set. When the WriteFlag is set, we reopen the file |
| 402 | // but verify that it still refers to the same underlying file |
| 403 | // (by device and inode). If it does not, we fall back to |
| 404 | // reusing the existing already opened file handle. |
| 405 | if tx.WriteFlag != 0 { |
| 406 | // Attempt to open reader with WriteFlag |
| 407 | f, err = tx.db.openFile(tx.db.path, os.O_RDONLY|tx.WriteFlag, 0) |
| 408 | if err != nil { |
| 409 | return 0, err |
| 410 | } |
| 411 | |
| 412 | if ok, err := sameFile(tx.db.file, f); !ok { |
| 413 | lg := tx.db.Logger() |
| 414 | if cerr := f.Close(); cerr != nil { |
| 415 | lg.Errorf("failed to close the file (%s): %v", tx.db.path, cerr) |
| 416 | } |
| 417 | lg.Warningf("The underlying file has changed, so reuse the already opened file (%s): %v", tx.db.path, err) |
| 418 | f = tx.db.file |
| 419 | } else { |
| 420 | defer func() { |
| 421 | if cerr := f.Close(); err == nil { |
| 422 | err = cerr |
| 423 | } |
| 424 | }() |
| 425 | } |
| 426 | } else { |
| 427 | f = tx.db.file |
| 428 | } |
| 429 | |
| 430 | // Generate a meta page. We use the same page data for both meta pages. |
| 431 | buf := make([]byte, tx.db.pageSize) |
| 432 | page := (*common.Page)(unsafe.Pointer(&buf[0])) |
| 433 | page.SetFlags(common.MetaPageFlag) |
| 434 | *page.Meta() = *tx.meta |
| 435 | |
| 436 | // Write meta 0. |
| 437 | page.SetId(0) |
| 438 | page.Meta().SetChecksum(page.Meta().Sum64()) |
| 439 | nn, err := w.Write(buf) |
| 440 | n += int64(nn) |
| 441 | if err != nil { |
| 442 | return n, fmt.Errorf("meta 0 copy: %s", err) |
| 443 | } |
| 444 | |
| 445 | // Write meta 1 with a lower transaction id. |
| 446 | page.SetId(1) |
| 447 | page.Meta().DecTxid() |
| 448 | page.Meta().SetChecksum(page.Meta().Sum64()) |