parseBinaryColumnData function

Tuple2<dynamic, int> parseBinaryColumnData(
  1. int columnType,
  2. ByteData data,
  3. Uint8List buffer,
  4. int startOffset,
)

Função auxiliar para analisar dados de coluna em formato binário.

columnType: O tipo da coluna (valor inteiro representando o tipo MySQL). data: Um ByteData que fornece acesso aos bytes do buffer. buffer: O Uint8List original contendo os dados. startOffset: A posição inicial no buffer para leitura.

Retorna uma Tuple2 contendo:

  • item1: O valor lido da coluna (geralmente convertido para string ou, em casos binários, um Uint8List).
  • item2: O número de bytes consumidos durante a leitura.

Caso o tipo da coluna não seja implementado, lança MySQLProtocolException.

Implementation

Tuple2<dynamic, int> parseBinaryColumnData(
  int columnType,
  ByteData data,
  Uint8List buffer,
  int startOffset,
) {
  switch (columnType) {
    case mysqlColumnTypeTiny:
      {
        final value = data.getInt8(startOffset);
        return Tuple2(value.toString(), 1);
      }

    case mysqlColumnTypeShort:
      {
        final value = data.getInt16(startOffset, Endian.little);
        return Tuple2(value.toString(), 2);
      }

    case mysqlColumnTypeLong:
    case mysqlColumnTypeInt24:
      {
        final value = data.getInt32(startOffset, Endian.little);
        return Tuple2(value.toString(), 4);
      }

    case mysqlColumnTypeLongLong:
      {
        final value = data.getInt64(startOffset, Endian.little);
        return Tuple2(value.toString(), 8);
      }

    case mysqlColumnTypeFloat:
      {
        final value = data.getFloat32(startOffset, Endian.little);
        return Tuple2(value.toString(), 4);
      }

    case mysqlColumnTypeDouble:
      {
        final value = data.getFloat64(startOffset, Endian.little);
        return Tuple2(value.toString(), 8);
      }

    case mysqlColumnTypeDate:
    case mysqlColumnTypeDateTime:
    case mysqlColumnTypeTimestamp:
      {
        final initialOffset = startOffset;
        // Lê o número de bytes (pode ser 0, 4, 7 ou 11)
        final numOfBytes = data.getUint8(startOffset);
        startOffset += 1;

        // Quando numOfBytes == 0, MySQL envia datas/timestamps '0000-00-00 00:00:00'
        if (numOfBytes == 0) {
          return Tuple2("0000-00-00 00:00:00", 1);
        }

        var year = 0, month = 0, day = 0;
        var hour = 0, minute = 0, second = 0, microSecond = 0;

        if (numOfBytes >= 4) {
          year = data.getUint16(startOffset, Endian.little);
          startOffset += 2;
          month = data.getUint8(startOffset);
          startOffset += 1;
          day = data.getUint8(startOffset);
          startOffset += 1;
        }
        if (numOfBytes >= 7) {
          hour = data.getUint8(startOffset);
          startOffset += 1;
          minute = data.getUint8(startOffset);
          startOffset += 1;
          second = data.getUint8(startOffset);
          startOffset += 1;
        }
        if (numOfBytes >= 11) {
          microSecond = data.getUint32(startOffset, Endian.little);
          startOffset += 4;
        }

        final result = StringBuffer()
          ..write('$year-')
          ..write('${month.toString().padLeft(2, '0')}-')
          ..write('${day.toString().padLeft(2, '0')} ')
          ..write('${hour.toString().padLeft(2, '0')}:')
          ..write('${minute.toString().padLeft(2, '0')}:')
          ..write(second.toString().padLeft(2, '0'));

        if (numOfBytes >= 11) {
          result.write('.$microSecond');
        }

        final consumed = startOffset - initialOffset;
        return Tuple2(result.toString(), consumed);
      }

    case mysqlColumnTypeTime:
    case mysqlColumnTypeTime2:
      {
        final initialOffset = startOffset;
        // Lê o número de bytes (pode ser 0, 8 ou 12)
        final numOfBytes = data.getUint8(startOffset);
        startOffset += 1;

        if (numOfBytes == 0) {
          return Tuple2("00:00:00", 1);
        }

        var isNegative = false;
        var days = 0, hours = 0, minutes = 0, seconds = 0, microSecond = 0;

        if (numOfBytes >= 8) {
          isNegative = data.getUint8(startOffset) > 0;
          startOffset += 1;
          days = data.getUint32(startOffset, Endian.little);
          startOffset += 4;
          hours = data.getUint8(startOffset);
          startOffset += 1;
          minutes = data.getUint8(startOffset);
          startOffset += 1;
          seconds = data.getUint8(startOffset);
          startOffset += 1;
        }

        if (numOfBytes >= 12) {
          microSecond = data.getUint32(startOffset, Endian.little);
          startOffset += 4;
        }

        hours += days * 24;
        final timeResult = StringBuffer();
        if (isNegative) {
          timeResult.write("-");
        }
        timeResult.write('${hours.toString().padLeft(2, '0')}:');
        timeResult.write('${minutes.toString().padLeft(2, '0')}:');
        timeResult.write(seconds.toString().padLeft(2, '0'));

        if (numOfBytes >= 12) {
          timeResult.write('.${microSecond.toString()}');
        }

        final consumed = startOffset - initialOffset;
        return Tuple2(timeResult.toString(), consumed);
      }

    case mysqlColumnTypeString:
    case mysqlColumnTypeVarString:
    case mysqlColumnTypeVarChar:
    case mysqlColumnTypeEnum:
    case mysqlColumnTypeSet:
      {
        // Dados textuais length-encoded
        final result = buffer.getUtf8LengthEncodedString(startOffset);
        return Tuple2(result.item1, result.item2);
      }

    case mysqlColumnTypeDecimal:
    case mysqlColumnTypeNewDecimal:
      {
        final lengthEncoded = buffer.getLengthEncodedBytes(startOffset);
        // Converte ASCII para string, p. ex. "99.99"
        final strValue = String.fromCharCodes(lengthEncoded.item1);
        return Tuple2(strValue, lengthEncoded.item2);
      }

    case mysqlColumnTypeLongBlob:
    case mysqlColumnTypeMediumBlob:
    case mysqlColumnTypeBlob:
    case mysqlColumnTypeTinyBlob:
    case mysqlColumnTypeGeometry:
    case mysqlColumnTypeBit:
      {
        // Para dados binários, retorna os bytes crus
        final lengthEncoded = buffer.getLengthEncodedBytes(startOffset);
        return Tuple2(lengthEncoded.item1, lengthEncoded.item2);
      }

    case mysqlColumnTypeYear:
      {
        // Lê 2 bytes para YEAR
        final yearValue = data.getUint16(startOffset, Endian.little);
        return Tuple2(yearValue.toString(), 2);
      }
  }

  throw MySQLProtocolException(
    "Can not parse binary column data: column type $columnType is not implemented",
  );
}