Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slick 3.5 support #351

Closed
8 changes: 4 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ def paradiseFlag(scalaVersion: String): Seq[String] =

val scalaTest = Def.setting("org.scalatest" %%% "scalatest" % "3.2.17")
val scalaCheck = Def.setting("org.scalacheck" %%% "scalacheck" % "1.17.0")
val slick = "com.typesafe.slick" %% "slick" % "3.4.1"
val slick = "com.typesafe.slick" %% "slick" % "3.5.0"
val optionalSlick = optional(slick)
val playJson = "com.typesafe.play" %% "play-json" % "2.10.1"
val slickPg = "com.github.tminglei" %% "slick-pg" % "0.21.1"
val slickPg = "com.github.tminglei" %% "slick-pg" % "0.22.0"
val doobie = "org.tpolecat" %% "doobie-core" % "1.0.0-RC4"
val doobiePg = "org.tpolecat" %% "doobie-postgres" % "1.0.0-RC4"
val sprayJson = "io.spray" %% "spray-json" % "1.3.6"
Expand Down Expand Up @@ -176,7 +176,7 @@ lazy val commonSettings = baseSettings ++ Seq(

lazy val slickSettings = commonSettings ++ Seq(
libraryDependencies += slick.cross(CrossVersion.for3Use2_13),
libraryDependencies += (slickPg % "test").cross(CrossVersion.for3Use2_13),
libraryDependencies += (slickPg).cross(CrossVersion.for3Use2_13),
libraryDependencies += optionalEnumeratum
)

Expand Down Expand Up @@ -329,7 +329,7 @@ lazy val macroUtils = crossProject(JSPlatform, JVMPlatform)

lazy val slickSupport = project
.in(file("slick"))
.dependsOn(core.jvm, enumeratumSupport, instances % "test -> test")
.dependsOn(core.jvm, enumeratumSupport, instances)
.settings(slickSettings: _*)
.settings(publishSettings: _*)
.settings(disableScala(List("3")))
Expand Down
303 changes: 206 additions & 97 deletions slick/src/main/scala/pl/iterators/kebs/slick/Kebs.scala

Large diffs are not rendered by default.

51 changes: 33 additions & 18 deletions slick/src/main/scala/pl/iterators/kebs/slick/enums/KebsEnums.scala
Original file line number Diff line number Diff line change
@@ -1,42 +1,57 @@
package pl.iterators.kebs.slick.enums

import pl.iterators.kebs.slick.Kebs
import pl.iterators.kebs.core.enums.{EnumLike, ValueEnumLike, ValueEnumLikeEntry}
import slick.lifted.Isomorphism
import slick.jdbc.{JdbcProfile, JdbcType}

trait SlickEnum {
def enumIsomorphism[E](`enum`: EnumLike[E]): Isomorphism[E, String] = new Isomorphism[E, String](_.toString, `enum`.withName)
import scala.reflect.ClassTag

def uppercaseEnumIsomorphism[E](`enum`: EnumLike[E]): Isomorphism[E, String] =
new Isomorphism[E, String](_.toString.toUpperCase, `enum`.withNameUppercaseOnly)
trait SlickEnum extends Kebs {

def lowercaseEnumIsomorphism[E](`enum`: EnumLike[E]): Isomorphism[E, String] =
new Isomorphism[E, String](_.toString.toLowerCase, `enum`.withNameLowercaseOnly)
def enumIsomorphism[E](
`enum`: EnumLike[E])(
implicit ct: ClassTag[E],
jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String] = {
jp.MappedColumnType.base[E, String](_.toString, `enum`.withName).asInstanceOf[slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String]]
}

def uppercaseEnumIsomorphism[E](
`enum`: EnumLike[E])(
implicit ct: ClassTag[E],
jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String] = jp.MappedColumnType.base[E, String](_.toString.toUpperCase, `enum`.withNameUppercaseOnly).asInstanceOf[slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String]]

implicit def enumListColumnType[E](implicit iso: Isomorphism[E, String]): Isomorphism[List[E], List[String]] =
new Isomorphism[List[E], List[String]](_.map(iso.map), _.map(iso.comap))
def lowercaseEnumIsomorphism[E](
`enum`: EnumLike[E])(
implicit ct: ClassTag[E],
jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String] = jp.MappedColumnType.base[E, String](_.toString.toLowerCase, `enum`.withNameLowercaseOnly).asInstanceOf[slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String]]

