Skip to content

Commit

Permalink
Merge pull request #7 from penland365/results
Browse files Browse the repository at this point in the history
Decoding Int and String from Result.
  • Loading branch information
penland365 committed Feb 23, 2016
2 parents 50a5dc4 + d7b5404 commit a2a02de
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 26 deletions.
47 changes: 47 additions & 0 deletions core/src/main/scala/roc/postgresql/ByteDecoders.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.github.finagle
package roc
package postgresql

import cats.data.Xor

trait ByteDecoder[A] {
def fromText(bytes: Option[Array[Byte]]): ByteDecodingFailure Xor Option[A]
def fromBinary(bytes: Option[Array[Byte]]): Error Xor Option[A]
}

sealed trait FormatCode
case object Text extends FormatCode
case object Binary extends FormatCode

object `package` extends ByteDecoderImplicits

trait ByteDecoderImplicits {

implicit val intByteDecoder: ByteDecoder[Int] = new ByteDecoder[Int] {
def fromText(bytes: Option[Array[Byte]]): ByteDecodingFailure Xor Option[Int] =
bytes match {
case Some(b) => {
val int = b.map(_.toChar).mkString.toInt
Xor.Right(Some(int))
}
case None => Xor.Right(None)
}

def fromBinary(bytes: Option[Array[Byte]]): Error Xor Option[Int] =
Xor.Left(new UnsupportedDecodingFailure("Decoding int from Binary is not Supported"))
}

implicit val stringByteDecoder: ByteDecoder[String] = new ByteDecoder[String] {
def fromText(bytes: Option[Array[Byte]]): ByteDecodingFailure Xor Option[String] =
bytes match {
case Some(b) => {
val str = b.map(_.toChar).mkString
Xor.Right(Some(str))
}
case None => Xor.Right(None)
}

def fromBinary(bytes: Option[Array[Byte]]): Error Xor Option[String] =
Xor.Left(new UnsupportedDecodingFailure("Decoding string from Binary is not Supported"))
}
}
8 changes: 0 additions & 8 deletions core/src/main/scala/roc/postgresql/Exceptions.scala

This file was deleted.

23 changes: 10 additions & 13 deletions core/src/main/scala/roc/postgresql/Messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ object AuthenticationMessage {
val salt = br.take(4)
Future.value(new AuthenticationMD5Passwd(salt))
}
case x => Future.exception(new Exceptions.InvalidAuthenticationRequest(x))
case x => Future.exception(new InvalidAuthenticationRequest(x))
}
}
}
Expand All @@ -69,9 +69,7 @@ object ParameterStatusMessage {
val br = BufferReader(packet.body)
val param = br.readNullTerminatedString()
val value = br.readNullTerminatedString()
val ms = new ParameterStatusMessage(param, value)
println(ms)
Future.value(ms)
Future.value(new ParameterStatusMessage(param, value))
}
}

Expand All @@ -81,9 +79,7 @@ object BackendKeyData {
val br = BufferReader(packet.body)
val procId = br.readInt
val secretKey = br.readInt
val bkd = new BackendKeyData(procId, secretKey)
println(bkd)
Future.value(bkd)
Future.value(new BackendKeyData(procId, secretKey))
}
}

Expand Down Expand Up @@ -151,7 +147,11 @@ object RowDescription {
val dataTypeObjectId = br.readInt
val dataTypeSize = br.readShort
val typeModifier = br.readInt
val formatCode = br.readShort
val formatCode = br.readShort match {
case 0 => Text
case 1 => Binary
case _ => throw new Exception()
}

val rdf = RowDescriptionField(name, tableObjectId, tableAttributeId, dataTypeObjectId,
dataTypeSize, typeModifier, formatCode)
Expand All @@ -161,14 +161,11 @@ object RowDescription {
}

val fs = loop(0, List.empty[RowDescriptionField]).reverse
println(s"Number of Fields $numFields")
fs.foreach(x => println(x))
val rd = RowDescription(numFields, fs)
Future.value(rd)
Future.value(RowDescription(numFields, fs))
}
}
case class RowDescriptionField(name: String, tableObjectId: Int, tableAttributeId: Short,
dataTypeObjectId: Int, dataTypeSize: Short, typeModifier: Int, formatCode: Short)
dataTypeObjectId: Int, dataTypeSize: Short, typeModifier: Int, formatCode: FormatCode)

