build method

Uint8List build({
  1. BulkPayloadVersion version = BulkPayloadVersion.v2,
})
inherited

Builds the binary data buffer for bulk insert.

Version BulkPayloadVersion.v2 is the default and preserves variable-width binary values, including embedded NUL bytes. Use BulkPayloadVersion.legacy only when talking to native engines that do not understand the versioned BLK2 payload.

Uses a two-pass strategy: phase 1 pre-encodes variable-width payloads and computes the exact total byte count; phase 2 writes directly into a single pre-sized Uint8List.

Validates that table name, columns, and at least one row are present. Returns a Uint8List containing the serialized bulk insert data.

Throws StateError if table name is empty, no columns are defined, no rows have been added, or a non-nullable column contains null.

Implementation

Uint8List build({BulkPayloadVersion version = BulkPayloadVersion.v2}) {
  if (_table.isEmpty) {
    throw StateError('Table name required');
  }
  if (_columns.isEmpty) {
    throw StateError('At least one column required');
  }
  if (_effectiveRowCount == 0) {
    throw StateError('At least one row required');
  }

  // Keep a final nullability check because addRow stores row references.
  // Caller code can still mutate rows after insertion.
  if (!_usesColumnar) {
    for (var c = 0; c < _columns.length; c++) {
      final spec = _columns[c];
      if (!spec.nullable) {
        for (var r = 0; r < _rows.length; r++) {
          final value = _rows[r][c];
          if (value == null) {
            _throwNullabilityError(spec.name, r + 1);
          }
        }
      }
    }
  }

  final cache = _prepareEncodeCache(version);
  final out = Uint8List(cache.totalBytes);
  final w = _WriteCursor(out);

  if (version == BulkPayloadVersion.v2) {
    w
      ..writeBytes(_bulkPayloadV2Magic)
      ..writeU16Le(_bulkPayloadV2Version)
      ..writeU16Le(_bulkPayloadV2Flags);
  }

  w
    ..writeU32Le(cache.tableBytes.length)
    ..writeBytes(cache.tableBytes)
    ..writeU32Le(_columns.length);

  for (var i = 0; i < _columns.length; i++) {
    final spec = _columns[i];
    final nameBytes = cache.columnNameBytes[i];
    w
      ..writeU32Le(nameBytes.length)
      ..writeBytes(nameBytes)
      ..writeByte(spec.tag)
      ..writeByte(spec.nullable ? 1 : 0)
      ..writeU32Le(spec.maxLen);
  }

  final rowCount = _effectiveRowCount;
  w.writeU32Le(rowCount);

  for (var c = 0; c < _columns.length; c++) {
    _writeColumn(
      w,
      version,
      _columns[c],
      c,
      rowCount,
      cache.v2VariableCells[c],
      _usesColumnar ? _columnarData[c] : null,
    );
  }

  assert(
    w.offset == cache.totalBytes,
    'bulk payload write cursor ${w.offset} != ${cache.totalBytes}',
  );
  return out;
}