Database Verification

This CL adds logic to verify @Query sql's at compile time.
This is done by creating an in-memory version of the database
during compile time and preparing queries against it.

This is an optional process that developers can disable via
SkipQueryVerification since there is always a chance that we
won't be able to find the sqlite on the host machine. To minimize
the risk, we are shipping xerial with the compiler.
SkipQueryVerification might be necessary if the developer modifies
the database directly.

Right now, we don't use this information besides making sure
query compiles. In followup CLs, we'll use this information
to validate return types and also allow arbitrary java classes
as return types (fi their fields match the fields in the response).

Bug: 33463891
Test: DatabaseVerifierTest
Change-Id: I9667a3bba15a54f25bbe7795de02eaeb435900f9
diff --git a/app-toolkit/init.gradle b/app-toolkit/init.gradle
index ca59733..626badc 100644
--- a/app-toolkit/init.gradle
+++ b/app-toolkit/init.gradle
@@ -72,6 +72,10 @@
 ext.intellij_annotation = "12.0"
 ext.espresso_version = "2.2.2"
 ext.release_version = "1.0-SNAPSHOT"
+// this Xerial version is newer than we want but we need it to fix
+// https://github.com/xerial/sqlite-jdbc/issues/97
+ext.xerial_version = "3.16.1"
+ext.antlr_version = "4.5.3"
 ext.enablePublicRepos = System.getenv("ALLOW_PUBLIC_REPOS")
 
 // repository creation task
diff --git a/lifecycle/compiler/src/main/resources/NOTICE.txt b/lifecycle/compiler/src/main/resources/NOTICE.txt
index 9041f2f..7cd74cc 100644
--- a/lifecycle/compiler/src/main/resources/NOTICE.txt
+++ b/lifecycle/compiler/src/main/resources/NOTICE.txt
@@ -908,10 +908,10 @@
     
 
     <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/frameworks-c07e6f4b02b556d1d85052fb3853caf84c80e6b23dcdb1ae1b00f051da1115a2.css" media="all" rel="stylesheet" />
-    <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/github-09e1c38d593bf8fc6e4c4f1b526d0184e27c433d64963942c1e8c361589f8125.css" media="all" rel="stylesheet" />
+    <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/github-85f6307e0a74c78ba00f8c5bf9c7fb17a418d361627638f51f989be45f709416.css" media="all" rel="stylesheet" />
     
     
-    <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/site-293f92180d0a619a750fa2b5eae9e36740f5723a59c0ec308972c70d24e834fc.css" media="all" rel="stylesheet" />
+    <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/site-73b3dae8eb441c98982c7306f0e59decca409e87188e07bc1a961b8cea511aab.css" media="all" rel="stylesheet" />
     
 
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -941,7 +941,7 @@
     
     <meta name="pjax-timeout" content="1000">
     
-    <meta name="request-id" content="DD6E:16377:37776CE:5A0350B:588674AA" data-pjax-transient>
+    <meta name="request-id" content="F616:4D80:3D52FD2:640C534:588819F9" data-pjax-transient>
 
     <meta name="msapplication-TileImage" content="/windows-tile.png">
     <meta name="msapplication-TileColor" content="#ffffff">
@@ -951,7 +951,7 @@
 <meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA">
     <meta name="google-analytics" content="UA-3769691-2">
 
-<meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" /><meta content="DD6E:16377:37776CE:5A0350B:588674AA" name="octolytics-dimension-request_id" />
+<meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" /><meta content="F616:4D80:3D52FD2:640C534:588819F9" name="octolytics-dimension-request_id" />
 <meta content="/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show" data-pjax-transient="true" name="analytics-location" />
 
 
@@ -964,15 +964,15 @@
     <meta name="user-login" content="">
 
         <meta name="expected-hostname" content="github.com">
-      <meta name="js-proxy-site-detection-payload" content="YmU4NWZjNzExMmYxZTVlYjQxOGUxYjIyZTlkZDEzYWQzODQwNTNkZDgxNjg0ZjIxMDRkZjFlODdmZTk3MWY3MXx7InJlbW90ZV9hZGRyZXNzIjoiMTA0LjEzMi4wLjc1IiwicmVxdWVzdF9pZCI6IkRENkU6MTYzNzc6Mzc3NzZDRTo1QTAzNTBCOjU4ODY3NEFBIiwidGltZXN0YW1wIjoxNDg1MjA2Njk4LCJob3N0IjoiZ2l0aHViLmNvbSJ9">
+      <meta name="js-proxy-site-detection-payload" content="YmQzYzA4OTVmOTU4OGNkNDVlZmQwODdjYTU0MDk0YjUzOTJiYTg0NWJhNzRjZmMzN2E0NWY3MmI1M2E2MWMwM3x7InJlbW90ZV9hZGRyZXNzIjoiNzMuMjMxLjEzMS44IiwicmVxdWVzdF9pZCI6IkY2MTY6NEQ4MDozRDUyRkQyOjY0MEM1MzQ6NTg4ODE5RjkiLCJ0aW1lc3RhbXAiOjE0ODUzMTQ1NTMsImhvc3QiOiJnaXRodWIuY29tIn0=">
 
 
       <link rel="mask-icon" href="https://assets-cdn.github.com/pinned-octocat.svg" color="#000000">
       <link rel="icon" type="image/x-icon" href="https://assets-cdn.github.com/favicon.ico">
 
-    <meta name="html-safe-nonce" content="ce49fbfa72b8a7a3afe651f2e2ba293eea50fadd">
+    <meta name="html-safe-nonce" content="60b5f53f2584bab59a420ca40699efced4b0a6ce">
 
-    <meta http-equiv="x-pjax-version" content="7d887a4a7e9afaef47434b8286514583">
+    <meta http-equiv="x-pjax-version" content="5a52992acda5541f43c6302475c81d61">
     
 
       
@@ -1092,8 +1092,8 @@
   </a>
 
     <a class="social-count js-social-count" href="/square/javapoet/stargazers"
-      aria-label="2911 users starred this repository">
-      2,911
+      aria-label="2916 users starred this repository">
+      2,916
     </a>
 
   </li>
@@ -2570,7 +2570,7 @@
       <svg aria-hidden="true" class="octicon octicon-mark-github" height="24" version="1.1" viewBox="0 0 16 16" width="24"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
 </a>
     <ul class="site-footer-links">
-      <li>&copy; 2017 <span title="0.07064s from github-fe155-cp1-prd.iad.github.net">GitHub</span>, Inc.</li>
+      <li>&copy; 2017 <span title="0.04921s from github-fe-bd32a9f.cp1-iad.github.net">GitHub</span>, Inc.</li>
         <li><a href="https://github.com/site/terms" data-ga-click="Footer, go to terms, text:terms">Terms</a></li>
         <li><a href="https://github.com/site/privacy" data-ga-click="Footer, go to privacy, text:privacy">Privacy</a></li>
         <li><a href="https://github.com/security" data-ga-click="Footer, go to security, text:security">Security</a></li>
@@ -2595,7 +2595,7 @@
 
       <script crossorigin="anonymous" src="https://assets-cdn.github.com/assets/compat-8e19569aacd39e737a14c8515582825f3c90d1794c0e5539f9b525b8eb8b5a8e.js"></script>
       <script crossorigin="anonymous" src="https://assets-cdn.github.com/assets/frameworks-d23e32e482112ba5e3ea9bd8dc7e555680116a74e002746ac91f41a4e5875a9b.js"></script>
-      <script async="async" crossorigin="anonymous" src="https://assets-cdn.github.com/assets/github-5b30a39e1d9d97a79d4a297aa3ff67bf56fab7480252464db1b6e80dbab10690.js"></script>
+      <script async="async" crossorigin="anonymous" src="https://assets-cdn.github.com/assets/github-2789a710829edc0c146edb49db485fdf15e3005c2e255ed209595bfe0e4c43a5.js"></script>
       
       
       
diff --git a/room/common/src/main/java/com/android/support/room/SkipQueryVerification.java b/room/common/src/main/java/com/android/support/room/SkipQueryVerification.java
new file mode 100644
index 0000000..44b74f4
--- /dev/null
+++ b/room/common/src/main/java/com/android/support/room/SkipQueryVerification.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.support.room;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Skips database verification for the annotated element.
+ * <p>
+ * If it is a class annotated with {@link @Database}, none of the queries for the database will
+ * be verified at compile time.
+ * <p>
+ * If it is a class annotated with {@link @Dao}, none of the queries in the Dao class will
+ * be verified at compile time.
+ * <p>
+ * If it is a method in a Dao class, just the method's sql verification will be skipped.
+ * <p>
+ * You should use this as the last resort if Room cannot properly understand your query and you are
+ * 100% sure it works. Removing validation may limit the functionality of Room since it won't be
+ * able to understand the query response.
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface SkipQueryVerification {
+}
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index e170321..42cf3c8 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -40,7 +40,8 @@
     compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
     compile "com.google.auto:auto-common:$auto_common_version"
     compile "com.squareup:javapoet:$javapoet_version"
-    compile 'org.antlr:antlr4:4.5.3'
+    compile "org.antlr:antlr4:$antlr_version"
+    compile "org.xerial:sqlite-jdbc:$xerial_version"
     testCompile "com.google.testing.compile:compile-testing:$compile_testing_version"
     testCompile "junit:junit:$junit_version"
     testCompile "com.intellij:annotations:$intellij_annotation"
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt
index 256d0d9..b3def39 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/RoomProcessor.kt
@@ -17,9 +17,9 @@
 package com.android.support.room
 
 import com.android.support.room.processor.Context
-import com.android.support.room.processor.DaoProcessor
 import com.android.support.room.processor.DatabaseProcessor
 import com.android.support.room.processor.EntityProcessor
+import com.android.support.room.vo.DaoMethod
 import com.android.support.room.writer.DaoWriter
 import com.android.support.room.writer.DatabaseWriter
 import com.android.support.room.writer.EntityCursorConverterWriter
