Skip to content

Commit

Permalink
Merge pull request #84 from lucidsoftware/lenient-string
Browse files Browse the repository at this point in the history
Add lenientString to SqlRow
  • Loading branch information
tmccombs authored Sep 27, 2023
2 parents fd73b7a + 0d6e141 commit fae9766
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 87 deletions.
33 changes: 6 additions & 27 deletions relate/src/main/scala/com/lucidchart/relate/SqlRow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,7 @@ class SqlRow(val resultSet: java.sql.ResultSet) extends ResultSetWrapper {
implicitly[ColReader[A]].read(col, this)

def string(column: String): String = stringOption(column).get
def stringOption(column: String): Option[String] = {
extractOption(column) {
case x: String => x
case x: java.sql.Clob => x.getSubString(1, x.length.asInstanceOf[Int])
}
}
def stringOption(column: String): Option[String] = getResultSetOption(resultSet.getString(column))

def int(column: String): Int = intOption(column).get
def intOption(column: String): Option[Int] = getResultSetOption(resultSet.getInt(column))
Expand All @@ -115,49 +110,33 @@ class SqlRow(val resultSet: java.sql.ResultSet) extends ResultSetWrapper {
def boolOption(column: String): Option[Boolean] = getResultSetOption(resultSet.getBoolean(column))

def long(column: String): Long = longOption(column).get
def longOption(column: String): Option[Long] = {
extractOption(column) {
case x: Long => x
case x: Int => x.toLong
}
}
def longOption(column: String): Option[Long] = getResultSetOption(resultSet.getLong(column))

def bigInt(column: String): BigInt = bigIntOption(column).get
def bigIntOption(column: String): Option[BigInt] = {
extractOption(column) {
case x: java.math.BigInteger => BigInt(x)
case x: Int => BigInt(x)
case x: Long => BigInt(x)
case x: String => BigInt(x)
case x: java.math.BigInteger => BigInt(x.toString)
}
}

def bigDecimal(column: String): BigDecimal = bigDecimalOption(column).get
def bigDecimalOption(column: String): Option[BigDecimal] = {
extractOption(column) {
case x: Int => BigDecimal(x)
case x: Long => BigDecimal(x)
case x: String => BigDecimal(x)
case x: java.math.BigDecimal => BigDecimal(x.toString)
}
}
def bigDecimalOption(column: String): Option[BigDecimal] = javaBigDecimalOption(column).map(BigDecimal.apply)

def javaBigInteger(column: String): java.math.BigInteger = javaBigIntegerOption(column).get
def javaBigIntegerOption(column: String): Option[java.math.BigInteger] = {
extractOption(column) {
case x: java.math.BigInteger => x
case x: Int => java.math.BigInteger.valueOf(x)
case x: Long => java.math.BigInteger.valueOf(x)
case x: String => new java.math.BigInteger(x)
}
}

def javaBigDecimal(column: String): java.math.BigDecimal = javaBigDecimalOption(column).get
def javaBigDecimalOption(column: String): Option[java.math.BigDecimal] = {
extractOption(column) {
case x: java.math.BigDecimal => x
case x: Double => new java.math.BigDecimal(x)
}
}
def javaBigDecimalOption(column: String): Option[java.math.BigDecimal] = getResultSetOption(resultSet.getBigDecimal(column))

def date(column: String): Date = dateOption(column).get
// Timestamp documentation says that "it is recommended that code not view Timestamp values generically as an instance
Expand Down
18 changes: 9 additions & 9 deletions relate/src/test/scala/ImplicitParsingTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ImplicitParsingTest extends Specification with Mockito {

rs.getRow returns 0 thenReturns 1 thenReturns 2
rs.next returns true thenReturns true thenReturns false
rs.getObject("name") returns "hello" thenReturns "world"
rs.getString("name") returns "hello" thenReturns "world"

result.as[List[TestRecord]] mustEqual List(
TestRecord("hello"),
Expand All @@ -53,7 +53,7 @@ class ImplicitParsingTest extends Specification with Mockito {

rs.getRow returns 0 thenReturns 1 thenReturns 2
rs.next returns true thenReturns true thenReturns false
rs.getObject("name") returns "hello" thenReturns "world"
rs.getString("name") returns "hello" thenReturns "world"

result.as[Seq[TestRecord]] mustEqual Seq(
TestRecord("hello"),
Expand All @@ -68,7 +68,7 @@ class ImplicitParsingTest extends Specification with Mockito {

rs.getRow returns 0 thenReturns 1 thenReturns 2
rs.next returns true thenReturns true thenReturns false
rs.getObject("name") returns "hello" thenReturns "world"
rs.getString("name") returns "hello" thenReturns "world"

result.as[Iterable[TestRecord]] mustEqual Iterable(
TestRecord("hello"),
Expand All @@ -83,7 +83,7 @@ class ImplicitParsingTest extends Specification with Mockito {

rs.getRow returns 0 thenReturns 1 thenReturns 2
rs.next returns true thenReturns true thenReturns false
rs.getObject("name") returns "hello" thenReturns "world"
rs.getString("name") returns "hello" thenReturns "world"

result.as[Iterable[TestRecord]] mustEqual Iterable(
TestRecord("hello"),
Expand All @@ -98,8 +98,8 @@ class ImplicitParsingTest extends Specification with Mockito {

rs.getRow returns 0 thenReturns 1 thenReturns 2
rs.next returns true thenReturns true thenReturns false
rs.getObject("name") returns "hello" thenReturns "world"
rs.getObject("key") returns "1" thenReturns "2"
rs.getString("name") returns "hello" thenReturns "world"
rs.getString("key") returns "1" thenReturns "2"

result.as[Map[TestKey, TestRecord]] mustEqual Map(
TestKey("1") -> TestRecord("hello"),
Expand All @@ -112,8 +112,8 @@ class ImplicitParsingTest extends Specification with Mockito {

rs.getRow returns 0 thenReturns 1 thenReturns 2 thenReturns 3
rs.next returns true thenReturns true thenReturns true thenReturns false
rs.getObject("name") returns "hello" thenReturns "world" thenReturns "relate"
rs.getObject("key") returns "1" thenReturns "2" thenReturns "1"
rs.getString("name") returns "hello" thenReturns "world" thenReturns "relate"
rs.getString("key") returns "1" thenReturns "2" thenReturns "1"

result.as[Map[TestKey, Set[TestRecord]]] mustEqual Map(
TestKey("1") -> Set(TestRecord("hello"), TestRecord("relate")),
Expand All @@ -126,7 +126,7 @@ class ImplicitParsingTest extends Specification with Mockito {

rs.getRow returns 0 thenReturns 1 thenReturns 2 thenReturns 3
rs.next returns true thenReturns true thenReturns true thenReturns false
rs.getObject("name") returns "hello" thenReturns "world" thenReturns "relate"
rs.getString("name") returns "hello" thenReturns "world" thenReturns "relate"

result.as[Option[TestRecord]] mustEqual Some(TestRecord("hello"))
}
Expand Down
87 changes: 36 additions & 51 deletions relate/src/test/scala/SqlResultSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ class SqlResultSpec extends Specification with Mockito {

rs.getRow returns 0 thenReturn 1
rs.next returns true thenReturns false
rs.getObject("id") returns (100L: java.lang.Long)
rs.getObject("name") returns "the name"
rs.getLong("id") returns (100L: java.lang.Long)
rs.getString("name") returns "the name"

result.asSingle(parser) mustEqual TestRecord(100L, "the name")
}
Expand All @@ -64,8 +64,8 @@ class SqlResultSpec extends Specification with Mockito {

rs.getRow returns 0 thenReturn 1
rs.next returns true thenReturns false
rs.getObject("id") returns (100L: java.lang.Long)
rs.getObject("name") returns "the name"
rs.getLong("id") returns (100L: java.lang.Long)
rs.getString("name") returns "the name"

result.asSingle[TestRecord] mustEqual TestRecord(100L, "the name")
}
Expand All @@ -75,8 +75,8 @@ class SqlResultSpec extends Specification with Mockito {
def init(rs: java.sql.ResultSet, next: Boolean) = {
rs.getRow returns 0 thenReturn 1
rs.next returns next
rs.getObject("id") returns (100L: java.lang.Long)
rs.getObject("name") returns "the name"
rs.getLong("id") returns (100L: java.lang.Long)
rs.getString("name") returns "the name"
}

"return a single row with an explicit parser" in {
Expand Down Expand Up @@ -114,8 +114,8 @@ class SqlResultSpec extends Specification with Mockito {

rs.getRow returns 0 thenReturn 1 thenReturn 2 thenReturn 3
rs.next returns true thenReturn true thenReturn true thenReturn false
rs.getObject("id") returns (100L: java.lang.Long)
rs.getObject("name") returns "the name"
rs.getLong("id") returns (100L: java.lang.Long)
rs.getString("name") returns "the name"

result.asList(parser) mustEqual List(TestRecord(100L, "the name"), TestRecord(100L, "the name"), TestRecord(100L, "the name"))
}
Expand All @@ -134,8 +134,8 @@ class SqlResultSpec extends Specification with Mockito {

rs.getRow returns 0 thenReturn 1 thenReturn 2 thenReturn 3
rs.next returns true thenReturn true thenReturn true thenReturn false
rs.getObject("id") returns (100L: java.lang.Long)
rs.getObject("name") returns "the name"
rs.getLong("id") returns (100L: java.lang.Long)
rs.getString("name") returns "the name"

result.asList[TestRecord] mustEqual List(TestRecord(100L, "the name"), TestRecord(100L, "the name"), TestRecord(100L, "the name"))
}
Expand All @@ -153,12 +153,11 @@ class SqlResultSpec extends Specification with Mockito {
"asMap" should {
"return a map of 3 elements with an explicit parser" in {
val (rs, _, result) = getMocks
import java.lang.{Long => L}

rs.getRow returns 0 thenReturn 1 thenReturn 2 thenReturn 3
rs.next returns true thenReturn true thenReturn true thenReturn false
rs.getObject("id") returns (1: L) thenReturns (2: L) thenReturns (3: L)
rs.getObject("name") returns "the name"
rs.getLong("id") returns 1L thenReturns 2L thenReturns 3L
rs.getString("name") returns "the name"

val res = result.asMap(pairparser)
res(1L) mustEqual TestRecord(1L, "the name")
Expand Down Expand Up @@ -186,8 +185,8 @@ class SqlResultSpec extends Specification with Mockito {

rs.getRow returns 0 thenReturn 1 thenReturn 2 thenReturn 3
rs.next returns true thenReturn true thenReturn true thenReturn false
rs.getObject("id") returns (1: L) thenReturns (2: L) thenReturns (3: L)
rs.getObject("name") returns "the name"
rs.getLong("id") returns (1: L) thenReturns (2: L) thenReturns (3: L)
rs.getString("name") returns "the name"

val res = result.asMap[Long, TestRecord]
res(1L) mustEqual TestRecord(1L, "the name")
Expand All @@ -210,8 +209,8 @@ class SqlResultSpec extends Specification with Mockito {

rs.getRow returns 0 thenReturn 1 thenReturn 2 thenReturn 3 thenReturn 4
rs.next returns true thenReturn true thenReturn true thenReturn true thenReturn false
rs.getObject("id") returns "1" thenReturns "2" thenReturns "1" thenReturns "2"
rs.getObject("name") returns "one" thenReturns "two" thenReturns "three" thenReturns "four"
rs.getString("id") returns "1" thenReturns "2" thenReturns "1" thenReturns "2"
rs.getString("name") returns "one" thenReturns "two" thenReturns "three" thenReturns "four"

val res = result.asMultiMap { row =>
row.string("id") -> row.string("name")
Expand Down Expand Up @@ -651,7 +650,7 @@ class SqlResultSpec extends Specification with Mockito {
val (rs, row, _) = getMocks

val res = "hello"
rs.getObject("string") returns res
rs.getString("string") returns res
row.string("string") mustEqual res
row.stringOption("string") must beSome(res)
}
Expand Down Expand Up @@ -734,8 +733,8 @@ class SqlResultSpec extends Specification with Mockito {
"return the correct value" in {
val (rs, row, _) = getMocks

val res: Object = 100000L: java.lang.Long
rs.getObject("long") returns res
val res: java.lang.Long = 100000L
rs.getLong("long") returns res
row.long("long") mustEqual res
row.longOption("long") must beSome(res)
}
Expand Down Expand Up @@ -775,23 +774,8 @@ class SqlResultSpec extends Specification with Mockito {

val number = 1.013

val int: Object = number.toInt: java.lang.Integer
rs.getObject("bigDecimal") returns int
row.bigDecimal("bigDecimal") mustEqual BigDecimal(number.toInt)
row.bigDecimalOption("bigDecimal") must beSome(BigDecimal(number.toInt))

val long: Object = number.toLong: java.lang.Long
rs.getObject("bigDecimal") returns long
row.bigDecimal("bigDecimal") mustEqual BigDecimal(number.toLong)
row.bigDecimalOption("bigDecimal") must beSome(BigDecimal(number.toLong))

val string: Object = number.toString
rs.getObject("bigDecimal") returns string
row.bigDecimal("bigDecimal") mustEqual BigDecimal(number)
row.bigDecimalOption("bigDecimal") must beSome(BigDecimal(number))

val bigint: Object = new java.math.BigDecimal(number.toString)
rs.getObject("bigDecimal") returns bigint
val bigint = new java.math.BigDecimal(number.toString)
rs.getBigDecimal("bigDecimal") returns bigint
row.bigDecimal("bigDecimal") mustEqual BigDecimal(number)
row.bigDecimalOption("bigDecimal") must beSome(BigDecimal(number))
}
Expand All @@ -802,21 +786,27 @@ class SqlResultSpec extends Specification with Mockito {
val (rs, row, _) = getMocks

val number = 1010101
val bigNumber = java.math.BigInteger.valueOf(number)

val int: Object = number.toInt: java.lang.Integer
rs.getObject("javaBigInteger") returns int
row.javaBigInteger("javaBigInteger") mustEqual new java.math.BigInteger(number.toString)
row.javaBigIntegerOption("javaBigInteger") must beSome(new java.math.BigInteger(number.toString))
row.javaBigInteger("javaBigInteger") mustEqual bigNumber
row.javaBigIntegerOption("javaBigInteger") must beSome(bigNumber)

val long: Object = number.toLong: java.lang.Long
rs.getObject("javaBigInteger") returns long
row.javaBigInteger("javaBigInteger") mustEqual new java.math.BigInteger(number.toString)
row.javaBigIntegerOption("javaBigInteger") must beSome(new java.math.BigInteger(number.toString))
row.javaBigInteger("javaBigInteger") mustEqual bigNumber
row.javaBigIntegerOption("javaBigInteger") must beSome(bigNumber)

val bigint: Object = new java.math.BigInteger(number.toString)
rs.getObject("javaBigInteger") returns bigint
row.javaBigInteger("javaBigInteger") mustEqual new java.math.BigInteger(number.toString)
row.javaBigIntegerOption("javaBigInteger") must beSome(new java.math.BigInteger(number.toString))
row.javaBigInteger("javaBigInteger") mustEqual bigNumber
row.javaBigIntegerOption("javaBigInteger") must beSome(bigNumber)

val str: Object = number.toString
rs.getObject("javaBigInteger") returns str
row.javaBigInteger("javaBigInteger") mustEqual bigNumber
row.javaBigIntegerOption("javaBigInteger") must beSome(bigNumber)
}
}

Expand All @@ -826,13 +816,8 @@ class SqlResultSpec extends Specification with Mockito {

val number = 1

val double: Object = number.toDouble: java.lang.Double
rs.getObject("javaBigDecimal") returns double
row.javaBigDecimal("javaBigDecimal") mustEqual new java.math.BigDecimal(number.toString)
row.javaBigDecimalOption("javaBigDecimal") must beSome(new java.math.BigDecimal(number.toString))

val bigdec: Object = new java.math.BigDecimal(number.toString)
rs.getObject("javaBigDecimal") returns bigdec
val bigdec = java.math.BigDecimal.valueOf(number)
rs.getBigDecimal("javaBigDecimal") returns bigdec
row.javaBigDecimal("javaBigDecimal") mustEqual new java.math.BigDecimal(number.toString)
row.javaBigDecimalOption("javaBigDecimal") must beSome(new java.math.BigDecimal(number.toString))
}
Expand Down Expand Up @@ -926,7 +911,7 @@ class SqlResultSpec extends Specification with Mockito {
val (rs, row, _) = getMocks

val res = "000102030405060708090a0b0c0d0e0f"
rs.getObject("uuidFromString") returns res
rs.getString("uuidFromString") returns res
row.uuidFromString("uuidFromString") mustEqual new UUID(283686952306183L, 579005069656919567L)
row.uuidFromStringOption("uuidFromString") must beSome(new UUID(283686952306183L, 579005069656919567L))
}
Expand Down

0 comments on commit fae9766

Please sign in to comment.