Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

make query probing opt-in and enable fatal warnings #176

Merged
merged 1 commit into from
Feb 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,22 +452,22 @@ db.run(qNested)

# Query probing #

If configured, queries are verified against the database at compile time and the compilation fails if it is not valid. The query validation does not alter the database state.
Query probing is an experimental feature that validates queries against the database at compile time, failing the compilation if it is not valid. The query validation does not alter the database state.

If query probing is enabled, the config file must be available at compile time. You can achieve it by adding this line to your project settings:
This feature is disabled by default. To enable it, mix the `QueryProbing` trait to the database configuration:

```
unmanagedClasspath in Compile += baseDirectory.value / "src" / "main" / "resources"
lazy val db = source(new MySourceConfig("configKey") with QueryProbing)
```

If your project doesn't have a standard layout, e.g. a play project, you should configure the path to point to the folder that contains your config file.

It's possible to disable the query probing by adding the `NoQueryProbing` trait to your source configuration:
The configurations correspondent to the config key must be available at compile time. You can achieve it by adding this line to your project settings:

```
lazy val db = source(new MySourceConfig("configKey") with NoQueryProbing)
unmanagedClasspath in Compile += baseDirectory.value / "src" / "main" / "resources"
```

If your project doesn't have a standard layout, e.g. a play project, you should configure the path to point to the folder that contains your config file.

# Actions #

Database actions are defined using quotations as well. These actions don't have a collection-like API but rather a custom DSL to express inserts, deletes and updates.
Expand Down Expand Up @@ -887,7 +887,19 @@ The transformations are applied from left to right.

## Configuration ##

Sources must be defined as `object` and the object name is used as the key to obtain configurations using the [typesafe config](http://github.com/typesafehub/config) library.
The string passed to the source configuration is used as the key to obtain configurations using the [typesafe config](http://github.com/typesafehub/config) library.

Additionally, any member of a source configuration can be overriden. Example:

```
import io.getquill._
import io.getquill.naming.SnakeCase
import io.getquill.sources.sql.idiom.MySQLDialect

lazy val db = source(new JdbcSourceConfig[MySQLDialect, SnakeCase]("db") {
override def dataSource = ??? // create the datasource manually
})
```

### quill-jdbc ###

Expand Down
7 changes: 5 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ lazy val quill =
IO.write(file, str)
Seq()
})
.settings(tutScalacOptions := Seq())
.dependsOn(`quill-core`, `quill-sql`, `quill-jdbc`, `quill-finagle-mysql`, `quill-async`, `quill-cassandra`)
.aggregate(`quill-core`, `quill-sql`, `quill-jdbc`, `quill-finagle-mysql`, `quill-async`, `quill-cassandra`)