implicit def enumSeqColumnType[E](implicit iso: Isomorphism[E, String]): Isomorphism[Seq[E], List[String]] =
new Isomorphism[Seq[E], List[String]](_.map(iso.map).toList, _.map(iso.comap))
implicit def enumListColumnType[E](implicit iso: slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String], jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[List[E], List[String]] = {
jp.MappedColumnType.base[List[E], List[String]](_.map(iso.map), _.map(iso.comap)).asInstanceOf[slick.jdbc.JdbcTypesComponent#MappedJdbcType[List[E], List[String]]]
}

implicit def enumSeqColumnType[E](implicit iso: slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String], jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[Seq[E], List[String]] = {
jp.MappedColumnType.base[Seq[E], List[String]](_.map(iso.map).toList, _.map(iso.comap)).asInstanceOf[slick.jdbc.JdbcTypesComponent#MappedJdbcType[Seq[E], List[String]]]
}
}

trait SlickValueEnum {
def valueEnumIsomorphism[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E]): Isomorphism[E, V] =
new Isomorphism[E, V](_.value, `enum`.withValue)
def valueEnumIsomorphism[V, E <: ValueEnumLikeEntry[V]](`enum`: ValueEnumLike[V, E])(implicit bct: JdbcType[V], ct: ClassTag[E], jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, V] =
jp.MappedColumnType.base[E, V](_.value, `enum`.withValue).asInstanceOf[slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, V]]
}

trait KebsEnums extends SlickEnum with SlickValueEnum {
implicit def enumValueColumn[E](implicit ev: EnumLike[E]): Isomorphism[E, String] = enumIsomorphism(ev)
implicit def enumValueColumn[E](implicit ev: EnumLike[E], ct: ClassTag[E], jt: slick.jdbc.JdbcType[String], jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String] = enumIsomorphism(ev)

implicit def valueEnumColumn[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E]): Isomorphism[E, V] =
implicit def valueEnumColumn[V, E <: ValueEnumLikeEntry[V]](implicit ev: ValueEnumLike[V, E], bct: JdbcProfile#BaseColumnType[V], ct: ClassTag[E], jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, V] =
valueEnumIsomorphism(ev)

trait Uppercase extends SlickEnum {
implicit def enumValueColumn[E](implicit ev: EnumLike[E]): Isomorphism[E, String] = uppercaseEnumIsomorphism(ev)
implicit def enumValueColumn[E](implicit ev: EnumLike[E], jt: slick.jdbc.JdbcType[String], ct: ClassTag[E], jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String] = uppercaseEnumIsomorphism(ev)
}

trait Lowercase extends SlickEnum {
implicit def enumValueColumn[E](implicit ev: EnumLike[E]): Isomorphism[E, String] = lowercaseEnumIsomorphism(ev)
implicit def enumValueColumn[E](implicit ev: EnumLike[E], jt: slick.jdbc.JdbcType[String], ct: ClassTag[E], jp: JdbcProfile): slick.jdbc.JdbcTypesComponent#MappedJdbcType[E, String] = lowercaseEnumIsomorphism(ev)
}
}

object KebsEnums extends KebsEnums
object KebsEnums extends KebsEnums
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import slick.jdbc.JdbcType
import slick.lifted.{ExtensionMethods, FunctionSymbolExtensionMethods, Rep}

