diff --git a/project/Build.scala b/project/Build.scala index b5b53305d..494235920 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -121,6 +121,7 @@ object ScalikeJDBCProjects extends Build { libraryDependencies ++= { Seq( // scope: compile + "org.scala-lang" % "scala-reflect" % scalaVersion.value, "org.apache.commons" % "commons-dbcp2" % "2.1.1" % "compile", "org.slf4j" % "slf4j-api" % _slf4jApiVersion % "compile", "joda-time" % "joda-time" % "2.9.2" % "compile", @@ -136,9 +137,17 @@ object ScalikeJDBCProjects extends Build { ) ++ (CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, scalaMajor)) if scalaMajor >= 11 => Seq("org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4" % "compile") + case Some((2, 10)) => + libraryDependencies.value ++ Seq( + compilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), + "org.scalamacros" %% "quasiquotes" % "2.1.0" cross CrossVersion.binary) case _ => Nil }) ++ scalaTestDependenciesInTestScope(scalatestVersion.value) ++ jdbcDriverDependenciesInTestScope + }, + unmanagedSourceDirectories in Compile <+= (scalaVersion, sourceDirectory in Compile){(v, dir) => + if (v.startsWith("2.10")) dir / "scala2.10" + else dir / "scala2.11" } ) ) @@ -275,17 +284,13 @@ object ScalikeJDBCProjects extends Build { "ch.qos.logback" % "logback-classic" % _logbackVersion % "test", "org.hibernate" % "hibernate-core" % _hibernateVersion % "test" ) ++ scalaTestDependenciesInTestScope(scalatestVersion.value) ++ jdbcDriverDependenciesInTestScope ++ macroDependenciesInCompileScope(scalaVersion.value) - }, - unmanagedSourceDirectories in Compile <+= (scalaVersion, sourceDirectory in Compile){(v, dir) => - if (v.startsWith("2.10")) dir / "scala2.10" - else dir / "scala2.11" } ) ) dependsOn(scalikejdbcLibrary) def macroDependenciesInCompileScope(scalaVersion: String) = { if (scalaVersion.startsWith("2.10")) Seq( - "org.scalamacros" %% "quasiquotes" % "2.1.0" % "compile", + "org.scalamacros" % "quasiquotes_2.10" % "2.1.0" % "compile", compilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full) ) else Seq() } diff --git a/project/GenerateOneToManies.scala b/project/GenerateOneToManies.scala index 9f8ff32ff..50a5b5ca5 100644 --- a/project/GenerateOneToManies.scala +++ b/project/GenerateOneToManies.scala @@ -80,27 +80,27 @@ s" to$i.map(t => Vector(t)).getOrElse(Vector.empty)" class OneToManies${n}SQL[A, $bs, E <: WithExtractor, Z]( override val statement: String, - override val parameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toManies(RS => Option[B1]...)) is specified, use #map((A,B) =>Z) instead.")) + override private[scalikejdbc] val rawParameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toManies(RS => Option[B1]...)) is specified, use #map((A,B) =>Z) instead.")) with AllOutputDecisionsUnsupported[Z, E] { def map(extractor: (A, $seq) => Z): OneToManies${n}SQL[A, $bs, HasExtractor, Z] = { - new OneToManies${n}SQL(statement, parameters)(one)($to)(extractor) + new OneToManies${n}SQL(statement, rawParameters)(one)($to)(extractor) } override def toTraversable(): OneToManies${n}SQLToTraversable[A, $bs, E, Z] = { - new OneToManies${n}SQLToTraversable[A, $bs, E, Z](statement, parameters)(one)($to)(extractor) + new OneToManies${n}SQLToTraversable[A, $bs, E, Z](statement, rawParameters)(one)($to)(extractor) } override def toList(): OneToManies${n}SQLToList[A, $bs, E, Z] = { - new OneToManies${n}SQLToList[A, $bs, E, Z](statement, parameters)(one)($to)(extractor) + new OneToManies${n}SQLToList[A, $bs, E, Z](statement, rawParameters)(one)($to)(extractor) } override def toOption(): OneToManies${n}SQLToOption[A, $bs, E, Z] = { - new OneToManies${n}SQLToOption[A, $bs, E, Z](statement, parameters)(one)($to)(extractor)(true) + new OneToManies${n}SQLToOption[A, $bs, E, Z](statement, rawParameters)(one)($to)(extractor)(true) } override def headOption(): OneToManies${n}SQLToOption[A, $bs, E, Z] = { - new OneToManies${n}SQLToOption[A, $bs, E, Z](statement, parameters)(one)($to)(extractor)(false) + new OneToManies${n}SQLToOption[A, $bs, E, Z](statement, rawParameters)(one)($to)(extractor)(false) } override def toCollection: OneToManies${n}SQLToCollection[A, $bs, E, Z] = { - new OneToManies${n}SQLToCollection[A, ${bs}, E, Z](statement, parameters)(one)($to)(extractor) + new OneToManies${n}SQLToCollection[A, ${bs}, E, Z](statement, rawParameters)(one)($to)(extractor) } override def single(): OneToManies${n}SQLToOption[A, $bs, E, Z] = toOption() @@ -112,8 +112,8 @@ class OneToManies${n}SQL[A, $bs, E <: WithExtractor, Z]( class OneToManies${n}SQLToList[A, $bs, E <: WithExtractor, Z]( override val statement: String, - override val parameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toManies(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) + override private[scalikejdbc] val rawParameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toManies(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) with SQLToList[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManies${n}Extractor[A, $bs, E, Z] { @@ -121,7 +121,7 @@ class OneToManies${n}SQLToList[A, $bs, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): List[Z] = { - executeQuery[List](session, (session: DBSession) => toTraversable(session, statement, parameters, extractor).toList) + executeQuery[List](session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor).toList) } $extractOne @@ -131,8 +131,8 @@ $transform final class OneToManies${n}SQLToCollection[A, $bs, E <: WithExtractor, Z] private[scalikejdbc]( override val statement: String, - override val parameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toManies(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) + override private[scalikejdbc] val rawParameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toManies(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) with SQLToCollection[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManies${n}Extractor[A, $bs, E, Z] { @@ -140,7 +140,7 @@ final class OneToManies${n}SQLToCollection[A, $bs, E <: WithExtractor, Z] privat import GeneralizedTypeConstraintsForWithExtractor._ override def apply[C[_]]()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor, cbf: CanBuildFrom[Nothing, Z, C[Z]]): C[Z] = { - executeQuery(session, (session: DBSession) => toTraversable(session, statement, parameters, extractor).to[C]) + executeQuery(session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor).to[C]) } $extractOne @@ -150,8 +150,8 @@ $transform class OneToManies${n}SQLToTraversable[A, $bs, E <: WithExtractor, Z]( override val statement: String, - override val parameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) + override private[scalikejdbc] val rawParameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) with SQLToTraversable[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManies${n}Extractor[A, $bs, E, Z] { @@ -159,7 +159,7 @@ class OneToManies${n}SQLToTraversable[A, $bs, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): Traversable[Z] = { - executeQuery[Traversable](session, (session: DBSession) => toTraversable(session, statement, parameters, extractor)) + executeQuery[Traversable](session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor)) } $extractOne @@ -169,15 +169,15 @@ $transform class OneToManies${n}SQLToOption[A, $bs, E <: WithExtractor, Z]( override val statement: String, - override val parameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z)(protected val isSingle: Boolean = true) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) + override private[scalikejdbc] val rawParameters: Seq[Any])(one: WrappedResultSet => A)($resultSetToOptions)(extractor: (A, $seq) => Z)(protected val isSingle: Boolean = true) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) with SQLToOption[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManies${n}Extractor[A, $bs, E, Z] { import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): Option[Z] = { - executeQuery[Option](session, (session: DBSession) => toSingle(toTraversable(session, statement, parameters, extractor))) + executeQuery[Option](session, (session: DBSession) => toSingle(toTraversable(session, statement, rawParameters, extractor))) } $extractOne diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/OneToManies2SQL.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/OneToManies2SQL.scala index 1b2535107..9132e7dd1 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/OneToManies2SQL.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/OneToManies2SQL.scala @@ -30,7 +30,7 @@ private[scalikejdbc] trait OneToManies2Extractor[A, B1, B2, E <: WithExtractor, } private[scalikejdbc] def toTraversable(session: DBSession, sql: String, params: Seq[_], extractor: (A, Seq[B1], Seq[B2]) => Z): Traversable[Z] = { - session.foldLeft(statement, parameters: _*)(LinkedHashMap[A, (Seq[B1], Seq[B2])]())(processResultSet).map { + session.foldLeft(statement, rawParameters: _*)(LinkedHashMap[A, (Seq[B1], Seq[B2])]())(processResultSet).map { case (one, (t1, t2)) => extractor(one, t1, t2) } } @@ -39,28 +39,28 @@ private[scalikejdbc] trait OneToManies2Extractor[A, B1, B2, E <: WithExtractor, class OneToManies2SQL[A, B1, B2, E <: WithExtractor, Z]( override val statement: String, - override val parameters: Seq[Any] + override private[scalikejdbc] val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(to1: WrappedResultSet => Option[B1], to2: WrappedResultSet => Option[B2])(extractor: (A, Seq[B1], Seq[B2]) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with AllOutputDecisionsUnsupported[Z, E] { def map(extractor: (A, Seq[B1], Seq[B2]) => Z): OneToManies2SQL[A, B1, B2, HasExtractor, Z] = { - new OneToManies2SQL(statement, parameters)(one)(to1, to2)(extractor) + new OneToManies2SQL(statement, rawParameters)(one)(to1, to2)(extractor) } override def toTraversable(): OneToManies2SQLToTraversable[A, B1, B2, E, Z] = { - new OneToManies2SQLToTraversable[A, B1, B2, E, Z](statement, parameters)(one)(to1, to2)(extractor) + new OneToManies2SQLToTraversable[A, B1, B2, E, Z](statement, rawParameters)(one)(to1, to2)(extractor) } override def toList(): OneToManies2SQLToList[A, B1, B2, E, Z] = { - new OneToManies2SQLToList[A, B1, B2, E, Z](statement, parameters)(one)(to1, to2)(extractor) + new OneToManies2SQLToList[A, B1, B2, E, Z](statement, rawParameters)(one)(to1, to2)(extractor) } override def toOption(): OneToManies2SQLToOption[A, B1, B2, E, Z] = { - new OneToManies2SQLToOption[A, B1, B2, E, Z](statement, parameters)(one)(to1, to2)(extractor)(true) + new OneToManies2SQLToOption[A, B1, B2, E, Z](statement, rawParameters)(one)(to1, to2)(extractor)(true) } override def toCollection: OneToManies2SQLToCollection[A, B1, B2, E, Z] = { - new OneToManies2SQLToCollection[A, B1, B2, E, Z](statement, parameters)(one)(to1, to2)(extractor) + new OneToManies2SQLToCollection[A, B1, B2, E, Z](statement, rawParameters)(one)(to1, to2)(extractor) } override def headOption(): OneToManies2SQLToOption[A, B1, B2, E, Z] = { - new OneToManies2SQLToOption[A, B1, B2, E, Z](statement, parameters)(one)(to1, to2)(extractor)(false) + new OneToManies2SQLToOption[A, B1, B2, E, Z](statement, rawParameters)(one)(to1, to2)(extractor)(false) } override def single(): OneToManies2SQLToOption[A, B1, B2, E, Z] = toOption() override def first(): OneToManies2SQLToOption[A, B1, B2, E, Z] = headOption() @@ -72,16 +72,16 @@ class OneToManies2SQL[A, B1, B2, E <: WithExtractor, Z]( class OneToManies2SQLToList[A, B1, B2, E <: WithExtractor, Z]( override val statement: String, - override val parameters: Seq[Any] + override private[scalikejdbc] val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(to1: WrappedResultSet => Option[B1], to2: WrappedResultSet => Option[B2])(extractor: (A, Seq[B1], Seq[B2]) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) with SQLToList[Z, E] with OneToManies2Extractor[A, B1, B2, E, Z] { import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): List[Z] = { - executeQuery[List](session, (session: DBSession) => toTraversable(session, statement, parameters, extractor).toList) + executeQuery[List](session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor).toList) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -92,16 +92,16 @@ class OneToManies2SQLToList[A, B1, B2, E <: WithExtractor, Z]( final class OneToManies2SQLToCollection[A, B1, B2, E <: WithExtractor, Z] private[scalikejdbc] ( override val statement: String, - override val parameters: Seq[Any] + override private[scalikejdbc] val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(to1: WrappedResultSet => Option[B1], to2: WrappedResultSet => Option[B2])(extractor: (A, Seq[B1], Seq[B2]) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) with SQLToCollection[Z, E] with OneToManies2Extractor[A, B1, B2, E, Z] { import GeneralizedTypeConstraintsForWithExtractor._ override def apply[C[_]]()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor, cbf: CanBuildFrom[Nothing, Z, C[Z]]): C[Z] = { - executeQuery(session, (session: DBSession) => toTraversable(session, statement, parameters, extractor).to[C]) + executeQuery(session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor).to[C]) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -112,9 +112,9 @@ final class OneToManies2SQLToCollection[A, B1, B2, E <: WithExtractor, Z] privat class OneToManies2SQLToTraversable[A, B1, B2, E <: WithExtractor, Z]( override val statement: String, - override val parameters: Seq[Any] + override private[scalikejdbc] val rawParameters: Seq[Any] )(val one: WrappedResultSet => A)(to1: WrappedResultSet => Option[B1], to2: WrappedResultSet => Option[B2])(extractor: (A, Seq[B1], Seq[B2]) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) with SQLToTraversable[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManies2Extractor[A, B1, B2, E, Z] { @@ -122,7 +122,7 @@ class OneToManies2SQLToTraversable[A, B1, B2, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): Traversable[Z] = { - executeQuery[Traversable](session, (session: DBSession) => toTraversable(session, statement, parameters, extractor)) + executeQuery[Traversable](session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor)) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -133,16 +133,16 @@ class OneToManies2SQLToTraversable[A, B1, B2, E <: WithExtractor, Z]( class OneToManies2SQLToOption[A, B1, B2, E <: WithExtractor, Z]( override val statement: String, - override val parameters: Seq[Any] + override private[scalikejdbc] val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(to1: WrappedResultSet => Option[B1], to2: WrappedResultSet => Option[B2])(extractor: (A, Seq[B1], Seq[B2]) => Z)(protected val isSingle: Boolean = true) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B1])) is specified, use #map((A,B) =>Z) instead.")) with SQLToOption[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManies2Extractor[A, B1, B2, E, Z] { import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): Option[Z] = { - executeQuery[Option](session, (session: DBSession) => toSingle(toTraversable(session, statement, parameters, extractor))) + executeQuery[Option](session, (session: DBSession) => toSingle(toTraversable(session, statement, rawParameters, extractor))) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/OneToManySQL.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/OneToManySQL.scala index 8cbf10f73..941194598 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/OneToManySQL.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/OneToManySQL.scala @@ -22,7 +22,7 @@ private[scalikejdbc] trait OneToManyExtractor[A, B, E <: WithExtractor, Z] } private[scalikejdbc] def toTraversable(session: DBSession, sql: String, params: Seq[_], extractor: (A, Seq[B]) => Z): Traversable[Z] = { - session.foldLeft(statement, parameters: _*)(LinkedHashMap[A, (Seq[B])]())(processResultSet).map { + session.foldLeft(statement, rawParameters: _*)(LinkedHashMap[A, (Seq[B])]())(processResultSet).map { case (one, (to)) => extractor(one, to) } } @@ -30,33 +30,33 @@ private[scalikejdbc] trait OneToManyExtractor[A, B, E <: WithExtractor, Z] } class OneToManySQL[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toMany: WrappedResultSet => Option[B])(extractor: (A, Seq[B]) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with AllOutputDecisionsUnsupported[Z, E] { def map(extractor: (A, Seq[B]) => Z): OneToManySQL[A, B, HasExtractor, Z] = { - new OneToManySQL[A, B, HasExtractor, Z](statement, parameters)(one)(toMany)(extractor) + new OneToManySQL[A, B, HasExtractor, Z](statement, rawParameters)(one)(toMany)(extractor) } override def toTraversable(): OneToManySQLToTraversable[A, B, E, Z] = { - new OneToManySQLToTraversable[A, B, E, Z](statement, parameters)(one)(toMany)(extractor) + new OneToManySQLToTraversable[A, B, E, Z](statement, rawParameters)(one)(toMany)(extractor) } override def toList(): OneToManySQLToList[A, B, E, Z] = { - new OneToManySQLToList[A, B, E, Z](statement, parameters)(one)(toMany)(extractor) + new OneToManySQLToList[A, B, E, Z](statement, rawParameters)(one)(toMany)(extractor) } override def toOption(): OneToManySQLToOption[A, B, E, Z] = { - new OneToManySQLToOption[A, B, E, Z](statement, parameters)(one)(toMany)(extractor)(true) + new OneToManySQLToOption[A, B, E, Z](statement, rawParameters)(one)(toMany)(extractor)(true) } override def headOption(): OneToManySQLToOption[A, B, E, Z] = { - new OneToManySQLToOption[A, B, E, Z](statement, parameters)(one)(toMany)(extractor)(false) + new OneToManySQLToOption[A, B, E, Z](statement, rawParameters)(one)(toMany)(extractor)(false) } override def toCollection: OneToManySQLToCollection[A, B, E, Z] = { - new OneToManySQLToCollection[A, B, E, Z](statement, parameters)(one)(toMany)(extractor) + new OneToManySQLToCollection[A, B, E, Z](statement, rawParameters)(one)(toMany)(extractor) } override def single(): OneToManySQLToOption[A, B, E, Z] = toOption() @@ -68,9 +68,9 @@ class OneToManySQL[A, B, E <: WithExtractor, Z]( } class OneToManySQLToList[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toMany: WrappedResultSet => Option[B])(extractor: (A, Seq[B]) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with SQLToList[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManyExtractor[A, B, E, Z] { @@ -78,7 +78,7 @@ class OneToManySQLToList[A, B, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): List[Z] = { - executeQuery[List](session, (session: DBSession) => toTraversable(session, statement, parameters, extractor).toList) + executeQuery[List](session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor).toList) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -87,9 +87,9 @@ class OneToManySQLToList[A, B, E <: WithExtractor, Z]( } class OneToManySQLToTraversable[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toMany: WrappedResultSet => Option[B])(extractor: (A, Seq[B]) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with SQLToTraversable[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManyExtractor[A, B, E, Z] { @@ -97,7 +97,7 @@ class OneToManySQLToTraversable[A, B, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): Traversable[Z] = { - executeQuery[Traversable](session, (session: DBSession) => toTraversable(session, statement, parameters, extractor)) + executeQuery[Traversable](session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor)) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -106,9 +106,9 @@ class OneToManySQLToTraversable[A, B, E <: WithExtractor, Z]( } class OneToManySQLToCollection[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toMany: WrappedResultSet => Option[B])(extractor: (A, Seq[B]) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with SQLToCollection[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManyExtractor[A, B, E, Z] { @@ -116,7 +116,7 @@ class OneToManySQLToCollection[A, B, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply[C[_]]()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor, cbf: CanBuildFrom[Nothing, Z, C[Z]]): C[Z] = { - executeQuery(session, (session: DBSession) => toTraversable(session, statement, parameters, extractor).to[C]) + executeQuery(session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor).to[C]) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -125,16 +125,16 @@ class OneToManySQLToCollection[A, B, E <: WithExtractor, Z]( } class OneToManySQLToOption[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toMany: WrappedResultSet => Option[B])(extractor: (A, Seq[B]) => Z)(protected val isSingle: Boolean = true) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor("one-to-many extractor(one(RS => A).toMany(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with SQLToOption[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToManyExtractor[A, B, E, Z] { import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): Option[Z] = { - executeQuery[Option](session, (session: DBSession) => toSingle(toTraversable(session, statement, parameters, extractor))) + executeQuery[Option](session, (session: DBSession) => toSingle(toTraversable(session, statement, rawParameters, extractor))) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/OneToOneSQL.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/OneToOneSQL.scala index e62c83584..b4672662d 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/OneToOneSQL.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/OneToOneSQL.scala @@ -22,7 +22,7 @@ private[scalikejdbc] trait OneToOneExtractor[A, B, E <: WithExtractor, Z] } private[scalikejdbc] def toTraversable(session: DBSession, sql: String, params: Seq[_], extractor: (A, B) => Z): Traversable[Z] = { - session.foldLeft(statement, parameters: _*)(LinkedHashMap[A, Option[B]]())(processResultSet).map { + session.foldLeft(statement, rawParameters: _*)(LinkedHashMap[A, Option[B]]())(processResultSet).map { case (one, Some(to)) => extractor(one, to) case (one, None) => one.asInstanceOf[Z] } @@ -31,29 +31,29 @@ private[scalikejdbc] trait OneToOneExtractor[A, B, E <: WithExtractor, Z] } class OneToOneSQL[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toOne: WrappedResultSet => Option[B])(extractor: (A, B) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with AllOutputDecisionsUnsupported[Z, E] { def map(extractor: (A, B) => Z): OneToOneSQL[A, B, HasExtractor, Z] = { - new OneToOneSQL(statement, parameters)(one)(toOne)(extractor) + new OneToOneSQL(statement, rawParameters)(one)(toOne)(extractor) } override def toTraversable(): OneToOneSQLToTraversable[A, B, E, Z] = { - new OneToOneSQLToTraversable[A, B, E, Z](statement, parameters)(one)(toOne)(extractor) + new OneToOneSQLToTraversable[A, B, E, Z](statement, rawParameters)(one)(toOne)(extractor) } override def toList(): OneToOneSQLToList[A, B, E, Z] = { - new OneToOneSQLToList[A, B, E, Z](statement, parameters)(one)(toOne)(extractor) + new OneToOneSQLToList[A, B, E, Z](statement, rawParameters)(one)(toOne)(extractor) } override def toOption(): OneToOneSQLToOption[A, B, E, Z] = { - new OneToOneSQLToOption[A, B, E, Z](statement, parameters)(one)(toOne)(extractor)(true) + new OneToOneSQLToOption[A, B, E, Z](statement, rawParameters)(one)(toOne)(extractor)(true) } override def headOption(): OneToOneSQLToOption[A, B, E, Z] = { - new OneToOneSQLToOption[A, B, E, Z](statement, parameters)(one)(toOne)(extractor)(false) + new OneToOneSQLToOption[A, B, E, Z](statement, rawParameters)(one)(toOne)(extractor)(false) } override def toCollection: OneToOneSQLToCollection[A, B, E, Z] = { - new OneToOneSQLToCollection[A, B, E, Z](statement, parameters)(one)(toOne)(extractor) + new OneToOneSQLToCollection[A, B, E, Z](statement, rawParameters)(one)(toOne)(extractor) } override def single(): OneToOneSQLToOption[A, B, E, Z] = toOption() @@ -64,9 +64,9 @@ class OneToOneSQL[A, B, E <: WithExtractor, Z]( } class OneToOneSQLToTraversable[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override private[scalikejdbc] val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toOne: WrappedResultSet => Option[B])(map: (A, B) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with SQLToTraversable[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToOneExtractor[A, B, E, Z] { @@ -74,7 +74,7 @@ class OneToOneSQLToTraversable[A, B, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): Traversable[Z] = { - executeQuery[Traversable](session, (session: DBSession) => toTraversable(session, statement, parameters, transform)) + executeQuery[Traversable](session, (session: DBSession) => toTraversable(session, statement, rawParameters, transform)) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -83,9 +83,9 @@ class OneToOneSQLToTraversable[A, B, E <: WithExtractor, Z]( } class OneToOneSQLToList[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override private[scalikejdbc] val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toOne: WrappedResultSet => Option[B])(extractor: (A, B) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with SQLToList[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToOneExtractor[A, B, E, Z] { @@ -93,7 +93,7 @@ class OneToOneSQLToList[A, B, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): List[Z] = { - executeQuery[List](session, (session: DBSession) => toTraversable(session, statement, parameters, extractor).toList) + executeQuery[List](session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor).toList) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -102,9 +102,9 @@ class OneToOneSQLToList[A, B, E <: WithExtractor, Z]( } class OneToOneSQLToCollection[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override private[scalikejdbc] val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toOne: WrappedResultSet => Option[B])(extractor: (A, B) => Z) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with SQLToCollection[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToOneExtractor[A, B, E, Z] { @@ -112,7 +112,7 @@ class OneToOneSQLToCollection[A, B, E <: WithExtractor, Z]( import GeneralizedTypeConstraintsForWithExtractor._ override def apply[C[_]]()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor, cbf: CanBuildFrom[Nothing, Z, C[Z]]): C[Z] = { - executeQuery(session, (session: DBSession) => toTraversable(session, statement, parameters, extractor).to[C]) + executeQuery(session, (session: DBSession) => toTraversable(session, statement, rawParameters, extractor).to[C]) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one @@ -121,16 +121,16 @@ class OneToOneSQLToCollection[A, B, E <: WithExtractor, Z]( } class OneToOneSQLToOption[A, B, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override private[scalikejdbc] val rawParameters: Seq[Any] )(one: WrappedResultSet => A)(toOne: WrappedResultSet => Option[B])(extractor: (A, B) => Z)(protected val isSingle: Boolean = true) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-one extractor(one(RS => A).toOne(RS => Option[B])) is specified, use #map((A,B) =>Z) instead.")) with SQLToOption[Z, E] with AllOutputDecisionsUnsupported[Z, E] with OneToOneExtractor[A, B, E, Z] { import GeneralizedTypeConstraintsForWithExtractor._ override def apply()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor): Option[Z] = { - executeQuery[Option](session, (session: DBSession) => toSingle(toTraversable(session, statement, parameters, extractor))) + executeQuery[Option](session, (session: DBSession) => toSingle(toTraversable(session, statement, rawParameters, extractor))) } private[scalikejdbc] def extractOne: WrappedResultSet => A = one diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/ParameterBinder.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/ParameterBinder.scala index b64a24c48..0b2b07f1d 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/ParameterBinder.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/ParameterBinder.scala @@ -3,7 +3,7 @@ package scalikejdbc import java.sql.PreparedStatement /** - * ParameterBinder which enables customizing StatementExecutor#binParams. + * EssentialParameterBinder which enables customizing StatementExecutor#binParams. * * {{{ * val bytes = Array[Byte](1,2,3, ...) @@ -15,19 +15,26 @@ import java.sql.PreparedStatement * sql"insert into table (bin) values (${bin})".update.apply() * }}} */ -trait ParameterBinder[A] { - - /** - * Parameter value. - */ - @deprecated("This unused field will be removed", since = "2.2.4") - def value: A +trait ParameterBinder { self => /** * Applies parameter to PreparedStatement. */ def apply(stmt: PreparedStatement, idx: Int): Unit +} +trait ParameterBinderWithValue[A] extends ParameterBinder { self => + + def value: A + + // [[scalikejdbc.ParameterBinder#map]] breaks the Functor-law + private[scalikejdbc] def map[B](f: A => B): ParameterBinderWithValue[B] = new ParameterBinderWithValue[B] { + lazy val value: B = f(self.value) + def apply(stmt: PreparedStatement, idx: Int): Unit = self(stmt, idx) + } + + override def toString: String = s"ParameterBinder(value=$value)" + } /** @@ -38,13 +45,25 @@ object ParameterBinder { /** * Factory method for ParameterBinder. */ - def apply[A](value: A, binder: (PreparedStatement, Int) => Unit): ParameterBinder[A] = { - val _v = value - new ParameterBinder[A] { - override def value: A = _v - override def apply(stmt: PreparedStatement, idx: Int): Unit = binder.apply(stmt, idx) + def apply[A](value: A, binder: (PreparedStatement, Int) => Unit): ParameterBinderWithValue[A] = { + val _value = value + new ParameterBinderWithValue[A] { + val value: A = _value + override def apply(stmt: PreparedStatement, idx: Int): Unit = binder(stmt, idx) + } + } + + def unapply(a: Any): Option[Any] = { + PartialFunction.condOpt(a) { + case x: ParameterBinderWithValue[_] => x.value } } + def NullParameterBinder[A]: ParameterBinderWithValue[A] = new ParameterBinderWithValue[A] { + val value = null.asInstanceOf[A] + def apply(stmt: PreparedStatement, idx: Int): Unit = stmt.setObject(idx, null) + override def toString: String = s"ParameterBinder(value=NULL)" + } + } diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/ParameterBinderFactory.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/ParameterBinderFactory.scala new file mode 100644 index 000000000..6fe467634 --- /dev/null +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/ParameterBinderFactory.scala @@ -0,0 +1,100 @@ +package scalikejdbc + +import java.io.InputStream +import java.sql.PreparedStatement +import scalikejdbc.UnixTimeInMillisConverterImplicits._ +import scalikejdbc.interpolation.SQLSyntax +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +private[scalikejdbc] case class SQLSyntaxParameterBinder(syntax: SQLSyntax) extends ParameterBinderWithValue[SQLSyntax] { + val value = syntax + def apply(stmt: PreparedStatement, idx: Int): Unit = () +} + +trait ParameterBinderFactory[A] { self => + + def apply(value: A): ParameterBinderWithValue[A] + + def xmap[B](f: A => B, g: B => A): ParameterBinderFactory[B] = new ParameterBinderFactory[B] { + def apply(value: B): ParameterBinderWithValue[B] = { + if (value == null) ParameterBinder.NullParameterBinder + else self(g(value)).map(f) + } + } + +} + +object ParameterBinderFactory extends LowPriorityImplicitsParameterBinderFactory1 { + + def apply[A](f: A => (PreparedStatement, Int) => Unit): ParameterBinderFactory[A] = new ParameterBinderFactory[A] { + def apply(value: A): ParameterBinderWithValue[A] = { + if (value == null) ParameterBinder.NullParameterBinder + else ParameterBinder(value, f(value)) + } + } + + implicit val intParameterBinderFactory: ParameterBinderFactory[Int] = ParameterBinderFactory { v => (ps, idx) => ps.setInt(idx, v) } + implicit val stringParameterBinderFactory: ParameterBinderFactory[String] = ParameterBinderFactory { v => (ps, idx) => ps.setString(idx, v) } + implicit val sqlArrayParameterBinderFactory: ParameterBinderFactory[java.sql.Array] = ParameterBinderFactory { v => (ps, idx) => ps.setArray(idx, v) } + implicit val bigDecimalParameterBinderFactory: ParameterBinderFactory[BigDecimal] = ParameterBinderFactory { v => (ps, idx) => ps.setBigDecimal(idx, v.bigDecimal) } + implicit val booleanParameterBinderFactory: ParameterBinderFactory[Boolean] = ParameterBinderFactory { v => (ps, idx) => ps.setBoolean(idx, v) } + implicit val byteParameterBinderFactory: ParameterBinderFactory[Byte] = ParameterBinderFactory { v => (ps, idx) => ps.setByte(idx, v) } + implicit val sqlDateParameterBinderFactory: ParameterBinderFactory[java.sql.Date] = ParameterBinderFactory { v => (ps, idx) => ps.setDate(idx, v) } + implicit val doubleParameterBinderFactory: ParameterBinderFactory[Double] = ParameterBinderFactory { v => (ps, idx) => ps.setDouble(idx, v) } + implicit val floatParameterBinderFactory: ParameterBinderFactory[Float] = ParameterBinderFactory { v => (ps, idx) => ps.setFloat(idx, v) } + implicit val longParameterBinderFactory: ParameterBinderFactory[Long] = ParameterBinderFactory { v => (ps, idx) => ps.setLong(idx, v) } + implicit val shortParameterBinderFactory: ParameterBinderFactory[Short] = ParameterBinderFactory { v => (ps, idx) => ps.setShort(idx, v) } + implicit val sqlXmlParameterBinderFactory: ParameterBinderFactory[java.sql.SQLXML] = ParameterBinderFactory { v => (ps, idx) => ps.setSQLXML(idx, v) } + implicit val sqlTimeParameterBinderFactory: ParameterBinderFactory[java.sql.Time] = ParameterBinderFactory { v => (ps, idx) => ps.setTime(idx, v) } + implicit val sqlTimestampParameterBinderFactory: ParameterBinderFactory[java.sql.Timestamp] = ParameterBinderFactory { v => (ps, idx) => ps.setTimestamp(idx, v) } + implicit val urlParameterBinderFactory: ParameterBinderFactory[java.net.URL] = ParameterBinderFactory { v => (ps, idx) => ps.setURL(idx, v) } + implicit val utilDateParameterBinderFactory: ParameterBinderFactory[java.util.Date] = sqlTimestampParameterBinderFactory.xmap(identity, _.toSqlTimestamp) + implicit val jodaDateTimeParameterBinderFactory: ParameterBinderFactory[org.joda.time.DateTime] = utilDateParameterBinderFactory.xmap(_.toJodaDateTime, _.toDate) + implicit val jodaLocalDateTimeParameterBinderFactory: ParameterBinderFactory[org.joda.time.LocalDateTime] = utilDateParameterBinderFactory.xmap(_.toJodaLocalDateTime, _.toDate) + implicit val jodaLocalDateParameterBinderFactory: ParameterBinderFactory[org.joda.time.LocalDate] = sqlDateParameterBinderFactory.xmap(_.toJodaLocalDate, _.toDate.toSqlDate) + implicit val jodaLocalTimeParameterBinderFactory: ParameterBinderFactory[org.joda.time.LocalTime] = sqlTimeParameterBinderFactory.xmap(_.toJodaLocalTime, _.toSqlTime) + implicit val inputStreamParameterBinderFactory: ParameterBinderFactory[InputStream] = ParameterBinderFactory { v => (ps, idx) => ps.setBinaryStream(idx, v) } + implicit val nullParameterBinderFactory: ParameterBinderFactory[Null] = new ParameterBinderFactory[Null] { def apply(value: Null) = ParameterBinder.NullParameterBinder } + implicit val noneParameterBinderFactory: ParameterBinderFactory[None.type] = new ParameterBinderFactory[None.type] { def apply(value: None.type) = ParameterBinder.NullParameterBinder } + implicit val sqlSyntaxParameterBinderFactory: ParameterBinderFactory[SQLSyntax] = new ParameterBinderFactory[SQLSyntax] { def apply(value: SQLSyntax) = SQLSyntaxParameterBinder(value) } + implicit val optionalSqlSyntaxParameterBinderFactory: ParameterBinderFactory[Option[SQLSyntax]] = sqlSyntaxParameterBinderFactory.xmap(Option.apply, _ getOrElse SQLSyntax.empty) + +} + +trait LowPriorityImplicitsParameterBinderFactory1 extends LowPriorityImplicitsParameterBinderFactory0 { + + implicit def optionalParameterBinderFactory[A](implicit ev: ParameterBinderFactory[A]): ParameterBinderFactory[Option[A]] = new ParameterBinderFactory[Option[A]] { + def apply(value: Option[A]): ParameterBinderWithValue[Option[A]] = { + if (value == null) ParameterBinder.NullParameterBinder + else value.fold[ParameterBinderWithValue[Option[A]]](ParameterBinder.NullParameterBinder)(v => ev(v).map(Option.apply)) + } + } + +} + +trait LowPriorityImplicitsParameterBinderFactory0 { + implicit def anyParameterBinderFactory[A]: ParameterBinderFactory[A] = macro ParameterBinderFactoryMacro.any[A] +} + +private[scalikejdbc] object ParameterBinderFactoryMacro { + + def any[A: c.WeakTypeTag](c: Context): c.Expr[ParameterBinderFactory[A]] = { + import c.universe._ + val A = weakTypeTag[A].tpe + val expr = A.toString match { + case "java.time.ZonedDateTime" | "java.time.OffsetDateTime" => + q"scalikejdbc.ParameterBinderFactory[$A] { v => (ps, idx) => ps.setTimestamp(idx, java.sql.Timestamp.from(v.toInstant)) }" + case "java.time.LocalDateTime" => + q"scalikejdbc.ParameterBinderFactory[$A] { v => (ps, idx) => ps.setTimestamp(idx, java.sql.Timestamp.valueOf(v)) }" + case "java.time.LocalDate" => + q"scalikejdbc.ParameterBinderFactory[$A] { v => (ps, idx) => ps.setDate(idx, java.sql.Date.valueOf(v)) }" + case "java.time.LocalTime" => + q"scalikejdbc.ParameterBinderFactory[$A] { v => (ps, idx) => ps.setTime(idx, java.sql.Time.valueOf(v)) }" + case _ => + c.abort(c.enclosingPosition, s"Could not find an implicit value of the ParameterBinderFactory[$A].") + } + c.Expr[ParameterBinderFactory[A]](expr) + } + +} diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/RelationalSQL.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/RelationalSQL.scala index 559f9c45c..ff5237f55 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/RelationalSQL.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/RelationalSQL.scala @@ -49,28 +49,28 @@ trait AllOutputDecisionsUnsupported[Z, E <: scalikejdbc.WithExtractor] extends S * Endpoint of one-to-x APIs */ class OneToXSQL[A, E <: WithExtractor, Z]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, override val rawParameters: Seq[Any] )(one: WrappedResultSet => A) - extends SQL[Z, E](statement, parameters)(SQL.noExtractor[Z]("one-to-one/one-to-many operation needs toOne(RS => Option[B]).map((A,B) => A) or toMany(RS => Option[B]).map((A,Seq(B) => A).")) + extends SQL[Z, E](statement, rawParameters)(SQL.noExtractor[Z]("one-to-one/one-to-many operation needs toOne(RS => Option[B]).map((A,B) => A) or toMany(RS => Option[B]).map((A,Seq(B) => A).")) with AllOutputDecisionsUnsupported[Z, E] { def toOne[B](to: WrappedResultSet => B): OneToOneSQL[A, B, E, Z] = { - new OneToOneSQL(statement, parameters)(one)(to.andThen((b: B) => Option(b)))((a, b) => a.asInstanceOf[Z]) + new OneToOneSQL(statement, rawParameters)(one)(to.andThen((b: B) => Option(b)))((a, b) => a.asInstanceOf[Z]) } def toOptionalOne[B](to: WrappedResultSet => Option[B]): OneToOneSQL[A, B, E, Z] = { - new OneToOneSQL(statement, parameters)(one)(to)((a, b) => a.asInstanceOf[Z]) + new OneToOneSQL(statement, rawParameters)(one)(to)((a, b) => a.asInstanceOf[Z]) } def toMany[B](to: WrappedResultSet => Option[B]): OneToManySQL[A, B, E, Z] = { - new OneToManySQL(statement, parameters)(one)(to)((a, bs) => a.asInstanceOf[Z]) + new OneToManySQL(statement, rawParameters)(one)(to)((a, bs) => a.asInstanceOf[Z]) } def toManies[B1, B2]( to1: WrappedResultSet => Option[B1], to2: WrappedResultSet => Option[B2] ): OneToManies2SQL[A, B1, B2, E, Z] = { - new OneToManies2SQL(statement, parameters)(one)(to1, to2)((a, bs1, bs2) => a.asInstanceOf[Z]) + new OneToManies2SQL(statement, rawParameters)(one)(to1, to2)((a, bs1, bs2) => a.asInstanceOf[Z]) } def toManies[B1, B2, B3]( @@ -78,7 +78,7 @@ class OneToXSQL[A, E <: WithExtractor, Z]( to2: WrappedResultSet => Option[B2], to3: WrappedResultSet => Option[B3] ): OneToManies3SQL[A, B1, B2, B3, E, Z] = { - new OneToManies3SQL(statement, parameters)(one)(to1, to2, to3)((a, bs1, bs2, bs3) => a.asInstanceOf[Z]) + new OneToManies3SQL(statement, rawParameters)(one)(to1, to2, to3)((a, bs1, bs2, bs3) => a.asInstanceOf[Z]) } def toManies[B1, B2, B3, B4]( @@ -87,7 +87,7 @@ class OneToXSQL[A, E <: WithExtractor, Z]( to3: WrappedResultSet => Option[B3], to4: WrappedResultSet => Option[B4] ): OneToManies4SQL[A, B1, B2, B3, B4, E, Z] = { - new OneToManies4SQL(statement, parameters)(one)(to1, to2, to3, to4)((a, bs1, bs2, bs3, bs4) => a.asInstanceOf[Z]) + new OneToManies4SQL(statement, rawParameters)(one)(to1, to2, to3, to4)((a, bs1, bs2, bs3, bs4) => a.asInstanceOf[Z]) } def toManies[B1, B2, B3, B4, B5]( @@ -97,7 +97,7 @@ class OneToXSQL[A, E <: WithExtractor, Z]( to4: WrappedResultSet => Option[B4], to5: WrappedResultSet => Option[B5] ): OneToManies5SQL[A, B1, B2, B3, B4, B5, E, Z] = { - new OneToManies5SQL(statement, parameters)(one)(to1, to2, to3, to4, to5)((a, bs1, bs2, bs3, bs4, bs5) => a.asInstanceOf[Z]) + new OneToManies5SQL(statement, rawParameters)(one)(to1, to2, to3, to4, to5)((a, bs1, bs2, bs3, bs4, bs5) => a.asInstanceOf[Z]) } def toManies[B1, B2, B3, B4, B5, B6]( @@ -108,7 +108,7 @@ class OneToXSQL[A, E <: WithExtractor, Z]( to5: WrappedResultSet => Option[B5], to6: WrappedResultSet => Option[B6] ): OneToManies6SQL[A, B1, B2, B3, B4, B5, B6, E, Z] = { - new OneToManies6SQL(statement, parameters)(one)(to1, to2, to3, to4, to5, to6)((a, bs1, bs2, bs3, bs4, bs5, bs6) => a.asInstanceOf[Z]) + new OneToManies6SQL(statement, rawParameters)(one)(to1, to2, to3, to4, to5, to6)((a, bs1, bs2, bs3, bs4, bs5, bs6) => a.asInstanceOf[Z]) } def toManies[B1, B2, B3, B4, B5, B6, B7]( @@ -120,7 +120,7 @@ class OneToXSQL[A, E <: WithExtractor, Z]( to6: WrappedResultSet => Option[B6], to7: WrappedResultSet => Option[B7] ): OneToManies7SQL[A, B1, B2, B3, B4, B5, B6, B7, E, Z] = { - new OneToManies7SQL(statement, parameters)(one)(to1, to2, to3, to4, to5, to6, to7)((a, bs1, bs2, bs3, bs4, bs5, bs6, bs7) => a.asInstanceOf[Z]) + new OneToManies7SQL(statement, rawParameters)(one)(to1, to2, to3, to4, to5, to6, to7)((a, bs1, bs2, bs3, bs4, bs5, bs6, bs7) => a.asInstanceOf[Z]) } def toManies[B1, B2, B3, B4, B5, B6, B7, B8]( @@ -133,7 +133,7 @@ class OneToXSQL[A, E <: WithExtractor, Z]( to7: WrappedResultSet => Option[B7], to8: WrappedResultSet => Option[B8] ): OneToManies8SQL[A, B1, B2, B3, B4, B5, B6, B7, B8, E, Z] = { - new OneToManies8SQL(statement, parameters)(one)(to1, to2, to3, to4, to5, to6, to7, to8)((a, bs1, bs2, bs3, bs4, bs5, bs6, bs7, bs8) => a.asInstanceOf[Z]) + new OneToManies8SQL(statement, rawParameters)(one)(to1, to2, to3, to4, to5, to6, to7, to8)((a, bs1, bs2, bs3, bs4, bs5, bs6, bs7, bs8) => a.asInstanceOf[Z]) } def toManies[B1, B2, B3, B4, B5, B6, B7, B8, B9]( @@ -147,7 +147,7 @@ class OneToXSQL[A, E <: WithExtractor, Z]( to8: WrappedResultSet => Option[B8], to9: WrappedResultSet => Option[B9] ): OneToManies9SQL[A, B1, B2, B3, B4, B5, B6, B7, B8, B9, E, Z] = { - new OneToManies9SQL(statement, parameters)(one)(to1, to2, to3, to4, to5, to6, to7, to8, to9)((a, bs1, bs2, bs3, bs4, bs5, bs6, bs7, bs8, bs9) => a.asInstanceOf[Z]) + new OneToManies9SQL(statement, rawParameters)(one)(to1, to2, to3, to4, to5, to6, to7, to8, to9)((a, bs1, bs2, bs3, bs4, bs5, bs6, bs7, bs8, bs9) => a.asInstanceOf[Z]) } } diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/SQL.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/SQL.scala index 75393829f..7968404ea 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/SQL.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/SQL.scala @@ -5,7 +5,7 @@ import scala.language.higherKinds import scala.collection.generic.CanBuildFrom /** - * SQL abstraction's companion object. + * SQL abstraction's companion object * * {{{ * ConnectionPool.singleton("jdbc:...","user","password") @@ -133,16 +133,21 @@ private[scalikejdbc] trait Extractor[A] { * SQL abstraction. * * @param statement SQL template - * @param parameters parameters + * @param rawParameters parameters * @param f extractor function * @tparam A return type */ abstract class SQL[A, E <: WithExtractor]( val statement: String, - val parameters: Seq[Any] + private[scalikejdbc] val rawParameters: Seq[Any] )(f: WrappedResultSet => A) extends Extractor[A] { + final lazy val parameters: Seq[Any] = rawParameters.map { + case ParameterBinder(v) => v + case x => x + } + override def extractor: (WrappedResultSet) => A = f private[this] var _fetchSize: Option[Int] = None @@ -160,6 +165,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Set fetchSize for this query. + * * @param fetchSize fetch size * @return this */ @@ -175,6 +181,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Appends tags to this SQL object. + * * @param tags tags * @return this */ @@ -185,18 +192,21 @@ abstract class SQL[A, E <: WithExtractor]( /** * Returns tags for this SQL object. + * * @return tags */ def tags: Seq[String] = this._tags.toSeq /** * Returns fetchSize for this query. + * * @return fetch size */ def fetchSize: Option[Int] = this._fetchSize /** * Set queryTimeout for this query. + * * @param seconds query timeout seconds * @return this */ @@ -212,6 +222,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Returns queryTimeout for this query. + * * @return query timeout seconds */ def queryTimeout: Option[Int] = this._queryTimeout @@ -219,10 +230,11 @@ abstract class SQL[A, E <: WithExtractor]( /** * Returns One-to-X API builder. */ - def one[Z](f: (WrappedResultSet) => A): OneToXSQL[A, E, Z] = new OneToXSQL[A, E, Z](statement, parameters)(f) + def one[Z](f: (WrappedResultSet) => A): OneToXSQL[A, E, Z] = new OneToXSQL[A, E, Z](statement, rawParameters)(f) /** * Binds parameters to SQL template in order. + * * @param parameters parameters * @return SQL instance */ @@ -232,6 +244,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Binds named parameters to SQL template. + * * @param parametersByName named parameters * @return SQL instance */ @@ -242,6 +255,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Binds parameters for batch + * * @param parameters parameters * @return SQL for batch */ @@ -251,6 +265,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Binds parameters for batch + * * @param parameters parameters * @return SQL for batch */ @@ -260,6 +275,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Binds parameters for batch + * * @param generatedKeyName generated key name * @param parameters parameters * @return SQL for batch @@ -270,6 +286,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Binds parameters for batch + * * @param parameters parameters * @return SQL for batch */ @@ -283,11 +300,12 @@ abstract class SQL[A, E <: WithExtractor]( /** * Apply the operation to all elements of result set + * * @param op operation */ def foreach(op: WrappedResultSet => Unit)(implicit session: DBSession): Unit = { val f: DBSession => Unit = - _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).foreach(statement, parameters: _*)(op) + _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).foreach(statement, rawParameters: _*)(op) // format: OFF session match { case AutoSession => DB.autoCommit(f) @@ -301,12 +319,13 @@ abstract class SQL[A, E <: WithExtractor]( /** * folding into one value + * * @param z initial value * @param op operation */ def foldLeft[A](z: A)(op: (A, WrappedResultSet) => A)(implicit session: DBSession): A = { val f: DBSession => A = - _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).foldLeft(statement, parameters: _*)(z)(op) + _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).foldLeft(statement, rawParameters: _*)(z)(op) // format: OFF session match { case AutoSession => DB.autoCommit(f) @@ -320,6 +339,7 @@ abstract class SQL[A, E <: WithExtractor]( /** * Maps values from each [[scalikejdbc.WrappedResultSet]] object. + * * @param f extractor function * @tparam A return type * @return SQL instance @@ -330,111 +350,126 @@ abstract class SQL[A, E <: WithExtractor]( /** * Maps values as a Map value from each [[scalikejdbc.WrappedResultSet]] object. + * * @return SQL instance */ def toMap(): SQL[Map[String, Any], HasExtractor] = map(_.toMap) /** * Same as #single. + * * @return SQL instance */ def toOption(): SQLToOption[A, E] = { - new SQLToOptionImpl[A, E](statement, parameters)(extractor)(isSingle = true) + new SQLToOptionImpl[A, E](statement, rawParameters)(extractor)(isSingle = true) .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout) } /** * Set execution type as single. + * * @return SQL instance */ def single(): SQLToOption[A, E] = toOption() /** * Same as #first. + * * @return SQL instance */ def headOption(): SQLToOption[A, E] = { - new SQLToOptionImpl[A, E](statement, parameters)(extractor)(isSingle = false) + new SQLToOptionImpl[A, E](statement, rawParameters)(extractor)(isSingle = false) .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout) } /** * Set execution type as first. + * * @return SQL instance */ def first(): SQLToOption[A, E] = headOption() /** * Same as #list + * * @return SQL instance */ def toList(): SQLToList[A, E] = { - new SQLToListImpl[A, E](statement, parameters)(extractor) + new SQLToListImpl[A, E](statement, rawParameters)(extractor) .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout) } /** * Set execution type as list. + * * @return SQL instance */ def list(): SQLToList[A, E] = toList() /** * Same as #collection + * * @return SQL instance */ def toCollection: SQLToCollection[A, E] = { - new SQLToCollectionImpl[A, E](statement, parameters)(extractor) + new SQLToCollectionImpl[A, E](statement, rawParameters)(extractor) .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout) } /** * Set execution type as collection. + * * @return SQL instance */ def collection: SQLToCollection[A, E] = toCollection /** * Same as #traversable. + * * @return SQL instance */ def toTraversable(): SQLToTraversable[A, E] = { - new SQLToTraversableImpl[A, E](statement, parameters)(extractor) + new SQLToTraversableImpl[A, E](statement, rawParameters)(extractor) .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout) } /** * Set execution type as traversable. + * * @return SQL instance */ def traversable(): SQLToTraversable[A, E] = toTraversable() /** * Set execution type as execute + * * @return SQL instance */ def execute(): SQLExecution = { - new SQLExecution(statement, parameters, tags)((stmt: PreparedStatement) => {})((stmt: PreparedStatement) => {}) + new SQLExecution(statement, rawParameters, tags)((stmt: PreparedStatement) => {})((stmt: PreparedStatement) => {}) } /** * Set execution type as execute with filters + * * @param before before filter * @param after after filter * @return SQL instance */ def executeWithFilters(before: (PreparedStatement) => Unit, after: (PreparedStatement) => Unit) = { - new SQLExecution(statement, parameters, tags)(before)(after) + new SQLExecution(statement, rawParameters, tags)(before)(after) } /** * Set execution type as executeUpdate + * * @return SQL instance */ def executeUpdate(): SQLUpdate = update() /** * Set execution type as executeUpdate with filters + * * @param before before filter * @param after after filter * @return SQL instance @@ -445,24 +480,27 @@ abstract class SQL[A, E <: WithExtractor]( /** * Set execution type as executeUpdate + * * @return SQL instance */ def update(): SQLUpdate = { - new SQLUpdate(statement, parameters, tags)((stmt: PreparedStatement) => {})((stmt: PreparedStatement) => {}) + new SQLUpdate(statement, rawParameters, tags)((stmt: PreparedStatement) => {})((stmt: PreparedStatement) => {}) } /** * Set execution type as executeUpdate with filters + * * @param before before filter * @param after after filter * @return SQL instance */ def updateWithFilters(before: (PreparedStatement) => Unit, after: (PreparedStatement) => Unit): SQLUpdate = { - new SQLUpdate(statement, parameters, tags)(before)(after) + new SQLUpdate(statement, rawParameters, tags)(before)(after) } /** * Set execution type as updateAndReturnGeneratedKey + * * @return SQL instance */ def updateAndReturnGeneratedKey(): SQLUpdateWithGeneratedKey = { @@ -470,25 +508,26 @@ abstract class SQL[A, E <: WithExtractor]( } def updateAndReturnGeneratedKey(name: String): SQLUpdateWithGeneratedKey = { - new SQLUpdateWithGeneratedKey(statement, parameters, this.tags)(name) + new SQLUpdateWithGeneratedKey(statement, rawParameters, this.tags)(name) } def updateAndReturnGeneratedKey(index: Int): SQLUpdateWithGeneratedKey = { - new SQLUpdateWithGeneratedKey(statement, parameters, this.tags)(index) + new SQLUpdateWithGeneratedKey(statement, rawParameters, this.tags)(index) } def stripMargin(marginChar: Char): SQL[A, E] = - withStatementAndParameters(statement.stripMargin(marginChar), parameters) + withStatementAndParameters(statement.stripMargin(marginChar), rawParameters) .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout) def stripMargin: SQL[A, E] = - withStatementAndParameters(statement.stripMargin, parameters) + withStatementAndParameters(statement.stripMargin, rawParameters) .fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout) } /** * SQL which execute java.sql.Statement#executeBatch(). + * * @param statement SQL template * @param parameters parameters */ @@ -533,6 +572,7 @@ class SQLBatchWithGeneratedKey(val statement: String, val parameters: Seq[Seq[An /** * SQL which execute java.sql.Statement#execute(). + * * @param statement SQL template * @param parameters parameters * @param before before filter @@ -561,6 +601,7 @@ class SQLExecution(val statement: String, val parameters: Seq[Any], val tags: Se /** * SQL which execute java.sql.Statement#executeUpdate(). + * * @param statement SQL template * @param parameters parameters * @param before before filter @@ -589,6 +630,7 @@ class SQLUpdate(val statement: String, val parameters: Seq[Any], val tags: Seq[S /** * SQL which execute java.sql.Statement#executeUpdate() and get generated key value. + * * @param statement SQL template * @param parameters parameters */ @@ -611,9 +653,10 @@ class SQLUpdateWithGeneratedKey(val statement: String, val parameters: Seq[Any], trait SQLToResult[A, E <: WithExtractor, C[_]] extends SQL[A, E] with Extractor[A] { import GeneralizedTypeConstraintsForWithExtractor._ + def result[AA](f: WrappedResultSet => AA, session: DBSession): C[AA] val statement: String - val parameters: Seq[Any] + private[scalikejdbc] val rawParameters: Seq[Any] def apply()( implicit session: DBSession, @@ -635,29 +678,31 @@ trait SQLToResult[A, E <: WithExtractor, C[_]] extends SQL[A, E] with Extractor[ /** * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.collection.Traversable value. + * * @tparam A return type */ trait SQLToTraversable[A, E <: WithExtractor] extends SQLToResult[A, E, Traversable] { def result[AA](f: WrappedResultSet => AA, session: DBSession): Traversable[AA] = { - session.traversable[AA](statement, parameters: _*)(f) + session.traversable[AA](statement, rawParameters: _*)(f) } } /** * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.collection.Traversable value. + * * @param statement SQL template - * @param parameters parameters + * @param rawParameters parameters * @param extractor extractor function * @tparam A return type */ class SQLToTraversableImpl[A, E <: WithExtractor]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, private[scalikejdbc] override val rawParameters: Seq[Any] )( override val extractor: WrappedResultSet => A ) - extends SQL[A, E](statement, parameters)(extractor) + extends SQL[A, E](statement, rawParameters)(extractor) with SQLToTraversable[A, E] { override protected def withParameters(params: Seq[Any]): SQLToResult[A, E, Traversable] = { @@ -669,22 +714,23 @@ class SQLToTraversableImpl[A, E <: WithExtractor]( } override protected def withExtractor[B](f: WrappedResultSet => B): SQLToResult[B, HasExtractor, Traversable] = { - new SQLToTraversableImpl[B, HasExtractor](statement, parameters)(f) + new SQLToTraversableImpl[B, HasExtractor](statement, rawParameters)(f) } } /** * SQL to Collection + * * @tparam A return type * @tparam E extractor settings */ trait SQLToCollection[A, E <: WithExtractor] extends SQL[A, E] with Extractor[A] { import GeneralizedTypeConstraintsForWithExtractor._ val statement: String - val parameters: Seq[Any] + private[scalikejdbc] val rawParameters: Seq[Any] def apply[C[_]]()(implicit session: DBSession, context: ConnectionPoolContext = NoConnectionPoolContext, hasExtractor: ThisSQL =:= SQLWithExtractor, cbf: CanBuildFrom[Nothing, A, C[A]]): C[A] = { - val f: DBSession => C[A] = _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).collection[A, C](statement, parameters: _*)(extractor) + val f: DBSession => C[A] = _.fetchSize(fetchSize).tags(tags: _*).queryTimeout(queryTimeout).collection[A, C](statement, rawParameters: _*)(extractor) // format: OFF session match { case AutoSession | ReadOnlyAutoSession => DB.readOnly(f) @@ -698,11 +744,11 @@ trait SQLToCollection[A, E <: WithExtractor] extends SQL[A, E] with Extractor[A] } class SQLToCollectionImpl[A, E <: WithExtractor]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, private[scalikejdbc] override val rawParameters: Seq[Any] )( override val extractor: WrappedResultSet => A ) - extends SQL[A, E](statement, parameters)(extractor) + extends SQL[A, E](statement, rawParameters)(extractor) with SQLToCollection[A, E] { override protected def withParameters(params: Seq[Any]): SQLToCollection[A, E] = { @@ -714,37 +760,39 @@ class SQLToCollectionImpl[A, E <: WithExtractor]( } override protected def withExtractor[B](f: WrappedResultSet => B): SQLToCollection[B, HasExtractor] = { - new SQLToCollectionImpl[B, HasExtractor](statement, parameters)(f) + new SQLToCollectionImpl[B, HasExtractor](statement, rawParameters)(f) } } /** * SQL to List + * * @tparam A return type * @tparam E extractor settings */ trait SQLToList[A, E <: WithExtractor] extends SQLToResult[A, E, List] { def result[AA](f: WrappedResultSet => AA, session: DBSession): List[AA] = { - session.list[AA](statement, parameters: _*)(f) + session.list[AA](statement, rawParameters: _*)(f) } } /** * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.collection.immutable.List value. + * * @param statement SQL template - * @param parameters parameters + * @param rawParameters parameters * @param extractor extractor function * @tparam A return type */ class SQLToListImpl[A, E <: WithExtractor]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, private[scalikejdbc] override val rawParameters: Seq[Any] )( override val extractor: WrappedResultSet => A ) - extends SQL[A, E](statement, parameters)(extractor) + extends SQL[A, E](statement, rawParameters)(extractor) with SQLToList[A, E] { override protected def withParameters(params: Seq[Any]): SQLToList[A, E] = { @@ -756,13 +804,14 @@ class SQLToListImpl[A, E <: WithExtractor]( } override protected def withExtractor[B](f: WrappedResultSet => B): SQLToList[B, HasExtractor] = { - new SQLToListImpl[B, HasExtractor](statement, parameters)(f) + new SQLToListImpl[B, HasExtractor](statement, rawParameters)(f) } } /** * SQL to Option + * * @tparam A return type * @tparam E extractor settings */ @@ -772,9 +821,9 @@ trait SQLToOption[A, E <: WithExtractor] extends SQLToResult[A, E, Option] { def result[AA](f: WrappedResultSet => AA, session: DBSession): Option[AA] = { if (isSingle) { - session.single[AA](statement, parameters: _*)(f) + session.single[AA](statement, rawParameters: _*)(f) } else { - session.first[AA](statement, parameters: _*)(f) + session.first[AA](statement, rawParameters: _*)(f) } } @@ -782,17 +831,18 @@ trait SQLToOption[A, E <: WithExtractor] extends SQLToResult[A, E, Option] { /** * SQL which execute java.sql.Statement#executeQuery() and returns the result as scala.Option value. + * * @param statement SQL template - * @param parameters parameters + * @param rawParameters parameters * @param extractor extractor function * @tparam A return type */ class SQLToOptionImpl[A, E <: WithExtractor]( - override val statement: String, override val parameters: Seq[Any] + override val statement: String, private[scalikejdbc] override val rawParameters: Seq[Any] )( override val extractor: WrappedResultSet => A )(protected val isSingle: Boolean = true) - extends SQL[A, E](statement, parameters)(extractor) + extends SQL[A, E](statement, rawParameters)(extractor) with SQLToOption[A, E] { override protected def withParameters(params: Seq[Any]): SQLToOption[A, E] = { @@ -804,7 +854,7 @@ class SQLToOptionImpl[A, E <: WithExtractor]( } override protected def withExtractor[B](f: WrappedResultSet => B): SQLToOption[B, HasExtractor] = { - new SQLToOptionImpl[B, HasExtractor](statement, parameters)(f)(isSingle) + new SQLToOptionImpl[B, HasExtractor](statement, rawParameters)(f)(isSingle) } } diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/SQLInterpolationString.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/SQLInterpolationString.scala index 5b98eb9b7..c9996f6fc 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/SQLInterpolationString.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/SQLInterpolationString.scala @@ -13,7 +13,7 @@ class SQLInterpolationString(val s: StringContext) extends AnyVal { def sql[A](params: Any*): SQL[A, NoExtractor] = { val syntax = sqls(params: _*) - SQL[A](syntax.value).bind(syntax.parameters: _*) + SQL[A](syntax.value).bind(syntax.rawParameters: _*) } def sqls(params: Any*): SQLSyntax = { @@ -40,10 +40,12 @@ class SQLInterpolationString(val s: StringContext) extends AnyVal { case _: String => sb += '?' case t: Traversable[_] => t.map { case SQLSyntax(s, _) => s + case SQLSyntaxParameterBinder(SQLSyntax(s, _)) => s case _ => "?" }.addString(sb, ", ") // e.g. in clause case LastParameter => sb case SQLSyntax(s, _) => sb ++= s + case SQLSyntaxParameterBinder(SQLSyntax(s, _)) => sb ++= s case _ => sb += '?' } @@ -51,9 +53,11 @@ class SQLInterpolationString(val s: StringContext) extends AnyVal { case (b, s: String) => b += s case (b, t: Traversable[_]) => t.foldLeft(b) { case (b, SQLSyntax(_, params)) => b ++= params + case (b, SQLSyntaxParameterBinder(SQLSyntax(_, params))) => b ++= params case (b, e) => b += e } case (b, SQLSyntax(_, params)) => b ++= params + case (b, SQLSyntaxParameterBinder(SQLSyntax(_, params))) => b ++= params case (b, n) => b += n }.result() diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/StatementExecutor.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/StatementExecutor.scala index da1b84b14..d3544a2b8 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/StatementExecutor.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/StatementExecutor.scala @@ -67,7 +67,7 @@ case class StatementExecutor( for ((param, idx) <- paramsWithIndices; i = idx + 1) { param match { case null => underlying.setObject(i, null) - case binder: ParameterBinder[_] => binder.apply(underlying, i) + case binder: ParameterBinder => binder(underlying, i) case p: java.sql.Array => underlying.setArray(i, p) case p: BigDecimal => underlying.setBigDecimal(i, p.bigDecimal) case p: BigInt => underlying.setBigDecimal(i, new java.math.BigDecimal(p.bigInteger)) @@ -135,6 +135,7 @@ case class StatementExecutor( def toPrintable(param: Any): String = { def normalize(param: Any): Any = { param match { + case ParameterBinder(v) => normalize(v) case None => null case Some(p) => normalize(p) case p: String => p diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/UnixTimeInMillisConverterImplicits.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/UnixTimeInMillisConverterImplicits.scala index 59825a3a9..0a157a1e1 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/UnixTimeInMillisConverterImplicits.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/UnixTimeInMillisConverterImplicits.scala @@ -22,3 +22,5 @@ trait UnixTimeInMillisConverterImplicits { implicit def convertLocalTimeToConverter(t: LocalTime): LocalTimeConverter = new LocalTimeConverter(t) } + +object UnixTimeInMillisConverterImplicits extends UnixTimeInMillisConverterImplicits diff --git a/scalikejdbc-core/src/main/scala/scalikejdbc/interpolation/SQLSyntax.scala b/scalikejdbc-core/src/main/scala/scalikejdbc/interpolation/SQLSyntax.scala index 198f57778..bf4f1bffe 100644 --- a/scalikejdbc-core/src/main/scala/scalikejdbc/interpolation/SQLSyntax.scala +++ b/scalikejdbc-core/src/main/scala/scalikejdbc/interpolation/SQLSyntax.scala @@ -1,4 +1,5 @@ -package scalikejdbc.interpolation +package scalikejdbc +package interpolation /** * Value as a part of SQL syntax. @@ -8,23 +9,28 @@ package scalikejdbc.interpolation * Note: The constructor should NOT be used by library users at the considerable risk of SQL injection vulnerability. * [[https://github.com/scalikejdbc/scalikejdbc/issues/116]] */ -class SQLSyntax private[scalikejdbc] (val value: String, val parameters: Seq[Any] = Vector()) { +class SQLSyntax private[scalikejdbc] (val value: String, private[scalikejdbc] val rawParameters: Seq[Any] = Vector()) { import Implicits._ import SQLSyntax._ override def equals(that: Any): Boolean = { if (that.isInstanceOf[SQLSyntax]) { val thatSqls = that.asInstanceOf[SQLSyntax] - value == thatSqls.value && parameters == thatSqls.parameters + value == thatSqls.value && rawParameters == thatSqls.rawParameters } else { false } } - override def hashCode: Int = (value, parameters).## + override def hashCode: Int = (value, rawParameters).## override def toString(): String = s"SQLSyntax(value: ${value}, parameters: ${parameters})" + lazy val parameters: Seq[Any] = rawParameters.map { + case ParameterBinder(v) => v + case x => x + } + def append(syntax: SQLSyntax): SQLSyntax = sqls"${this} ${syntax}" def groupBy(columns: SQLSyntax*): SQLSyntax = { @@ -54,125 +60,126 @@ class SQLSyntax private[scalikejdbc] (val value: String, val parameters: Seq[Any def roundBracket(inner: SQLSyntax) = sqls"$this ($inner)" - def eq(column: SQLSyntax, value: Any): SQLSyntax = { + def eq[A](column: SQLSyntax, value: A)(implicit ev: ParameterBinderFactory[A]): SQLSyntax = { value match { case null | None => sqls"${this} ${column} is null" - case _ => sqls"${this} ${column} = ${value}" + case _ => sqls"${this} ${column} = ${ev(value)}" } } - def ne(column: SQLSyntax, value: Any): SQLSyntax = { + def ne[A](column: SQLSyntax, value: A)(implicit ev: ParameterBinderFactory[A]): SQLSyntax = { value match { case null | None => sqls"${this} ${column} is not null" - case _ => sqls"${this} ${column} <> ${value}" + case _ => sqls"${this} ${column} <> ${ev(value)}" } } - def gt(column: SQLSyntax, value: Any): SQLSyntax = sqls"${this} ${column} > ${value}" - def ge(column: SQLSyntax, value: Any): SQLSyntax = sqls"${this} ${column} >= ${value}" - def lt(column: SQLSyntax, value: Any): SQLSyntax = sqls"${this} ${column} < ${value}" - def le(column: SQLSyntax, value: Any): SQLSyntax = sqls"${this} ${column} <= ${value}" + def gt[A](column: SQLSyntax, value: A)(implicit ev: ParameterBinderFactory[A]): SQLSyntax = sqls"${this} ${column} > ${ev(value)}" + def ge[A](column: SQLSyntax, value: A)(implicit ev: ParameterBinderFactory[A]): SQLSyntax = sqls"${this} ${column} >= ${ev(value)}" + def lt[A](column: SQLSyntax, value: A)(implicit ev: ParameterBinderFactory[A]): SQLSyntax = sqls"${this} ${column} < ${ev(value)}" + def le[A](column: SQLSyntax, value: A)(implicit ev: ParameterBinderFactory[A]): SQLSyntax = sqls"${this} ${column} <= ${ev(value)}" def isNull(column: SQLSyntax): SQLSyntax = sqls"${this} ${column} is null" def isNotNull(column: SQLSyntax): SQLSyntax = sqls"${this} ${column} is not null" - def between(column: SQLSyntax, a: Any, b: Any): SQLSyntax = sqls"${this} ${column} between ${a} and ${b}" - def notBetween(column: SQLSyntax, a: Any, b: Any): SQLSyntax = sqls"${this} ${column} not between ${a} and ${b}" - def in(column: SQLSyntax, values: Seq[Any]): SQLSyntax = { + def between[A, B, C](a: A, b: B, c: C)(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B], ev3: ParameterBinderFactory[C]): SQLSyntax = sqls"${this} ${ev1(a)} between ${ev2(b)} and ${ev3(c)}" + def notBetween[A, B, C](a: A, b: B, c: C)(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B], ev3: ParameterBinderFactory[C]): SQLSyntax = sqls"${this} not ${ev1(a)} between ${ev2(b)} and ${ev3(c)}" + + def in[A](column: SQLSyntax, values: Seq[A])(implicit ev: ParameterBinderFactory[A]): SQLSyntax = { if (values.isEmpty) { sqls"${this} FALSE" } else { - sqls"${this} ${column} in (${values})" + sqls"${this} ${column} in (${values.map(ev.apply)})" } } - def notIn(column: SQLSyntax, values: Seq[Any]): SQLSyntax = { + def notIn[A](column: SQLSyntax, values: Seq[A])(implicit ev: ParameterBinderFactory[A]): SQLSyntax = { if (values.isEmpty) { sqls"${this} TRUE" } else { - sqls"${this} ${column} not in (${values})" + sqls"${this} ${column} not in (${values.map(ev.apply)})" } } def in(column: SQLSyntax, subQuery: SQLSyntax): SQLSyntax = sqls"${this} ${column} in (${subQuery})" def notIn(column: SQLSyntax, subQuery: SQLSyntax): SQLSyntax = sqls"${this} ${column} not in (${subQuery})" - def in(columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any)]): SQLSyntax = { + def in[A, B](columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B)])(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B]): SQLSyntax = { if (valueSeqs.isEmpty) { sqls"${this} FALSE" } else { val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value})") - val values = csv(valueSeqs.map { case (v1, v2) => sqls"($v1, $v2)" }: _*) + val values = csv(valueSeqs.map { case (v1, v2) => sqls"(${ev1(v1)}, ${ev2(v2)})" }: _*) val inClause = sqls"${column} in (${values})" sqls"${this} ${inClause}" } } - def notIn(columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any)]): SQLSyntax = { + def notIn[A, B](columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B)])(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B]): SQLSyntax = { if (valueSeqs.isEmpty) { sqls"${this} TRUE" } else { val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value})") - val values = csv(valueSeqs.map { case (v1, v2) => sqls"($v1, $v2)" }: _*) + val values = csv(valueSeqs.map { case (v1, v2) => sqls"(${ev1(v1)}, ${ev2(v2)})" }: _*) val inClause = sqls"${column} not in (${values})" sqls"${this} ${inClause}" } } - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any)]): SQLSyntax = { + def in[A, B, C](columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C)])(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B], ev3: ParameterBinderFactory[C]): SQLSyntax = { if (valueSeqs.isEmpty) { sqls"${this} FALSE" } else { val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value})") - val values = csv(valueSeqs.map { case (v1, v2, v3) => sqls"($v1, $v2, $v3)" }: _*) + val values = csv(valueSeqs.map { case (v1, v2, v3) => sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)})" }: _*) val inClause = sqls"${column} in (${values})" sqls"${this} ${inClause}" } } - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any)]): SQLSyntax = { + def notIn[A, B, C](columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C)])(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B], ev3: ParameterBinderFactory[C]): SQLSyntax = { if (valueSeqs.isEmpty) { sqls"${this} TRUE" } else { val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value})") - val values = csv(valueSeqs.map { case (v1, v2, v3) => sqls"($v1, $v2, $v3)" }: _*) + val values = csv(valueSeqs.map { case (v1, v2, v3) => sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)})" }: _*) val inClause = sqls"${column} not in (${values})" sqls"${this} ${inClause}" } } - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any)]): SQLSyntax = { + def in[A, B, C, D](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C, D)])(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B], ev3: ParameterBinderFactory[C], ev4: ParameterBinderFactory[D]): SQLSyntax = { if (valueSeqs.isEmpty) { sqls"${this} FALSE" } else { val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value}, ${columns._4.value})") - val values = csv(valueSeqs.map { case (v1, v2, v3, v4) => sqls"($v1, $v2, $v3, $v4)" }: _*) + val values = csv(valueSeqs.map { case (v1, v2, v3, v4) => sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)}, ${ev4(v4)})" }: _*) val inClause = sqls"${column} in (${values})" sqls"${this} ${inClause}" } } - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any)]): SQLSyntax = { + def notIn[A, B, C, D](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C, D)])(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B], ev3: ParameterBinderFactory[C], ev4: ParameterBinderFactory[D]): SQLSyntax = { if (valueSeqs.isEmpty) { sqls"${this} TRUE" } else { val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value}, ${columns._4.value})") - val values = csv(valueSeqs.map { case (v1, v2, v3, v4) => sqls"($v1, $v2, $v3, $v4)" }: _*) + val values = csv(valueSeqs.map { case (v1, v2, v3, v4) => sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)}, ${ev4(v4)})" }: _*) val inClause = sqls"${column} not in (${values})" sqls"${this} ${inClause}" } } - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any, Any)]): SQLSyntax = { + def in[A, B, C, D, E](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C, D, E)])(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B], ev3: ParameterBinderFactory[C], ev4: ParameterBinderFactory[D], ev5: ParameterBinderFactory[E]): SQLSyntax = { if (valueSeqs.isEmpty) { sqls"${this} FALSE" } else { val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value}, ${columns._4.value}, ${columns._5.value})") - val values = csv(valueSeqs.map { case (v1, v2, v3, v4, v5) => sqls"($v1, $v2, $v3, $v4, $v5)" }: _*) + val values = csv(valueSeqs.map { case (v1, v2, v3, v4, v5) => sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)}, ${ev4(v4)}, ${ev5(v5)})" }: _*) val inClause = sqls"${column} in (${values})" sqls"${this} ${inClause}" } } - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any, Any)]): SQLSyntax = { + def notIn[A, B, C, D, E](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C, D, E)])(implicit ev1: ParameterBinderFactory[A], ev2: ParameterBinderFactory[B], ev3: ParameterBinderFactory[C], ev4: ParameterBinderFactory[D], ev5: ParameterBinderFactory[E]): SQLSyntax = { if (valueSeqs.isEmpty) { sqls"${this} TRUE" } else { val column = SQLSyntax(s"(${columns._1.value}, ${columns._2.value}, ${columns._3.value}, ${columns._4.value}, ${columns._5.value})") - val values = csv(valueSeqs.map { case (v1, v2, v3, v4, v5) => sqls"($v1, $v2, $v3, $v4, $v5)" }: _*) + val values = csv(valueSeqs.map { case (v1, v2, v3, v4, v5) => sqls"(${ev1(v1)}, ${ev2(v2)}, ${ev3(v3)}, ${ev4(v4)}, ${ev5(v5)})" }: _*) val inClause = sqls"${column} not in (${values})" sqls"${this} ${inClause}" } @@ -184,10 +191,11 @@ class SQLSyntax private[scalikejdbc] (val value: String, val parameters: Seq[Any def exists(sqlPart: SQLSyntax): SQLSyntax = sqls"${this} exists (${sqlPart})" def notExists(sqlPart: SQLSyntax): SQLSyntax = sqls"${this} not exists (${sqlPart})" - def stripMargin: SQLSyntax = new SQLSyntax(value.stripMargin, parameters) + def stripMargin: SQLSyntax = new SQLSyntax(value.stripMargin, rawParameters) - def stripMargin(marginChar: Char): SQLSyntax = new SQLSyntax(value.stripMargin(marginChar), parameters) + def stripMargin(marginChar: Char): SQLSyntax = new SQLSyntax(value.stripMargin(marginChar), rawParameters) + def -> [A](value: A)(implicit ev: ParameterBinderFactory[A]): (SQLSyntax, ParameterBinder) = (this, ev(value)) } /** @@ -222,7 +230,7 @@ object SQLSyntax { */ def createUnsafely(value: String, parameters: Seq[Any] = Nil): SQLSyntax = apply(value, parameters) - def unapply(syntax: SQLSyntax): Option[(String, Seq[Any])] = Some((syntax.value, syntax.parameters)) + def unapply(syntax: SQLSyntax): Option[(String, Seq[Any])] = Some((syntax.value, syntax.rawParameters)) def join(parts: Seq[SQLSyntax], delimiter: SQLSyntax, spaceBeforeDelimier: Boolean = true): SQLSyntax = { val sep = if (spaceBeforeDelimier) { @@ -231,11 +239,11 @@ object SQLSyntax { s"${delimiter.value} " } val value = parts.collect { case p if p.value.nonEmpty => p.value }.mkString(sep) - val parameters = if (delimiter.parameters.isEmpty) { - parts.flatMap(_.parameters) + val parameters = if (delimiter.rawParameters.isEmpty) { + parts.flatMap(_.rawParameters) } else { - parts.tail.foldLeft(parts.headOption.fold(Seq.empty[Any])(_.parameters)) { - case (params, part) => params ++ delimiter.parameters ++ part.parameters + parts.tail.foldLeft(parts.headOption.fold(Seq.empty[Any])(_.rawParameters)) { + case (params, part) => params ++ delimiter.rawParameters ++ part.rawParameters } } apply(value, parameters) @@ -272,32 +280,32 @@ object SQLSyntax { def or(sqlPart: SQLSyntax): SQLSyntax = SQLSyntax.empty.or(sqlPart) def or(orOpt: Option[SQLSyntax]): SQLSyntax = SQLSyntax.empty.or(orOpt) - def eq(column: SQLSyntax, value: Any): SQLSyntax = SQLSyntax.empty.eq(column, value) - def ne(column: SQLSyntax, value: Any): SQLSyntax = SQLSyntax.empty.ne(column, value) - def gt(column: SQLSyntax, value: Any): SQLSyntax = SQLSyntax.empty.gt(column, value) - def ge(column: SQLSyntax, value: Any): SQLSyntax = SQLSyntax.empty.ge(column, value) - def lt(column: SQLSyntax, value: Any): SQLSyntax = SQLSyntax.empty.lt(column, value) - def le(column: SQLSyntax, value: Any): SQLSyntax = SQLSyntax.empty.le(column, value) + def eq[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax = SQLSyntax.empty.eq(column, value) + def ne[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax = SQLSyntax.empty.ne(column, value) + def gt[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax = SQLSyntax.empty.gt(column, value) + def ge[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax = SQLSyntax.empty.ge(column, value) + def lt[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax = SQLSyntax.empty.lt(column, value) + def le[A: ParameterBinderFactory](column: SQLSyntax, value: A): SQLSyntax = SQLSyntax.empty.le(column, value) def isNull(column: SQLSyntax): SQLSyntax = SQLSyntax.empty.isNull(column) def isNotNull(column: SQLSyntax): SQLSyntax = SQLSyntax.empty.isNotNull(column) - def between(column: SQLSyntax, a: Any, b: Any): SQLSyntax = SQLSyntax.empty.between(column, a, b) - def notBetween(column: SQLSyntax, a: Any, b: Any): SQLSyntax = SQLSyntax.empty.notBetween(column, a, b) + def between[A: ParameterBinderFactory, B: ParameterBinderFactory](column: SQLSyntax, a: A, b: B): SQLSyntax = SQLSyntax.empty.between(column, a, b) + def notBetween[A: ParameterBinderFactory, B: ParameterBinderFactory](column: SQLSyntax, a: A, b: B): SQLSyntax = SQLSyntax.empty.notBetween(column, a, b) - def in(column: SQLSyntax, values: Seq[Any]): SQLSyntax = SQLSyntax.empty.in(column, values) - def notIn(column: SQLSyntax, values: Seq[Any]): SQLSyntax = SQLSyntax.empty.notIn(column, values) + def in[A: ParameterBinderFactory](column: SQLSyntax, values: Seq[A]): SQLSyntax = SQLSyntax.empty.in(column, values) + def notIn[A: ParameterBinderFactory](column: SQLSyntax, values: Seq[A]): SQLSyntax = SQLSyntax.empty.notIn(column, values) - def in(columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any)]): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs) - def notIn(columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any)]): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs) + def in[A: ParameterBinderFactory, B: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B)]): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs) + def notIn[A: ParameterBinderFactory, B: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B)]): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs) - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any)]): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs) - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any)]): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs) + def in[A: ParameterBinderFactory, B: ParameterBinderFactory, C: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C)]): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs) + def notIn[A: ParameterBinderFactory, B: ParameterBinderFactory, C: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C)]): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs) - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any)]): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs) - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any)]): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs) + def in[A: ParameterBinderFactory, B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C, D)]): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs) + def notIn[A: ParameterBinderFactory, B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C, D)]): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs) - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any, Any)]): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs) - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any, Any)]): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs) + def in[A: ParameterBinderFactory, B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory, E: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C, D, E)]): SQLSyntax = SQLSyntax.empty.in(columns, valueSeqs) + def notIn[A: ParameterBinderFactory, B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory, E: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(A, B, C, D, E)]): SQLSyntax = SQLSyntax.empty.notIn(columns, valueSeqs) def in(column: SQLSyntax, subQuery: SQLSyntax): SQLSyntax = SQLSyntax.empty.in(column, subQuery) def notIn(column: SQLSyntax, subQuery: SQLSyntax): SQLSyntax = SQLSyntax.empty.notIn(column, subQuery) diff --git a/scalikejdbc-syntax-support-macro/src/main/scala2.10/blackbox.scala b/scalikejdbc-core/src/main/scala2.10/blackbox.scala similarity index 100% rename from scalikejdbc-syntax-support-macro/src/main/scala2.10/blackbox.scala rename to scalikejdbc-core/src/main/scala2.10/blackbox.scala diff --git a/scalikejdbc-syntax-support-macro/src/main/scala2.10/scalikejdbc/MacroCompatible.scala b/scalikejdbc-core/src/main/scala2.10/scalikejdbc/MacroCompatible.scala similarity index 100% rename from scalikejdbc-syntax-support-macro/src/main/scala2.10/scalikejdbc/MacroCompatible.scala rename to scalikejdbc-core/src/main/scala2.10/scalikejdbc/MacroCompatible.scala diff --git a/scalikejdbc-syntax-support-macro/src/main/scala2.11/scalikejdbc/MacroCompatible.scala b/scalikejdbc-core/src/main/scala2.11/scalikejdbc/MacroCompatible.scala similarity index 100% rename from scalikejdbc-syntax-support-macro/src/main/scala2.11/scalikejdbc/MacroCompatible.scala rename to scalikejdbc-core/src/main/scala2.11/scalikejdbc/MacroCompatible.scala diff --git a/scalikejdbc-core/src/test/scala/scalikejdbc/DBSessionSpec.scala b/scalikejdbc-core/src/test/scala/scalikejdbc/DBSessionSpec.scala index a0d20f989..e03449114 100644 --- a/scalikejdbc-core/src/test/scala/scalikejdbc/DBSessionSpec.scala +++ b/scalikejdbc-core/src/test/scala/scalikejdbc/DBSessionSpec.scala @@ -1050,7 +1050,7 @@ class DBSessionSpec extends FlatSpec with Matchers with BeforeAndAfter with Sett try { SQL("drop table dbsession_work_with_parameter_binder").execute.apply() } catch { - case e: Exception => e.printStackTrace + case e: Exception => e.printStackTrace() } } } diff --git a/scalikejdbc-core/src/test/scala/scalikejdbc/interpolation/SQLSyntaxSpec.scala b/scalikejdbc-core/src/test/scala/scalikejdbc/interpolation/SQLSyntaxSpec.scala index 8125d24a7..3f0b8f6a0 100644 --- a/scalikejdbc-core/src/test/scala/scalikejdbc/interpolation/SQLSyntaxSpec.scala +++ b/scalikejdbc-core/src/test/scala/scalikejdbc/interpolation/SQLSyntaxSpec.scala @@ -1,4 +1,5 @@ -package scalikejdbc.interpolation +package scalikejdbc +package interpolation import org.scalatest._ import org.joda.time.DateTime @@ -125,7 +126,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { } it should "have #in with empty" in { - val s = SQLSyntax.in(sqls"id", Seq()) + val s = SQLSyntax.in(sqls"id", Seq[Int]()) s.value should equal(" FALSE") s.parameters should equal(Seq()) } @@ -137,7 +138,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { } it should "have #in for 2 columns with empty" in { - val s = SQLSyntax.in((sqls"id", sqls"name"), Seq()) + val s = SQLSyntax.in((sqls"id", sqls"name"), Seq[(Int, String)]()) s.value should equal(" FALSE") s.parameters should equal(Seq()) } @@ -148,7 +149,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { s.parameters should equal(Seq(1, "Alice", 20, 2, "Bob", 23)) } it should "have #in for 3 columns with empty" in { - val s = SQLSyntax.in((sqls"id", sqls"name", sqls"age"), Seq()) + val s = SQLSyntax.in((sqls"id", sqls"name", sqls"age"), Seq[(Int, String, Int)]()) s.value should equal(" FALSE") s.parameters should equal(Seq()) } @@ -158,7 +159,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { s.parameters should equal(Seq(1, "Alice", 20, "bar", 2, "Bob", 23, "baz")) } it should "have #in for 4 columns with empty" in { - val s = SQLSyntax.in((sqls"id", sqls"name", sqls"age", sqls"foo"), Seq()) + val s = SQLSyntax.in((sqls"id", sqls"name", sqls"age", sqls"foo"), Seq[(Int, String, Int, String)]()) s.value should equal(" FALSE") s.parameters should equal(Seq()) } @@ -170,7 +171,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { } it should "have #in for 5 columns with empty" in { val time = DateTime.now - val s = SQLSyntax.in((sqls"id", sqls"name", sqls"age", sqls"foo", sqls"created_at"), Seq()) + val s = SQLSyntax.in((sqls"id", sqls"name", sqls"age", sqls"foo", sqls"created_at"), Seq[(Int, String, Int, String, DateTime)]()) s.value should equal(" FALSE") s.parameters should equal(Seq()) } @@ -181,7 +182,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { s.parameters should equal(Seq(1, 2, 3)) } it should "have #notIn woth empty" in { - val s = SQLSyntax.notIn(sqls"id", Seq()) + val s = SQLSyntax.notIn(sqls"id", Seq[Int]()) s.value should equal(" TRUE") s.parameters should equal(Seq()) } @@ -192,7 +193,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { s.parameters should equal(Seq(1, "Alice", 2, "Bob")) } it should "have #notIn for 2 columns with empty" in { - val s = SQLSyntax.notIn((sqls"id", sqls"name"), Seq()) + val s = SQLSyntax.notIn((sqls"id", sqls"name"), Seq[(Int, String)]()) s.value should equal(" TRUE") s.parameters should equal(Seq()) } @@ -202,7 +203,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { s.parameters should equal(Seq(1, "Alice", 20, 2, "Bob", 23)) } it should "have #notIn for 3 columns with empty" in { - val s = SQLSyntax.notIn((sqls"id", sqls"name", sqls"age"), Seq()) + val s = SQLSyntax.notIn((sqls"id", sqls"name", sqls"age"), Seq[(Int, String, Int)]()) s.value should equal(" TRUE") s.parameters should equal(Seq()) } @@ -212,7 +213,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { s.parameters should equal(Seq(1, "Alice", 20, "bar", 2, "Bob", 23, "baz")) } it should "have #notIn for 4 columns with empty" in { - val s = SQLSyntax.notIn((sqls"id", sqls"name", sqls"age", sqls"foo"), Seq()) + val s = SQLSyntax.notIn((sqls"id", sqls"name", sqls"age", sqls"foo"), Seq[(Int, String, Int, String)]()) s.value should equal(" TRUE") s.parameters should equal(Seq()) } @@ -224,7 +225,7 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { } it should "have #notIn for 5 columns with empty" in { val time = DateTime.now - val s = SQLSyntax.notIn((sqls"id", sqls"name", sqls"age", sqls"foo", sqls"created_at"), Seq()) + val s = SQLSyntax.notIn((sqls"id", sqls"name", sqls"age", sqls"foo", sqls"created_at"), Seq[(Int, String, Int, String, DateTime)]()) s.value should equal(" TRUE") s.parameters should equal(Seq()) } @@ -474,13 +475,13 @@ class SQLSyntaxSpec extends FlatSpec with Matchers { it should "strip margin by stripMargin" in { sqls"""a = |${123} - |""".stripMargin.value should equal("a =\n?\n") + |""".stripMargin.value.replaceAll("""\\r\\n""", """\n""") should equal("a =\n?\n") } it should "strip margin specifying marginChar by stripMargin" in { sql"""a = /${123} - /""".stripMargin('/').statement should equal("a =\n?\n") + /""".stripMargin('/').statement.replaceAll("""\\r\\n""", """\n""") should equal("a =\n?\n") } } diff --git a/scalikejdbc-interpolation/src/main/scala/scalikejdbc/QueryDSLFeature.scala b/scalikejdbc-interpolation/src/main/scala/scalikejdbc/QueryDSLFeature.scala index 5c14f473c..c69b29be3 100644 --- a/scalikejdbc-interpolation/src/main/scala/scalikejdbc/QueryDSLFeature.scala +++ b/scalikejdbc-interpolation/src/main/scala/scalikejdbc/QueryDSLFeature.scala @@ -216,50 +216,50 @@ trait QueryDSLFeature { self: SQLInterpolationFeature with SQLSyntaxSupportFeatu def not: ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} not") - def eq(column: SQLSyntax, value: Any): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.eq(column, value)}") - def ne(column: SQLSyntax, value: Any): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.ne(column, value)}") - def gt(column: SQLSyntax, value: Any): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.gt(column, value)}") - def ge(column: SQLSyntax, value: Any): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.ge(column, value)}") - def lt(column: SQLSyntax, value: Any): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.lt(column, value)}") - def le(column: SQLSyntax, value: Any): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.le(column, value)}") + def eq[B: ParameterBinderFactory](column: SQLSyntax, value: B): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.eq(column, value)}") + def ne[B: ParameterBinderFactory](column: SQLSyntax, value: B): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.ne(column, value)}") + def gt[B: ParameterBinderFactory](column: SQLSyntax, value: B): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.gt(column, value)}") + def ge[B: ParameterBinderFactory](column: SQLSyntax, value: B): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.ge(column, value)}") + def lt[B: ParameterBinderFactory](column: SQLSyntax, value: B): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.lt(column, value)}") + def le[B: ParameterBinderFactory](column: SQLSyntax, value: B): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.le(column, value)}") def isNull(column: SQLSyntax): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.isNull(column)}") def isNotNull(column: SQLSyntax): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.isNotNull(column)}") - def between(column: SQLSyntax, a: Any, b: Any): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.between(column, a, b)}") - def notBetween(column: SQLSyntax, a: Any, b: Any): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.notBetween(column, a, b)}") + def between[B: ParameterBinderFactory, C: ParameterBinderFactory](column: SQLSyntax, a: B, b: C): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.between(column, a, b)}") + def notBetween[B: ParameterBinderFactory, C: ParameterBinderFactory](column: SQLSyntax, a: B, b: C): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.notBetween(column, a, b)}") - def in(column: SQLSyntax, values: Seq[Any]): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.in(column, values)}") + def in[B: ParameterBinderFactory](column: SQLSyntax, values: Seq[B]): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.in(column, values)}") def in(column: SQLSyntax, subQuery: SQLBuilder[_]): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${column} in (${subQuery.toSQLSyntax})") - def in(columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any)]): ConditionSQLBuilder[A] = { + def in[B: ParameterBinderFactory, C: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(B, C)]): ConditionSQLBuilder[A] = { ConditionSQLBuilder[A](sqls"${sql} ${sqls.in(columns, valueSeqs)}") } - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any)]): ConditionSQLBuilder[A] = { + def in[B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(B, C, D)]): ConditionSQLBuilder[A] = { ConditionSQLBuilder[A](sqls"${sql} ${sqls.in(columns, valueSeqs)}") } - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any)]): ConditionSQLBuilder[A] = { + def in[B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory, E: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(B, C, D, E)]): ConditionSQLBuilder[A] = { ConditionSQLBuilder[A](sqls"${sql} ${sqls.in(columns, valueSeqs)}") } - def in(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any, Any)]): ConditionSQLBuilder[A] = { + def in[B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory, E: ParameterBinderFactory, G: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(B, C, D, E, G)]): ConditionSQLBuilder[A] = { ConditionSQLBuilder[A](sqls"${sql} ${sqls.in(columns, valueSeqs)}") } - def notIn(column: SQLSyntax, values: Seq[Any]): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.notIn(column, values)}") + def notIn[B: ParameterBinderFactory](column: SQLSyntax, values: Seq[B]): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${sqls.notIn(column, values)}") def notIn(column: SQLSyntax, subQuery: SQLBuilder[_]): ConditionSQLBuilder[A] = ConditionSQLBuilder[A](sqls"${sql} ${column} not in (${subQuery.toSQLSyntax})") - def notIn(columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any)]): ConditionSQLBuilder[A] = { + def notIn[B: ParameterBinderFactory, C: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax), valueSeqs: Seq[(B, C)]): ConditionSQLBuilder[A] = { ConditionSQLBuilder[A](sqls"${sql} ${sqls.notIn(columns, valueSeqs)}") } - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any)]): ConditionSQLBuilder[A] = { + def notIn[B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(B, C, D)]): ConditionSQLBuilder[A] = { ConditionSQLBuilder[A](sqls"${sql} ${sqls.notIn(columns, valueSeqs)}") } - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any)]): ConditionSQLBuilder[A] = { + def notIn[B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory, E: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(B, C, D, E)]): ConditionSQLBuilder[A] = { ConditionSQLBuilder[A](sqls"${sql} ${sqls.notIn(columns, valueSeqs)}") } - def notIn(columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(Any, Any, Any, Any, Any)]): ConditionSQLBuilder[A] = { + def notIn[B: ParameterBinderFactory, C: ParameterBinderFactory, D: ParameterBinderFactory, E: ParameterBinderFactory, G: ParameterBinderFactory](columns: (SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax, SQLSyntax), valueSeqs: Seq[(B, C, D, E, G)]): ConditionSQLBuilder[A] = { ConditionSQLBuilder[A](sqls"${sql} ${sqls.notIn(columns, valueSeqs)}") } @@ -479,7 +479,7 @@ trait QueryDSLFeature { self: SQLInterpolationFeature with SQLSyntaxSupportFeatu val vs = sqls.csv(values.map(v => sqls"${v}"): _*) this.copy(sql = sqls"${sql} values (${vs})") } - def namedValues(columnsAndValues: (SQLSyntax, Any)*): InsertSQLBuilder = { + def namedValues(columnsAndValues: (SQLSyntax, ParameterBinder)*): InsertSQLBuilder = { val (cs, vs) = columnsAndValues.unzip columns(cs: _*).values(vs: _*) } @@ -512,7 +512,7 @@ trait QueryDSLFeature { self: SQLInterpolationFeature with SQLSyntaxSupportFeatu with WhereSQLBuilder[UpdateOperation] { def set(sqlPart: SQLSyntax): UpdateSQLBuilder = this.copy(sql = sqls"${sql} set ${sqlPart}") - def set(tuples: (SQLSyntax, Any)*): UpdateSQLBuilder = set(sqls.csv(tuples.map(each => sqls"${each._1} = ${each._2}"): _*)) + def set(tuples: (SQLSyntax, ParameterBinder)*): UpdateSQLBuilder = set(sqls.csv(tuples.map(each => sqls"${each._1} = ${each._2}"): _*)) override def append(part: SQLSyntax): UpdateSQLBuilder = this.copy(sql = sqls"${sql} ${part}") } diff --git a/scalikejdbc-interpolation/src/main/scala/scalikejdbc/SQLSyntaxSupportFeature.scala b/scalikejdbc-interpolation/src/main/scala/scalikejdbc/SQLSyntaxSupportFeature.scala index 617d5f4bd..bfd16820a 100644 --- a/scalikejdbc-interpolation/src/main/scala/scalikejdbc/SQLSyntaxSupportFeature.scala +++ b/scalikejdbc-interpolation/src/main/scala/scalikejdbc/SQLSyntaxSupportFeature.scala @@ -252,7 +252,7 @@ trait SQLSyntaxSupportFeature { self: SQLInterpolationFeature => * }}} */ def as(provider: QuerySQLSyntaxProvider[SQLSyntaxSupport[A], A]): TableAsAliasSQLSyntax = { - if (tableName == provider.tableAliasName) { TableAsAliasSQLSyntax(table.value, table.parameters, Some(provider)) } + if (tableName == provider.tableAliasName) { TableAsAliasSQLSyntax(table.value, table.rawParameters, Some(provider)) } else { TableAsAliasSQLSyntax(tableNameWithSchema + " " + provider.tableAliasName, Nil, Some(provider)) } } } @@ -262,17 +262,17 @@ trait SQLSyntaxSupportFeature { self: SQLInterpolationFeature => */ case class TableAsAliasSQLSyntax private[scalikejdbc] ( override val value: String, - override val parameters: Seq[Any] = Vector(), + override val rawParameters: Seq[Any] = Vector(), resultAllProvider: Option[ResultAllProvider] = None - ) extends SQLSyntax(value, parameters) + ) extends SQLSyntax(value, rawParameters) /** * Table definition part SQLSyntax */ case class TableDefSQLSyntax private[scalikejdbc] ( - override val value: String, override val parameters: Seq[Any] = Vector() + override val value: String, override val rawParameters: Seq[Any] = Vector() ) - extends SQLSyntax(value, parameters) + extends SQLSyntax(value, rawParameters) /** * SQLSyntax Provider @@ -556,7 +556,7 @@ trait SQLSyntaxSupportFeature { self: SQLInterpolationFeature => def column(name: String): SQLSyntax = cachedColumns.getOrElse(name, { columns.find(_.value.equalsIgnoreCase(name)).map { c => val name = toAliasName(c.value, support) - SQLSyntax(s"${syntax.value} as ${name}${delimiterForResultName}${aliasName}", syntax.parameters) + SQLSyntax(s"${syntax.value} as ${name}${delimiterForResultName}${aliasName}", syntax.rawParameters) }.getOrElse(throw notFoundInColumns(aliasName, name)) }) } diff --git a/scalikejdbc-interpolation/src/test/scala/scalikejdbc/QueryInterfaceSpec.scala b/scalikejdbc-interpolation/src/test/scala/scalikejdbc/QueryInterfaceSpec.scala index dd4aeda68..1584de705 100644 --- a/scalikejdbc-interpolation/src/test/scala/scalikejdbc/QueryInterfaceSpec.scala +++ b/scalikejdbc-interpolation/src/test/scala/scalikejdbc/QueryInterfaceSpec.scala @@ -11,9 +11,14 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL behavior of "QueryInterface" + case class Price(value: Int) + object Price { + implicit val bider: TypeBinder[Price] = TypeBinder.int.map(Price.apply) + implicit val unbinder: ParameterBinderFactory[Price] = ParameterBinderFactory.intParameterBinderFactory.xmap(Price.apply, _.value) + } case class Order(id: Int, productId: Int, accountId: Option[Int], createdAt: DateTime, product: Option[Product] = None, account: Option[Account] = None) case class LegacyProduct(id: Option[Int], name: Option[String], price: Int) - case class Product(id: Int, name: Option[String], price: Int) + case class Product(id: Int, name: Option[String], price: Price) case class Account(id: Int, name: Option[String]) case class SchemaExample(id: Int) @@ -36,7 +41,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL object Product extends SQLSyntaxSupport[Product] { override val tableName = "qi_products" def apply(p: SyntaxProvider[Product])(rs: WrappedResultSet): Product = apply(p.resultName)(rs) - def apply(p: ResultName[Product])(rs: WrappedResultSet): Product = new Product(rs.int(p.id), rs.stringOpt(p.name), rs.int(p.price)) + def apply(p: ResultName[Product])(rs: WrappedResultSet): Product = new Product(rs.int(p.id), rs.stringOpt(p.name), rs.get(p.price)) } object Account extends SQLSyntaxSupport[Account] { override val tableName = "qi_accounts" @@ -107,7 +112,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL insert.into(LegacyProduct).values(Some(100), "Old Cookie", 40), insert.into(LegacyProduct).values(Some(200), "Green Tea", 20), insert.into(Product).values(1, "Cookie", 120), - insert.into(Product).values(2, "Tea", 80), + insert.into(Product).namedValues(pc.id -> 2, pc.name -> "Tea", pc.price -> Price(80)), insert.into(Product).select(_.from(LegacyProduct as lp).where.isNotNull(lp.id)), insert.into(Product).select(lp.id, lp.name, lp.price)(_.from(LegacyProduct as lp).where.isNotNull(lp.id)), insert.into(Product).selectAll(lp)(_.from(LegacyProduct as lp).where.isNotNull(lp.id)), @@ -125,6 +130,14 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL insert.into(Order).values(26, 2, None, DateTime.now) ).foreach(sql => applyUpdate(sql)) + { + val p = Product.syntax("p") + val products = withSQL { + select.from(Product as p).orderBy(p.id) + }.map(Product(p)).list.apply() + assert(products === List(Product(1, Some("Cookie"), Price(120)), Product(2, Some("Tea"), Price(80)))) + } + // batch insert val batchInsertQuery = withSQL { insert into Product columns (pc.id, pc.name, pc.price) values (sqls.?, sqls.?, sqls.?) @@ -195,7 +208,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL findByOptionalAccountName(Option.empty).size should be(11) { - val (productId, accountId) = (Some(1), None) + val (productId, accountId): (Option[Int], Option[Int]) = (Some(1), None) val ids = withSQL { select(o.result.id).from(Order as o) .where(sqls.toAndConditionOpt( @@ -220,7 +233,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL } { - val (id1, id2) = (Some(1), None) + val (id1, id2): (Option[Int], Option[Int]) = (Some(1), None) val ids = withSQL { select(o.result.id).from(Order as o) .where @@ -329,7 +342,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL { val inClauseResults = withSQL { select.from(Order as o) - .where.in(o.id, Seq()) + .where.in(o.id, Seq[Int]()) .orderBy(o.id) }.map(Order(o)).list.apply() inClauseResults.map(_.id) should equal(List()) @@ -345,7 +358,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL { val notInClauseResults = withSQL { select.from(Order as o) - .where.notIn(o.id, Seq()) + .where.notIn(o.id, Seq[Int]()) .orderBy(o.id) }.map(Order(o)).list.apply() notInClauseResults.map(_.id) should equal(List(11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 26)) @@ -353,7 +366,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL { val notInClauseResults = withSQL { select.from(Order as o) - .where.not.in(o.id, Seq()) + .where.not.in(o.id, Seq[Int]()) .orderBy(o.id) }.map(Order(o)).list.apply() notInClauseResults.map(_.id) should equal(List(11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 26)) @@ -402,7 +415,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL { val inClauseResults = withSQL { select.from(Order as o) - .where.in((o.id, o.productId), Seq()) + .where.in((o.id, o.productId), Seq[(Int, Int)]()) .orderBy(o.id) }.map(Order(o)).list.apply() inClauseResults.map(_.id) should equal(List()) @@ -418,7 +431,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL { val notInClauseResults = withSQL { select.from(Order as o) - .where.notIn((o.id, o.productId), Seq()) + .where.notIn((o.id, o.productId), Seq[(Int, Int)]()) .orderBy(o.id) }.map(Order(o)).list.apply() notInClauseResults.map(_.id) should equal(List(11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 26)) @@ -426,7 +439,7 @@ class QueryInterfaceSpec extends FlatSpec with Matchers with DBSettings with SQL { val notInClauseResults = withSQL { select.from(Order as o) - .where.not.in((o.id, o.productId), Seq()) + .where.not.in((o.id, o.productId), Seq[(Int, Int)]()) .orderBy(o.id) }.map(Order(o)).list.apply() notInClauseResults.map(_.id) should equal(List(11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 26)) diff --git a/scalikejdbc-mapper-generator-core/src/main/scala/scalikejdbc/mapper/CodeGenerator.scala b/scalikejdbc-mapper-generator-core/src/main/scala/scalikejdbc/mapper/CodeGenerator.scala index 3c786471c..064329d3d 100644 --- a/scalikejdbc-mapper-generator-core/src/main/scala/scalikejdbc/mapper/CodeGenerator.scala +++ b/scalikejdbc-mapper-generator-core/src/main/scala/scalikejdbc/mapper/CodeGenerator.scala @@ -323,7 +323,11 @@ class CodeGenerator(table: Table, specifiedClassName: Option[String] = None)(imp createColumns.map(c => 4.indent + "${" + c.nameInScala + "}").mkString(comma + eol) case GeneratorTemplate.queryDsl => // id, name - createColumns.map(c => 4.indent + c.nameInScala).mkString(comma + eol) + createColumns.map { c => + 4.indent + + (if (c.isAny) "(column." + c.nameInScala + ", ParameterBinder(" + c.nameInScala + ", (ps, i) => ps.setObject(i, " + c.nameInScala + ")))" + else "column." + c.nameInScala + " -> " + c.nameInScala) + }.mkString(comma + eol) } // def create( @@ -338,20 +342,20 @@ class CodeGenerator(table: Table, specifiedClassName: Option[String] = None)(imp "sql\"\"\"" + eol + 3.indent + "insert into ${" + className + ".table} (" case GeneratorTemplate.queryDsl => // withSQL { insert.into(User).columns( - "withSQL {" + eol + 3.indent + "insert.into(" + className + ").columns(" + "withSQL {" + eol + 3.indent + "insert.into(" + className + ").namedValues(" }) + eol + (config.template match { case GeneratorTemplate.interpolation => createColumns.map(c => 4.indent + "${" + "column." + c.nameInScala + "}").mkString(comma + eol) + eol + 3.indent + ") values (" + eol case GeneratorTemplate.queryDsl => - createColumns.map(c => 4.indent + "column." + c.nameInScala).mkString(comma + eol) + eol + 3.indent + ").values(" + eol + "" }) + placeHolderPart + eol + 3.indent + ")" + eol + (config.template match { case GeneratorTemplate.interpolation => - 3.indent + "\"\"\"" + table.autoIncrementColumns.headOption.map(_ => ".updateAndReturnGeneratedKey.apply()").getOrElse(".update.apply()") + 3.indent + "\"\"\"" + (if (table.autoIncrementColumns.nonEmpty) ".updateAndReturnGeneratedKey.apply()" else ".update.apply()") case GeneratorTemplate.queryDsl => - 2.indent + table.autoIncrementColumns.headOption.map(_ => "}.updateAndReturnGeneratedKey.apply()").getOrElse("}.update.apply()") + 2.indent + (if (table.autoIncrementColumns.nonEmpty) "}.updateAndReturnGeneratedKey.apply()" else "}.update.apply()") }) + eol + eol + @@ -404,8 +408,11 @@ class CodeGenerator(table: Table, specifiedClassName: Option[String] = None)(imp // ${column.id} = ${entity.id}, ${column.name} = ${entity.name} allColumns.map(c => 4.indent + "${column." + c.nameInScala + "} = ${entity." + c.nameInScala + "}").mkString(comma + eol) case GeneratorTemplate.queryDsl => - // column.id -> entity.id, column.name -> entity.name - allColumns.map(c => 4.indent + "column." + c.nameInScala + " -> entity." + c.nameInScala).mkString(comma + eol) + allColumns.map { c => + 4.indent + + (if (c.isAny) "(column." + c.nameInScala + ", ParameterBinder(entity." + c.nameInScala + ", (ps, i) => ps.setObject(i, entity." + c.nameInScala + ")))" + else "column." + c.nameInScala + " -> entity." + c.nameInScala) + }.mkString(comma + eol) } val wherePart = config.template match {