Fail when non-null fields are missing in query

This CL change Room query verification to fail IF one of
the missing columns is annotated with @NonNull. It is
probably un-intended by the developer so it is better
to fail instead of a warning.

Bug: 67115337
Test: QueryMethodProcessorTest#pojo_missingNonNull
Change-Id: I22bd5797eb5fbc22d0dd242683cbbdf0c60457db
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt
index 2ab0182..8058ff9 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/processor/ProcessorErrors.kt
@@ -199,6 +199,16 @@
                 """.trim()
     }
 
+    fun pojoMissingNonNull(pojoTypeName: TypeName, missingPojoFields: List<String>,
+                           allQueryColumns: List<String>) : String {
+        return """
+        The columns returned by the query does not have the fields
+        [${missingPojoFields.joinToString(",")}] in $pojoTypeName even though they are
+        annotated as non-null or primitive.
+        Columns returned by the query: [${allQueryColumns.joinToString(",")}]
+        """.trim()
+    }
+
     fun cursorPojoMismatch(pojoTypeName: TypeName,
                            unusedColumns: List<String>, allColumns: List<String>,
                            unusedFields: List<Field>, allFields: List<Field>): String {
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PojoRowAdapter.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PojoRowAdapter.kt
index 062b179..a013dc7 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PojoRowAdapter.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/query/result/PojoRowAdapter.kt
@@ -19,7 +19,6 @@
 import android.arch.persistence.room.ext.L
 import android.arch.persistence.room.ext.S
 import android.arch.persistence.room.ext.T
-import android.arch.persistence.room.ext.typeName
 import android.arch.persistence.room.processor.Context
 import android.arch.persistence.room.processor.ProcessorErrors
 import android.arch.persistence.room.solver.CodeGenScope
@@ -71,6 +70,13 @@
             )
             context.logger.w(Warning.CURSOR_MISMATCH, null, warningMsg)
         }
+        val nonNulls = remainingFields.filter { it.nonNull }
+        if (nonNulls.isNotEmpty()) {
+            context.logger.e(ProcessorErrors.pojoMissingNonNull(
+                    pojoTypeName = pojo.typeName,
+                    missingPojoFields = nonNulls.map { it.name },
+                    allQueryColumns = info.columns.map { it.name }))
+        }
         if (matchedFields.isEmpty()) {
             context.logger.e(ProcessorErrors.CANNOT_FIND_QUERY_RESULT_ADAPTER)
         }
diff --git a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/QueryMethodProcessorTest.kt b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/QueryMethodProcessorTest.kt
index 7e892b7..a2cb768 100644
--- a/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/QueryMethodProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/android/arch/persistence/room/processor/QueryMethodProcessorTest.kt
@@ -70,6 +70,7 @@
     companion object {
         const val DAO_PREFIX = """
                 package foo.bar;
+                import android.support.annotation.NonNull;
                 import android.arch.persistence.room.*;
                 @Dao
                 abstract class MyClass {
@@ -599,6 +600,30 @@
     }
 
     @Test
+    fun pojo_missingNonNull() {
+        pojoTest("""
+            @NonNull
+            String name;
+            String lastName;
+            """, listOf("lastName")) { adapter, _, _ ->
+            assertThat(adapter?.mapping?.unusedColumns, `is`(emptyList()))
+            assertThat(adapter?.mapping?.unusedFields, `is`(
+                    adapter?.pojo?.fields?.filter { it.name == "name" }
+            ))
+        }?.failsToCompile()?.withWarningContaining(
+                ProcessorErrors.cursorPojoMismatch(
+                        pojoTypeName = POJO,
+                        unusedColumns = emptyList(),
+                        unusedFields = listOf(createField("name")),
+                        allColumns = listOf("lastName"),
+                        allFields = listOf(createField("name"), createField("lastName"))
+                ))?.and()?.withErrorContaining(
+                ProcessorErrors.pojoMissingNonNull(pojoTypeName = POJO,
+                        missingPojoFields = listOf("name"),
+                        allQueryColumns = listOf("lastName")))
+    }
+
+    @Test
     fun pojo_tooManyFieldsAndColumns() {
         pojoTest("""
             String name;
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java
index 7bb137f..18e8d93 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java
@@ -35,16 +35,16 @@
     @Query("SELECT * from School WHERE address_street LIKE '%' || :street || '%'")
     public abstract List<School> findByStreet(String street);
 
-    @Query("SELECT mName, manager_mName FROM School")
+    @Query("SELECT mId, mName, manager_mName FROM School")
     public abstract List<School> schoolAndManagerNames();
 
-    @Query("SELECT mName, manager_mName FROM School")
+    @Query("SELECT mId, mName, manager_mName FROM School")
     @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
     public abstract List<SchoolRef> schoolAndManagerNamesAsPojo();
 
     @Query("SELECT address_lat as lat, address_lng as lng FROM School WHERE mId = :schoolId")
     public abstract Coordinates loadCoordinates(int schoolId);
 
-    @Query("SELECT address_lat, address_lng FROM School WHERE mId = :schoolId")
+    @Query("SELECT mId, address_lat, address_lng FROM School WHERE mId = :schoolId")
     public abstract School loadCoordinatesAsSchool(int schoolId);
 }