Expand Down Expand Up @@ -87,12 +88,14 @@ lazy val commonSettings = Seq(
libraryDependencies ++= Seq(
"org.scalamacros" %% "resetallattrs" % "1.0.0",
"org.scalatest" %% "scalatest" % "2.2.4" % "test",
"ch.qos.logback" % "logback-classic" % "1.1.3" % "test"
"ch.qos.logback" % "logback-classic" % "1.1.3" % "test",
"com.google.code.findbugs" % "jsr305" % "3.0.1" % "provided" // just to avoid warnings during compilation
),
EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource,
unmanagedClasspath in Test += baseDirectory.value / "src" / "test" / "resources",
scalacOptions ++= Seq(
"-deprecation",
"-Xfatal-warnings",
"-deprecation",
"-encoding", "UTF-8",
"-feature",
"-unchecked",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class CassandraSourceMacroSpec extends Spec {
val q = quote {
qr1.filter(_.s == "fail")
}
"mirrorSource.run(q)" mustNot compile
val s = source(new CassandraMirrorSourceConfig("test") with QueryProbing)
"s.run(q)" mustNot compile
}

"binds inputs according to the sql terms order" - {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ package object cassandra {

val mirrorSource = source(new CassandraMirrorSourceConfig("test"))

val testSyncDB = source(new CassandraSyncSourceConfig[Literal]("testSyncDB") with NoQueryProbing)
val testSyncDB = source(new CassandraSyncSourceConfig[Literal]("testSyncDB"))

val testAsyncDB = source(new CassandraAsyncSourceConfig[Literal]("testAsyncDB") with NoQueryProbing)
val testAsyncDB = source(new CassandraAsyncSourceConfig[Literal]("testAsyncDB"))

val testStreamDB = source(new CassandraStreamSourceConfig[Literal]("testStreamDB") with NoQueryProbing)
val testStreamDB = source(new CassandraStreamSourceConfig[Literal]("testStreamDB"))

def await[T](f: Future[T]): T = Await.result(f, Duration.Inf)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package io.getquill

import io.getquill.sources.SourceConfig

trait NoQueryProbing {
trait QueryProbing {
this: SourceConfig[_] =>
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import scala.util.Failure
import scala.util.Success
import scala.util.Try
import io.getquill.util.Messages._
import io.getquill.NoQueryProbing
import io.getquill.QueryProbing

import org.scalamacros.resetallattrs.ResetAllAttrs

Expand All @@ -22,7 +22,7 @@ trait ResolveSourceMacro {
import c.universe.{ Try => _, _ }

def quoteSource[T <: Source[_, _]](config: Expr[SourceConfig[T]])(implicit t: WeakTypeTag[T]) = {
val probingEnabled = !(config.actualType <:< c.weakTypeOf[NoQueryProbing])
val probingEnabled = config.actualType <:< c.weakTypeOf[QueryProbing]
q"""
new $t($config) {
@${c.weakTypeOf[QuotedSource]}(new $t($config), $probingEnabled)
Expand Down Expand Up @@ -52,11 +52,11 @@ trait ResolveSourceMacro {
case Success(value) =>
Some(value)
case Failure(exception) =>
c.warn(s"Can't load source at compile time. Reason: '${exception.getMessage}'.")
c.error(s"Can't load source at compile time. Reason: '${exception.getMessage}'.")
None
}
case o =>
c.warn("Can't load source at compile time. Query probing disabled.")
c.error("Can't load source at compile time. Query probing disabled.")
None
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class MirrorSourceMacro(val c: Context) extends SourceMacro {
case false =>
val normalized = Normalize(ast)
resolveSource[MirrorSource].map(_.probe(normalized)) match {
case Some(Failure(e)) => c.warn(s"Probe failed. Reason $e")
case Some(Failure(e)) => c.error(s"Probe failed. Reason $e")
case other =>
}
c.info(normalized.toString)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class RebindSpec extends Spec {
val q = quote { (i: Int) =>
unquote(query[TestEntity].insert(e => e.i -> i).returnId[Long])
}
mirrorSource.run(q)(List(1)).ast.toString must equal("infix\"${query[TestEntity].insert(e => e.i -> p1)} RETURNING ID\"")
mirrorSource.run(q)(List(1)).ast.toString must equal("infix\"" + "$" + "{query[TestEntity].insert(e => e.i -> p1)} RETURNING ID\"")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixes compilation warnings

}

"with no type param" in {
Expand All @@ -22,7 +22,7 @@ class RebindSpec extends Spec {
val q = quote { (i: Int) =>
unquote(query[TestEntity].insert(e => e.i -> i).returnId)
}
mirrorSource.run(q)(List(1)).ast.toString must equal("infix\"${query[TestEntity].insert(e => e.i -> p1)} RETURNING ID\"")
mirrorSource.run(q)(List(1)).ast.toString must equal("infix\"" + "$" + "{query[TestEntity].insert(e => e.i -> p1)} RETURNING ID\"")
}
}

Expand All @@ -33,7 +33,7 @@ class RebindSpec extends Spec {
}

val q = quote(query[TestEntity].map(e => unquote(e.i.plus(10))))
mirrorSource.run(q).ast.toString must equal("query[TestEntity].map(e => infix\"${e.i} + ${10}\")")
mirrorSource.run(q).ast.toString must equal("query[TestEntity].map(e => infix\"" + "$" + "{e.i} + " + "$" + "{10}\")")
}

"no type param" in {
Expand All @@ -42,7 +42,7 @@ class RebindSpec extends Spec {
}

val q = quote(query[TestEntity].map(e => unquote(e.i.plus(10))))
mirrorSource.run(q).ast.toString must equal("query[TestEntity].map(e => infix\"${e.i} + ${10}\")")
mirrorSource.run(q).ast.toString must equal("query[TestEntity].map(e => infix\"" + "$" + "{e.i} + " + "$" + "{10}\")")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ class ResolveSourceMacroSpec extends Spec {

class BuggyConfig extends MirrorSourceConfig("buggy")

"warns if the source can't be resolved at compile time" in {
"source(new BuggyConfig)" must compile
"fails if the source can't be resolved at compile time" in {
val s = source(new BuggyConfig with QueryProbing)
"s.run(qr1)" mustNot compile
()
}

"doesn't warn if query probing is disabled and the source can't be resolved at compile time" in {
val s = source(new BuggyConfig with NoQueryProbing)
val s = source(new BuggyConfig)
s.run(qr1.delete)
()
}

"warns if the probe fails" in {
"fails if the probe fails" in {
case class Fail()
val s = source(new MirrorSourceConfig("s"))
"s.run(query[Fail].delete)" must compile
val s = source(new MirrorSourceConfig("s") with QueryProbing)
"s.run(query[Fail].delete)" mustNot compile
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import io.getquill.sources.sql.idiom.SqlIdiom
import scala.util.Try
import java.util.Date
import io.getquill.sources.mirror.Row
import io.getquill.naming.Literal

class SqlSourceMacroSpec extends Spec {

Expand All @@ -30,9 +31,10 @@ class SqlSourceMacroSpec extends Spec {
}
}

"warns if the sql probing fails" in {
"fails if the sql probing fails" in {
case class Fail()
"mirrorSource.run(query[Fail])" mustNot compile
val s = source(new SqlMirrorSourceConfig[Literal]("test") with QueryProbing)
"s.run(query[Fail])" mustNot compile
}

"fails if the query can't be translated to sql" in {
Expand Down