@@ -38,43 +38,63 @@
     override fun initSteps(): MutableIterable<ProcessingStep>? {
         val context = Context(processingEnv)
         return arrayListOf(EntityProcessingStep(context),
-                DaoProcessingStep(context),
                 DatabaseProcessingStep(context))
     }
 
-    class DaoProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
-        override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
-                : MutableSet<Element> {
-            elementsByAnnotation[Dao::class.java]
-                    ?.map {
-                        DaoProcessor(context).parse(MoreElements.asType(it))
-                    }
-                    ?.forEach {
-                        DaoWriter(it).write(context.processingEnv)
-                    }
-            return mutableSetOf()
-        }
-
-        override fun annotations(): MutableSet<out Class<out Annotation>> {
-            return mutableSetOf(Dao::class.java)
-        }
-    }
-
     class DatabaseProcessingStep(context: Context) : ContextBoundProcessingStep(context) {
         override fun process(elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>)
                 : MutableSet<Element> {
-            elementsByAnnotation[Database::class.java]
+            // TODO multi step support
+            val databases = elementsByAnnotation[Database::class.java]
                     ?.map {
                         DatabaseProcessor(context).parse(MoreElements.asType(it))
                     }
-                    ?.forEach {
-                        DatabaseWriter(it).write(context.processingEnv)
-                    }
+            val allDaoMethods = databases?.flatMap { it.daoMethods }
+            allDaoMethods?.let {
+                prepareDaosForWriting(databases!!, it)
+                it.forEach {
+                    DaoWriter(it.dao).write(context.processingEnv)
+                }
+            }
+
+            databases?.forEach {
+                DatabaseWriter(it).write(context.processingEnv)
+            }
             return mutableSetOf()
         }
 
         override fun annotations(): MutableSet<out Class<out Annotation>> {
-            return mutableSetOf(Database::class.java)
+            return mutableSetOf(Database::class.java, Dao::class.java)
+        }
+
+        /**
+         * Traverses all dao methods and assigns them suffix if they are used in multiple databases.
+         */
+        private fun prepareDaosForWriting(databases: List<com.android.support.room.vo.Database>,
+                                          daoMethods: List<DaoMethod>) {
+            daoMethods.groupBy { it.dao.typeName }
+                    // if used only in 1 database, nothing to do.
+                    .filter { entry -> entry.value.size > 1 }
+                    .forEach { entry ->
+                        entry.value.groupBy { daoMethod ->
+                            // first suffix guess: Database's simple name
+                            val db = databases.first { db -> db.daoMethods.contains(daoMethod) }
+                            db.typeName.simpleName()
+                        }.forEach { entry ->
+                            val dbName = entry.key
+                            val methods = entry.value
+                            if (methods.size == 1) {
+                                //good, db names do not clash, use db name as suffix
+                                methods.first().dao.setSuffix(dbName)
+                            } else {
+                                // ok looks like a dao is used in 2 different databases both of
+                                // which have the same name. enumerate.
+                                methods.forEachIndexed { index, method ->
+                                    method.dao.setSuffix("${dbName}_$index")
+                                }
+                            }
+                        }
+                    }
         }
     }
 
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/parser/ParsedQuery.kt b/room/compiler/src/main/kotlin/com/android/support/room/parser/ParsedQuery.kt
index 5f63f1c..d633488 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/parser/ParsedQuery.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/parser/ParsedQuery.kt
@@ -19,6 +19,7 @@
 import com.android.support.room.parser.SectionType.BIND_VAR
 import com.android.support.room.parser.SectionType.NEWLINE
 import com.android.support.room.parser.SectionType.TEXT
+import com.android.support.room.verifier.QueryResultInfo
 import org.antlr.v4.runtime.tree.TerminalNode
 
 enum class SectionType {
@@ -48,6 +49,13 @@
                 emptyList())
     }
 