class KebsHStoreColumnExtensionMethods[KEY, VALUE, P1](val c: Rep[P1])(
implicit tm0: JdbcType[KEY],
tm1: JdbcType[VALUE],
tm2: JdbcType[List[KEY]],
tm3: JdbcType[List[VALUE]],
tm4: JdbcType[Map[KEY, VALUE]]
implicit tm0: JdbcType[KEY],
tm1: JdbcType[VALUE],
tm2: JdbcType[List[KEY]],
tm3: JdbcType[List[VALUE]],
tm4: JdbcType[Map[KEY, VALUE]]
) extends ExtensionMethods[Map[KEY, VALUE], P1] {
import FunctionSymbolExtensionMethods._

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package pl.iterators.kebs.slick.types

import slick.ast.{FieldSymbol, ScalaType}

import java.sql.{PreparedStatement, ResultSet}
import scala.reflect.ClassTag

class GenericJdbcType[T](val sqlTypeName: String,
fnFromString: (String => T),
fnToString: (T => String) = ((r: T) => r.toString),
val sqlType: Int = java.sql.Types.OTHER,
override val hasLiteralForm: Boolean = false)(
implicit override val classTag: ClassTag[T]) extends slick.jdbc.JdbcType[T] {

override def sqlTypeName(sym: Option[FieldSymbol]): String = sqlTypeName

override def getValue(r: ResultSet, idx: Int): T = {
val value = r.getString(idx)
if (r.wasNull) null.asInstanceOf[T] else fnFromString(value)
}

override def setValue(v: T, p: PreparedStatement, idx: Int): Unit = p.setObject(idx, toStr(v), java.sql.Types.OTHER)

override def updateValue(v: T, r: ResultSet, idx: Int): Unit = r.updateObject(idx, toStr(v), java.sql.Types.OTHER)

override def valueToSQLLiteral(v: T) = if(v == null) "NULL" else s"'${fnToString(v)}'"

private def toStr(v: T) = if(v == null) null else fnToString(v)

override def setNull(p: PreparedStatement, idx: Int): Unit = p.setNull(idx, sqlType)

override def wasNull(r: ResultSet, idx: Int): Boolean = r.wasNull()

override def scalaType: ScalaType[T] = null
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,39 @@ package pl.iterators.kebs.slick.arrays
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import pl.iterators.kebs.instances.time.YearMonthString
import slick.lifted.Isomorphism

class ListIsomorphismTest extends AnyFunSuite with Matchers with YearMonthString {
import pl.iterators.kebs.slick._
import pl.iterators.kebs.core.macros.CaseClass1ToValueClass._
import slick.jdbc.PostgresProfile.api._

case class C(a: String)

test("No ValueClassLike implicits derived") {
import pl.iterators.kebs.core.macros.ValueClassLike
test("No CaseClass1Rep implicits derived") {

"implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck
"implicitly[ValueClassLike[String, YearMonth]]" shouldNot typeCheck
"implicitly[CaseClass1Rep[YearMonth, String]]" shouldNot typeCheck
"implicitly[CaseClass1Rep[String, YearMonth]]" shouldNot typeCheck
}

test("Case class isomorphism implies list isomorphism") {
val iso = implicitly[Isomorphism[List[C], List[String]]]
val iso = implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[List[C], List[String]]]
iso.map(List(C("a"), C("b"))) shouldBe List("a", "b")
iso.comap(List("a", "b")) shouldBe List(C("a"), C("b"))
}

test("Case class isomorphism implies seq to list isomorphism") {
val iso = implicitly[Isomorphism[Seq[C], List[String]]]
val iso = implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[Seq[C], List[String]]]
iso.map(Seq(C("a"), C("b"))) shouldBe List("a", "b")
iso.comap(List("a", "b")) shouldBe Seq(C("a"), C("b"))
}

import java.time.YearMonth

test("List[Obj[String]] <-> List[String]") {
"val iso = implicitly[Isomorphism[List[YearMonth], List[String]]]" should compile
"val iso = implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[List[YearMonth], List[String]]]" should compile
}

test("Seq[Obj[String]] <-> List[String]") {
"val iso = implicitly[Isomorphism[Seq[YearMonth], List[String]]]" should compile
"val iso = implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[Seq[YearMonth], List[String]]]" should compile
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import com.github.tminglei.slickpg._
import enumeratum.{Enum, EnumEntry}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import pl.iterators.kebs.enumeratum.KebsEnumeratum
import pl.iterators.kebs.slick.enums.KebsEnums

class SlickPgArrayColumnTypeTests extends AnyFunSuite with Matchers with KebsEnumeratum {
class SlickPgArrayColumnTypeTests extends AnyFunSuite with Matchers {
import pl.iterators.kebs.core.macros.CaseClass1ToValueClass._

case class Institution(value: Long)
case class MarketFinancialProduct(value: String)

import pl.iterators.kebs.slick.Kebs
import pl.iterators.kebs.slick.enums.KebsEnums

object MyPostgresProfile extends ExPostgresProfile with PgArraySupport {
override val api: APIWithArrays = new APIWithArrays {}
trait APIWithArrays extends super.API with ArrayImplicits with Kebs with KebsEnums
override val api: ExtPostgresAPI = new ExtPostgresAPI {}
trait APIWithArrays extends ExPostgresProfile with ArrayImplicits with Kebs with KebsEnums
}

import MyPostgresProfile.api._
Expand Down Expand Up @@ -50,7 +51,8 @@ class SlickPgArrayColumnTypeTests extends AnyFunSuite with Matchers with KebsEnu

override val values = findValues
}
import enums._
import pl.iterators.kebs.slick.enums._
import pl.iterators.kebs.enumeratum._

test("Seq column type with enums") {
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import java.time.YearMonth
import java.util.UUID

class SlickPgArrayTests extends AnyFunSuite with Matchers {
import pl.iterators.kebs.instances.time.YearMonthString
import pl.iterators.kebs.slick.Kebs
import pl.iterators.kebs.instances.time.YearMonthString
import pl.iterators.kebs.core.macros.CaseClass1ToValueClass._

trait PostgresDriver extends ExPostgresProfile with PgArraySupport {
override val api: ArrayAPI = new ArrayAPI {}
trait ArrayAPI extends super.API with ArrayImplicits with Kebs with YearMonthString
trait ArrayAPI extends ExtPostgresAPI with ArrayImplicits with Kebs with YearMonthString
}
object PostgresDriver extends PostgresDriver

Expand All @@ -40,7 +41,7 @@ class SlickPgArrayTests extends AnyFunSuite with Matchers {
override def * : ProvenShape[Test] = (id, ccList) <> ((Test.apply _).tupled, Test.unapply)
}

test("No ValueClassLike implicits derived") {
test("No CaseClass1Rep implicits derived") {
import pl.iterators.kebs.core.macros.ValueClassLike

"implicitly[ValueClassLike[YearMonth, String]]" shouldNot typeCheck
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,57 @@ package pl.iterators.kebs.slick.caseclasses

import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import pl.iterators.kebs.slick.Kebs
import slick.jdbc.PostgresProfile.api._

class CaseClassIsomorphismTests extends AnyFunSuite with Matchers {
import slick.lifted.Isomorphism
import pl.iterators.kebs._
import scala.reflect.ClassTag

class CaseClassIsomorphismTests extends AnyFunSuite with Matchers with Kebs {
import pl.iterators.kebs.core.macros.CaseClass1ToValueClass._

case class Simple1(a: Int)
case class Simple2(a: Option[Int])

test("Implicit isomorphism for case class of arity 1") {
val iso = implicitly[Isomorphism[Simple1, Int]]
val iso = implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[Simple1, Int]]
iso.map(Simple1(10)) shouldBe 10
iso.comap(10) shouldBe Simple1(10)
}

test("Implicit isomorphism for case class of arity 1 - parametrized return type") {
val iso = implicitly[Isomorphism[Simple2, Option[Int]]]
iso.map(Simple2(Some(10))) shouldBe Some(10)
iso.comap(Some(10)) shouldBe Simple2(Some(10))
}
test("Implicit isomorphism for case class of arity 1 - parametrized return type") {
val iso = implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[Simple2, Option[Int]]]
iso.map(Simple2(Some(10))) shouldBe Some(10)
iso.comap(Some(10)) shouldBe Simple2(Some(10))
}

case class TooBig(a: Int, b: Int)

test("No isomorphism for case classes of arity > 1") {
"implicitly[Isomorphism[TooBig, _]]" shouldNot typeCheck
"implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[TooBig, _]]" shouldNot typeCheck
}

case object NoIsoForYou

test("No isomorphism for case classes of arity == 0") {
"implicitly[Isomorphism[NoIsoForYou.type, _]]" shouldNot typeCheck
"implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[NoIsoForYou.type, _]]" shouldNot typeCheck
}

class JustAClass(val a: Int)

test("No isomorphism for ordinary classes") {
"implicitly[Isomorphism[JustAClass, Int]]" shouldNot typeCheck
"implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[JustAClass, Int]]" shouldNot typeCheck
}

case class Parametrized[P](a: P)

test("Implicit isomorphism for parametrized case class of arity 1") {
val iso = implicitly[Isomorphism[Parametrized[Int], Int]]
val iso = implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[Parametrized[Int], Int]]
iso.map(Parametrized(10)) shouldBe 10
iso.comap(10) shouldBe Parametrized(10)
}

test("Implicit isomorphism for parametrized case class of arity 1 - unrefined type parameter") {
def iso[P]: Isomorphism[Parametrized[P], P] = implicitly[Isomorphism[Parametrized[P], P]]
def iso[P](implicit cls: ClassTag[P]): slick.jdbc.JdbcTypesComponent#MappedJdbcType[Parametrized[P], P] = implicitly[slick.jdbc.JdbcTypesComponent#MappedJdbcType[Parametrized[P], P]]
iso[Int].map(Parametrized(10)) shouldBe 10
iso[Option[Int]].comap(Some(10)) shouldBe Parametrized(Some(10))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.scalatest.matchers.should.Matchers
class SlickMappedColumnTypeTests extends AnyFunSuite with Matchers {
import slick.lifted.ProvenShape
import slick.jdbc.PostgresProfile.api._
import pl.iterators.kebs._
import pl.iterators.kebs.core.macros.CaseClass1ToValueClass._

case class Id(id: Long)
case class Row(id: Id, name: String, num: Long)
Expand Down Expand Up @@ -46,7 +46,7 @@ class SlickMappedColumnTypeTests extends AnyFunSuite with Matchers {
"""
|class OneElement(tag: Tag) extends Table[Name](tag, "ONE_ELEMENT_TABLE") {
| def name = column[String]("name")
|
|
| override def * : ProvenShape[Name] = name <> (Name.apply, Name.unapply)
| }
""".stripMargin should compile
Expand All @@ -56,7 +56,7 @@ class SlickMappedColumnTypeTests extends AnyFunSuite with Matchers {
"""
|class Matryoshka(tag: Tag) extends Table[WrappedName](tag, "MATRYOSHKA") {
| def name = column[Name]("name")
|
|
| override def * : ProvenShape[WrappedName] = name <> (WrappedName.apply, WrappedName.unapply)
|}
""".stripMargin should compile
Expand All @@ -67,7 +67,7 @@ class SlickMappedColumnTypeTests extends AnyFunSuite with Matchers {
|class Matryoshka(tag: Tag) extends Table[WrappedName](tag, "MATRYOSHKA") {
| def name = column[Name]("name")
| private def mappedProjection = name <> (WrappedName.apply, WrappedName.unapply)
|
|
| override def * : ProvenShape[WrappedName] = mappedProjection
| }
""".stripMargin should compile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import org.scalatest.matchers.should.Matchers
import java.util.UUID

class SlickPgTests extends AnyFunSuite with Matchers {

import pl.iterators.kebs.slick.Kebs
import pl.iterators.kebs.core.macros.CaseClass1ToValueClass._
import slick.lifted.ProvenShape

case class ServiceLineName(name: String)
Expand All @@ -17,7 +17,7 @@ class SlickPgTests extends AnyFunSuite with Matchers {

trait PostgresDriver extends ExPostgresProfile {
override val api = PostgresApi
object PostgresApi extends API with Kebs
object PostgresApi extends ExtPostgresAPI with Kebs
}
object PostgresDriver extends PostgresDriver

Expand Down
Loading