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