parseWithOutputs static method

ParsedQueryMessage parseWithOutputs(
  1. Uint8List data
)

Parses a full buffer into rows/columns and optional OUT1 outputs.

Supports v1 row-major, v2 columnar, optional OUT1 trailer, optional RC1\0 ref-cursor trailer (each sub-message is a full v1 buffer).

Implementation

static ParsedQueryMessage parseWithOutputs(Uint8List data) {
  if (data.length < 6) {
    throw const FormatException('Buffer too small for version');
  }
  final readMagic = ByteData.sublistView(
    data,
    0,
    4,
  ).getUint32(0, _littleEndian);
  if (readMagic != magic) {
    throw FormatException(
      'Invalid magic number: 0x${readMagic.toRadixString(16)}',
    );
  }
  final version = ByteData.sublistView(
    data,
    4,
    6,
  ).getUint16(0, _littleEndian);

  late final ParsedRowBuffer buffer;
  late final int mainEnd;
  if (version == protocolVersionRowMajor) {
    if (data.length < headerSizeV1) {
      throw const FormatException('Buffer too small for header');
    }
    mainEnd = messageLengthFromHeader(data);
    if (data.length < mainEnd) {
      throw const FormatException('Buffer too small for payload');
    }
    buffer = _parseRowMajorV1(Uint8List.sublistView(data, 0, mainEnd));
  } else if (version == protocolVersionColumnarV2) {
    if (data.length < headerSizeColumnarV2) {
      throw const FormatException('Buffer too small for columnar v2 header');
    }
    final payloadSize =
        ByteData.sublistView(data, 15, 19).getUint32(0, _littleEndian);
    mainEnd = headerSizeColumnarV2 + payloadSize;
    if (data.length < mainEnd) {
      throw const FormatException('Buffer too small for columnar payload');
    }
    buffer = _parseColumnarV2(Uint8List.sublistView(data, 0, mainEnd));
  } else {
    throw FormatException('Unsupported protocol version: $version');
  }

  var off = mainEnd;
  final outputs = <ParamValue>[];
  off = _parseOut1IfPresent(
    data: data,
    start: off,
    outputs: outputs,
  );
  final refCursors = <ParsedRowBuffer>[];
  off = _parseRc1IfPresent(
    data: data,
    start: off,
    out: refCursors,
  );
  if (off < data.length) {
    if (data.length - off >= 4) {
      final peek = ByteData.sublistView(
        data,
        off,
        off + 4,
      ).getUint32(0, _littleEndian);
      if (peek == outputFooterMagic || peek == refCursorFooterMagic) {
        throw const FormatException(
          'Buffer too small for complete OUT1 or RC1 trailer',
        );
      }
    }
  }
  return ParsedQueryMessage(
    rowBuffer: buffer,
    outputParamValues: outputs,
    refCursorRowBuffers: refCursors,
  );
}