Skip to content
Permalink
Browse files

Use NumericTypeMapper for BigDecimal and Short. Related to issue #24.

A completely revamped DataTypeTest.testNumeric can test the different
numeric types individually while ensuring they actually have a
NumericTypeMapper and only running tests for supported types on each
driver.

This is the first test for BigDecimal so it uncovered some problems.
We need to pick an arbitrary default scale and precision because they
differ wildly across database systems. I chose 21,2 to get the full
scale of scala.Long plus 2 extra digits of precision after the decimal
point. SQLite and Access do not support decimal types, so there is a
new capability BasicProfile.capabilities.typeBigDecimal to reflect this
restriction.

BasicProfile.capabilities.all was also missing some previously introduced
capabilities (enabling these capabilities and thus the associated test
cases fortunately did not turn up any regressions).
  • Loading branch information
szeiger committed Jan 9, 2013
1 parent ef9403e commit 57af0c8966f4aae97684cf2514e50d5c11af5642
@@ -1,6 +1,6 @@
package com.typesafe.slick.testkit.tests

import scala.slick.lifted.BaseTypeMapper
import scala.slick.lifted.{NumericTypeMapper, BaseTypeMapper}
import com.typesafe.slick.testkit.util.{TestkitTest, TestDB}
import java.io.{ObjectInputStream, ObjectOutputStream, ByteArrayOutputStream}
import java.sql.{Blob, Date, Time, Timestamp}
@@ -49,43 +49,29 @@ class DataTypeTest(val tdb: TestDB) extends TestkitTest {
}