+    /**
+     * Optional data that might be assigned when the query is parsed inside an annotation processor.
+     * User may turn this off or it might be disabled for any reason so generated code should
+     * always handle not having it.
+     */
+    var resultInfo : QueryResultInfo? = null
+
     val sections by lazy {
         val lines = original.lines()
         val inputsByLine = inputs.groupBy { it.symbol.line }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/parser/SqlParser.kt b/room/compiler/src/main/kotlin/com/android/support/room/parser/SqlParser.kt
index 3013d18..a6eeb8c 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/parser/SqlParser.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/parser/SqlParser.kt
@@ -144,8 +144,8 @@
 }
 
 enum class SQLTypeAffinity {
+    NULL,
     TEXT,
-    NUMERIC,
     INTEGER,
     REAL,
     BLOB
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/preconditions/Checks.kt b/room/compiler/src/main/kotlin/com/android/support/room/preconditions/Checks.kt
index 39faa9c..af2c515 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/preconditions/Checks.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/preconditions/Checks.kt
@@ -32,6 +32,7 @@
  * much code as possible, leaving only the errors in javac output.
  */
 class Checks(private val logger: RLog) {
+
     fun check(predicate: Boolean, element: Element, errorMsg: String, vararg args: Any): Boolean {
         if (!predicate) {
             logger.e(element, errorMsg, args)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
index 3deee97..018e816 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/Context.kt
@@ -16,14 +16,13 @@
 
 package com.android.support.room.processor
 
-import com.android.support.room.ext.LifecyclesTypeNames
-import com.android.support.room.ext.RoomTypeNames
 import com.android.support.room.log.RLog
 import com.android.support.room.preconditions.Checks
 import com.android.support.room.solver.TypeAdapterStore
+import com.android.support.room.vo.Dao
+import com.android.support.room.vo.Database
+import com.android.support.room.vo.Entity
 import javax.annotation.processing.ProcessingEnvironment
-import javax.annotation.processing.RoundEnvironment
-import javax.lang.model.type.TypeMirror
 
 data class Context(val processingEnv: ProcessingEnvironment) {
     val logger = RLog(processingEnv)
@@ -35,13 +34,5 @@
         val STRING by lazy {
             processingEnv.elementUtils.getTypeElement("java.lang.String").asType()
         }
-        val LIVE_DATA: TypeMirror? by lazy {
-            processingEnv.elementUtils.getTypeElement(LifecyclesTypeNames.LIVE_DATA.toString())
-                    ?.asType()
-        }
-        val COMPUTABLE_LIVE_DATA : TypeMirror? by lazy {
-            processingEnv.elementUtils.getTypeElement(LifecyclesTypeNames.COMPUTABLE_LIVE_DATA
-                    .toString())?.asType()
-        }
     }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/DaoProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/DaoProcessor.kt
index 0b6ec74..bbadc2e 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/DaoProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/DaoProcessor.kt
@@ -19,8 +19,10 @@
 import com.android.support.room.Delete
 import com.android.support.room.Insert
 import com.android.support.room.Query
+import com.android.support.room.SkipQueryVerification
 import com.android.support.room.ext.hasAnnotation
 import com.android.support.room.ext.hasAnyOf
+import com.android.support.room.verifier.DatabaseVerifier
 import com.android.support.room.vo.Dao
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
@@ -35,6 +37,8 @@
     val insertionProcessor = InsertionMethodProcessor(context)
     val deletionProcessor = DeletionMethodProcessor(context)
 
+    var dbVerifier: DatabaseVerifier? = null
+
     companion object {
         val PROCESSED_ANNOTATIONS = listOf(Insert::class, Delete::class, Query::class)
     }
@@ -66,6 +70,11 @@
                     Any::class
                 }
             }
+        queryProcessor.dbVerifier = if (element.hasAnnotation(SkipQueryVerification::class)) {
+            null
+        } else {
+            dbVerifier
+        }
         val queryMethods = methods[Query::class]?.map {
             queryProcessor.parse(declaredType, it)
         } ?: emptyList()
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/DatabaseProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/DatabaseProcessor.kt
index 6aa8f5f..9a5f31e 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/DatabaseProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/DatabaseProcessor.kt
@@ -16,8 +16,11 @@
 
 package com.android.support.room.processor
 
+import com.android.support.room.SkipQueryVerification
 import com.android.support.room.ext.RoomTypeNames
+import com.android.support.room.ext.hasAnnotation
 import com.android.support.room.ext.hasAnyOf
+import com.android.support.room.verifier.DatabaseVerifier
 import com.android.support.room.vo.DaoMethod
 import com.android.support.room.vo.Database
 import com.android.support.room.vo.Entity
@@ -49,12 +52,17 @@
                 .getAnnotationMirror(element, com.android.support.room.Database::class.java)
                 .orNull()
         val entities = processEntities(dbAnnotation, element)
-
+        validateUniqueTableNames(element, entities)
         val extendsRoomDb = context.processingEnv.typeUtils.isAssignable(
                 MoreElements.asType(element).asType(), baseClassElement)
         context.checker.check(extendsRoomDb, element, ProcessorErrors.DB_MUST_EXTEND_ROOM_DB)
 
         val allMembers = context.processingEnv.elementUtils.getAllMembers(element)
+
+        if (!element.hasAnnotation(SkipQueryVerification::class)) {
+            daoParser.dbVerifier = DatabaseVerifier.create(context, element, entities)
+        }
+
         val daoMethods = allMembers.filter {
             it.hasAnyOf(Modifier.ABSTRACT) && it.kind == ElementKind.METHOD
         }.filterNot {
@@ -69,18 +77,31 @@
             val dao = daoParser.parse(MoreTypes.asTypeElement(executable.returnType))
             DaoMethod(executable, executable.simpleName.toString(), dao)
         }
-
+        validateUniqueDaoClasses(element, daoMethods)
         val database = Database(element = element,
                 type = MoreElements.asType(element).asType(),
                 entities = entities,
                 daoMethods = daoMethods)
-        validateAccessedTables(database)
-        validateUniqueTableNames(database)
         return database
     }
 
-    private fun validateUniqueTableNames(database: Database) {
-        database.entities
+    private fun validateUniqueDaoClasses(dbElement: TypeElement, daoMethods: List<DaoMethod>) {
+        daoMethods.groupBy { it.dao.typeName }
+                .forEach {
+                    if (it.value.size > 1) {
+                        val error = ProcessorErrors.duplicateDao(it.key, it.value.map { it.name })
+                        it.value.forEach { daoMethod ->
+                            context.logger.e(daoMethod.element,
+                                    ProcessorErrors.DAO_METHOD_CONFLICTS_WITH_OTHERS)
+                        }
+                        // also report the full error for the database
+                        context.logger.e(dbElement, error)
+                    }
+                }
+    }
+
+    private fun validateUniqueTableNames(dbElement : TypeElement, entities : List<Entity>) {
+        entities
                 .groupBy {
                     it.tableName.toLowerCase()
                 }.filter {
@@ -93,26 +114,7 @@
             byTableName.value.forEach { entity ->
                 context.logger.e(entity.element, error)
             }
-            context.logger.e(database.element, error)
-        }
-    }
-
-    private fun validateAccessedTables(database: Database) {
-        val definedTables = database.entities.mapTo(mutableSetOf()) { it.tableName.toLowerCase() }
-        database.daoMethods.forEach { daoMethod ->
-            daoMethod.dao.queryMethods.forEach { queryMethod ->
-                queryMethod.query.tables
-                        .filterNot {
-                            definedTables.contains(it.name.toLowerCase())
-                        }.forEach { table ->
-                    context.logger.e(queryMethod.element,
-                            ProcessorErrors.missingTable(
-                                    tableName = table.name,
-                                    daoName = daoMethod.dao.typeName.toString(),
-                                    methodName = queryMethod.name,
-                                    databaseName = database.typeName.toString()))
-                }
-            }
+            context.logger.e(dbElement, error)
         }
     }
 
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/ProcessorErrors.kt
index 7158a72..417da86 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/ProcessorErrors.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/ProcessorErrors.kt
@@ -21,6 +21,7 @@
 import com.android.support.room.Query
 import com.android.support.room.ext.RoomTypeNames
 import com.android.support.room.vo.Field
+import com.squareup.javapoet.TypeName
 
 object ProcessorErrors {
     val MISSING_QUERY_ANNOTATION = "Query methods must be annotated with ${Query::class.java}"
@@ -120,13 +121,6 @@
                 unusedParams.joinToString(","))
     }
 
-    private val MISSING_TABLE = "Table \"%s\" is accessed in %s#%s but %s does not have any" +
-            " entity that declares the table \"%s\""
-    fun missingTable(tableName: String, daoName: String, methodName: String,
-                     databaseName : String): String {
-        return MISSING_TABLE.format(tableName, daoName, methodName, databaseName, tableName)
-    }
-
     private val DUPLICATE_TABLES = "Table name \"%s\" is used by multiple entities: %s"
     fun  duplicateTableNames(tableName: String, entityNames: List<String>): String {
         return DUPLICATE_TABLES.format(tableName, entityNames.joinToString(", "))
@@ -134,4 +128,16 @@
 
     val DELETION_METHODS_MUST_RETURN_VOID_OR_INT = "Deletion methods must either return void or" +
             " return int (the number of deleted rows)."
+
+    val DAO_METHOD_CONFLICTS_WITH_OTHERS = "Dao method has conflicts."
+
+    fun duplicateDao(dao : TypeName, methodNames : List<String>) : String {
+        return """
+        All of these functions (${methodNames.joinToString(", ")}) return the same DAO class ($dao).
+        A database can use a DAO only once so you should remove ${methodNames.size - 1} of these
+        conflicting DAO methods. If you are implementing any of these to fulfill an interface, don't
+        make it abstract, instead, implement the code that calls the other one.
+        """.trimIndent().replace(System.lineSeparator(), " ")
+    }
+
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryMethodProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryMethodProcessor.kt
index e1add0e..6d3b838 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryMethodProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/QueryMethodProcessor.kt
@@ -17,11 +17,14 @@
 package com.android.support.room.processor
 
 import com.android.support.room.Query
+import com.android.support.room.SkipQueryVerification
+import com.android.support.room.ext.hasAnnotation
 import com.android.support.room.parser.ParsedQuery
 import com.android.support.room.parser.QueryType
 import com.android.support.room.parser.SqlParser
-import com.android.support.room.solver.query.result.InstantQueryResultBinder
 import com.android.support.room.solver.query.result.LiveDataQueryResultBinder
+import com.android.support.room.verifier.DatabaseVerificaitonErrors
+import com.android.support.room.verifier.DatabaseVerifier
 import com.android.support.room.vo.QueryMethod
 import com.google.auto.common.AnnotationMirrors
 import com.google.auto.common.MoreElements
@@ -33,6 +36,9 @@
 
 class QueryMethodProcessor(val context: Context) {
     val parameterParser = QueryParameterProcessor(context)
+    // not enforced
+    var dbVerifier : DatabaseVerifier? = null
+
     fun parse(containing: DeclaredType, executableElement: ExecutableElement): QueryMethod {
         val asMember = context.processingEnv.typeUtils.asMemberOf(containing, executableElement)
         val executableType = MoreTypes.asExecutable(asMember)
@@ -47,6 +53,14 @@
                     AnnotationMirrors.getAnnotationValue(annotation, "value").value.toString())
             context.checker.check(query.errors.isEmpty(), executableElement,
                     query.errors.joinToString("\n"))
+            if (!executableElement.hasAnnotation(SkipQueryVerification::class)) {
+                query.resultInfo = dbVerifier?.analyze(query.original)
+            }
+            if (query.resultInfo?.error != null) {
+                context.logger.e(executableElement,
+                        DatabaseVerificaitonErrors.cannotVerifyQuery(query.resultInfo!!.error!!))
+            }
+
             context.checker.check(executableType.returnType.kind != TypeKind.ERROR,
                     executableElement, ProcessorErrors.CANNOT_RESOLVE_RETURN_TYPE,
                     executableElement)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/verifier/ColumnInfo.kt b/room/compiler/src/main/kotlin/com/android/support/room/verifier/ColumnInfo.kt
new file mode 100644
index 0000000..aa419a1
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/verifier/ColumnInfo.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.support.room.verifier
+
+import com.android.support.room.parser.SQLTypeAffinity
+
+/**
+ * Represents a column in a query response
+ */
+data class ColumnInfo(val name : String, val type : SQLTypeAffinity)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/verifier/DatabaseVerificaitonErrors.kt b/room/compiler/src/main/kotlin/com/android/support/room/verifier/DatabaseVerificaitonErrors.kt
new file mode 100644
index 0000000..9c6278e
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/verifier/DatabaseVerificaitonErrors.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.support.room.verifier
+
+import java.sql.SQLException
+
+object DatabaseVerificaitonErrors {
+    private val CANNOT_CREATE_TABLE : String = "Create table statement had an error: %s"
+    fun cannotCreateTable(exception: SQLException) : String {
+        return CANNOT_CREATE_TABLE.format(exception.message)
+    }
+
+    private val CANNOT_VERIFY_QUERY : String = "There is a problem with the query: %s"
+    fun cannotVerifyQuery(exception: SQLException) : String {
+        return CANNOT_VERIFY_QUERY.format(exception.message)
+    }
+
+    private val CANNOT_CREATE_SQLITE_CONNECTION : String = "Room cannot create an SQLite" +
+            " connection to verify the queries. Query verification will be disabled. Error: %s"
+    fun cannotCreateConnection(exception: Exception) : String {
+        return CANNOT_CREATE_SQLITE_CONNECTION.format(exception.message)
+    }
+
+    val CANNOT_GET_TMP_JAVA_DIR = "Cannot read tmp java dir which is necessary to load sqlite" +
+            " lib. Database SQL verification will be disabled"
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/verifier/DatabaseVerifier.kt b/room/compiler/src/main/kotlin/com/android/support/room/verifier/DatabaseVerifier.kt
new file mode 100644
index 0000000..126438c
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/verifier/DatabaseVerifier.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.support.room.verifier
+
+import columnInfo
+import com.android.support.room.processor.Context
+import com.android.support.room.vo.Entity
+import java.io.File
+import java.sql.Connection
+import java.sql.DriverManager
+import java.sql.SQLException
+import java.util.UUID
+import javax.lang.model.element.Element
+
+/**
+ * Builds an in-memory version of the database and verifies the queries against it.
+ * This class is also used to resolve the return types.
+ */
+class DatabaseVerifier private constructor(
+        val connection : Connection, val context : Context, val entities : List<Entity>) {
+    companion object {
+        /**
+         * Tries to create a verifier but returns null if it cannot find the driver.
+         */
+        fun create(context: Context, element: Element, entities: List<Entity>) : DatabaseVerifier? {
+            return try {
+                // see: https://github.com/xerial/sqlite-jdbc/issues/97
+                val tmpDir = System.getProperty("java.io.tmpdir")
+                if (tmpDir == null) {
+                    context.logger.w(element, DatabaseVerificaitonErrors.CANNOT_GET_TMP_JAVA_DIR)
+                    return null
+                }
+                val outDir = File(tmpDir, "room-${UUID.randomUUID()}")
+                outDir.mkdirs()
+                outDir.deleteOnExit()
+                System.setProperty("org.sqlite.tmpdir", outDir.absolutePath)
+                //force load:
+                Class.forName("org.sqlite.JDBC")
+                val connection = DriverManager.getConnection("jdbc:sqlite::memory:")
+                DatabaseVerifier(connection, context, entities)
+            } catch (ex : Exception) {
+                context.logger.w(element, DatabaseVerificaitonErrors.cannotCreateConnection(ex))
+                null
+            }
+        }
+    }
+    init {
+        entities.forEach { entity ->
+            val stmt = connection.createStatement()
+            stmt.executeUpdate(entity.createTableQuery)
+        }
+    }
+
+    fun analyze(sql : String) : QueryResultInfo {
+        return try {
+            val stmt = connection.prepareStatement(sql)
+            QueryResultInfo(stmt.columnInfo())
+        } catch (ex : SQLException) {
+            QueryResultInfo(emptyList(), ex)
+        }
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/verifier/QueryResultInfo.kt b/room/compiler/src/main/kotlin/com/android/support/room/verifier/QueryResultInfo.kt
new file mode 100644
index 0000000..36c6ba7
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/verifier/QueryResultInfo.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.support.room.verifier
+
+import java.sql.SQLException
+
+/**
+ * Represents the result of a query.
+ * <p>
+ * This information is obtained by preparing the query against an in memory database at compile
+ * time.
+ */
+data class QueryResultInfo(val columns : List<ColumnInfo>, val error : SQLException? = null)
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/verifier/jdbc_ext.kt b/room/compiler/src/main/kotlin/com/android/support/room/verifier/jdbc_ext.kt
new file mode 100644
index 0000000..d466d48
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/verifier/jdbc_ext.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.support.room.parser.SQLTypeAffinity
+import com.android.support.room.verifier.ColumnInfo
+import java.sql.PreparedStatement
+import java.sql.ResultSet
+import java.sql.ResultSetMetaData
+import java.sql.SQLException
+
+internal fun <T> ResultSet.collect(f: (ResultSet) -> T): List<T> {
+    val result = arrayListOf<T>()
+    try {
+        while (next()) {
+            result.add(f.invoke(this))
+        }
+    } finally {
+        close()
+    }
+    return result
+}
+
+private fun <T> PreparedStatement.map(f : (Int, ResultSetMetaData) ->  T) : List<T> {
+    val columnCount = try {
+        metaData.columnCount
+    } catch (ex : SQLException) {
+        // ignore, no-result query
+        0
+    }
+    // return is separate than data creation because we want to know who throws the exception
+    return (1.rangeTo(columnCount)).map { f(it, metaData) }
+}
+
+internal fun PreparedStatement.columnNames(): List<String> {
+    return map { index, data -> data.getColumnName(index) }
+}
+
+private fun PreparedStatement.tryGetAffinity(columnIndex : Int) : SQLTypeAffinity {
+    return try {
+        SQLTypeAffinity.valueOf(metaData.getColumnTypeName(columnIndex).capitalize())
+    } catch (ex : IllegalArgumentException) {
+        SQLTypeAffinity.NULL
+    }
+}
+
+internal fun PreparedStatement.columnInfo(): List<ColumnInfo> {
+    return map { index, data -> ColumnInfo(data.getColumnName(index), tryGetAffinity(index)) }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
index 5493dea..9f1862b 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Dao.kt
@@ -24,10 +24,22 @@
                val queryMethods: List<QueryMethod>,
                val insertionMethods : List<InsertionMethod>,
                val deletionMethods : List<DeletionMethod>) {
+    // parsed dao might have a suffix if it is used in multiple databases.
+    private var suffix : String? = null
+    fun setSuffix(newSuffix : String) {
+        if (this.suffix != null) {
+            throw IllegalStateException("cannot set suffix twice")
+        }
+        this.suffix = if (newSuffix == "") "" else "_$newSuffix"
+    }
+
     val typeName by lazy { ClassName.get(type) as ClassName }
 
-    val implClassName by lazy {
-        "${typeName.simpleName()}_Impl"
+    private val implClassName by lazy {
+        if (suffix == null) {
+            suffix = ""
+        }
+        "${typeName.simpleName()}${suffix}_Impl"
     }
 
     val implTypeName by lazy {
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Entity.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Entity.kt
index 0fade53..02611b9 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Entity.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Entity.kt
@@ -38,4 +38,18 @@
     val primaryKeys by lazy {
         fields.filter { it.primaryKey }
     }
+
+    val createTableQuery by lazy {
+        val definitions = fields.map { it.databaseDefinition } +
+                createPrimaryKeyDefinition()
+        "CREATE TABLE IF NOT EXISTS `$tableName` (${definitions.joinToString(", ")})"
+    }
+
+    private fun createPrimaryKeyDefinition(): String {
+        val keys = fields
+                .filter { it.primaryKey }
+                .map { "`${it.columnName}`" }
+                .joinToString(", ")
+        return "PRIMARY KEY($keys)"
+    }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
index 02618e4..750395e 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Field.kt
@@ -17,6 +17,7 @@
 package com.android.support.room.vo
 
 import com.android.support.room.ext.typeName
+import com.android.support.room.parser.SQLTypeAffinity
 import com.squareup.javapoet.TypeName
 import javax.lang.model.element.Element
 import javax.lang.model.type.TypeMirror
@@ -67,4 +68,15 @@
     val setterNameWithVariations by lazy {
         nameWithVariations.map { "set${it.capitalize()}" }
     }
+
+    /**
+     * definition to be used in create query
+     */
+    val databaseDefinition by lazy {
+        val affinity = let {
+            val adapter = getter.columnAdapter ?: setter.columnAdapter
+            adapter?.typeAffinity ?: SQLTypeAffinity.TEXT
+        }
+        "`$columnName` ${affinity.name}"
+    }
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriter.kt
index 3049073..7723a58 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriter.kt
@@ -115,29 +115,11 @@
         }.build()
     }
 
-    private fun createDatabaseDefinition(field : Field) : String {
-        val affinity = field.let {
-            val adapter = it.getter.columnAdapter ?: it.setter.columnAdapter
-            adapter?.typeAffinity ?: SQLTypeAffinity.TEXT
-        }
-        return "`${field.columnName}` ${affinity.name}"
-    }
+
 
     @VisibleForTesting
     fun createQuery(entity : Entity) : String {
-        val definitions = entity.fields.map {
-            field -> createDatabaseDefinition(field)
-        } + createPrimaryKeyDefinition(entity)
-        return "CREATE TABLE IF NOT EXISTS `${entity.tableName}` " +
-                "(${definitions.joinToString(", ")})"
-    }
-
-    private fun createPrimaryKeyDefinition(entity: Entity): String {
-        val keys = entity.fields
-                .filter { it.primaryKey }
-                .map { "`${it.columnName}`" }
-                .joinToString(", ")
-        return "PRIMARY KEY($keys)"
+        return entity.createTableQuery
     }
 
     @VisibleForTesting
diff --git a/room/compiler/src/main/resources/NOTICE.txt b/room/compiler/src/main/resources/NOTICE.txt
index c452213..2575e9b 100644
--- a/room/compiler/src/main/resources/NOTICE.txt
+++ b/room/compiler/src/main/resources/NOTICE.txt
@@ -271,6 +271,242 @@
 
 
 -----------------------------------------------------------------------------
+* sqlite-jdbc.jar (org.xerial:sqlite-jdbc:3.16.1)
+
+ ****** LICENSE:
+

+                                 Apache License

+                           Version 2.0, January 2004

+                        http://www.apache.org/licenses/

+

+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

+

+   1. Definitions.

+

+      "License" shall mean the terms and conditions for use, reproduction,

+      and distribution as defined by Sections 1 through 9 of this document.

+

+      "Licensor" shall mean the copyright owner or entity authorized by

+      the copyright owner that is granting the License.

+

+      "Legal Entity" shall mean the union of the acting entity and all

+      other entities that control, are controlled by, or are under common

+      control with that entity. For the purposes of this definition,

+      "control" means (i) the power, direct or indirect, to cause the

+      direction or management of such entity, whether by contract or

+      otherwise, or (ii) ownership of fifty percent (50%) or more of the

+      outstanding shares, or (iii) beneficial ownership of such entity.

+

+      "You" (or "Your") shall mean an individual or Legal Entity

+      exercising permissions granted by this License.

+

+      "Source" form shall mean the preferred form for making modifications,

+      including but not limited to software source code, documentation

+      source, and configuration files.

+

+      "Object" form shall mean any form resulting from mechanical

+      transformation or translation of a Source form, including but

+      not limited to compiled object code, generated documentation,

+      and conversions to other media types.

+

+      "Work" shall mean the work of authorship, whether in Source or

+      Object form, made available under the License, as indicated by a

+      copyright notice that is included in or attached to the work

+      (an example is provided in the Appendix below).

+

+      "Derivative Works" shall mean any work, whether in Source or Object

+      form, that is based on (or derived from) the Work and for which the

+      editorial revisions, annotations, elaborations, or other modifications

+      represent, as a whole, an original work of authorship. For the purposes

+      of this License, Derivative Works shall not include works that remain

+      separable from, or merely link (or bind by name) to the interfaces of,

+      the Work and Derivative Works thereof.

+

+      "Contribution" shall mean any work of authorship, including

+      the original version of the Work and any modifications or additions

+      to that Work or Derivative Works thereof, that is intentionally

+      submitted to Licensor for inclusion in the Work by the copyright owner

+      or by an individual or Legal Entity authorized to submit on behalf of

+      the copyright owner. For the purposes of this definition, "submitted"

+      means any form of electronic, verbal, or written communication sent

+      to the Licensor or its representatives, including but not limited to

+      communication on electronic mailing lists, source code control systems,

+      and issue tracking systems that are managed by, or on behalf of, the

+      Licensor for the purpose of discussing and improving the Work, but

+      excluding communication that is conspicuously marked or otherwise

+      designated in writing by the copyright owner as "Not a Contribution."

+

+      "Contributor" shall mean Licensor and any individual or Legal Entity

+      on behalf of whom a Contribution has been received by Licensor and

+      subsequently incorporated within the Work.

+

+   2. Grant of Copyright License. Subject to the terms and conditions of

+      this License, each Contributor hereby grants to You a perpetual,

+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable

+      copyright license to reproduce, prepare Derivative Works of,

+      publicly display, publicly perform, sublicense, and distribute the

+      Work and such Derivative Works in Source or Object form.

+

+   3. Grant of Patent License. Subject to the terms and conditions of

+      this License, each Contributor hereby grants to You a perpetual,

+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable

+      (except as stated in this section) patent license to make, have made,

+      use, offer to sell, sell, import, and otherwise transfer the Work,

+      where such license applies only to those patent claims licensable

+      by such Contributor that are necessarily infringed by their

+      Contribution(s) alone or by combination of their Contribution(s)

+      with the Work to which such Contribution(s) was submitted. If You

+      institute patent litigation against any entity (including a

+      cross-claim or counterclaim in a lawsuit) alleging that the Work

+      or a Contribution incorporated within the Work constitutes direct

+      or contributory patent infringement, then any patent licenses

+      granted to You under this License for that Work shall terminate

+      as of the date such litigation is filed.

+

+   4. Redistribution. You may reproduce and distribute copies of the

+      Work or Derivative Works thereof in any medium, with or without

+      modifications, and in Source or Object form, provided that You

+      meet the following conditions:

+

+      (a) You must give any other recipients of the Work or

+          Derivative Works a copy of this License; and

+

+      (b) You must cause any modified files to carry prominent notices

+          stating that You changed the files; and

+

+      (c) You must retain, in the Source form of any Derivative Works

+          that You distribute, all copyright, patent, trademark, and

+          attribution notices from the Source form of the Work,

+          excluding those notices that do not pertain to any part of

+          the Derivative Works; and

+

+      (d) If the Work includes a "NOTICE" text file as part of its

+          distribution, then any Derivative Works that You distribute must

+          include a readable copy of the attribution notices contained

+          within such NOTICE file, excluding those notices that do not

+          pertain to any part of the Derivative Works, in at least one

+          of the following places: within a NOTICE text file distributed

+          as part of the Derivative Works; within the Source form or

+          documentation, if provided along with the Derivative Works; or,

+          within a display generated by the Derivative Works, if and

+          wherever such third-party notices normally appear. The contents

+          of the NOTICE file are for informational purposes only and

+          do not modify the License. You may add Your own attribution

+          notices within Derivative Works that You distribute, alongside

+          or as an addendum to the NOTICE text from the Work, provided

+          that such additional attribution notices cannot be construed

+          as modifying the License.

+

+      You may add Your own copyright statement to Your modifications and

+      may provide additional or different license terms and conditions

+      for use, reproduction, or distribution of Your modifications, or

+      for any such Derivative Works as a whole, provided Your use,

+      reproduction, and distribution of the Work otherwise complies with

+      the conditions stated in this License.

+

+   5. Submission of Contributions. Unless You explicitly state otherwise,

+      any Contribution intentionally submitted for inclusion in the Work

+      by You to the Licensor shall be under the terms and conditions of

+      this License, without any additional terms or conditions.

+      Notwithstanding the above, nothing herein shall supersede or modify

+      the terms of any separate license agreement you may have executed

+      with Licensor regarding such Contributions.

+

+   6. Trademarks. This License does not grant permission to use the trade

+      names, trademarks, service marks, or product names of the Licensor,

+      except as required for reasonable and customary use in describing the

+      origin of the Work and reproducing the content of the NOTICE file.

+

+   7. Disclaimer of Warranty. Unless required by applicable law or

+      agreed to in writing, Licensor provides the Work (and each

+      Contributor provides its Contributions) on an "AS IS" BASIS,

+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or

+      implied, including, without limitation, any warranties or conditions

+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A

+      PARTICULAR PURPOSE. You are solely responsible for determining the

+      appropriateness of using or redistributing the Work and assume any

+      risks associated with Your exercise of permissions under this License.

+

+   8. Limitation of Liability. In no event and under no legal theory,

+      whether in tort (including negligence), contract, or otherwise,

+      unless required by applicable law (such as deliberate and grossly

+      negligent acts) or agreed to in writing, shall any Contributor be

+      liable to You for damages, including any direct, indirect, special,

+      incidental, or consequential damages of any character arising as a

+      result of this License or out of the use or inability to use the

+      Work (including but not limited to damages for loss of goodwill,

+      work stoppage, computer failure or malfunction, or any and all

+      other commercial damages or losses), even if such Contributor

+      has been advised of the possibility of such damages.

+

+   9. Accepting Warranty or Additional Liability. While redistributing

+      the Work or Derivative Works thereof, You may choose to offer,

+      and charge a fee for, acceptance of support, warranty, indemnity,

+      or other liability obligations and/or rights consistent with this

+      License. However, in accepting such obligations, You may act only

+      on Your own behalf and on Your sole responsibility, not on behalf

+      of any other Contributor, and only if You agree to indemnify,

+      defend, and hold each Contributor harmless for any liability

+      incurred by, or claims asserted against, such Contributor by reason

+      of your accepting any such warranty or additional liability.

+

+   END OF TERMS AND CONDITIONS

+

+   APPENDIX: How to apply the Apache License to your work.

+

+      To apply the Apache License to your work, attach the following

+      boilerplate notice, with the fields enclosed by brackets "[]"

+      replaced with your own identifying information. (Don't include

+      the brackets!)  The text should be enclosed in the appropriate

+      comment syntax for the file format. We also recommend that a

+      file or class name and description of purpose be included on the

+      same "printed page" as the copyright notice for easier

+      identification within third-party archives.

+

+   Copyright [yyyy] [name of copyright owner]

+

+   Licensed under the Apache License, Version 2.0 (the "License");

+   you may not use this file except in compliance with the License.

+   You may obtain a copy of the License at

+

+       http://www.apache.org/licenses/LICENSE-2.0

+

+   Unless required by applicable law or agreed to in writing, software

+   distributed under the License is distributed on an "AS IS" BASIS,

+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+   See the License for the specific language governing permissions and

+   limitations under the License.

+
+ ****** LICENSE:
+Copyright (c) 2006, David Crawshaw.  All rights reserved.

+

+Redistribution and use in source and binary forms, with or without

+modification, are permitted provided that the following conditions

+are met:

+

+1. Redistributions of source code must retain the above copyright

+   notice, this list of conditions and the following disclaimer.

+2. Redistributions in binary form must reproduce the above copyright

+   notice, this list of conditions and the following disclaimer in the

+   documentation and/or other materials provided with the distribution.

+

+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND

+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE

+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL

+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS

+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)

+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT

+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY

+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF

+SUCH DAMAGE.

+

+
+
+
+
+-----------------------------------------------------------------------------
 * auto-common.jar (com.google.auto:auto-common:0.6)
 
  ****** LICENSE:
@@ -942,10 +1178,10 @@
     
 
     <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/frameworks-c07e6f4b02b556d1d85052fb3853caf84c80e6b23dcdb1ae1b00f051da1115a2.css" media="all" rel="stylesheet" />
-    <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/github-09e1c38d593bf8fc6e4c4f1b526d0184e27c433d64963942c1e8c361589f8125.css" media="all" rel="stylesheet" />
+    <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/github-85f6307e0a74c78ba00f8c5bf9c7fb17a418d361627638f51f989be45f709416.css" media="all" rel="stylesheet" />
     
     
-    <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/site-293f92180d0a619a750fa2b5eae9e36740f5723a59c0ec308972c70d24e834fc.css" media="all" rel="stylesheet" />
+    <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/site-73b3dae8eb441c98982c7306f0e59decca409e87188e07bc1a961b8cea511aab.css" media="all" rel="stylesheet" />
     
 
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -975,7 +1211,7 @@
     
     <meta name="pjax-timeout" content="1000">
     
-    <meta name="request-id" content="E155:16376:1EEA475:323ED03:588674A2" data-pjax-transient>
+    <meta name="request-id" content="F589:4D7F:4003D62:681CE2A:588819F0" data-pjax-transient>
 
     <meta name="msapplication-TileImage" content="/windows-tile.png">
     <meta name="msapplication-TileColor" content="#ffffff">
@@ -985,7 +1221,7 @@
 <meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA">
     <meta name="google-analytics" content="UA-3769691-2">
 
-<meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" /><meta content="E155:16376:1EEA475:323ED03:588674A2" name="octolytics-dimension-request_id" />
+<meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" /><meta content="F589:4D7F:4003D62:681CE2A:588819F0" name="octolytics-dimension-request_id" />
 <meta content="/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show" data-pjax-transient="true" name="analytics-location" />
 
 
@@ -998,15 +1234,15 @@
     <meta name="user-login" content="">
 
         <meta name="expected-hostname" content="github.com">
-      <meta name="js-proxy-site-detection-payload" content="MjkyMzEwMDc0ODQyNzk2ZjhjN2E0Y2NhNzQ3MmM1ZDA0ZTNiYTE5OGU2ZGUzMjY2ZTliOTFjYjMwYTg5MjlmYnx7InJlbW90ZV9hZGRyZXNzIjoiMTA0LjEzMi4wLjc1IiwicmVxdWVzdF9pZCI6IkUxNTU6MTYzNzY6MUVFQTQ3NTozMjNFRDAzOjU4ODY3NEEyIiwidGltZXN0YW1wIjoxNDg1MjA2NjkwLCJob3N0IjoiZ2l0aHViLmNvbSJ9">
+      <meta name="js-proxy-site-detection-payload" content="MzgwODcxZTYwMTg2MjUyOGMwYjFkNzEzZWQxOTMzODJhZTQyNmY0ZjcwOTBhYTk4NTkyZDQ0Yzg1NDQyM2FlOXx7InJlbW90ZV9hZGRyZXNzIjoiNzMuMjMxLjEzMS44IiwicmVxdWVzdF9pZCI6IkY1ODk6NEQ3Rjo0MDAzRDYyOjY4MUNFMkE6NTg4ODE5RjAiLCJ0aW1lc3RhbXAiOjE0ODUzMTQ1NDUsImhvc3QiOiJnaXRodWIuY29tIn0=">
 
 
       <link rel="mask-icon" href="https://assets-cdn.github.com/pinned-octocat.svg" color="#000000">
       <link rel="icon" type="image/x-icon" href="https://assets-cdn.github.com/favicon.ico">
 
-    <meta name="html-safe-nonce" content="3183a4e7a6d2bad082417e94a272279780ff7af4">
+    <meta name="html-safe-nonce" content="15b8293c9d294a2dbe72cb0a081192e3c4764a4a">
 
-    <meta http-equiv="x-pjax-version" content="7d887a4a7e9afaef47434b8286514583">
+    <meta http-equiv="x-pjax-version" content="5a52992acda5541f43c6302475c81d61">
     
 
       
@@ -1126,8 +1362,8 @@
   </a>
 
     <a class="social-count js-social-count" href="/square/javapoet/stargazers"
-      aria-label="2911 users starred this repository">
-      2,911
+      aria-label="2916 users starred this repository">
+      2,916
     </a>
 
   </li>
@@ -2604,7 +2840,7 @@
       <svg aria-hidden="true" class="octicon octicon-mark-github" height="24" version="1.1" viewBox="0 0 16 16" width="24"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
 </a>
     <ul class="site-footer-links">
-      <li>&copy; 2017 <span title="0.06748s from github-fe155-cp1-prd.iad.github.net">GitHub</span>, Inc.</li>
+      <li>&copy; 2017 <span title="0.06770s from github-fe126-cp1-prd.iad.github.net">GitHub</span>, Inc.</li>
         <li><a href="https://github.com/site/terms" data-ga-click="Footer, go to terms, text:terms">Terms</a></li>
         <li><a href="https://github.com/site/privacy" data-ga-click="Footer, go to privacy, text:privacy">Privacy</a></li>
         <li><a href="https://github.com/security" data-ga-click="Footer, go to security, text:security">Security</a></li>
@@ -2629,7 +2865,7 @@
 
       <script crossorigin="anonymous" src="https://assets-cdn.github.com/assets/compat-8e19569aacd39e737a14c8515582825f3c90d1794c0e5539f9b525b8eb8b5a8e.js"></script>
       <script crossorigin="anonymous" src="https://assets-cdn.github.com/assets/frameworks-d23e32e482112ba5e3ea9bd8dc7e555680116a74e002746ac91f41a4e5875a9b.js"></script>
-      <script async="async" crossorigin="anonymous" src="https://assets-cdn.github.com/assets/github-5b30a39e1d9d97a79d4a297aa3ff67bf56fab7480252464db1b6e80dbab10690.js"></script>
+      <script async="async" crossorigin="anonymous" src="https://assets-cdn.github.com/assets/github-2789a710829edc0c146edb49db485fdf15e3005c2e255ed209595bfe0e4c43a5.js"></script>
       
       
       
diff --git a/room/compiler/src/test/data/daoWriter/input/ComplexDao.java b/room/compiler/src/test/data/daoWriter/input/ComplexDao.java
index eaf9f66..ba282eb 100644
--- a/room/compiler/src/test/data/daoWriter/input/ComplexDao.java
+++ b/room/compiler/src/test/data/daoWriter/input/ComplexDao.java
@@ -30,13 +30,13 @@
     @Query("SELECT * FROM user where uid IN (:ids)")
     abstract public List<User> loadAllByIds(int... ids);
 
-    @Query("SELECT age FROM user where id = :id")
+    @Query("SELECT ageColumn FROM user where uid = :id")
     abstract int getAge(int id);
 
-    @Query("SELECT age FROM user where id = IN(:ids)")
+    @Query("SELECT ageColumn FROM user where uid IN(:ids)")
     abstract public int[] getAllAges(int... ids);
 
-    @Query("SELECT age FROM user where id = IN(:ids)")
+    @Query("SELECT ageColumn FROM user where uid IN(:ids)")
     abstract public List<Integer> getAllAgesAsList(List<Integer> ids);
 
     @Query("SELECT * FROM user where uid = :id")
diff --git a/room/compiler/src/test/data/daoWriter/output/ComplexDao.java b/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
index a54bfc2..ffcd320 100644
--- a/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
+++ b/room/compiler/src/test/data/daoWriter/output/ComplexDao.java
@@ -110,7 +110,7 @@
 
     @Override
     int getAge(int id) {
-        final String _sql = "SELECT age FROM user where id = ?";
+        final String _sql = "SELECT ageColumn FROM user where uid = ?";
         final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
         int _argIndex = 1;
         _statement.bindLong(_argIndex, id);
@@ -132,7 +132,7 @@
     @Override
     public int[] getAllAges(int... ids) {
         StringBuilder _stringBuilder = StringUtil.newStringBuilder();
-        _stringBuilder.append("SELECT age FROM user where id = IN(");
+        _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
         final int _inputSize = ids.length;
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
         _stringBuilder.append(")");
@@ -164,7 +164,7 @@
     @Override
     public List<Integer> getAllAgesAsList(List<Integer> ids) {
         StringBuilder _stringBuilder = StringUtil.newStringBuilder();
-        _stringBuilder.append("SELECT age FROM user where id = IN(");
+        _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
         final int _inputSize = ids.size();
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
         _stringBuilder.append(")");
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/DaoProcessorTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/DaoProcessorTest.kt
index a34cd89..bd0c1e1 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/DaoProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/DaoProcessorTest.kt
@@ -25,19 +25,23 @@
 import com.google.testing.compile.CompileTester
 import com.google.testing.compile.JavaFileObjects
 import com.google.testing.compile.JavaSourcesSubjectFactory
+import createVerifierFromEntities
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
+import org.junit.runners.Parameterized
 
-@RunWith(JUnit4::class)
-class DaoProcessorTest {
+@RunWith(Parameterized::class)
+class DaoProcessorTest(val enableVerification : Boolean) {
     companion object {
         const val DAO_PREFIX = """
             package foo.bar;
             import com.android.support.room.*;
             """
+        @Parameterized.Parameters(name = "enableDbVerification={0}")
+        @JvmStatic
+        fun getParams() = arrayOf(true, false)
     }
 
     @Test
@@ -75,7 +79,7 @@
     fun testAbstractClass() {
         singleDao("""
                 @Dao abstract class MyDao {
-                    @Query("SELECT id FROM users")
+                    @Query("SELECT uid FROM User")
                     abstract int[] getIds();
                 }
                 """) { dao, invocation ->
@@ -89,7 +93,7 @@
     fun testInterface() {
         singleDao("""
                 @Dao interface MyDao {
-                    @Query("SELECT id FROM users")
+                    @Query("SELECT uid FROM User")
                     abstract int[] getIds();
                 }
                 """) { dao, invocation ->
@@ -103,7 +107,7 @@
     fun testWithInsertAndQuery() {
         singleDao("""
                 @Dao abstract class MyDao {
-                    @Query("SELECT id FROM users")
+                    @Query("SELECT uid FROM User")
                     abstract int[] getIds();
                     @Insert
                     abstract void insert(User user);
@@ -118,6 +122,20 @@
         }.compilesWithoutError()
     }
 
+    @Test
+    fun skipQueryVerification() {
+        singleDao("""
+                @Dao @SkipQueryVerification interface MyDao {
+                    @Query("SELECT nonExistingField FROM User")
+                    abstract int[] getIds();
+                }
+                """) { dao, invocation ->
+            assertThat(dao.queryMethods.size, `is`(1))
+            val method = dao.queryMethods.first()
+            assertThat(method.name, `is`("getIds"))
+        }.compilesWithoutError()
+    }
+
     fun singleDao(vararg inputs: String, handler: (Dao, TestInvocation) -> Unit):
             CompileTester {
         return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
@@ -125,14 +143,20 @@
                         DAO_PREFIX + inputs.joinToString("\n")
                 ), COMMON.USER))
                 .processedWith(TestProcessor.builder()
-                        .forAnnotations(com.android.support.room.Dao::class)
+                        .forAnnotations(com.android.support.room.Dao::class,
+                                com.android.support.room.Entity::class)
                         .nextRunHandler { invocation ->
-                            val entity = invocation.roundEnv
+                            val dao = invocation.roundEnv
                                     .getElementsAnnotatedWith(
                                             com.android.support.room.Dao::class.java)
                                     .first()
                             val parser = DaoProcessor(invocation.context)
-                            val parsedDao = parser.parse(MoreElements.asType(entity))
+                            parser.dbVerifier = if (enableVerification) {
+                                createVerifierFromEntities(invocation)
+                            } else {
+                                null
+                            }
+                            val parsedDao = parser.parse(MoreElements.asType(dao))
                             handler(parsedDao, invocation)
                             true
                         }
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
index 3ec804b..9078341 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.support.room.processor
 
+import com.android.support.room.RoomProcessor
 import com.android.support.room.testing.TestInvocation
 import com.android.support.room.testing.TestProcessor
 import com.android.support.room.vo.Database
@@ -24,12 +25,14 @@
 import com.google.testing.compile.CompileTester
 import com.google.testing.compile.JavaFileObjects
 import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.ClassName
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import javax.tools.JavaFileObject
+import javax.tools.StandardLocation
 
 @RunWith(JUnit4::class)
 class DatabaseProcessorTest {
@@ -139,10 +142,7 @@
                 }
                 """)){ db, invocation ->
 
-        }.failsToCompile().withErrorContaining(
-                ProcessorErrors.missingTable("nonExistentTable", "foo.bar.BookDao", "loadAllBooks",
-                        "foo.bar.MyDb")
-        )
+        }.failsToCompile().withErrorContaining("no such table: nonExistentTable")
     }
 
     @Test
@@ -168,6 +168,96 @@
         )
     }
 
+    @Test
+    fun skipBadQueryVerification() {
+        singleDb(
+                """
+                @SkipQueryVerification
+                @Database(entities = {Book.class})
+                public abstract class MyDb extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """, BOOK, JavaFileObjects.forSourceString("foo.bar.BookDao",
+                """
+                package foo.bar;
+                import com.android.support.room.*;
+                @Dao
+                public interface BookDao {
+                    @Query("SELECT nonExistingField FROM Book")
+                    public java.util.List<Book> loadAllBooks();
+                }
+                """)){ db, invocation ->
+
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun multipleDatabases() {
+        val db1 = JavaFileObjects.forSourceString("foo.bar.Db1",
+                """
+                $DATABASE_PREFIX
+                @Database(entities = {Book.class})
+                public abstract class Db1 extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """)
+        val db2 = JavaFileObjects.forSourceString("foo.bar.Db2",
+                """
+                $DATABASE_PREFIX
+                @Database(entities = {Book.class})
+                public abstract class Db2 extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """)
+        val db1_2 = JavaFileObjects.forSourceString("foo.barx.Db1",
+                """
+                package foo.barx;
+                import com.android.support.room.*;
+                import foo.bar.*;
+                @Database(entities = {Book.class})
+                public abstract class Db1 extends RoomDatabase {
+                    abstract BookDao bookDao();
+                }
+                """)
+        Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(listOf(BOOK, BOOK_DAO, db1, db2, db1_2))
+                .processedWith(RoomProcessor())
+                .compilesWithoutError()
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", "Db1_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar", "Db2_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.barx", "Db1_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar",
+                        "BookDao_Db1_0_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar",
+                        "BookDao_Db1_1_Impl.class")
+                .and()
+                .generatesFileNamed(StandardLocation.CLASS_OUTPUT, "foo.bar",
+                        "BookDao_Db2_Impl.class")
+    }
+
+    @Test
+    fun twoDaoMethodsForTheSameDao() {
+        singleDb(
+                """
+                @Database(entities = {User.class})
+                public abstract class MyDb extends RoomDatabase {
+                    abstract UserDao userDao();
+                    abstract UserDao userDao2();
+                }
+                """, USER, USER_DAO){db, invocation -> }
+                .failsToCompile()
+                .withErrorContaining(ProcessorErrors.DAO_METHOD_CONFLICTS_WITH_OTHERS)
+                .and()
+                .withErrorContaining(ProcessorErrors.duplicateDao(
+                        ClassName.get("foo.bar", "UserDao"), listOf("userDao", "userDao2")
+                ))
+    }
+
     fun singleDb(input: String, vararg otherFiles: JavaFileObject,
                  handler: (Database, TestInvocation) -> Unit): CompileTester {
         return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/QueryMethodProcessorTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/QueryMethodProcessorTest.kt
index 3943e46..1099157 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/QueryMethodProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/QueryMethodProcessorTest.kt
@@ -37,6 +37,7 @@
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeVariableName
+import createVerifierFromEntities
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.instanceOf
 import org.hamcrest.CoreMatchers.notNullValue
@@ -44,12 +45,13 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import org.junit.runners.Parameterized
 import javax.lang.model.type.TypeKind.INT
 import javax.lang.model.type.TypeMirror
 
 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
-@RunWith(JUnit4::class)
-class QueryMethodProcessorTest {
+@RunWith(Parameterized::class)
+class QueryMethodProcessorTest(val enableVerification : Boolean) {
     companion object {
         const val DAO_PREFIX = """
                 package foo.bar;
@@ -58,13 +60,16 @@
                 abstract class MyClass {
                 """
         const val DAO_SUFFIX = "}"
+        @Parameterized.Parameters(name = "enableDbVerification={0}")
+        @JvmStatic
+        fun getParams() = arrayOf(true, false)
     }
 
     @Test
     fun testReadNoParams() {
         singleQueryMethod(
                 """
-                @Query("SELECT * from users")
+                @Query("SELECT * from User")
                 abstract public int[] foo();
                 """) { parsedQuery, invocation ->
             assertThat(parsedQuery.name, `is`("foo"))
@@ -78,7 +83,7 @@
     fun testSingleParam() {
         singleQueryMethod(
                 """
-                @Query("SELECT * from users")
+                @Query("SELECT * from User")
                 abstract public long foo(int x);
                 """) { parsedQuery, invocation ->
             assertThat(parsedQuery.name, `is`("foo"))
@@ -95,7 +100,7 @@
     fun testVarArgs() {
         singleQueryMethod(
                 """
-                @Query("SELECT * from users where id in (?)")
+                @Query("SELECT * from User where uid in (?)")
                 abstract public long foo(int... ids);
                 """) { parsedQuery, invocation ->
             assertThat(parsedQuery.name, `is`("foo"))
@@ -113,7 +118,7 @@
     fun testParamBindingMatchingNoName() {
         singleQueryMethod(
                 """
-                @Query("SELECT id from users where id = ?")
+                @Query("SELECT uid from User where uid = ?")
                 abstract public long getIdById(int id);
                 """) { parsedQuery, invocation ->
             val section = parsedQuery.query.bindSections.first()
@@ -128,7 +133,7 @@
     fun testParamBindingMatchingSimpleBind() {
         singleQueryMethod(
                 """
-                @Query("SELECT id from users where id = :id")
+                @Query("SELECT uid from User where uid = :id")
                 abstract public long getIdById(int id);
                 """) { parsedQuery, invocation ->
             val section = parsedQuery.query.bindSections.first()
@@ -144,7 +149,7 @@
     fun testParamBindingTwoBindVarsIntoTheSameParameter() {
         singleQueryMethod(
                 """
-                @Query("SELECT id from users where id = :id OR uid = :id")
+                @Query("SELECT uid from User where uid = :id OR uid = :id")
                 abstract public long getIdById(int id);
                 """) { parsedQuery, invocation ->
             val section = parsedQuery.query.bindSections[0]
@@ -162,7 +167,7 @@
     fun testMissingParameterForBinding() {
         singleQueryMethod(
                 """
-                @Query("SELECT id from users where id = :id OR uid = :uid")
+                @Query("SELECT uid from User where uid = :id OR uid = :uid")
                 abstract public long getIdById(int id);
                 """) { parsedQuery, invocation ->
             val section = parsedQuery.query.bindSections[0]
@@ -183,7 +188,7 @@
     fun test2MissingParameterForBinding() {
         singleQueryMethod(
                 """
-                @Query("SELECT id from users where foo = :bar AND id = :id OR uid = :uid")
+                @Query("SELECT uid from User where name = :bar AND uid = :id OR uid = :uid")
                 abstract public long getIdById(int id);
                 """) { parsedQuery, invocation ->
             val bar = parsedQuery.query.bindSections[0]
@@ -206,7 +211,7 @@
     fun testUnusedParameters() {
         singleQueryMethod(
                 """
-                @Query("SELECT id from users where foo = :bar")
+                @Query("SELECT uid from User where name = :bar")
                 abstract public long getIdById(int bar, int whyNotUseMe);
                 """) { parsedQuery, invocation ->
             val bar = parsedQuery.query.bindSections[0]
@@ -223,7 +228,7 @@
     fun testNameWithUnderscore() {
         singleQueryMethod(
                 """
-                @Query("select * from users where id = :_blah")
+                @Query("select * from User where uid = :_blah")
                 abstract public long getSth(int _blah);
                 """
         ) { parsedQuery, invocation -> }
@@ -235,7 +240,7 @@
     fun testGenericReturnType() {
         singleQueryMethod(
                 """
-                @Query("select * from users")
+                @Query("select * from User")
                 abstract public <T> java.util.List<T> foo(int x);
                 """) { parsedQuery, invocation ->
             val expected: TypeName = ParameterizedTypeName.get(ClassName.get(List::class.java),
@@ -262,7 +267,7 @@
         singleQueryMethod(
                 """
                 static abstract class BaseModel<T> {
-                    @Query("select COUNT(*) from users")
+                    @Query("select COUNT(*) from User")
                     abstract public T getT();
                 }
                 @Dao
@@ -279,7 +284,7 @@
         singleQueryMethod(
                 """
                 static abstract class BaseModel<T> {
-                    @Query("select COUNT(*) from users where :t")
+                    @Query("select COUNT(*) from User where :t")
                     abstract public int getT(T t);
                 }
                 @Dao
@@ -296,7 +301,7 @@
     fun testReadDeleteWithBadReturnType() {
         singleQueryMethod(
                 """
-                @Query("DELETE FROM users where id = ?")
+                @Query("DELETE from User where uid = ?")
                 abstract public float foo(int id);
                 """) { parsedQuery, invocation ->
         }.failsToCompile().withErrorContaining(
@@ -308,7 +313,7 @@
     fun testSimpleDelete() {
         singleQueryMethod(
                 """
-                @Query("DELETE FROM users where id = ?")
+                @Query("DELETE from User where uid = ?")
                 abstract public int foo(int id);
                 """) { parsedQuery, invocation ->
             assertThat(parsedQuery.name, `is`("foo"))
@@ -321,7 +326,7 @@
     fun testVoidDeleteQuery() {
         singleQueryMethod(
                 """
-                @Query("DELETE FROM users where id = ?")
+                @Query("DELETE from User where uid = ?")
                 abstract public void foo(int id);
                 """) { parsedQuery, invocation ->
             assertThat(parsedQuery.name, `is`("foo"))
@@ -334,7 +339,7 @@
     fun testVoidUpdateQuery() {
         singleQueryMethod(
                 """
-                @Query("update users set name = :name")
+                @Query("update user set name = :name")
                 abstract public void updateAllNames(String name);
                 """) { parsedQuery, invocation ->
             assertThat(parsedQuery.name, `is`("updateAllNames"))
@@ -349,7 +354,7 @@
     fun testLiveDataQuery() {
         singleQueryMethod(
                 """
-                @Query("select name from user where id = :id")
+                @Query("select name from user where uid = :id")
                 abstract ${LifecyclesTypeNames.LIVE_DATA}<String> nameLiveData(String id);
                 """
         ) { parsedQuery, invocation ->
@@ -365,7 +370,7 @@
     fun testNonSelectLiveData() {
         singleQueryMethod(
                 """
-                @Query("delete from user where id = :id")
+                @Query("delete from user where uid = :id")
                 abstract ${LifecyclesTypeNames.LIVE_DATA}<Integer> deleteLiveData(String id);
                 """
         ) { parsedQuery, invocation ->
@@ -373,13 +378,28 @@
                 .withErrorContaining(ProcessorErrors.DELETION_METHODS_MUST_RETURN_VOID_OR_INT)
     }
 
+    @Test
+    fun skipVerification() {
+        singleQueryMethod(
+                """
+                @SkipQueryVerification
+                @Query("SELECT foo from User")
+                abstract public int[] foo();
+                """) { parsedQuery, invocation ->
+            assertThat(parsedQuery.name, `is`("foo"))
+            assertThat(parsedQuery.parameters.size, `is`(0))
+            assertThat(parsedQuery.returnType.typeName(),
+                    `is`(ArrayTypeName.of(TypeName.INT) as TypeName))
+        }.compilesWithoutError()
+    }
+
     fun singleQueryMethod(vararg input: String,
                           handler: (QueryMethod, TestInvocation) -> Unit):
             CompileTester {
         return assertAbout(JavaSourcesSubjectFactory.javaSources())
                 .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyClass",
                         DAO_PREFIX + input.joinToString("\n") + DAO_SUFFIX
-                ), COMMON.LIVE_DATA, COMMON.COMPUTABLE_LIVE_DATA))
+                ), COMMON.LIVE_DATA, COMMON.COMPUTABLE_LIVE_DATA, COMMON.USER))
                 .processedWith(TestProcessor.builder()
                         .forAnnotations(Query::class, Dao::class)
                         .nextRunHandler { invocation ->
@@ -394,7 +414,13 @@
                                                         }
                                         )
                                     }.filter { it.second.isNotEmpty() }.first()
+                            val verifier = if (enableVerification) {
+                                createVerifierFromEntities(invocation)
+                            } else {
+                                null
+                            }
                             val parser = QueryMethodProcessor(invocation.context)
+                            parser.dbVerifier = verifier
                             val parsedQuery = parser.parse(MoreTypes.asDeclared(owner.asType()),
                                     MoreElements.asExecutable(methods.first()))
                             handler(parsedQuery, invocation)
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/testing/test_util.kt b/room/compiler/src/test/kotlin/com/android/support/room/testing/test_util.kt
index 5e25b90..fa11d20 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/testing/test_util.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/testing/test_util.kt
@@ -14,17 +14,23 @@
  * limitations under the License.
  */
 
+import com.android.support.room.Entity
 import com.android.support.room.Query
 import com.android.support.room.ext.LifecyclesTypeNames
 import com.android.support.room.ext.RoomTypeNames
+import com.android.support.room.processor.EntityProcessor
 import com.android.support.room.testing.TestInvocation
 import com.android.support.room.testing.TestProcessor
+import com.android.support.room.verifier.DatabaseVerifier
+import com.google.auto.common.MoreElements
 import com.google.common.truth.Truth
 import com.google.testing.compile.CompileTester
 import com.google.testing.compile.JavaFileObjects
 import com.google.testing.compile.JavaSourceSubjectFactory
 import com.squareup.javapoet.ClassName
+import org.mockito.Mockito
 import java.io.File
+import javax.lang.model.element.Element
 import javax.tools.JavaFileObject
 
 object COMMON {
@@ -74,3 +80,11 @@
     val contents = File("src/test/data/$fileName").readText(Charsets.UTF_8)
     return JavaFileObjects.forSourceString(qName, contents)
 }
+
+fun createVerifierFromEntities(invocation: TestInvocation) : DatabaseVerifier {
+    val entities = invocation.roundEnv.getElementsAnnotatedWith(Entity::class.java).map {
+        EntityProcessor(invocation.context).parse(MoreElements.asType(it))
+    }
+    return DatabaseVerifier.create(invocation.context, Mockito.mock(Element::class.java),
+            entities)!!
+}
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/verifier/DatabaseVerifierTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/verifier/DatabaseVerifierTest.kt
new file mode 100644
index 0000000..3bb1caa
--- /dev/null
+++ b/room/compiler/src/test/kotlin/com/android/support/room/verifier/DatabaseVerifierTest.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.support.room.verifier
+
+import collect
+import columnNames
+import com.android.support.room.parser.SQLTypeAffinity
+import com.android.support.room.processor.Context
+import com.android.support.room.testing.TestInvocation
+import com.android.support.room.vo.CallType
+import com.android.support.room.vo.Database
+import com.android.support.room.vo.Entity
+import com.android.support.room.vo.Field
+import com.android.support.room.vo.FieldGetter
+import com.android.support.room.vo.FieldSetter
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.hasItem
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.mock
+import simpleRun
+import java.sql.Connection
+import javax.lang.model.element.Element
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+@RunWith(JUnit4::class)
+class DatabaseVerifierTest {
+    @Test
+    fun testSimpleDatabase() {
+        simpleRun { invocation ->
+            val verifier = createVerifier(invocation)
+            val stmt = verifier.connection.createStatement()
+            val rs = stmt.executeQuery("select * from sqlite_master WHERE type='table'")
+            assertThat(
+                    rs.collect { set -> set.getString("name") }, hasItem(`is`("User")))
+            val table = verifier.connection.prepareStatement("select * from User")
+            assertThat(table.columnNames(), `is`(listOf("id", "name", "lastName", "ratio")))
+
+            assertThat(getPrimaryKeys(verifier.connection, "User"), `is`(listOf("id")))
+        }.compilesWithoutError()
+    }
+
+    fun createVerifier(invocation : TestInvocation) : DatabaseVerifier {
+        return DatabaseVerifier.create(invocation.context, mock(Element::class.java),
+                userDb(invocation.context).entities)!!
+    }
+
+    @Test
+    fun testFullEntityQuery() {
+        validQueryTest("select * from User") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            ColumnInfo("id", SQLTypeAffinity.INTEGER),
+                            ColumnInfo("name", SQLTypeAffinity.TEXT),
+                            ColumnInfo("lastName", SQLTypeAffinity.TEXT),
+                            ColumnInfo("ratio", SQLTypeAffinity.REAL)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testPartialFields() {
+        validQueryTest("select id, lastName from User") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            ColumnInfo("id", SQLTypeAffinity.INTEGER),
+                            ColumnInfo("lastName", SQLTypeAffinity.TEXT)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testRenamedField() {
+        validQueryTest("select id as myId, lastName from User") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            ColumnInfo("myId", SQLTypeAffinity.INTEGER),
+                            ColumnInfo("lastName", SQLTypeAffinity.TEXT)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testGrouped() {
+        validQueryTest("select MAX(ratio) from User GROUP BY name") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            // unfortunately, we don't get this information
+                            ColumnInfo("MAX(ratio)", SQLTypeAffinity.NULL)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testConcat() {
+        validQueryTest("select name || lastName as mergedName from User") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            // unfortunately, we don't get this information
+                            ColumnInfo("mergedName", SQLTypeAffinity.NULL)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testResultWithArgs() {
+        validQueryTest("select id, name || lastName as mergedName from User where name LIKE ?") {
+            assertThat(it, `is`(
+                    QueryResultInfo(listOf(
+                            // unfortunately, we don't get this information
+                            ColumnInfo("id", SQLTypeAffinity.INTEGER),
+                            ColumnInfo("mergedName", SQLTypeAffinity.NULL)
+                    ))))
+        }
+    }
+
+    @Test
+    fun testDeleteQuery() {
+        validQueryTest("delete from User where name LIKE ?") {
+            assertThat(it, `is`(QueryResultInfo(emptyList())))
+        }
+    }
+
+    @Test
+    fun testUpdateQuery() {
+        validQueryTest("update User set name = ? WHERE id = ?") {
+            assertThat(it, `is`(QueryResultInfo(emptyList())))
+        }
+    }
+
+    @Test
+    fun testBadQuery() {
+        simpleRun { invocation ->
+            val verifier = createVerifier(invocation)
+            val (columns, error) = verifier.analyze("select foo from User")
+            assertThat(error, notNullValue())
+        }.compilesWithoutError()
+    }
+
+    private fun validQueryTest(sql: String, cb: (QueryResultInfo) -> Unit) {
+        simpleRun { invocation ->
+            val verifier = createVerifier(invocation)
+            val info = verifier.analyze(sql)
+            cb(info)
+        }.compilesWithoutError()
+    }
+
+    private fun userDb(context: Context): Database {
+        return database(entity("User",
+                primaryField(context, "id", primitive(context, TypeKind.INT)),
+                field(context, "name", context.COMMON_TYPES.STRING),
+                field(context, "lastName", context.COMMON_TYPES.STRING),
+                field(context, "ratio", primitive(context, TypeKind.FLOAT))))
+    }
+
+    private fun database(vararg entities: Entity): Database {
+        return Database(
+                element = mock(Element::class.java),
+                type = mock(TypeMirror::class.java),
+                entities = entities.toList(),
+                daoMethods = emptyList())
+    }
+
+    private fun entity(tableName: String, vararg fields: Field): Entity {
+        return Entity(
+                element = mock(TypeElement::class.java),
+                tableName = tableName,
+                type = mock(DeclaredType::class.java),
+                fields = fields.toList()
+        )
+    }
+
+    private fun field(context: Context, name: String, type: TypeMirror): Field {
+        val f = Field(
+                element = mock(Element::class.java),
+                name = name,
+                type = type,
+                primaryKey = false,
+                columnName = name
+        )
+        assignGetterSetter(context, f, name, type)
+        return f
+    }
+
+    private fun primaryField(context: Context, name: String, type: TypeMirror): Field {
+        val f = Field(
+                element = mock(Element::class.java),
+                name = name,
+                type = type,
+                primaryKey = true,
+                columnName = name
+        )
+        assignGetterSetter(context, f, name, type)
+        return f
+    }
+
+    private fun assignGetterSetter(context: Context, f: Field, name: String, type: TypeMirror) {
+        f.getter = FieldGetter(name, type, CallType.FIELD,
+                context.typeAdapterStore.findColumnTypeAdapter(f.type))
+        f.setter = FieldSetter(name, type, CallType.FIELD,
+                context.typeAdapterStore.findColumnTypeAdapter(f.type))
+    }
+
+    private fun primitive(context: Context, kind: TypeKind): PrimitiveType {
+        return context.processingEnv.typeUtils.getPrimitiveType(kind)
+    }
+
+    private fun getPrimaryKeys(connection: Connection, tableName: String): List<String> {
+        val stmt = connection.createStatement()
+        val resultSet = stmt.executeQuery("PRAGMA table_info($tableName)")
+        return resultSet.collect {
+            Pair(it.getString("name"), it.getInt("pk"))
+        }
+                .filter { it.second > 0 }
+                .sortedBy { it.second }
+                .map { it.first }
+
+    }
+}