insert method

Future<TupleLocation> insert({
  1. required Map<String, dynamic> values,
  2. required int xmin,
  3. int rowId = 0,
})

Implementation

Future<TupleLocation> insert({
  required Map<String, dynamic> values,
  required int xmin,    // creating txnId
  int rowId = 0,        // 0 = auto-assign
}) async {
  if (rowId <= 0) {
    rowId = _nextRowId;
  }
  if (rowId >= _nextRowId) _nextRowId = rowId + 1;

  final header = MvccTupleHeader(xmin: xmin, xmax: 0, rowId: rowId);
  final tuple  = TupleCodec.encode(
    schema: schema,
    values: values,
    header: header,
  );

  if (tuple.length > PageConst.pageSize - PageConst.headerSize - PageConst.slotSize) {
    throw TupleTooBigException(
        tuple.length, PageConst.pageSize - PageConst.headerSize - PageConst.slotSize);
  }

  // Ensure root page exists
  if (meta.rootPageId < 0) {
    meta.rootPageId = await _allocatePage();
  }

  // Find a page with enough space
  TupleLocation? loc;
  for (final pid in await _allPageIds()) {
    final page = await _getPage(pid);
    final slotIdx = page.insertTuple(tuple);
    if (slotIdx >= 0) {
      await _putPage(page);
      loc = TupleLocation(pid, slotIdx);
      break;
    }
  }

  // No existing page had space — allocate new page
  if (loc == null) {
    // Link new page to end of chain
    final allIds = await _allPageIds();
    final lastId = allIds.isNotEmpty ? allIds.last : meta.rootPageId;

    final newPid = await _allocatePage();

    // Update last page's next pointer
    final lastPage = await _getPage(lastId);
    lastPage.setNextPage(newPid);
    await _putPage(lastPage);

    // Insert into new page
    final newPage = await _getPage(newPid);
    final slotIdx = newPage.insertTuple(tuple);
    await _putPage(newPage);
    loc = TupleLocation(newPid, slotIdx);
  }

  meta.rowCount++;
  return loc;
}