Skip to content
Permalink
Browse files

Bug fix for Option[Array[Byte]] on PostgreSQL. Closes issue #85.

New test case in DataTypeTest.testByteArrayOption. It turns out that
MS Access (using the standard JdbcOdbcDriver) also has problems with
this type but there is no known work-around for setting a LONGBINARY
value to NULL, so we add BasicProfile.capabilities.setByteArrayNull for
this feature and disable it in AccessDriver.
  • Loading branch information
szeiger committed Jan 8, 2013
1 parent 24fcb6e commit 7e429987e662d3a99c724a19030c8bdd0ff03c51
@@ -1,20 +1,20 @@
package com.typesafe.slick.testkit.tests

import org.junit.Assert._
import scala.slick.lifted.BaseTypeMapper
import com.typesafe.slick.testkit.util.{TestkitTest, TestDB}
import java.io.{ObjectInputStream, ObjectOutputStream, ByteArrayOutputStream}
import java.sql.{Blob, Date, Time, Timestamp}
import javax.sql.rowset.serial.SerialBlob
import slick.lifted.BaseTypeMapper
import java.util.UUID
import javax.sql.rowset.serial.SerialBlob
import org.junit.Assert._

class DataTypeTest(val tdb: TestDB) extends TestkitTest {
import tdb.profile.simple._

override val reuseInstance = true

def testByteArray {
object T extends Table[(Int, Array[Byte])]("test") {
object T extends Table[(Int, Array[Byte])]("test_ba") {
def id = column[Int]("id")
def data = column[Array[Byte]]("data")
def * = id ~ data
@@ -24,7 +24,28 @@ class DataTypeTest(val tdb: TestDB) extends TestkitTest {
T.ddl.create;
T insert (1, Array[Byte](1,2,3))
T insert (2, Array[Byte](4,5))
assertEquals(Set((1,"123"), (2,"45")), Query(T).list.map{ case (id, data) => (id, data.mkString) }.toSet)
assertEquals(
Set((1,"123"), (2,"45")),
Query(T).list.map{ case (id, data) => (id, data.mkString) }.toSet
)
}

def testByteArrayOption {
object T extends Table[(Int, Option[Array[Byte]])]("test_baopt") {
def id = column[Int]("id")
def data = column[Option[Array[Byte]]]("data")
def * = id ~ data
}

T.ddl.createStatements foreach println
T.ddl.create;
T insert (1, Some(Array[Byte](6,7)))
ifCap(bcap.setByteArrayNull)(T.insert(2, None))
ifNotCap(bcap.setByteArrayNull)(T.id.insert(2))
assertEquals(
Set((1,"67"), (2,"")),
Query(T).list.map{ case (id, data) => (id, data.map(_.mkString).getOrElse("")) }.toSet
)
}

def testNumeric {
@@ -47,6 +47,9 @@ import scala.slick.compiler.{QueryCompiler, CompilationState, Phase}
* Trying to use <code>java.sql.Blob</code> objects causes a NPE in the
* JdbcOdbcDriver. Binary data in the form of <code>Array[Byte]</code> is
* supported.</li>
* <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.zip]]:
@@ -70,6 +73,7 @@ trait AccessDriver extends ExtendedDriver { driver =>
- BasicProfile.capabilities.sequence
- BasicProfile.capabilities.returnInsertKey
- BasicProfile.capabilities.returnInsertOther
- BasicProfile.capabilities.setByteArrayNull
- BasicProfile.capabilities.typeBlob
- BasicProfile.capabilities.typeLong
- BasicProfile.capabilities.zip
@@ -245,6 +249,12 @@ trait AccessDriver extends ExtendedDriver { driver =>
override def updateValue(v: Byte, r: PositionedResult) = r.updateInt(v)
}

/* Reading null from a nullable LONGBINARY column does not cause wasNull
to be set, so we check for nulls directly. */
class ByteArrayTypeMapperDelegate extends super.ByteArrayTypeMapperDelegate {
override def nextOption(r: PositionedResult): Option[Array[Byte]] = Option(nextValue(r))
}

class LongTypeMapperDelegate extends super.LongTypeMapperDelegate {
override def setValue(v: Long, p: PositionedParameters) = p.setString(v.toString)
override def setOption(v: Option[Long], p: PositionedParameters) = p.setStringOption(v.map(_.toString))
@@ -113,6 +113,8 @@ object BasicProfile {
val sequenceMax = Capability("basic.sequenceMax")
/** Supports min value for sequences */
val sequenceMin = Capability("basic.sequenceMin")
/** Can set an Option[ Array[Byte] ] column to None */
val setByteArrayNull = Capability("basic.setByteArrayNull")
/** Supports the Blob data type */
val typeBlob = Capability("basic.typeBlob")
/** Supports the Long data type */
@@ -100,7 +100,12 @@ trait PostgresDriver extends ExtendedDriver { driver =>
override val uuidTypeMapperDelegate = new UUIDTypeMapperDelegate

class ByteArrayTypeMapperDelegate extends super.ByteArrayTypeMapperDelegate {
override val sqlType = java.sql.Types.BINARY
override val sqlTypeName = "BYTEA"
override def setOption(v: Option[Array[Byte]], p: PositionedParameters) = v match {
case Some(a) => p.setBytes(a)
case None => p.setNull(sqlType)
}
}

class UUIDTypeMapperDelegate extends super.UUIDTypeMapperDelegate {

0 comments on commit 7e42998

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