Class TransactionWriteSet

java.lang.Object
dev.frostlake.transaction.TransactionWriteSet

public class TransactionWriteSet extends Object
Per-transaction buffer of pending changes — the foundation of the deferred-apply / READ COMMITTED model (see docs/acid-snowflake-plan.md). Instead of mutating live storage, DML records changes here:
  • overlayRows(java.lang.String, dev.frostlake.storage.TableStorage) gives the transaction its OWN view — committed base rows with this write set's updates applied, deletes removed, and inserts appended. So own writes are visible to self, while other transactions' uncommitted writes are not (they live in their own write set, never in the shared base store until commit) — exactly READ COMMITTED.
  • applyTo(dev.frostlake.storage.StorageEngine) flushes the buffer to the base store on COMMIT; simply discarding the write set (never calling applyTo) IS the ROLLBACK.
Existing base rows are referenced by their STABLE id (TableStorage.getRowId(int)), never by position, so an interleaving mutation can't make the buffer target the wrong row.

Not yet wired into the executor — this is the tested foundation the Phase-1 DML wiring will build on. Known follow-up: modifying a row this same transaction just inserted (its id is not a base id) is handled by the executor mutating the pending insert directly, not here.

  • Constructor Details

    • TransactionWriteSet

      public TransactionWriteSet()
  • Method Details

    • beginStatementSavepoint

      public WriteSetSavepoint beginStatementSavepoint()
      Begin a statement savepoint (deferred-apply explicit transactions).
    • endStatementSavepoint

      public void endStatementSavepoint()
      End the statement savepoint without restoring (the statement succeeded).
    • rollbackToSavepoint

      public void rollbackToSavepoint(WriteSetSavepoint savepoint)
      Restore exactly the entries the failed statement touched, and the lock map.
    • recordInsert

      public void recordInsert(String table, Row row)
    • recordUpdate

      public void recordUpdate(String table, long rowId, Row newValue)
      Record a new value for an existing base row (by stable id). No-op if this txn already deleted it.
    • recordDelete

      public void recordDelete(String table, long rowId)
      Record deletion of an existing base row (by stable id); supersedes any pending update of that row.
    • getTableLocks

      public Map<String,TableLock> getTableLocks()
      The tables this write set touched, each with the lock its first touch acquired.
    • recordTruncate

      public void recordTruncate(String table, List<Long> committedRowIds)
      Record a TRUNCATE of table within this transaction: every committed base row (given by committedRowIds) becomes a pending delete and any not-yet-committed inserts/updates for the table are dropped. The transaction then sees the table as empty, while a rollback still restores the committed rows — the base store is cleared only when these deletes are applied at commit. Clearing the base store directly instead would let this transaction's buffered inserts reappear on next read.
    • forgetTable

      public void forgetTable(String table)
      Discard every pending change (inserts, updates, deletes) for table — used when the table is dropped or replaced within this transaction. The table (and its rows) will not exist after commit, so flushing these buffered writes would fail against missing storage; dropping them here also stops a drop-then-recreate of the same name in one transaction from resurrecting the pre-drop rows.
    • isDeleted

      public boolean isDeleted(String table, long rowId)
      True if this transaction has deleted the base row with the given stable id.
    • pendingUpdate

      public Row pendingUpdate(String table, long rowId)
      The pending new value for the base row with this stable id, or null if it isn't updated here.
    • pendingInserts

      public List<Row> pendingInserts(String table)
      A snapshot of the rows this transaction has inserted into table (empty if none); the indices line up with setPendingInsert(java.lang.String, int, dev.frostlake.storage.Row)/removePendingInsert(java.lang.String, int).
    • setPendingInsert

      public void setPendingInsert(String table, int index, Row newRow)
      Replace a not-yet-committed inserted row (in-transaction UPDATE of a row this txn just inserted).
    • removePendingInsert

      public void removePendingInsert(String table, int index)
      Drop a not-yet-committed inserted row (in-transaction DELETE of a row this txn just inserted).
    • overlayRows

      public List<Row> overlayRows(String table, TableStorage base)
      This transaction's view of table: committed base rows with this write set's updates applied and deletes removed, then this write set's inserts appended. base is read once; callers hold the engine lock so scan() and getRowIds() stay index-aligned.
    • bufferedChangeRecords

      public List<StreamRecord> bufferedChangeRecords(String table, TableStorage base, String captureName)
      Synthesize the stream change records for table's buffered (not-yet-committed) changes, exactly as applyTo(dev.frostlake.storage.StorageEngine) would track them at commit — an update as a DELETE(old)+INSERT(new) pair, a delete as a DELETE(old), an insert as an INSERT(new) — but WITHOUT mutating storage or any stream. Old images are read from base by stable id; every record is tagged with captureName (the bare source-table name, to match committed records) and carries a negative, per-record id that cannot collide with a real (positive) committed record id. Lets a stream read inside a transaction fold this transaction's own uncommitted DML into its net delta; empty when nothing is buffered for table.
    • applyTo

      public void applyTo(StorageEngine storage)
      Flush this write set to the base store (the COMMIT step).
    • applyTo

      public void applyTo(StorageEngine storage, StreamManager streamManager)
      Flush this write set to the base store (the COMMIT step): updates, then deletes, then inserts. When streamManager is non-null, change-tracking (CDC) events are emitted HERE — at apply/commit time, not at DML time — so streams observe only committed changes and a rolled-back write set emits nothing. Pre-change values for updates/deletes are read before the row is mutated.
    • changedTables

      public Set<String> changedTables()
      The fully-qualified names of every table this write set holds a buffered insert, update, or delete for.
    • isEmpty

      public boolean isEmpty()
    • clear

      public void clear()
    • copy

      public TransactionWriteSet copy()
      A statement savepoint: a copy of this write set's structure (Rows are shared — they're built fresh per change and never mutated in place). Pairs with restoreFrom(dev.frostlake.transaction.TransactionWriteSet) for statement-level rollback (a failed statement inside an explicit transaction undoes only itself and keeps the txn open).
    • restoreFrom

      public void restoreFrom(TransactionWriteSet savepoint)
      Restore this write set's contents from a copy() savepoint (statement-level rollback).