case class PasswordMessage(password: String) extends FrontendMessage {
def encode: Packet = {
Expand Down
5 changes: 0 additions & 5 deletions core/src/main/scala/roc/postgresql/Result.scala

This file was deleted.

37 changes: 37 additions & 0 deletions core/src/main/scala/roc/postgresql/errors.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.github.finagle
package roc
package postgresql

sealed trait Error extends Exception

final class UnknownPostgresTypeFailure(objectId: Int) extends Error {
final override def getMessage: String = s"Postgres Object ID $objectId is unknown"

def canEqual(a: Any) = a.isInstanceOf[UnknownPostgresTypeFailure]

final override def equals(that: Any): Boolean = that match {
case x: UnknownPostgresTypeFailure => x.canEqual(this) && getMessage == x.getMessage
case _ => false
}
}

final class ByteDecodingFailure(message: String) extends Error {
final override def getMessage: String = message
}

final class UnsupportedDecodingFailure(message: String) extends Error {
final override def getMessage: String = message
}

final class UnexpectedNoneFailure(message: String) extends Error {
final override def getMessage: String = message
}

final class InvalidAuthenticationRequest(authType: Int) extends Error {
final override def getMessage: String =
s"Got invalid and unkown Authentication Request: $authType"
}

final class ColumnNotFoundException(symbol: Symbol) extends Error {
final override def getMessage: String = s"Could not find column $symbol in Result"
}
76 changes: 76 additions & 0 deletions core/src/main/scala/roc/postgresql/results.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.github.finagle
package roc
package postgresql

import cats.data.Xor
import cats.Show
import com.github.finagle.roc.postgresql.transport.BufferReader
import com.twitter.util.Future

final class Result(rowDescription: RowDescription, data: List[DataRow]) {

val columns = rowDescription.fields
.map(x => Symbol(x.name))
.zip(rowDescription.fields)
.map(tuple => {
val postgresType = PostgresType(tuple._2.dataTypeObjectId) match {
case Xor.Right(t) => t
case Xor.Left(l) => throw l
}
new Column(tuple._1, postgresType, tuple._2.formatCode)
})

val rows = data.map(x => new Row(columns, x))
}

final case class Column(name: Symbol, columnType: PostgresType, formatCode: FormatCode) {
final override def toString: String = Column.columnShow.show(this)
}
object Column {

implicit val columnShow: Show[Column] = new Show[Column] {
def show(c: Column): String =
s"Column(name=${c.name}, columnType=${c.columnType}, formatCode=${c.formatCode})"
}
}


final class Row(private[postgresql] val columns: List[Column], dataRow: DataRow) {
object `package` extends postgresql.ByteDecoderImplicits

def get[A](columnName: Symbol)(implicit f: ByteDecoder[A]): A = {
val idx = indexOfColumn(columnName)
val column = columns(idx)
column.formatCode match {
case Text => f.fromText(dataRow.columnBytes(idx)) match {
case Xor.Right(r) => r match {
case Some(x) => x
case None => throw new UnexpectedNoneFailure("Got Option when None was expected")
}
case Xor.Left(l) => throw l
}
case Binary => f.fromBinary(dataRow.columnBytes(idx)) match {
case Xor.Right(r) => r match {
case Some(x) => x
case None => throw new UnexpectedNoneFailure("Got Option when None was expected")
}
case Xor.Left(l) => throw l
}
}
}

private[postgresql] def indexOfColumn(sym: Symbol): Int =
columns.indexWhere(_.name == sym) match {
case -1 => throw new ColumnNotFoundException(sym)
case n => n
}

final override def toString: String = Row.rowShow.show(this)
}
object Row {
implicit val rowShow: Show[Row] = new Show[Row] {
def show(r: Row): String =
s"Row(columns=${r.columns})"
}
}

27 changes: 27 additions & 0 deletions core/src/main/scala/roc/postgresql/types.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.finagle
package roc
package postgresql

import cats.data.Xor

trait PostgresType
object PostgresType {

def apply(objectId: Int): UnknownPostgresTypeFailure Xor PostgresType = objectId match {
case Int4OID => Xor.right(Int4)
case VarCharOID => Xor.right(VarChar)
case TimestampWithTimezoneOID => Xor.right(TimestampWithTimezone)
case n => Xor.left(new UnknownPostgresTypeFailure(n))
}

private[this] val Int4OID = 23
private[this] val VarCharOID = 1043
private[this] val TimestampWithTimezoneOID = 1184

// used for testing only
private[postgresql] val oids = List(Int4OID, VarCharOID, TimestampWithTimezoneOID)
}

case object Int4 extends PostgresType
case object VarChar extends PostgresType
case object TimestampWithTimezone extends PostgresType
20 changes: 20 additions & 0 deletions core/src/test/scala/roc/postgresql/ErrorsSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.finagle
package roc
package postgresql

import org.scalacheck.Prop.forAll
import org.specs2._
import org.specs2.specification.core._

final class ErrorsSpec extends Specification with ScalaCheck { def is = s2"""

Error
UnknownPostgresTypeFailure should have correct message $unkownPostgresTypeFailure
"""
val unkownPostgresTypeFailure = forAll { n: Int =>
val msg = s"Postgres Object ID $n is unknown"
val error = new UnknownPostgresTypeFailure(n)
error.getMessage must_== msg
}
}

29 changes: 29 additions & 0 deletions core/src/test/scala/roc/postgresql/ResultsSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.github.finagle
package roc
package postgresql

import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Prop.forAll
import org.scalacheck.{Arbitrary, Gen}
import org.specs2._
import org.specs2.mock.Mockito
import org.specs2.specification.core._

final class ResultsSpec extends Specification with ScalaCheck with Mockito { def is = s2"""

Row
get[A](column) should throw ColumnNotFoundException for unknown column $columnNotFound
"""

val columnNotFound = forAll { sym: Symbol =>
val m = mock[DataRow]
val row = new Row(List.empty[Column], m)
row.get[String](sym) must throwA[ColumnNotFoundException]
}

lazy val genSymbol: Gen[Symbol] = for {
str <- arbitrary[String]
} yield Symbol(str)
implicit lazy val arbitrarySymbol: Arbitrary[Symbol] =
Arbitrary(genSymbol)
}
31 changes: 31 additions & 0 deletions core/src/test/scala/roc/postgresql/TypesSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.finagle
package roc
package postgresql

import cats.data.Xor
import org.scalacheck.Gen
import org.scalacheck.Prop.forAll
import org.specs2._
import org.specs2.specification.core._

final class TypesSpec extends Specification with ScalaCheck { def is = s2"""

PostgresType
should create Int4 $int4
should create VarChar $varChar
should create TimestampWithTimezone $timestampWithTimezone
should create UnknownPostgresTypeFailure for invalid type $unknownOID
"""
def int4 = PostgresType(23) must_== Xor.Right(Int4)
def varChar = PostgresType(1043) must_== Xor.Right(VarChar)
def timestampWithTimezone = PostgresType(1184) must_== Xor.Right(TimestampWithTimezone)

val unknownOID = forAll(unknownOIDInt) { n =>
val error = new UnknownPostgresTypeFailure(n)
PostgresType(n) must_== Xor.Left(error)
}

lazy val unknownOIDInt: Gen[Int] = Gen.choose(Int.MinValue, Int.MaxValue)
.suchThat(!PostgresType.oids.contains(_))
}

0 comments on commit a2a02de

Please sign in to comment.