ProcessorErrors.kt revision cab865bed3b4e9df8b86b0b16c589c4e6dd0b71d
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.arch.persistence.room.processor
18
19import android.arch.persistence.room.Delete
20import android.arch.persistence.room.Insert
21import android.arch.persistence.room.Query
22import android.arch.persistence.room.RawQuery
23import android.arch.persistence.room.Update
24import android.arch.persistence.room.ext.RoomTypeNames
25import android.arch.persistence.room.parser.SQLTypeAffinity
26import android.arch.persistence.room.vo.CustomTypeConverter
27import android.arch.persistence.room.vo.Field
28import com.squareup.javapoet.TypeName
29
30object ProcessorErrors {
31    private fun String.trim(): String {
32        return this.trimIndent().replace("\n", " ")
33    }
34    val MISSING_QUERY_ANNOTATION = "Query methods must be annotated with ${Query::class.java}"
35    val MISSING_INSERT_ANNOTATION = "Insertion methods must be annotated with ${Insert::class.java}"
36    val MISSING_DELETE_ANNOTATION = "Deletion methods must be annotated with ${Delete::class.java}"
37    val MISSING_UPDATE_ANNOTATION = "Update methods must be annotated with ${Update::class.java}"
38    val MISSING_RAWQUERY_ANNOTATION = "RawQuery methods must be annotated with" +
39            " ${RawQuery::class.java}"
40    val INVALID_ON_CONFLICT_VALUE = "On conflict value must be one of @OnConflictStrategy values."
41    val INVALID_INSERTION_METHOD_RETURN_TYPE = "Methods annotated with @Insert can return either" +
42            " void, long, Long, long[], Long[] or List<Long>."
43    val TRANSACTION_REFERENCE_DOCS = "https://developer.android.com/reference/android/arch/" +
44            "persistence/room/Transaction.html"
45
46    fun insertionMethodReturnTypeMismatch(definedReturn: TypeName,
47                                          expectedReturnTypes: List<TypeName>): String {
48        return "Method returns $definedReturn but it should return one of the following: `" +
49                expectedReturnTypes.joinToString(", ") + "`. If you want to return the list of" +
50                " row ids from the query, your insertion method can receive only 1 parameter."
51    }
52
53    val ABSTRACT_METHOD_IN_DAO_MISSING_ANY_ANNOTATION = "Abstract method in DAO must be annotated" +
54            " with ${Query::class.java} AND ${Insert::class.java}"
55    val INVALID_ANNOTATION_COUNT_IN_DAO_METHOD = "An abstract DAO method must be" +
56            " annotated with one and only one of the following annotations: " +
57            DaoProcessor.PROCESSED_ANNOTATIONS.joinToString(",") {
58        it.java.simpleName
59    }
60    val CANNOT_RESOLVE_RETURN_TYPE = "Cannot resolve return type for %s"
61    val CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_METHODS = "Cannot use unbound generics in query" +
62            " methods. It must be bound to a type through base Dao class."
63    val CANNOT_USE_UNBOUND_GENERICS_IN_INSERTION_METHODS = "Cannot use unbound generics in" +
64            " insertion methods. It must be bound to a type through base Dao class."
65    val CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_FIELDS = "Cannot use unbound fields in entities."
66    val CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES = "Cannot use unbound generics in Dao classes." +
67            " If you are trying to create a base DAO, create a normal class, extend it with type" +
68            " params then mark the subclass with @Dao."
69    val CANNOT_FIND_GETTER_FOR_FIELD = "Cannot find getter for field."
70    val CANNOT_FIND_SETTER_FOR_FIELD = "Cannot find setter for field."
71    val MISSING_PRIMARY_KEY = "An entity must have at least 1 field annotated with @PrimaryKey"
72    val AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT = "If a primary key is annotated with" +
73            " autoGenerate, its type must be int, Integer, long or Long."
74    val AUTO_INCREMENT_EMBEDDED_HAS_MULTIPLE_FIELDS = "When @PrimaryKey annotation is used on a" +
75            " field annotated with @Embedded, the embedded class should have only 1 field."
76
77    fun multiplePrimaryKeyAnnotations(primaryKeys: List<String>): String {
78        return """
79                You cannot have multiple primary keys defined in an Entity. If you
80                want to declare a composite primary key, you should use @Entity#primaryKeys and
81                not use @PrimaryKey. Defined Primary Keys:
82                ${primaryKeys.joinToString(", ")}""".trim()
83    }
84
85    fun primaryKeyColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
86        return "$columnName referenced in the primary key does not exists in the Entity." +
87                " Available column names:${allColumns.joinToString(", ")}"
88    }
89
90    val DAO_MUST_BE_AN_ABSTRACT_CLASS_OR_AN_INTERFACE = "Dao class must be an abstract class or" +
91            " an interface"
92    val DATABASE_MUST_BE_ANNOTATED_WITH_DATABASE = "Database must be annotated with @Database"
93    val DAO_MUST_BE_ANNOTATED_WITH_DAO = "Dao class must be annotated with @Dao"
94
95    fun daoMustHaveMatchingConstructor(daoName: String, dbName: String): String {
96        return """
97                $daoName needs to have either an empty constructor or a constructor that takes
98                $dbName as its only parameter.
99                """.trim()
100    }
101
102    val ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY = "Entity class must be annotated with @Entity"
103    val DATABASE_ANNOTATION_MUST_HAVE_LIST_OF_ENTITIES = "@Database annotation must specify list" +
104            " of entities"
105    val COLUMN_NAME_CANNOT_BE_EMPTY = "Column name cannot be blank. If you don't want to set it" +
106            ", just remove the @ColumnInfo annotation or use @ColumnInfo.INHERIT_FIELD_NAME."
107
108    val ENTITY_TABLE_NAME_CANNOT_BE_EMPTY = "Entity table name cannot be blank. If you don't want" +
109            " to set it, just remove the tableName property."
110
111    val CANNOT_BIND_QUERY_PARAMETER_INTO_STMT = "Query method parameters should either be a" +
112            " type that can be converted into a database column or a List / Array that contains" +
113            " such type. You can consider adding a Type Adapter for this."
114
115    val QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE = "Query/Insert method parameters cannot " +
116            "start with underscore (_)."
117
118    val CANNOT_FIND_QUERY_RESULT_ADAPTER = "Not sure how to convert a Cursor to this method's " +
119            "return type"
120
121    val INSERTION_DOES_NOT_HAVE_ANY_PARAMETERS_TO_INSERT = "Method annotated with" +
122            " @Insert but does not have any parameters to insert."
123
124    val DELETION_MISSING_PARAMS = "Method annotated with" +
125            " @Delete but does not have any parameters to delete."
126
127    val UPDATE_MISSING_PARAMS = "Method annotated with" +
128            " @Update but does not have any parameters to update."
129
130    val TRANSACTION_METHOD_MODIFIERS = "Method annotated with @Transaction must not be " +
131            "private, final, or abstract. It can be abstract only if the method is also" +
132            " annotated with @Query."
133
134    val TRANSACTION_MISSING_ON_RELATION = "The return value includes a Pojo with a @Relation." +
135            " It is usually desired to annotate this method with @Transaction to avoid" +
136            " possibility of inconsistent results between the Pojo and its relations. See " +
137            TRANSACTION_REFERENCE_DOCS + " for details."
138
139    val CANNOT_FIND_ENTITY_FOR_SHORTCUT_QUERY_PARAMETER = "Type of the parameter must be a class " +
140            "annotated with @Entity or a collection/array of it."
141
142    val DB_MUST_EXTEND_ROOM_DB = "Classes annotated with @Database should extend " +
143            RoomTypeNames.ROOM_DB
144
145    val LIVE_DATA_QUERY_WITHOUT_SELECT = "LiveData return type can only be used with SELECT" +
146            " queries."
147
148    val OBSERVABLE_QUERY_NOTHING_TO_OBSERVE = "Observable query return type (LiveData, Flowable" +
149            ", DataSource, DataSourceFactory etc) can only be used with SELECT queries that" +
150            " directly or indirectly (via @Relation, for example) access at least one table. For" +
151            " @RawQuery, you should specify the list of tables to be observed via the" +
152            " observedEntities field."
153
154    val RECURSIVE_REFERENCE_DETECTED = "Recursive referencing through @Embedded and/or @Relation " +
155            "detected: %s"
156
157    private val TOO_MANY_MATCHING_GETTERS = "Ambiguous getter for %s. All of the following " +
158            "match: %s. You can @Ignore the ones that you don't want to match."
159
160    fun tooManyMatchingGetters(field: Field, methodNames: List<String>): String {
161        return TOO_MANY_MATCHING_GETTERS.format(field, methodNames.joinToString(", "))
162    }
163
164    private val TOO_MANY_MATCHING_SETTERS = "Ambiguous setter for %s. All of the following " +
165            "match: %s. You can @Ignore the ones that you don't want to match."
166
167    fun tooManyMatchingSetter(field: Field, methodNames: List<String>): String {
168        return TOO_MANY_MATCHING_SETTERS.format(field, methodNames.joinToString(", "))
169    }
170
171    val CANNOT_FIND_COLUMN_TYPE_ADAPTER = "Cannot figure out how to save this field into" +
172            " database. You can consider adding a type converter for it."
173
174    val CANNOT_FIND_STMT_BINDER = "Cannot figure out how to bind this field into a statement."
175
176    val CANNOT_FIND_CURSOR_READER = "Cannot figure out how to read this field from a cursor."
177
178    private val MISSING_PARAMETER_FOR_BIND = "Each bind variable in the query must have a" +
179            " matching method parameter. Cannot find method parameters for %s."
180
181    fun missingParameterForBindVariable(bindVarName: List<String>): String {
182        return MISSING_PARAMETER_FOR_BIND.format(bindVarName.joinToString(", "))
183    }
184
185    private val UNUSED_QUERY_METHOD_PARAMETER = "Unused parameter%s: %s"
186    fun unusedQueryMethodParameter(unusedParams: List<String>): String {
187        return UNUSED_QUERY_METHOD_PARAMETER.format(
188                if (unusedParams.size > 1) "s" else "",
189                unusedParams.joinToString(","))
190    }
191
192    private val DUPLICATE_TABLES = "Table name \"%s\" is used by multiple entities: %s"
193    fun duplicateTableNames(tableName: String, entityNames: List<String>): String {
194        return DUPLICATE_TABLES.format(tableName, entityNames.joinToString(", "))
195    }
196
197    val DELETION_METHODS_MUST_RETURN_VOID_OR_INT = "Deletion methods must either return void or" +
198            " return int (the number of deleted rows)."
199
200    val UPDATE_METHODS_MUST_RETURN_VOID_OR_INT = "Update methods must either return void or" +
201            " return int (the number of updated rows)."
202
203    val DAO_METHOD_CONFLICTS_WITH_OTHERS = "Dao method has conflicts."
204
205    fun duplicateDao(dao: TypeName, methodNames: List<String>): String {
206        return """
207                All of these functions [${methodNames.joinToString(", ")}] return the same DAO
208                class [$dao].
209                A database can use a DAO only once so you should remove ${methodNames.size - 1} of
210                these conflicting DAO methods. If you are implementing any of these to fulfill an
211                interface, don't make it abstract, instead, implement the code that calls the
212                other one.
213                """.trim()
214    }
215
216    fun pojoMissingNonNull(pojoTypeName: TypeName, missingPojoFields: List<String>,
217                           allQueryColumns: List<String>): String {
218        return """
219        The columns returned by the query does not have the fields
220        [${missingPojoFields.joinToString(",")}] in $pojoTypeName even though they are
221        annotated as non-null or primitive.
222        Columns returned by the query: [${allQueryColumns.joinToString(",")}]
223        """.trim()
224    }
225
226    fun cursorPojoMismatch(pojoTypeName: TypeName,
227                           unusedColumns: List<String>, allColumns: List<String>,
228                           unusedFields: List<Field>, allFields: List<Field>): String {
229        val unusedColumnsWarning = if (unusedColumns.isNotEmpty()) {
230            """
231                The query returns some columns [${unusedColumns.joinToString(", ")}] which are not
232                use by $pojoTypeName. You can use @ColumnInfo annotation on the fields to specify
233                the mapping.
234            """.trim()
235        } else {
236            ""
237        }
238        val unusedFieldsWarning = if (unusedFields.isNotEmpty()) {
239            """
240                $pojoTypeName has some fields
241                [${unusedFields.joinToString(", ") { it.columnName }}] which are not returned by the
242                query. If they are not supposed to be read from the result, you can mark them with
243                @Ignore annotation.
244            """.trim()
245        } else {
246            ""
247        }
248        return """
249            $unusedColumnsWarning
250            $unusedFieldsWarning
251            You can suppress this warning by annotating the method with
252            @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH).
253            Columns returned by the query: ${allColumns.joinToString(", ")}.
254            Fields in $pojoTypeName: ${allFields.joinToString(", ") { it.columnName }}.
255            """.trim()
256    }
257
258    val TYPE_CONVERTER_UNBOUND_GENERIC = "Cannot use unbound generics in Type Converters."
259    val TYPE_CONVERTER_BAD_RETURN_TYPE = "Invalid return type for a type converter."
260    val TYPE_CONVERTER_MUST_RECEIVE_1_PARAM = "Type converters must receive 1 parameter."
261    val TYPE_CONVERTER_EMPTY_CLASS = "Class is referenced as a converter but it does not have any" +
262            " converter methods."
263    val TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR = "Classes that are used as TypeConverters must" +
264            " have no-argument public constructors."
265    val TYPE_CONVERTER_MUST_BE_PUBLIC = "Type converters must be public."
266
267    fun duplicateTypeConverters(converters: List<CustomTypeConverter>): String {
268        return "Multiple methods define the same conversion. Conflicts with these:" +
269                " ${converters.joinToString(", ") { it.toString() }}"
270    }
271
272    // TODO must print field paths.
273    val POJO_FIELD_HAS_DUPLICATE_COLUMN_NAME = "Field has non-unique column name."
274
275    fun pojoDuplicateFieldNames(columnName: String, fieldPaths: List<String>): String {
276        return "Multiple fields have the same columnName: $columnName." +
277                " Field names: ${fieldPaths.joinToString(", ")}."
278    }
279
280    fun embeddedPrimaryKeyIsDropped(entityQName: String, fieldName: String): String {
281        return "Primary key constraint on $fieldName is ignored when being merged into " +
282                entityQName
283    }
284
285    val INDEX_COLUMNS_CANNOT_BE_EMPTY = "List of columns in an index cannot be empty"
286
287    fun indexColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
288        return "$columnName referenced in the index does not exists in the Entity." +
289                " Available column names:${allColumns.joinToString(", ")}"
290    }
291
292    fun duplicateIndexInEntity(indexName: String): String {
293        return "There are multiple indices with name $indexName. This happen if you've declared" +
294                " the same index multiple times or different indices have the same name. See" +
295                " @Index documentation for details."
296    }
297
298    fun duplicateIndexInDatabase(indexName: String, indexPaths: List<String>): String {
299        return "There are multiple indices with name $indexName. You should rename " +
300                "${indexPaths.size - 1} of these to avoid the conflict:" +
301                "${indexPaths.joinToString(", ")}."
302    }
303
304    fun droppedEmbeddedFieldIndex(fieldPath: String, grandParent: String): String {
305        return "The index will be dropped when being merged into $grandParent" +
306                "($fieldPath). You must re-declare it in $grandParent if you want to index this" +
307                " field in $grandParent."
308    }
309
310    fun droppedEmbeddedIndex(entityName: String, fieldPath: String, grandParent: String): String {
311        return "Indices defined in $entityName will be dropped when it is merged into" +
312                " $grandParent ($fieldPath). You can re-declare them in $grandParent."
313    }
314
315    fun droppedSuperClassIndex(childEntity: String, superEntity: String): String {
316        return "Indices defined in $superEntity will NOT be re-used in $childEntity. If you want" +
317                " to inherit them, you must re-declare them in $childEntity." +
318                " Alternatively, you can set inheritSuperIndices to true in the @Entity annotation."
319    }
320
321    fun droppedSuperClassFieldIndex(fieldName: String, childEntity: String,
322                                    superEntity: String): String {
323        return "Index defined on field `$fieldName` in $superEntity will NOT be re-used in" +
324                " $childEntity. " +
325                "If you want to inherit it, you must re-declare it in $childEntity." +
326                " Alternatively, you can set inheritSuperIndices to true in the @Entity annotation."
327    }
328
329    val RELATION_NOT_COLLECTION = "Fields annotated with @Relation must be a List or Set."
330
331    fun relationCannotFindEntityField(entityName: String, columnName: String,
332                                      availableColumns: List<String>): String {
333        return "Cannot find the child entity column `$columnName` in $entityName." +
334                " Options: ${availableColumns.joinToString(", ")}"
335    }
336
337    fun relationCannotFindParentEntityField(entityName: String, columnName: String,
338                                            availableColumns: List<String>): String {
339        return "Cannot find the parent entity column `$columnName` in $entityName." +
340                " Options: ${availableColumns.joinToString(", ")}"
341    }
342
343    val RELATION_IN_ENTITY = "Entities cannot have relations."
344
345    val CANNOT_FIND_TYPE = "Cannot find type."
346
347    fun relationAffinityMismatch(parentColumn: String, childColumn: String,
348                                 parentAffinity: SQLTypeAffinity?,
349                                 childAffinity: SQLTypeAffinity?): String {
350        return """
351        The affinity of parent column ($parentColumn : $parentAffinity) does not match the type
352        affinity of the child column ($childColumn : $childAffinity).
353        """.trim()
354    }
355
356    val CANNOT_USE_MORE_THAN_ONE_POJO_FIELD_ANNOTATION = "A field can be annotated with only" +
357            " one of the following:" + PojoProcessor.PROCESSED_ANNOTATIONS.joinToString(",") {
358        it.java.simpleName
359    }
360
361    fun relationBadProject(entityQName: String, missingColumnNames: List<String>,
362                           availableColumnNames: List<String>): String {
363        return """
364        $entityQName does not have the following columns: ${missingColumnNames.joinToString(",")}.
365        Available columns are: ${availableColumnNames.joinToString(",")}
366        """.trim()
367    }
368
369    val MISSING_SCHEMA_EXPORT_DIRECTORY = "Schema export directory is not provided to the" +
370            " annotation processor so we cannot export the schema. You can either provide" +
371            " `room.schemaLocation` annotation processor argument OR set exportSchema to false."
372
373    val INVALID_FOREIGN_KEY_ACTION = "Invalid foreign key action. It must be one of the constants" +
374            " defined in ForeignKey.Action"
375
376    fun foreignKeyNotAnEntity(className: String): String {
377        return """
378        Classes referenced in Foreign Key annotations must be @Entity classes. $className is not
379        an entity
380        """.trim()
381    }
382
383    val FOREIGN_KEY_CANNOT_FIND_PARENT = "Cannot find parent entity class."
384
385    fun foreignKeyChildColumnDoesNotExist(columnName: String, allColumns: List<String>): String {
386        return "($columnName) referenced in the foreign key does not exists in the Entity." +
387                " Available column names:${allColumns.joinToString(", ")}"
388    }
389
390    fun foreignKeyParentColumnDoesNotExist(parentEntity: String,
391                                           missingColumn: String,
392                                           allColumns: List<String>): String {
393        return "($missingColumn) does not exist in $parentEntity. Available columns are" +
394                " ${allColumns.joinToString(",")}"
395    }
396
397    val FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST = "Must specify at least 1 column name for the child"
398
399    val FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST = "Must specify at least 1 column name for the parent"
400
401    fun foreignKeyColumnNumberMismatch(
402            childColumns: List<String>, parentColumns: List<String>): String {
403        return """
404                Number of child columns in foreign key must match number of parent columns.
405                Child reference has ${childColumns.joinToString(",")} and parent reference has
406                ${parentColumns.joinToString(",")}
407               """.trim()
408    }
409
410    fun foreignKeyMissingParentEntityInDatabase(parentTable: String, childEntity: String): String {
411        return """
412                $parentTable table referenced in the foreign keys of $childEntity does not exist in
413                the database. Maybe you forgot to add the referenced entity in the entities list of
414                the @Database annotation?""".trim()
415    }
416
417    fun foreignKeyMissingIndexInParent(parentEntity: String, parentColumns: List<String>,
418                                       childEntity: String, childColumns: List<String>): String {
419        return """
420                $childEntity has a foreign key (${childColumns.joinToString(",")}) that references
421                $parentEntity (${parentColumns.joinToString(",")}) but $parentEntity does not have
422                a unique index on those columns nor the columns are its primary key.
423                SQLite requires having a unique constraint on referenced parent columns so you must
424                add a unique index to $parentEntity that has
425                (${parentColumns.joinToString(",")}) column(s).
426               """.trim()
427    }
428
429    fun foreignKeyMissingIndexInChildColumns(childColumns: List<String>): String {
430        return """
431                (${childColumns.joinToString(",")}) column(s) reference a foreign key but
432                they are not part of an index. This may trigger full table scans whenever parent
433                table is modified so you are highly advised to create an index that covers these
434                columns.
435               """.trim()
436    }
437
438    fun foreignKeyMissingIndexInChildColumn(childColumn: String): String {
439        return """
440                $childColumn column references a foreign key but it is not part of an index. This
441                may trigger full table scans whenever parent table is modified so you are highly
442                advised to create an index that covers this column.
443               """.trim()
444    }
445
446    fun shortcutEntityIsNotInDatabase(database: String, dao: String, entity: String): String {
447        return """
448                $dao is part of $database but this entity is not in the database. Maybe you forgot
449                to add $entity to the entities section of the @Database?
450                """.trim()
451    }
452
453    val MISSING_ROOM_GUAVA_ARTIFACT = "To use Guava features, you must add `guava`" +
454            " artifact from Room as a dependency. android.arch.persistence.room:guava:<version>"
455
456    val MISSING_ROOM_RXJAVA2_ARTIFACT = "To use RxJava2 features, you must add `rxjava2`" +
457            " artifact from Room as a dependency. android.arch.persistence.room:rxjava2:<version>"
458
459    fun ambigiousConstructor(
460            pojo: String, paramName: String, matchingFields: List<String>): String {
461        return """
462            Ambiguous constructor. The parameter ($paramName) in $pojo matches multiple fields:
463            [${matchingFields.joinToString(",")}]. If you don't want to use this constructor,
464            you can annotate it with @Ignore. If you want Room to use this constructor, you can
465            rename the parameters to exactly match the field name to fix the ambiguity.
466            """.trim()
467    }
468
469    val MISSING_POJO_CONSTRUCTOR = """
470            Entities and Pojos must have a usable public constructor. You can have an empty
471            constructor or a constructor whose parameters match the fields (by name and type).
472            """.trim()
473
474    val TOO_MANY_POJO_CONSTRUCTORS = """
475            Room cannot pick a constructor since multiple constructors are suitable. Try to annotate
476            unwanted constructors with @Ignore.
477            """.trim()
478
479    val TOO_MANY_POJO_CONSTRUCTORS_CHOOSING_NO_ARG = """
480            There are multiple good constructors and Room will pick the no-arg constructor.
481            You can use the @Ignore annotation to eliminate unwanted constructors.
482            """.trim()
483
484    val RELATION_CANNOT_BE_CONSTRUCTOR_PARAMETER = """
485            Fields annotated with @Relation cannot be constructor parameters. These values are
486            fetched after the object is constructed.
487            """.trim()
488
489    val PAGING_SPECIFY_DATA_SOURCE_TYPE = "For now, Room only supports PositionalDataSource class."
490
491    fun primaryKeyNull(field: String): String {
492        return "You must annotate primary keys with @NonNull. \"$field\" is nullable. SQLite " +
493                "considers this a " +
494                "bug and Room does not allow it. See SQLite docs for details: " +
495                "https://www.sqlite.org/lang_createtable.html"
496    }
497
498    val INVALID_COLUMN_NAME = "Invalid column name. Room does not allow using ` or \" in column" +
499            " names"
500
501    val INVALID_TABLE_NAME = "Invalid table name. Room does not allow using ` or \" in table names"
502
503    val RAW_QUERY_BAD_PARAMS = "RawQuery methods should have 1 and only 1 parameter with type" +
504            " String or SupportSQLiteQuery"
505
506    val RAW_QUERY_BAD_RETURN_TYPE = "RawQuery methods must return a non-void type."
507}
508