All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finagle.mysql.StringEncodedRow.scala Maven / Gradle / Ivy

There is a newer version: 24.2.0
Show newest version
package com.twitter.finagle.mysql

import com.twitter.finagle.mysql.transport.MysqlBuf
import com.twitter.io.Buf

/**
 * Defines a row where the data is presumed to be encoded with the mysql
 * text-based protocol.
 * [[https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::ResultsetRow]]
 */
private class StringEncodedRow(
  rawRow: Buf,
  val fields: IndexedSeq[Field],
  indexMap: Map[String, Int],
  ignoreUnsigned: Boolean)
    extends Row {

  /**
   * Convert the string representation of each value
   * into an appropriate Value object.
   * [[https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-ProtocolText::ResultsetRow]]
   */
  lazy val values: IndexedSeq[Value] = {
    val reader = MysqlBuf.reader(rawRow)
    for (field <- fields) yield {
      val charset = field.charset
      val bytes = reader.readLengthCodedBytes()
      if (bytes == null)
        NullValue
      else if (bytes.length == 0)
        EmptyValue
      else if (!MysqlCharset.isCompatible(charset))
        RawValue(field.fieldType, field.charset, isBinary = false, bytes)
      else {
        field.fieldType match {
          case Type.Tiny if isSigned(field) =>
            ByteValue(bytesToLong(bytes).toByte)
          case Type.Tiny =>
            ShortValue(bytesToLong(bytes).toShort)
          case Type.Short if isSigned(field) =>
            ShortValue(bytesToLong(bytes).toShort)
          case Type.Short =>
            IntValue(bytesToLong(bytes).toInt)
          case Type.Int24 =>
            // both signed and unsigned fit in 32 bits
            IntValue(bytesToLong(bytes).toInt)
          case Type.Long if isSigned(field) =>
            IntValue(bytesToLong(bytes).toInt)
          case Type.Long =>
            LongValue(bytesToLong(bytes))
          case Type.LongLong if isSigned(field) =>
            LongValue(bytesToLong(bytes))
          case Type.LongLong =>
            BigIntValue(BigInt(bytesToString(bytes, charset)))
          case Type.Float =>
            FloatValue(bytesToString(bytes, charset).toFloat)
          case Type.Double =>
            DoubleValue(bytesToString(bytes, charset).toDouble)
          case Type.Year =>
            ShortValue(bytesToLong(bytes).toShort)
          case Type.VarChar | Type.String | Type.VarString | Type.TinyBlob | Type.Blob |
              Type.MediumBlob if !MysqlCharset.isBinary(charset) =>
            // Nonbinary strings as stored in the CHAR, VARCHAR, and TEXT data types
            StringValue(bytesToString(bytes, charset))
          case Type.LongBlob =>
            // LongBlobs indicate a sequence of bytes with length >= 2^24 which
            // can't fit into a Array[Byte]. This should be streamed and
            // support for this needs to begin at the transport layer.
            throw new UnsupportedOperationException("LongBlob is not supported!")
          case typ =>
            RawValue(typ, charset, isBinary = false, bytes)
        }
      }
    }
  }

  def indexOf(name: String): Option[Int] = indexMap.get(name)

  override protected def indexOfOrSentinel(columnName: String): Int =
    indexMap.getOrElse(columnName, -1)

  @inline
  private[this] def isSigned(field: Field): Boolean =
    ignoreUnsigned || field.isSigned

  /**
   * Numbers are encoded as byte strings.
   * E.g. `127` is `[49, 50, 55]`.
   */
  private[this] def bytesToLong(bytes: Array[Byte]): Long = {
    val isNegative = bytes(0) == '-'
    var value = 0L
    var i = if (isNegative) 1 else 0
    while (i < bytes.length) {
      val b = bytes(i) - '0'.toByte
      value *= 10L
      value += b
      i += 1
    }
    if (isNegative)
      value * -1L
    else
      value
  }

  private[this] def bytesToString(bytes: Array[Byte], charset: Short): String =
    new String(bytes, MysqlCharset(charset))

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy