Permalink
Browse files

[jdbc-scala] Regex extractor for executed statement, usable with rich…

… pattern:

```scala
e/* : QueryExecution */ match {
  case ~(ExecutedStatement("^SELECT"), (sql, parameters)) => // ...
}
```
  • Loading branch information...
cchantep
cchantep committed Feb 27, 2014
1 parent 7bc9b54 commit 65a7e3313ab5681b77cba502c0aa945ef645f68c
@@ -8,7 +8,6 @@ import java.sql.{ Connection ⇒ SqlConnection, Statement, SQLWarning }
import scala.language.implicitConversions
import scala.collection.JavaConversions
import acolyte.ParameterMetaData.ParameterDef
import acolyte.StatementHandler.Parameter
import acolyte.AbstractCompositeHandler.{ QueryHandler, UpdateHandler }
import acolyte.RowList.{ Column Col }
@@ -22,8 +21,8 @@ import acolyte.RowList.{ Column ⇒ Col }
*
* connection {
* handleStatement.withQueryDetection("...").
* withQueryHandler({ e: Execution => ... }).
* withUpdateHandler({ e: Execution => ... })
* withQueryHandler({ e: QueryExecution => ... }).
* withUpdateHandler({ e: UpdateExecution => ... })
* }
* }}}
*/
@@ -129,36 +128,6 @@ object Implicits extends ScalaRowLists with CompositeHandlerImplicits {
}
sealed trait Execution
case class QueryExecution(
sql: String,
parameters: List[ExecutedParameter]) extends Execution
case class UpdateExecution(
sql: String,
parameters: List[ExecutedParameter]) extends Execution
trait ExecutedParameter {
def value: Any
}
case class DefinedParameter(
value: Any,
definition: ParameterDef) extends ExecutedParameter {
override lazy val toString = s"Param($value, ${definition.sqlTypeName})"
}
object ParameterVal {
def apply(v: Any): ExecutedParameter = new ExecutedParameter {
val value = v
override lazy val toString = s"Param($value)"
}
def unapply(p: ExecutedParameter): Option[Any] = Some(p.value)
}
final class ScalaCompositeHandler(qd: Array[Pattern], qh: QueryHandler, uh: UpdateHandler) extends AbstractCompositeHandler[ScalaCompositeHandler](qd, qh, uh) {
/**
@@ -247,8 +216,7 @@ trait CompositeHandlerImplicits { srl: ScalaRowLists ⇒
* ScalaCompositeHandler.empty withUpdateHandler { exec ⇒ 1/*count*/ }
* }}}
*/
implicit def IntUpdateResult(updateCount: Int): UpdateResult =
new UpdateResult(updateCount)
implicit def IntUpdateResult(updateCount: Int) = new UpdateResult(updateCount)
/**
* Allows to directly use row list as query result.
@@ -0,0 +1,53 @@
package acolyte
import acolyte.ParameterMetaData.ParameterDef
/** Execution information */
sealed trait Execution {
/** Executed SQL */
def sql: String
/** Execution parameters */
def parameters: List[ExecutedParameter]
}
case class QueryExecution(
sql: String, parameters: List[ExecutedParameter] = Nil) extends Execution
case class UpdateExecution(
sql: String, parameters: List[ExecutedParameter] = Nil) extends Execution
/** Statement extractor */
case class ExecutedStatement(
/** Statement pattern */
val p: String) {
val re = p.r
def unapply(x: Execution): Option[(String, List[ExecutedParameter])] =
re.findFirstIn(x.sql).map(_ (x.sql -> x.parameters))
}
sealed trait ExecutedParameter { def value: Any }
/** Executed parameter companion */
object ExecutedParameter {
def apply(v: Any): ExecutedParameter =
new ExecutedParameter { val value = v }
def unapply(p: ExecutedParameter): Option[Any] = Some(p.value)
}
/** Parameter along with its definition. */
case class DefinedParameter(
value: Any, definition: ParameterDef) extends ExecutedParameter {
override lazy val toString = s"Param($value, ${definition.sqlTypeName})"
}
/** Value extractor for execution parameter. */
@deprecated(
message = "Use [[acolyte.ExecutedParameter]] extractor", since = "1.0.15")
object ParameterVal {
def unapply(p: ExecutedParameter): Option[Any] = ExecutedParameter.unapply(p)
}
@@ -0,0 +1,81 @@
package acolyte
import acolyte.{ ExecutedParameter XP }
object ExecutionSpec extends org.specs2.mutable.Specification {
"Execution" title
"Query execution" >> {
val (q1, q2) = (
QueryExecution("SELECT * FROM Test WHERE id = ?", XP("x") :: Nil),
QueryExecution("EXEC reindex"))
"Query #1" should {
"match case class pattern" in {
q1 aka "q1" must beLike {
case QueryExecution("SELECT * FROM Test WHERE id = ?",
XP("x") :: Nil) ok
}
}
"not match case class pattern" in {
q1 aka "q1" must not(beLike {
case QueryExecution("EXEC reindex", Nil) ok
})
}
"match statement extractor #1" in {
q1 aka "q1" must beLike {
case ~(ExecutedStatement("FROM Test"), (sql, XP("x") :: Nil))
sql aka "matching SQL" must_== "SELECT * FROM Test WHERE id = ?"
}
}
"match statement extractor #2" in {
q1 aka "q1" must beLike {
case ~(ExecutedStatement("^SELECT"), (sql, _)) ok
}
}
"not match statement extractor" in {
q1 aka "q1" must not(beLike {
case ~(ExecutedStatement(" reindex$"), (_, XP("x") :: Nil)) ok
})
}
}
"Query #2" should {
"match case class pattern" in {
q2 aka "q2" must beLike {
case QueryExecution("EXEC reindex", Nil) ok
}
}
"not match case class pattern" in {
q2 aka "q2" must not(beLike {
case QueryExecution("SELECT * FROM Test WHERE id = ?",
XP("x") :: Nil) ok
})
}
"match statement extractor #1" in {
q2 aka "q2" must beLike {
case ~(ExecutedStatement("EXEC"), (sql, Nil))
sql aka "matching SQL" must_== "EXEC reindex"
}
}
"match statement extractor #2" in {
q2 aka "q2" must beLike {
case ~(ExecutedStatement(" reindex$"), (sql, _)) ok
}
}
"not match statement extractor" in {
q2 aka "q2" must not(beLike {
case ~(ExecutedStatement("^SELECT"), (_, Nil)) ok
})
}
}
}
}
@@ -28,13 +28,12 @@ object ScalaUseCases {
withQueryDetection(
"^SELECT ", // regex test from beginning
"EXEC that_proc"). // second detection regex
withUpdateHandler { e: UpdateExecution
if (e.sql.startsWith("DELETE ")) {
// Process deletion ...
/* deleted = */ 2
} else {
// ... Process ...
/* count = */ 1
withUpdateHandler {
_ match {
case ~(ExecutedStatement("^DELETE "), (sql, ps))
/* Process deletion ... deleted = */ 2
case _ /* ... Process ... count = */ 1
}
} withQueryHandler { e: QueryExecution
if (e.sql.startsWith("SELECT ")) {
@@ -95,7 +94,7 @@ object ScalaUseCases {
val handler: ScalaCompositeHandler = handleStatement.
withQueryDetection("^SELECT ") withQueryHandler {
_ match { // QueryExecution
case QueryExecution(s, ParameterVal("id") :: Nil)
case QueryExecution(s, ExecutedParameter("id") :: Nil)
(stringList :+ "useCase_3a").asResult
case QueryExecution(s,
View
@@ -2,14 +2,15 @@ import sbt._
import Keys._
// Multi-module project build
object Acolyte extends Build
object Acolyte extends Build with Dependencies
with JdbcDriver with ScalacPlugin with JdbcScala with Studio {
lazy val root = Project(id = "acolyte", base = file(".")).
aggregate(jdbcDriver, scalacPlugin, jdbcScala, studio).
settings(
organization in ThisBuild := "org.eu.acolyte",
version in ThisBuild := "1.0.15",
javaOptions in ThisBuild ++= Seq("-source", "1.6", "-target", "1.6"),
scalaVersion in ThisBuild := "2.10.3",
publishTo in ThisBuild := Some(Resolver.file("file",
new File(Path.userHome.absolutePath+"/.m2/repository"))),
@@ -46,3 +47,7 @@ object Acolyte extends Build
</developer>
</developers>))
}
trait Dependencies {
val specs2Test = "org.specs2" %% "specs2" % "2.3.8" % "test"
}
View
@@ -1,7 +1,7 @@
import sbt._
import Keys._
trait JdbcDriver {
trait JdbcDriver { deps: Dependencies
lazy val jdbcDriver =
Project(id = "jdbc-driver", base = file("jdbc-driver")).settings(
name := "jdbc-driver",
@@ -10,8 +10,7 @@ trait JdbcDriver {
scalacOptions += "-feature",
resolvers += "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/",
libraryDependencies ++= Seq(
"org.apache.commons" % "commons-lang3" % "3.1",
"org.specs2" %% "specs2" % "2.3.2" % "test"),
"org.apache.commons" % "commons-lang3" % "3.1", specs2Test),
crossPaths := false,
sourceGenerators in Compile <+= (baseDirectory in Compile) zip (sourceManaged in Compile) map (dirs {
val (base, managed) = dirs
View
@@ -1,56 +1,31 @@
import sbt._
import Keys._
trait JdbcScala {
trait JdbcScala { deps: Dependencies
// Dependencies
def jdbcDriver: Project
def scalacPlugin: Project
lazy val jdbcScala =
Project(id = "jdbc-scala", base = file("jdbc-scala")).settings(
name := "jdbc-scala",
javaOptions ++= Seq("-source", "1.6", "-target", "1.6"),
javacOptions in Test ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
scalacOptions ++= Seq("-feature", "-deprecation"),
resolvers += "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/",
libraryDependencies ++= Seq(
"org.eu.acolyte" % "jdbc-driver" % "1.0.14",
"org.scalaz" % "scalaz-core_2.10" % "7.0.5",
"org.specs2" %% "specs2" % "2.3.2" % "test"),
sourceGenerators in Compile <+= (baseDirectory in Compile) zip (sourceManaged in Compile) map (dirs {
val (base, managed) = dirs
generateRowClasses(base, managed)
}),
pomExtra := (
<url>https://github.com/cchantep/acolyte/</url>
<licenses>
<license>
<name>GNU Lesser General Public License, Version 2.1</name>
<url>
https://raw.github.com/cchantep/acolyte/master/LICENSE.txt
</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<connection>scm:git:git@github.com:cchantep/acolyte.git</connection>
<developerConnection>
scm:git:git@github.com:cchantep/acolyte.git
</developerConnection>
<url>git@github.com:cchantep/acolyte.git</url>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/cchantep/acolyte/issues</url>
</issueManagement>
<ciManagement>
<system>Travis CI</system>
<url>https://travis-ci.org/cchantep/acolyte</url>
</ciManagement>
<developers>
<developer>
<id>cchantep</id>
<name>Cedric Chantepie</name>
</developer>
</developers>)).dependsOn(jdbcDriver)
name := "jdbc-scala",
javacOptions in Test ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
scalacOptions in Test <++= (version in ThisBuild).
zip(baseDirectory in (scalacPlugin, Compile)).
zip(name in (scalacPlugin, Compile)) map { d =>
val ((v, b), n) = d
val j = b / "target" / "scala-2.10" / "%s_2.10-%s.jar".format(n, v)
Seq("-feature", "-deprecation",
"-Xplugin:%s".format(j.getAbsolutePath))
},
resolvers += "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/",
libraryDependencies ++= Seq(
"org.eu.acolyte" % "jdbc-driver" % "1.0.15", specs2Test),
sourceGenerators in Compile <+= (baseDirectory in Compile) zip (sourceManaged in Compile) map (dirs {
val (base, managed) = dirs
generateRowClasses(base, managed)
})).dependsOn(scalacPlugin, jdbcDriver)
// Source generator
private def generateRowClasses(base: File, managed: File): Seq[File] = {
@@ -1,19 +1,17 @@
import sbt._
import Keys._
trait ScalacPlugin {
trait ScalacPlugin { deps: Dependencies
lazy val scalacPlugin =
Project(id = "scalac-plugin", base = file("scalac-plugin")).settings(
name := "scalac-plugin",
javaOptions ++= Seq("-source", "1.6", "-target", "1.6"),
javacOptions ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
scalacOptions ++= Seq("-feature", "-deprecation"),
resolvers += "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/",
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-compiler" % "2.10.3",
"org.specs2" %% "specs2" % "2.3.2" % "test"),
"org.scala-lang" % "scala-compiler" % "2.10.3", specs2Test),
compile in Test <<= (compile in Test).dependsOn(
packageBin in Compile/* make sure plugin.jar is available */),
scalacOptions in Compile ++= Seq("-feature", "-deprecation"),
scalacOptions in Test <++= (version in ThisBuild).
zip(baseDirectory in Compile).
zip(name in Compile) map { d =>
Oops, something went wrong.

0 comments on commit 65a7e33

Please sign in to comment.