def testNumeric {
object T extends Table[(Int, Int, Long, Short, Byte, Double, Float)]("test2") {
def id = column[Int]("id")
def intData = column[Int]("int_data")
def longData = column[Long]("long_data")
def shortData = column[Short]("short_data")
def byteData = column[Byte]("byte_data")
def doubleData = column[Double]("double_data")
def floatData = column[Float]("float_data")
def * = id ~ intData ~ longData ~ shortData ~ byteData ~ doubleData ~ floatData
}

T.ddl.createStatements foreach println
T.ddl.create;

def test(data: List[(Int, Int, Long, Short, Byte, Double, Float)]) {
T.insertAll(data: _*)
val q = T.sortBy(_.id)
def testStore[T](values: T*)(implicit tm: BaseTypeMapper[T] with NumericTypeMapper) {
object Tbl extends Table[(Int, T)]("test_numeric") {
def id = column[Int]("id")
def data = column[T]("data")
def * = id ~ data
}
Tbl.ddl.create;
val data = values.zipWithIndex.map { case (d, i) => (i+1, d) }
Tbl.insertAll(data: _*)
val q = Tbl.sortBy(_.id)
assertEquals(data, q.list)
Query(T).delete
Tbl.ddl.drop
}

test(List(
( 2, -1, -1L, -1: Short, -1: Byte, -1.0, -1.0f),
( 3, 0, 0L, 0: Short, 0: Byte, 0.0, 0.0f),
( 4, 1, 1L, 1: Short, 1: Byte, 1.0, 1.0f)
))

test(List(
(1, Int.MinValue, 0L, Short.MinValue, Byte.MinValue, 0.0, 0.0f),
(5, Int.MaxValue, 0L, Short.MaxValue, Byte.MaxValue, 0.0, 0.0f)
))

ifCap(bcap.typeLong) {
test(List(
(1, 0, Long.MinValue, 0, 0, 0.0, 0.0f),
(5, 0, Long.MaxValue, 0, 0, 0.0, 0.0f)
))
testStore[Int](-1, 0, 1, Int.MinValue, Int.MaxValue)
ifCap(bcap.typeLong) { testStore[Long](-1L, 0L, 1L, Long.MinValue, Long.MaxValue) }
testStore[Short](-1, 0, 1, Short.MinValue, Short.MaxValue)
testStore[Byte](-1, 0, 1, Byte.MinValue, Byte.MaxValue)
testStore[Double](-1.0, 0.0, 1.0)
testStore[Float](-1.0f, 0.0f, 1.0f)
ifCap(bcap.typeBigDecimal) {
testStore[BigDecimal](BigDecimal("-1"), BigDecimal("0"), BigDecimal("1"),
BigDecimal(Long.MinValue), BigDecimal(Long.MaxValue))
}
}

@@ -50,8 +50,9 @@ import scala.slick.compiler.{QueryCompiler, CompilationState, Phase}
* <li>[[scala.slick.driver.BasicProfile.capabilities.setByteArrayNull]]:
* Setting an Option[ Array[Byte] ] column to None causes an Exception
* in the JdbcOdbcDriver.</li>
* <li>[[scala.slick.driver.BasicProfile.capabilities.typeLong]]:
* Access does not have a long integer type.</li>
* <li>[[scala.slick.driver.BasicProfile.capabilities.typeBigDecimal]],
* [[scala.slick.driver.BasicProfile.capabilities.typeLong]]:
* Access does not support decimal or long integer types.</li>
* <li>[[scala.slick.driver.BasicProfile.capabilities.zip]]:
* Row numbers (required by <code>zip</code> and
* <code>zipWithIndex</code>) are not supported. Trying to generate SQL
@@ -74,6 +75,7 @@ trait AccessDriver extends ExtendedDriver { driver =>
- BasicProfile.capabilities.returnInsertKey
- BasicProfile.capabilities.returnInsertOther
- BasicProfile.capabilities.setByteArrayNull
- BasicProfile.capabilities.typeBigDecimal
- BasicProfile.capabilities.typeBlob
- BasicProfile.capabilities.typeLong
- BasicProfile.capabilities.zip
@@ -115,6 +115,8 @@ object BasicProfile {
val sequenceMin = Capability("basic.sequenceMin")
/** Can set an Option[ Array[Byte] ] column to None */
val setByteArrayNull = Capability("basic.setByteArrayNull")
/** Supports the BigDecimal data type */
val typeBigDecimal = Capability("basic.typeBigDecimal")
/** Supports the Blob data type */
val typeBlob = Capability("basic.typeBlob")
/** Supports the Long data type */
@@ -126,11 +128,11 @@ object BasicProfile {
val basic = Capability("basic")

/** All basic capabilities */
val all = Set(basic, columnDefaults, foreignKeyActions, joinFull,
joinRight, likeEscape, mutable, pagingDrop, pagingNested,
pagingPreciseTake, returnInsertKey, returnInsertOther, sequence,
sequenceCurr, sequenceCycle, sequenceLimited, sequenceMax, sequenceMin,
typeBlob, typeLong, zip)
val all = Set(basic, columnDefaults, foreignKeyActions, functionDatabase,
functionUser, joinFull, joinRight, likeEscape, mutable, pagingDrop,
pagingNested, pagingPreciseTake, returnInsertKey, returnInsertOther,
sequence, sequenceCurr, sequenceCycle, sequenceLimited, sequenceMax,
sequenceMin, setByteArrayNull, typeBigDecimal, typeBlob, typeLong, zip)
}
}

@@ -10,6 +10,7 @@ trait BasicTypeMapperDelegatesComponent { driver: BasicDriver =>

def defaultSqlTypeName(tmd: TypeMapperDelegate[_]): String = tmd.sqlType match {
case java.sql.Types.VARCHAR => "VARCHAR(254)"
case java.sql.Types.DECIMAL => "DECIMAL(21,2)"
case t => TypeMapperDelegate.typeNames.getOrElse(t,
throw new SlickException("No SQL type name found in java.sql.Types for code "+t))
}
@@ -28,6 +28,8 @@ import java.sql.{Timestamp, Time, Date}
* <li>[[scala.slick.driver.BasicProfile.capabilities.returnInsertOther]]:
* When returning columns from an INSERT operation, only a single column
* may be specified which must be the table's AutoInc column.</li>
* <li>[[scala.slick.driver.BasicProfile.capabilities.typeBigDecimal]]:
* SQLite does not support a decimal type.</li>
* <li>[[scala.slick.driver.BasicProfile.capabilities.typeBlob]]: Blobs are
* not supported by the SQLite JDBC driver (but binary data in the form of
* <code>Array[Byte]</code> is).</li>
@@ -50,6 +52,7 @@ trait SQLiteDriver extends ExtendedDriver { driver =>
- BasicProfile.capabilities.mutable
- BasicProfile.capabilities.sequence
- BasicProfile.capabilities.returnInsertOther
- BasicProfile.capabilities.typeBigDecimal
- BasicProfile.capabilities.typeBlob
- BasicProfile.capabilities.zip
)
@@ -78,7 +78,7 @@ object TypeMapper {
def apply(profile: BasicProfile) = profile.typeMapperDelegates.longTypeMapperDelegate
}

implicit object ShortTypeMapper extends BaseTypeMapper[Short] {
implicit object ShortTypeMapper extends BaseTypeMapper[Short] with NumericTypeMapper {
def apply(profile: BasicProfile) = profile.typeMapperDelegates.shortTypeMapperDelegate
}

@@ -102,7 +102,7 @@ object TypeMapper {
def apply(profile: BasicProfile) = profile.typeMapperDelegates.uuidTypeMapperDelegate
}

implicit object BigDecimalTypeMapper extends BaseTypeMapper[BigDecimal] {
implicit object BigDecimalTypeMapper extends BaseTypeMapper[BigDecimal] with NumericTypeMapper {
def apply(profile: BasicProfile) = profile.typeMapperDelegates.bigDecimalTypeMapperDelegate
}

0 comments on commit 57af0c8

Please sign in to comment.
You can’t perform that action at this time.