Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add Slick Extensions drivers
These are the drivers for DB2, Oracle and SQL Server from Slick
Extensions 3.1.0, integrated into the main project and updated to
work on master for 3.2.

See http://slick.typesafe.com/news/2016/02/01/slick-extensions-licensing-change.html for details.
  • Loading branch information
szeiger committed Feb 2, 2016
1 parent 25ca1db commit 81b6919
Show file tree
Hide file tree
Showing 20 changed files with 1,220 additions and 64 deletions.
11 changes: 3 additions & 8 deletions README.md
Expand Up @@ -19,18 +19,13 @@ The following database systems are directly supported for type-safe queries:
- MySQL
- PostgreSQL
- SQLite

Support for Oracle, IBM DB2 and Microsoft SQL Server is available for
production use by
[Typesafe subscribers](http://www.typesafe.com/products/typesafe-subscription)
(free for evaluation and development) in the Slick Extensions package.
See the Slick [manual](http://slick.typesafe.com/docs/) for details.
- Oracle 11g
- IBM DB2 LUW 10
- Microsoft SQL Server 2008

Accessing other database systems is possible, with a reduced feature set.

The [manual and scaladocs](http://slick.typesafe.com/docs/) for Slick can be
found on the [Slick web site](http://slick.typesafe.com/).
There is some older documentation (which may still apply to some extent to
Slick) in the [ScalaQuery Wiki](https://github.com/szeiger/scala-query/wiki).

Licensing conditions (BSD-style) can be found in LICENSE.txt.
7 changes: 3 additions & 4 deletions project/Build.scala
Expand Up @@ -13,7 +13,6 @@ import com.typesafe.sbt.sdlc.Plugin._
object SlickBuild extends Build {

val slickVersion = "3.2.0-SNAPSHOT"
val slickExtensionsVersion = slickVersion // Slick extensions version for links in the manual
val binaryCompatSlickVersion = "3.2.0" // Slick base version for binary compatibility checks
val scalaVersions = Seq("2.11.7", "2.12.0-M2")

Expand Down Expand Up @@ -42,7 +41,8 @@ object SlickBuild extends Build {
"org.apache.derby" % "derby" % "10.9.1.0",
"org.hsqldb" % "hsqldb" % "2.2.8",
"postgresql" % "postgresql" % "9.1-901.jdbc4",
"mysql" % "mysql-connector-java" % "5.1.23"
"mysql" % "mysql-connector-java" % "5.1.23",
"net.sourceforge.jtds" % "jtds" % "1.3.1"
)
val paxExamVersion = "4.6.0"
val paxExam = Seq(
Expand Down Expand Up @@ -195,8 +195,7 @@ object SlickBuild extends Build {
)),
(sphinxEnv in Sphinx) := (sphinxEnv in Sphinx).value +
("version" -> version.value.replaceFirst("""(\d*.\d*).*""", """$1""")) +
("release" -> version.value) +
("slick_extensions_version" -> slickExtensionsVersion),
("release" -> version.value),
(sphinxProperties in Sphinx) := Map.empty,
makeSite <<= makeSite dependsOn (buildCapabilitiesTable in slickTestkitProject),
site.addMappingsToSiteDir(mappings in packageDoc in Compile in slickProject, "api"),
Expand Down
5 changes: 5 additions & 0 deletions slick-testkit/src/codegen/resources/dbs/oracle1.sql
@@ -0,0 +1,5 @@
CREATE TABLE "PERSON"(
"ID" NUMBER(11,0) NOT NULL ENABLE,
"PERSON_TYPE" CHAR(1 BYTE) DEFAULT 'Y' NOT NULL ENABLE,
CONSTRAINT "PERSON_PK" PRIMARY KEY ("ID")
);
Expand Up @@ -104,7 +104,11 @@ val SimpleA = CustomTyping.SimpleA
""".stripMargin
},
new UUIDConfig("Postgres2", StandardTestDBs.Postgres, "Postgres", Seq("/dbs/uuid.sql")),
new Config("EmptyDB", StandardTestDBs.H2Mem, "H2Mem", Nil)
new Config("EmptyDB", StandardTestDBs.H2Mem, "H2Mem", Nil),
new Config("Oracle1", StandardTestDBs.Oracle, "Oracle", Seq("/dbs/oracle1.sql")) {
override def useSingleLineStatements = true
override def testCode = "DBIO.successful(())"
}
)

//Unified UUID config
Expand Down
67 changes: 65 additions & 2 deletions slick-testkit/src/main/resources/testkit-reference.conf
Expand Up @@ -4,7 +4,7 @@

testkit {
# Use only the following DBs (or use all if not set)
#testDBs = [h2mem, h2rownum, h2disk, hsqldbmem, hsqldbdisk, sqlitemem, sqlitedisk, derbymem, derbydisk, postgres, mysql, access, heap]
#testDBs = [h2mem, h2rownum, h2disk, hsqldbmem, hsqldbdisk, sqlitemem, sqlitedisk, derbymem, derbydisk, postgres, mysql, oracle, db2, sqlserver-jtds, sqlserver-jdbc, access, heap]
testDBs = null

# Store database files in this path (ignored by MySQL and in-memory databases)
Expand Down Expand Up @@ -80,7 +80,7 @@ defaults {
}

###################################################################################################
# Test rest of this file consists of defaults for the standard database systems which
# The rest of this file consists of defaults for the standard database systems which
# are supported by TestKit.
###################################################################################################

Expand Down Expand Up @@ -109,3 +109,66 @@ mysql {
drop = DROP DATABASE IF EXISTS ${testDB}
driver = com.mysql.jdbc.Driver
}

oracle {
baseURL = "jdbc:oracle:thin:@//localhost:1521/xe"
testConn {
url = ${baseURL}
user = SLICK_TEST
}
adminConn {
url = ${baseURL}
user = SYSTEM
}
driver = "oracle.jdbc.OracleDriver"
create = [
create tablespace ${testConn.user}"_TS datafile '"${testkit.absTestDir}"/"${testConn.user}_${testDB}"_TS' size 2M autoextend on"
create user ${testConn.user}" identified by \""${testConn.password}"\" default tablespace "${testConn.user}_TS
"grant connect, resource, select any dictionary, select_catalog_role, analyze any, analyze any dictionary to "${testConn.user}
]
drop = [
"begin execute immediate 'drop user "${testConn.user}" cascade'; exception when others then if sqlcode != -1918 then raise; end if; end;"
"begin execute immediate 'drop tablespace "${testConn.user}"_TS including contents and datafiles cascade constraints'; exception when others then if sqlcode != -959 then raise; end if; end;"
]
testClasses = ${testkit.testClasses} [
${testkit.testPackage}.OracleExtraTests
]
}

db2 {
testDB = SLICKTST
schema = SLICK_TEST
baseURL = "jdbc:db2://localhost:50000/"${testDB}":currentSchema="${schema}";"
testConn.url = ${baseURL}
adminConn.url = ${baseURL}
user = db2admin
driver = "com.ibm.db2.jcc.DB2Driver"
driverJar = "file:///C:/Program%20Files/IBM/SQLLIB/java/db2jcc4.jar"
}

sqlserver-jtds {
baseURL = "jdbc:jtds:sqlserver://localhost/"
domain = local
urlSuffix = ";progName=Slick_TestKit;namedPipe=true;domain="${domain}
testConn.url = ${baseURL}${testDB}${urlSuffix}
adminConn.url = ${baseURL}${adminDB}${urlSuffix}
user = dbo
adminDB = master
defaultSchema = dbo
create = "CREATE DATABASE \""${testDB}"\" ON (NAME = '[DB]_dat', FILENAME = '"${testkit.absTestDir}"\\\\"${testDB}".mdf') LOG ON (NAME = '"${testDB}"_log', FILENAME = \""${testkit.absTestDir}"\\\\"${testDB}".ldf\")"
drop = "IF EXISTS(SELECT name FROM sys.databases WHERE name = '"${testDB}"') DROP DATABASE \""${testDB}"\""
driver = net.sourceforge.jtds.jdbc.Driver
}

sqlserver-sqljdbc {
baseURL = "jdbc:sqlserver://localhost;database="
urlSuffix = ";applicationName=Slick_TestKit"
testConn.url = ${baseURL}${testDB}${urlSuffix}
adminConn.url = ${baseURL}${adminDB}${urlSuffix}
user = sa
adminDB = master
defaultSchema = dbo
create = "CREATE DATABASE \""${testDB}"\" ON (NAME = '[DB]_dat', FILENAME = '"${testkit.absTestDir}"\\\\"${testDB}".mdf') LOG ON (NAME = '"${testDB}"_log', FILENAME = \""${testkit.absTestDir}"\\\\"${testDB}".ldf\")"
drop = "IF EXISTS(SELECT name FROM sys.databases WHERE name = '"${testDB}"') DROP DATABASE \""${testDB}"\""
driver = com.microsoft.sqlserver.jdbc.SQLServerDriver
}
@@ -0,0 +1,51 @@
package com.typesafe.slick.testkit.tests

import com.typesafe.slick.testkit.util.{AsyncTest, JdbcTestDB}
import slick.jdbc.OracleProfile

class OracleExtraTests extends AsyncTest[JdbcTestDB] {
lazy val oracleProfile = tdb.profile.asInstanceOf[OracleProfile]
import oracleProfile.api._

def testBlobCompare = {
class A(tag: Tag) extends Table[(Int, Option[Array[Byte]])](tag, "a") {
def id = column[Int]("id")
def a = column[Option[Array[Byte]]]("a")
def * = (id, a)
}
val as = TableQuery[A]

DBIO.seq(
as.schema.create,
as += (1, Some(Array[Byte](1, 2, 3))),
as.filter(_ => LiteralColumn[Option[Int]](None).isDefined).map(_.id).result.map(_ shouldBe Nil),
as.filter(_ => LiteralColumn[Option[Int]](None).bind.isDefined).map(_.id).result.map(_ shouldBe Nil),
as.filter(_.a.isEmpty).map(_.id).result.map(_ shouldBe Nil),
as.filter(_.a === (Some(Array[Byte](1, 2, 3)): Option[Array[Byte]])).map(_.id).result.map(_ shouldBe Seq(1)),
as.filter(_.a === (None: Option[Array[Byte]])).map(_.id).result.map(_ shouldBe Nil),
as.filter(_.a === (Some(Array[Byte](1, 2, 3)): Option[Array[Byte]]).bind).map(_.id).result.map(_ shouldBe Seq(1)),
as.filter(_.a === (None: Option[Array[Byte]]).bind).map(_.id).result.map(_ shouldBe Nil),
as.filter(_ => LiteralColumn[Option[Int]](None) === (None: Option[Int])).map(_.id).result.map(_ shouldBe Nil)
)
}

def testSequenceAndTriggerName = {
class A(tag: Tag) extends Table[(Int, Int)](tag, "A_SEQTRG") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc, O.AutoIncSequenceName("SEQ_SEQTRG"), O.AutoIncTriggerName("TRG_SEQTRG"))
def a = column[Int]("A")
def * = (id, a)
}
val as = TableQuery[A]

//as.schema.createStatements.foreach(println)
as.schema.createStatements.should(_.find(_.contains("sequence \"SEQ_SEQTRG\"")).isDefined)
as.schema.createStatements.should(_.find(_.contains("trigger \"TRG_SEQTRG\"")).isDefined)

DBIO.seq(
as.schema.create,
as.map(_.a) ++= Seq(1, 2, 3),
as.to[Set].result.map(_ shouldBe Set((1,1), (2,2), (3,3))),
as.schema.drop
)
}
}
Expand Up @@ -17,12 +17,15 @@ object BuildCapabilitiesTable extends App {
}

val profileNames = if(args.length > 1) args(1).split(",") else Array(
"slick.jdbc.DB2Profile",
"slick.jdbc.DerbyProfile",
"slick.jdbc.H2Profile",
"slick.jdbc.HsqldbProfile",
"slick.jdbc.MySQLProfile",
"slick.jdbc.OracleProfile",
"slick.jdbc.PostgresProfile",
"slick.jdbc.SQLiteProfile"
"slick.jdbc.SQLiteProfile",
"slick.jdbc.SQLServerProfile"
)

val profiles = profileNames.map { n =>
Expand Down
Expand Up @@ -5,8 +5,9 @@ import java.util.logging.{Level, Logger}
import java.sql.SQLException
import slick.compiler.Phase
import slick.dbio._
import slick.driver.JdbcProfile
import slick.memory.MemoryProfile
import slick.jdbc.{SimpleJdbcAction, ResultSetAction, H2Profile, HsqldbProfile, MySQLProfile, DerbyProfile, PostgresProfile, SQLiteProfile}
import slick.jdbc._
import slick.jdbc.GetResult._
import slick.jdbc.meta.MTable
import org.junit.Assert
Expand Down Expand Up @@ -139,6 +140,92 @@ object StandardTestDBs {
}
}
}

lazy val DB2 = new ExternalJdbcTestDB("db2") {
val profile = DB2Profile
import profile.api.actionBasedSQLInterpolation

override def canGetLocalTables = false

lazy val schema = config.getString("schema")

def dropSchema: DBIO[Unit] = {
import ExecutionContext.Implicits.global
for {
schema <- sql"select schemaname from syscat.schemata where schemaname = '#$schema'".as[String].headOption
_ <- if(schema.isDefined) {
println(s"[Dropping DB2 schema '$schema']")
sqlu"call sysproc.admin_drop_schema($schema, null, ${"ERRORSCHEMA"}, ${"ERRORTABLE"})"
} else DBIO.successful(())
} yield ()
}

override def cleanUpBefore(): Unit = {
import ExecutionContext.Implicits.global
await(databaseFor("testConn").run(for {
_ <- dropSchema
_ = println(s"[Creating DB2 schema '$schema']")
_ <- sqlu"create schema #$schema"
} yield ()))
}

override def cleanUpAfter(): Unit =
await(databaseFor("adminConn").run(dropSchema))

override def dropUserArtifacts(implicit session: profile.Backend#Session) = {
session.close()
cleanUpBefore()
}
}

class SQLServerDB(confName: String) extends ExternalJdbcTestDB(confName) {
val profile = SQLServerProfile
import profile.api.actionBasedSQLInterpolation

val defaultSchema = config.getString("defaultSchema")

override def localTables(implicit ec: ExecutionContext): DBIO[Vector[String]] =
ResultSetAction[(String,String,String, String)](_.conn.getMetaData().getTables(testDB, defaultSchema, null, null)).map { ts =>
ts.map(_._3).sorted
}

override def dropUserArtifacts(implicit session: profile.Backend#Session) = blockingRunOnSession { implicit ec =>
for {
constraints <- sql"""select constraint_name, table_name from information_schema.table_constraints where constraint_type = 'FOREIGN KEY'""".as[(String, String)]
constraintStatements = constraints.collect { case (c, t) if !c.startsWith("SQL") =>
sqlu"alter table #${profile.quoteIdentifier(t)} drop constraint #${profile.quoteIdentifier(c)}"
}
_ <- DBIO.sequence(constraintStatements)
tables <- localTables
tableStatements = tables.map(t => sqlu"drop table #${profile.quoteIdentifier(t)}")
_ <- DBIO.sequence(tableStatements)
} yield ()
}
}

lazy val SQLServerJTDS = new SQLServerDB("sqlserver-jtds") {
override def capabilities = super.capabilities - TestDB.capabilities.plainSql
}
lazy val SQLServerSQLJDBC = new SQLServerDB("sqlserver-sqljdbc") {
override def capabilities = profile.capabilities - JdbcCapabilities.createModel
}

lazy val Oracle = new ExternalJdbcTestDB("oracle") {
val profile = OracleProfile
import profile.api.actionBasedSQLInterpolation

override def canGetLocalTables = false
override def capabilities =
super.capabilities - TestDB.capabilities.jdbcMetaGetIndexInfo - TestDB.capabilities.transactionIsolation

/* Only drop and recreate the user. This is much faster than dropping
* the tablespace. */
override def dropUserArtifacts(implicit session: profile.Backend#Session) = {
session.close()
val a = DBIO.sequence(Seq(drop(0), create(1), create(2)).map(s => sqlu"#$s"))
await(databaseFor("adminConn").run(a))
}
}
}

abstract class H2TestDB(confName: String, keepAlive: Boolean) extends InternalJdbcTestDB(confName) {
Expand Down
12 changes: 12 additions & 0 deletions slick-testkit/src/test/scala/slick/test/profile/ProfileTest.scala
Expand Up @@ -38,3 +38,15 @@ class MySQLTest extends ProfileTest(StandardTestDBs.MySQL)

@RunWith(classOf[Testkit])
class HeapTest extends ProfileTest(StandardTestDBs.Heap)

@RunWith(classOf[Testkit])
class DB2Test extends ProfileTest(StandardTestDBs.DB2)

@RunWith(classOf[Testkit])
class OracleTest extends ProfileTest(StandardTestDBs.Oracle)

@RunWith(classOf[Testkit])
class SQLServerJTDSTest extends ProfileTest(StandardTestDBs.SQLServerJTDS)

@RunWith(classOf[Testkit])
class SQLServerSQLJDBCTest extends ProfileTest(StandardTestDBs.SQLServerSQLJDBC)
7 changes: 7 additions & 0 deletions slick/src/main/resources/reference.conf
Expand Up @@ -37,3 +37,10 @@ slick.jdbc.MySQLProfile {
# to "VARCHAR(254)"
defaultStringType = null
}

# Profile-specific settings for SQLServerProfile
slick.jdbc.SQLServerProfile {
# The default SQL type for strings without an explicit size limit.
# When set to null / undefined, pick "TEXT" where possible, otherwise fall back to "VARCHAR(254)"
defaultStringType = null
}

0 comments on commit 81b6919

Please sign in to comment.