| 1540 | } |
| 1541 | |
| 1542 | func (f *VFSFile) WriteAt(b []byte, off int64) (n int, err error) { |
| 1543 | f.logger.Debug("write at", "off", off, "len", len(b)) |
| 1544 | |
| 1545 | pageSize, err := f.pageSizeBytes() |
| 1546 | if err != nil { |
| 1547 | return 0, err |
| 1548 | } |
| 1549 | |
| 1550 | // Calculate page number and offset within page |
| 1551 | pgno := uint32(off/int64(pageSize)) + 1 |
| 1552 | pageOffset := int(off % int64(pageSize)) |
| 1553 | |
| 1554 | // Skip lock page |
| 1555 | if pgno == ltx.LockPgno(pageSize) { |
| 1556 | return 0, fmt.Errorf("cannot write to lock page") |
| 1557 | } |
| 1558 | |
| 1559 | f.mu.Lock() |
| 1560 | defer f.mu.Unlock() |
| 1561 | |
| 1562 | // If write support is not enabled, return read-only error |
| 1563 | if !f.writeEnabled { |
| 1564 | return 0, sqlite3vfs.ReadOnlyError |
| 1565 | } |
| 1566 | |
| 1567 | // Get page data - either from buffer file (if dirty) or from cache/remote |
| 1568 | page := make([]byte, pageSize) |
| 1569 | if bufferOff, ok := f.dirty[pgno]; ok { |
| 1570 | // Page is already dirty - read from buffer file |
| 1571 | if _, err := f.bufferFile.ReadAt(page, bufferOff); err != nil { |
| 1572 | return 0, fmt.Errorf("read dirty page from buffer: %w", err) |
| 1573 | } |
| 1574 | } else { |
| 1575 | // Page is not dirty - read from cache/remote |
| 1576 | if err := f.readPageForWrite(pgno, page); err != nil { |
| 1577 | // If page doesn't exist, use zero-filled page |
| 1578 | f.logger.Debug("page not found, using empty page", "pgno", pgno) |
| 1579 | } |
| 1580 | } |
| 1581 | |
| 1582 | // Apply write to page |
| 1583 | n = copy(page[pageOffset:], b) |
| 1584 | |
| 1585 | // Update commit count if this extends the database |
| 1586 | if pgno > f.commit { |
| 1587 | f.commit = pgno |
| 1588 | } |
| 1589 | |
| 1590 | // Write to buffer for durability (this updates f.dirty with the offset) |
| 1591 | if err := f.writeToBuffer(pgno, page); err != nil { |
| 1592 | f.logger.Error("failed to write to buffer", "error", err) |
| 1593 | return 0, fmt.Errorf("write to buffer: %w", err) |
| 1594 | } |
| 1595 | |
| 1596 | f.logger.Debug("wrote to dirty page", "pgno", pgno, "offset", pageOffset, "len", n, "commit", f.commit) |
| 1597 | return n, nil |
| 1598 | } |
| 1599 | |