1 /* <lambda>null2 * 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 17 package androidx.room.processor 18 19 import androidx.room.Delete 20 import androidx.room.Insert 21 import androidx.room.Query 22 import androidx.room.RawQuery 23 import androidx.room.RewriteQueriesToDropUnusedColumns 24 import androidx.room.Update 25 import androidx.room.Upsert 26 import androidx.room.ext.KotlinTypeNames 27 import androidx.room.ext.RoomTypeNames 28 import androidx.room.ext.RoomTypeNames.ROOM_DB 29 import androidx.room.parser.QueryType 30 import androidx.room.parser.SQLTypeAffinity 31 import androidx.room.vo.CustomTypeConverter 32 import androidx.room.vo.Property 33 34 object ProcessorErrors { 35 private fun String.trim(): String { 36 return this.trimIndent().replace("\n", " ") 37 } 38 39 const val ISSUE_TRACKER_LINK = "https://issuetracker.google.com/issues/new?component=413107" 40 41 val MISSING_QUERY_ANNOTATION = "Query functions must be annotated with ${Query::class.java}" 42 val MISSING_INSERT_ANNOTATION = "Insert functions must be annotated with ${Insert::class.java}" 43 val MISSING_DELETE_ANNOTATION = "Delete functions must be annotated with ${Delete::class.java}" 44 val MISSING_UPDATE_ANNOTATION = "Update functions must be annotated with ${Update::class.java}" 45 val MISSING_UPSERT_ANNOTATION = "Upsert functions must be annotated with ${Upsert::class.java}" 46 val MISSING_RAWQUERY_ANNOTATION = 47 "RawQuery functions must be annotated with" + " ${RawQuery::class.java}" 48 const val INVALID_ON_CONFLICT_VALUE = 49 "On conflict value must be one of @OnConflictStrategy values." 50 const val TRANSACTION_REFERENCE_DOCS = 51 "https://developer.android.com/reference/androidx/" + "room/Transaction.html" 52 val INVALID_ANNOTATION_COUNT_IN_DAO_FUNCTION = 53 "An abstract DAO function must be" + 54 " annotated with one and only one of the following annotations: " + 55 DaoProcessor.PROCESSED_ANNOTATIONS.joinToString(", ") { "@" + it.java.simpleName } 56 val INVALID_ANNOTATION_IN_DAO_PROPERTY = 57 "An abstract DAO property must be" + " annotated with @get:${Query::class.java}." 58 const val CANNOT_RESOLVE_RETURN_TYPE = "Cannot resolve return type for %s" 59 const val CANNOT_USE_UNBOUND_GENERICS_IN_QUERY_FUNCTIONS = 60 "Cannot use unbound generics in query" + 61 " functions. It must be bound to a type through base Dao class." 62 const val CANNOT_USE_UNBOUND_GENERICS_IN_INSERT_FUNCTIONS = 63 "Cannot use unbound generics in" + 64 " insert functions. It must be bound to a type through base Dao class." 65 const val CANNOT_USE_UNBOUND_GENERICS_IN_UPSERT_FUNCTIONS = 66 "Cannot use unbound generics in" + 67 " upsert functions. It must be bound to a type through base Dao class." 68 const val CANNOT_USE_UNBOUND_GENERICS_IN_ENTITY_PROPERTIES = 69 "Cannot use unbound properties in entities." 70 const val CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES = 71 "Cannot use unbound generics in Dao classes." + 72 " If you are trying to create a base DAO, create a normal class, extend it with type" + 73 " params then mark the subclass with @Dao." 74 const val CANNOT_USE_MAP_COLUMN_AND_MAP_INFO_SIMULTANEOUSLY = 75 "Cannot use @MapColumn and " + 76 " @MapInfo annotation in the same function. Please prefer using @MapColumn only." 77 const val CANNOT_FIND_GETTER_FOR_PROPERTY = "Cannot find getter for property." 78 const val CANNOT_FIND_SETTER_FOR_PROPERTY = "Cannot find setter for property." 79 const val MISSING_PRIMARY_KEY = 80 "An entity must have at least 1 property annotated with @PrimaryKey" 81 const val AUTO_INCREMENTED_PRIMARY_KEY_IS_NOT_INT = 82 "If a primary key is annotated with" + 83 " autoGenerate, its type must be int, Integer, long or Long." 84 const val AUTO_INCREMENT_EMBEDDED_HAS_MULTIPLE_PROPERTIES = 85 "When @PrimaryKey annotation is used on a" + 86 " property annotated with @Embedded, the embedded class should have only 1 property." 87 const val INVALID_INDEX_ORDERS_SIZE = 88 "The number of entries in @Index#orders() should be " + 89 "equal to the amount of columns defined in the @Index value." 90 91 const val DO_NOT_USE_GENERIC_IMMUTABLE_MULTIMAP = 92 "Do not use ImmutableMultimap as a type (as with" + 93 " Multimap itself). Instead use the subtypes such as ImmutableSetMultimap or " + 94 "ImmutableListMultimap." 95 96 fun multiplePrimaryKeyAnnotations(primaryKeys: List<String>): String { 97 return """ 98 You cannot have multiple primary keys defined in an Entity. If you 99 want to declare a composite primary key, you should use @Entity#primaryKeys and 100 not use @PrimaryKey. Defined Primary Keys: 101 ${primaryKeys.joinToString(", ")}""" 102 .trim() 103 } 104 105 fun primaryKeyColumnDoesNotExist(columnName: String, allColumns: List<String>): String { 106 return "$columnName referenced in the primary key does not exist in the Entity." + 107 " Available column names:${allColumns.joinToString(", ")}" 108 } 109 110 const val DAO_MUST_BE_AN_ABSTRACT_CLASS_OR_AN_INTERFACE = 111 "Dao class must be an abstract class or" + " an interface" 112 const val DAO_MUST_BE_ANNOTATED_WITH_DAO = "Dao class must be annotated with @Dao" 113 114 fun daoMustHaveMatchingConstructor(daoName: String, dbName: String): String { 115 return """ 116 $daoName needs to have either an empty constructor or a constructor that takes 117 $dbName as its only parameter. 118 """ 119 .trim() 120 } 121 122 const val ENTITY_MUST_BE_ANNOTATED_WITH_ENTITY = "Entity class must be annotated with @Entity" 123 const val DATABASE_ANNOTATION_MUST_HAVE_LIST_OF_ENTITIES = 124 "@Database annotation must specify list" + " of entities" 125 const val COLUMN_NAME_CANNOT_BE_EMPTY = 126 "Column name cannot be blank. If you don't want to set it" + 127 ", just remove the @ColumnInfo annotation or use @ColumnInfo.INHERIT_PROPERTY_NAME." 128 129 const val ENTITY_TABLE_NAME_CANNOT_BE_EMPTY = 130 "Entity table name cannot be blank. If you don't want" + 131 " to set it, just remove the tableName property." 132 133 const val ENTITY_TABLE_NAME_CANNOT_START_WITH_SQLITE = 134 "Entity table name cannot start with \"sqlite_\"." 135 136 const val VIEW_MUST_BE_ANNOTATED_WITH_DATABASE_VIEW = 137 "View class must be annotated with " + "@DatabaseView" 138 const val VIEW_NAME_CANNOT_BE_EMPTY = 139 "View name cannot be blank. If you don't want" + 140 " to set it, just remove the viewName property." 141 const val VIEW_NAME_CANNOT_START_WITH_SQLITE = "View name cannot start with \"sqlite_\"." 142 const val VIEW_QUERY_MUST_BE_SELECT = "Query for @DatabaseView must be a SELECT." 143 const val VIEW_QUERY_CANNOT_TAKE_ARGUMENTS = 144 "Query for @DatabaseView cannot take any arguments." 145 146 fun viewCircularReferenceDetected(views: List<String>): String { 147 return "Circular reference detected among views: ${views.joinToString(", ")}" 148 } 149 150 const val CANNOT_BIND_QUERY_PARAMETER_INTO_STMT = 151 "Query function parameters should either be a" + 152 " type that can be converted into a database column or a List / Array that contains" + 153 " such type. You can consider adding a Type Adapter for this." 154 155 const val QUERY_PARAMETERS_CANNOT_START_WITH_UNDERSCORE = 156 "Query/Insert function parameters cannot " + "start with underscore (_)." 157 158 fun cannotFindQueryResultAdapter(returnTypeName: String) = 159 "Not sure how to convert the query result to this function's return type ($returnTypeName)." 160 161 fun classMustImplementEqualsAndHashCode(keyType: String) = 162 "The key" + 163 " of the provided function's multimap return type must implement equals() and " + 164 "hashCode(). Key type is: $keyType." 165 166 const val INSERT_DOES_NOT_HAVE_ANY_PARAMETERS_TO_INSERT = 167 "Function annotated with" + " @Insert but does not have any parameters to insert." 168 169 const val UPSERT_DOES_NOT_HAVE_ANY_PARAMETERS_TO_UPSERT = 170 "Function annotated with" + " @Upsert but does not have any parameters to insert or update." 171 172 const val DELETE_MISSING_PARAMS = 173 "Function annotated with" + " @Delete but does not have any parameters to delete." 174 175 fun cannotMapSpecifiedColumn(column: String, columnsInQuery: List<String>, annotation: String) = 176 "Column specified in the provided @$annotation annotation must be present in the query. " + 177 "Provided: $column. Columns found: ${columnsInQuery.joinToString(", ")}" 178 179 const val MAP_INFO_MUST_HAVE_AT_LEAST_ONE_COLUMN_PROVIDED = 180 "To use the @MapInfo annotation, you " + 181 "must provide either the key column name, value column name, or both." 182 183 fun mayNeedMapColumn(columnArg: String): String { 184 return """ 185 Looks like you may need to use @MapColumn to clarify the 'columnName' needed for 186 type argument(s) in the return type of a function. Type argument that needs 187 @MapColumn: $columnArg 188 """ 189 .trim() 190 } 191 192 const val CANNOT_FIND_DELETE_RESULT_ADAPTER = 193 "Not sure how to handle delete function's " + 194 "return type. Currently the supported return types are void, int or Int." 195 196 const val CANNOT_FIND_UPDATE_RESULT_ADAPTER = 197 "Not sure how to handle update function's " + 198 "return type. Currently the supported return types are void, int or Int." 199 200 fun suspendReturnsDeferredType(returnTypeName: String) = 201 "Dao functions that have a suspend " + 202 "modifier must not return a deferred/async type ($returnTypeName). Most probably this " + 203 "is an error. Consider changing the return type or removing the suspend modifier." 204 205 const val CANNOT_FIND_INSERT_RESULT_ADAPTER = 206 "Not sure how to handle insert function's return type." 207 208 const val CANNOT_FIND_UPSERT_RESULT_ADAPTER = 209 "Not sure how to handle upsert function's return type." 210 211 const val INSERT_MULTI_PARAM_SINGLE_RETURN_MISMATCH = 212 "Insert function accepts multiple parameters " + 213 "but the return type is a single element. Try using a multiple element return type." 214 215 const val UPSERT_MULTI_PARAM_SINGLE_RETURN_MISMATCH = 216 "Upsert function accepts multiple parameters " + 217 "but the return type is a single element. Try using a multiple element return type." 218 219 const val INSERT_SINGLE_PARAM_MULTI_RETURN_MISMATCH = 220 "Insert function accepts a single parameter " + 221 "but the return type is a collection of elements. Try using a single element return type." 222 223 const val UPSERT_SINGLE_PARAM_MULTI_RETURN_MISMATCH = 224 "Upsert function accepts a single parameter " + 225 "but the return type is a collection of elements. Try using a single element return type." 226 227 const val UPDATE_MISSING_PARAMS = 228 "Function annotated with" + " @Update but does not have any parameters to update." 229 230 const val TRANSACTION_FUNCTION_MODIFIERS = 231 "Function annotated with @Transaction must not be " + 232 "private, final, or abstract. It can be abstract only if the function is also" + 233 " annotated with @Query." 234 235 fun nullableParamInShortcutFunction(param: String) = 236 "Functions annotated with [@Insert, " + 237 "@Upsert, @Update, @Delete] shouldn't declare nullable parameters ($param)." 238 239 fun transactionFunctionAsync(returnTypeName: String) = 240 "Function annotated with @Transaction must" + 241 " not return deferred/async return type $returnTypeName. Since transactions are" + 242 " thread confined and Room cannot guarantee that all queries in the function" + 243 " implementation are performed on the same thread, only synchronous @Transaction" + 244 " implemented functions are allowed. If a transaction is started and a change of thread" + 245 " is done and waited upon then a database deadlock can occur if the additional thread" + 246 " attempts to perform a query. This restrictions prevents such situation from" + 247 " occurring." 248 249 const val TRANSACTION_MISSING_ON_RELATION = 250 "The return value includes a data class with a @Relation." + 251 " It is usually desired to annotate this function with @Transaction to avoid" + 252 " possibility of inconsistent results between the data class and its relations. See " + 253 TRANSACTION_REFERENCE_DOCS + 254 " for details." 255 256 const val CANNOT_FIND_ENTITY_FOR_SHORTCUT_QUERY_PARAMETER = 257 "Type of the parameter must be a class " + 258 "annotated with @Entity or a collection/array of it." 259 260 val DB_MUST_EXTEND_ROOM_DB = 261 "Classes annotated with @Database should extend " + ROOM_DB.canonicalName 262 263 const val OBSERVABLE_QUERY_NOTHING_TO_OBSERVE = 264 "Observable query return type (LiveData, Flowable" + 265 ", DataSource, DataSourceFactory etc) can only be used with SELECT queries that" + 266 " directly or indirectly (via @Relation, for example) access at least one table. For" + 267 " @RawQuery, you should specify the list of tables to be observed via the" + 268 " observedEntities property." 269 270 const val RECURSIVE_REFERENCE_DETECTED = 271 "Recursive referencing through @Embedded and/or @Relation " + "detected: %s" 272 273 private const val TOO_MANY_MATCHING_GETTERS = 274 "Ambiguous getter for %s. All of the following " + 275 "match: %s. You can @Ignore the ones that you don't want to match." 276 277 fun tooManyMatchingGetters(property: Property, functionNames: List<String>): String { 278 return TOO_MANY_MATCHING_GETTERS.format(property, functionNames.joinToString(", ")) 279 } 280 281 private const val TOO_MANY_MATCHING_SETTERS = 282 "Ambiguous setter for %s. All of the following " + 283 "match: %s. You can @Ignore the ones that you don't want to match." 284 285 fun tooManyMatchingSetter(property: Property, functionNames: List<String>): String { 286 return TOO_MANY_MATCHING_SETTERS.format(property, functionNames.joinToString(", ")) 287 } 288 289 const val CANNOT_FIND_COLUMN_TYPE_ADAPTER = 290 "Cannot figure out how to save this property into" + 291 " database. You can consider adding a type converter for it." 292 293 const val VALUE_CLASS_ONLY_SUPPORTED_IN_KSP = 294 "Kotlin value classes are only supported " + 295 "in Room using KSP and generating Kotlin (room.generateKotlin=true)." 296 297 const val CANNOT_FIND_STMT_BINDER = 298 "Cannot figure out how to bind this property into a statement." 299 300 const val CANNOT_FIND_STMT_READER = 301 "Cannot figure out how to read this property from a statement." 302 303 const val DEFAULT_VALUE_NULLABILITY = "Use of NULL as the default value of a non-null property" 304 305 private const val MISSING_PARAMETER_FOR_BIND = 306 "Each bind variable in the query must have a" + 307 " matching function parameter. Cannot find function parameters for %s." 308 309 fun missingParameterForBindVariable(bindVarName: List<String>): String { 310 return MISSING_PARAMETER_FOR_BIND.format(bindVarName.joinToString(", ")) 311 } 312 313 fun valueCollectionMustBeListOrSetOrMap(mapValueTypeName: String): String { 314 return "Multimap 'value' collection type must be a List, Set or Map. " + 315 "Found $mapValueTypeName." 316 } 317 318 private const val UNUSED_QUERY_FUNCTION_PARAMETER = "Unused parameter%s: %s" 319 320 fun unusedQueryFunctionParameter(unusedParams: List<String>): String { 321 return UNUSED_QUERY_FUNCTION_PARAMETER.format( 322 if (unusedParams.size > 1) "s" else "", 323 unusedParams.joinToString(",") 324 ) 325 } 326 327 private const val DUPLICATE_TABLES_OR_VIEWS = 328 "The name \"%s\" is used by multiple entities or views: %s" 329 330 fun duplicateTableNames(tableName: String, entityNames: List<String>): String { 331 return DUPLICATE_TABLES_OR_VIEWS.format(tableName, entityNames.joinToString(", ")) 332 } 333 334 const val DAO_FUNCTION_CONFLICTS_WITH_OTHERS = "Dao function has conflicts." 335 336 fun duplicateDao(dao: String, functionNames: List<String>): String { 337 return """ 338 All of these functions [${functionNames.joinToString(", ")}] return the same DAO 339 class [$dao]. 340 A database can use a DAO only once so you should remove ${functionNames.size - 1} of 341 these conflicting DAO functions. If you are implementing any of these to fulfill an 342 interface, don't make it abstract, instead, implement the code that calls the 343 other one. 344 """ 345 .trim() 346 } 347 348 fun dataClassMissingNonNull( 349 dataClassTypeName: String, 350 missingDataClassProperties: List<String>, 351 allQueryColumns: List<String> 352 ): String { 353 return """ 354 The columns returned by the query does not have the properties 355 [${missingDataClassProperties.joinToString(",")}] in $dataClassTypeName even though they are 356 annotated as non-null or primitive. 357 Columns returned by the query: [${allQueryColumns.joinToString(",")}] 358 """ 359 .trim() 360 } 361 362 fun queryPropertyDataClassMismatch( 363 dataClassTypeNames: List<String>, 364 unusedColumns: List<String>, 365 allColumns: List<String>, 366 dataClassUnusedProperties: Map<String, List<Property>>, 367 ): String { 368 val unusedColumnsWarning = 369 if (unusedColumns.isNotEmpty()) { 370 val dataClassNames = 371 if (dataClassTypeNames.size > 1) { 372 "any of [${dataClassTypeNames.joinToString(", ")}]" 373 } else { 374 dataClassTypeNames.single().toString() 375 } 376 """ 377 The query returns some columns [${unusedColumns.joinToString(", ")}] which are not 378 used by $dataClassNames. You can use @ColumnInfo annotation on the properties to specify 379 the mapping. 380 You can annotate the function with @RewriteQueriesToDropUnusedColumns to direct Room 381 to rewrite your query to avoid fetching unused columns. 382 """ 383 .trim() 384 } else { 385 "" 386 } 387 val unusedPropertiesWarning = 388 dataClassUnusedProperties.map { (dataClassName, unusedProperties) -> 389 """ 390 $dataClassName has some properties 391 [${unusedProperties.joinToString(", ") { it.columnName }}] which are not returned by 392 the query. If they are not supposed to be read from the result, you can mark them 393 with @Ignore annotation. 394 """ 395 .trim() 396 } 397 return """ 398 $unusedColumnsWarning 399 ${unusedPropertiesWarning.joinToString(separator = " ")} 400 You can suppress this warning by annotating the function with 401 @SuppressWarnings(RoomWarnings.QUERY_MISMATCH). 402 Columns returned by the query: ${allColumns.joinToString(", ")}. 403 """ 404 .trim() 405 } 406 407 const val TYPE_CONVERTER_UNBOUND_GENERIC = "Cannot use unbound generics in Type Converters." 408 const val TYPE_CONVERTER_BAD_RETURN_TYPE = "Invalid return type for a type converter." 409 const val TYPE_CONVERTER_MUST_RECEIVE_1_PARAM = "Type converters must receive 1 parameter." 410 const val TYPE_CONVERTER_EMPTY_CLASS = 411 "Class is referenced as a converter but it does not have any" + " converter functions." 412 const val TYPE_CONVERTER_MISSING_NOARG_CONSTRUCTOR = 413 "Classes that are used as TypeConverters must" + 414 " have no-argument public constructors. Use a ProvidedTypeConverter annotation if you" + 415 " need to take control over creating an instance of a TypeConverter." 416 const val TYPE_CONVERTER_MUST_BE_PUBLIC = "Type converters must be public." 417 const val INNER_CLASS_TYPE_CONVERTER_MUST_BE_STATIC = 418 "An inner class TypeConverter must be " + "static." 419 420 fun duplicateTypeConverters(converters: List<CustomTypeConverter>): String { 421 return "Multiple functions define the same conversion. Conflicts with these:" + 422 " ${converters.joinToString(", ") { it.toString() }}" 423 } 424 425 fun typeConverterMustBeDeclared(typeName: String): String { 426 return "Invalid type converter type: $typeName. Type converters must be a class." 427 } 428 429 // TODO must print property paths. 430 const val DATA_CLASS_PROPERTY_HAS_DUPLICATE_COLUMN_NAME = "Property has non-unique column name." 431 432 fun dataClassDuplicatePropertyNames(columnName: String, propertyPaths: List<String>): String { 433 return "Multiple properties have the same columnName: $columnName." + 434 " Property names: ${propertyPaths.joinToString(", ")}." 435 } 436 437 fun embeddedPrimaryKeyIsDropped(entityQName: String, propertyName: String): String { 438 return "Primary key constraint on $propertyName is ignored when being merged into " + 439 entityQName 440 } 441 442 const val INDEX_COLUMNS_CANNOT_BE_EMPTY = "List of columns in an index cannot be empty" 443 444 fun indexColumnDoesNotExist(columnName: String, allColumns: List<String>): String { 445 return "$columnName referenced in the index does not exist in the Entity." + 446 " Available column names:${allColumns.joinToString(", ")}" 447 } 448 449 fun duplicateIndexInEntity(indexName: String): String { 450 return "There are multiple indices with name $indexName. This happen if you've declared" + 451 " the same index multiple times or different indices have the same name. See" + 452 " @Index documentation for details." 453 } 454 455 fun duplicateIndexInDatabase(indexName: String, indexPaths: List<String>): String { 456 return "There are multiple indices with name $indexName. You should rename " + 457 "${indexPaths.size - 1} of these to avoid the conflict:" + 458 "${indexPaths.joinToString(", ")}." 459 } 460 461 fun droppedEmbeddedPropertyIndex(propertyPath: String, grandParent: String): String { 462 return "The index will be dropped when being merged into $grandParent" + 463 "($propertyPath). You must re-declare it in $grandParent if you want to index this" + 464 " property in $grandParent." 465 } 466 467 fun droppedEmbeddedIndex( 468 entityName: String, 469 propertyPath: String, 470 grandParent: String 471 ): String { 472 return "Indices defined in $entityName will be dropped when it is merged into" + 473 " $grandParent ($propertyPath). You can re-declare them in $grandParent." 474 } 475 476 fun droppedSuperClassIndex(childEntity: String, superEntity: String): String { 477 return "Indices defined in $superEntity will NOT be re-used in $childEntity. If you want" + 478 " to inherit them, you must re-declare them in $childEntity." + 479 " Alternatively, you can set inheritSuperIndices to true in the @Entity annotation." 480 } 481 482 fun droppedSuperClassPropertyIndex( 483 propertyName: String, 484 childEntity: String, 485 superEntity: String 486 ): String { 487 return "Index defined on property `$propertyName` in $superEntity will NOT be re-used in" + 488 " $childEntity. " + 489 "If you want to inherit it, you must re-declare it in $childEntity." + 490 " Alternatively, you can set inheritSuperIndices to true in the @Entity annotation." 491 } 492 493 const val NOT_ENTITY_OR_VIEW = "The class must be either @Entity or @DatabaseView." 494 495 fun relationCannotFindEntityProperty( 496 entityName: String, 497 columnName: String, 498 availableColumns: List<String> 499 ): String { 500 return "Cannot find the child entity column `$columnName` in $entityName." + 501 " Options: ${availableColumns.joinToString(", ")}" 502 } 503 504 fun relationCannotFindParentEntityProperty( 505 entityName: String, 506 columnName: String, 507 availableColumns: List<String> 508 ): String { 509 return "Cannot find the parent entity column `$columnName` in $entityName." + 510 " Options: ${availableColumns.joinToString(", ")}" 511 } 512 513 fun relationCannotFindJunctionEntityProperty( 514 entityName: String, 515 columnName: String, 516 availableColumns: List<String> 517 ): String { 518 return "Cannot find the child entity referencing column `$columnName` in the junction " + 519 "$entityName. Options: ${availableColumns.joinToString(", ")}" 520 } 521 522 fun relationCannotFindJunctionParentProperty( 523 entityName: String, 524 columnName: String, 525 availableColumns: List<String> 526 ): String { 527 return "Cannot find the parent entity referencing column `$columnName` in the junction " + 528 "$entityName. Options: ${availableColumns.joinToString(", ")}" 529 } 530 531 fun junctionColumnWithoutIndex(entityName: String, columnName: String) = 532 "The column $columnName in the junction entity $entityName is being used to resolve " + 533 "a relationship but it is not covered by any index. This might cause a " + 534 "full table scan when resolving the relationship, it is highly advised to " + 535 "create an index that covers this column." 536 537 const val RELATION_IN_ENTITY = "Entities cannot have relations." 538 539 fun relationAffinityMismatch( 540 parentColumn: String, 541 childColumn: String, 542 parentAffinity: SQLTypeAffinity?, 543 childAffinity: SQLTypeAffinity? 544 ): String { 545 return """ 546 The affinity of parent column ($parentColumn : $parentAffinity) does not match the type 547 affinity of the child column ($childColumn : $childAffinity). 548 """ 549 .trim() 550 } 551 552 fun relationJunctionParentAffinityMismatch( 553 parentColumn: String, 554 junctionParentColumn: String, 555 parentAffinity: SQLTypeAffinity?, 556 junctionParentAffinity: SQLTypeAffinity? 557 ): String { 558 return """ 559 The affinity of parent column ($parentColumn : $parentAffinity) does not match the type 560 affinity of the junction parent column ($junctionParentColumn : $junctionParentAffinity). 561 """ 562 .trim() 563 } 564 565 fun relationJunctionChildAffinityMismatch( 566 childColumn: String, 567 junctionChildColumn: String, 568 childAffinity: SQLTypeAffinity?, 569 junctionChildAffinity: SQLTypeAffinity? 570 ): String { 571 return """ 572 The affinity of child column ($childColumn : $childAffinity) does not match the type 573 affinity of the junction child column ($junctionChildColumn : $junctionChildAffinity). 574 """ 575 .trim() 576 } 577 578 val CANNOT_USE_MORE_THAN_ONE_DATA_CLASS_PROPERTY_ANNOTATION = 579 "A property can be annotated with only" + 580 " one of the following:" + 581 DataClassProcessor.PROCESSED_ANNOTATIONS.joinToString(",") { it.java.simpleName } 582 583 fun missingIgnoredColumns(missingIgnoredColumns: List<String>): String { 584 return "Non-existent columns are specified to be ignored in ignoreColumns: " + 585 missingIgnoredColumns.joinToString(",") 586 } 587 588 fun relationBadProject( 589 entityQName: String, 590 missingColumnNames: List<String>, 591 availableColumnNames: List<String> 592 ): String { 593 return """ 594 $entityQName does not have the following columns: ${missingColumnNames.joinToString(",")}. 595 Available columns are: ${availableColumnNames.joinToString(",")} 596 """ 597 .trim() 598 } 599 600 const val MISSING_SCHEMA_EXPORT_DIRECTORY = 601 "Schema export directory was not provided to the" + 602 " annotation processor so Room cannot export the schema. You can either provide" + 603 " `room.schemaLocation` annotation processor argument by applying the Room Gradle plugin" + 604 " (id 'androidx.room') OR set exportSchema to false." 605 606 const val INVALID_FOREIGN_KEY_ACTION = 607 "Invalid foreign key action. It must be one of the constants" + 608 " defined in ForeignKey.Action" 609 610 fun foreignKeyNotAnEntity(className: String): String { 611 return """ 612 Classes referenced in Foreign Key annotations must be @Entity classes. $className is not 613 an entity 614 """ 615 .trim() 616 } 617 618 const val FOREIGN_KEY_CANNOT_FIND_PARENT = "Cannot find parent entity class." 619 620 fun foreignKeyChildColumnDoesNotExist(columnName: String, allColumns: List<String>): String { 621 return "($columnName) referenced in the foreign key does not exist in the Entity." + 622 " Available column names:${allColumns.joinToString(", ")}" 623 } 624 625 fun foreignKeyParentColumnDoesNotExist( 626 parentEntity: String, 627 missingColumn: String, 628 allColumns: List<String> 629 ): String { 630 return "($missingColumn) does not exist in $parentEntity. Available columns are" + 631 " ${allColumns.joinToString(",")}" 632 } 633 634 const val FOREIGN_KEY_EMPTY_CHILD_COLUMN_LIST = 635 "Must specify at least 1 column name for the child" 636 637 const val FOREIGN_KEY_EMPTY_PARENT_COLUMN_LIST = 638 "Must specify at least 1 column name for the parent" 639 640 fun foreignKeyColumnNumberMismatch( 641 childColumns: List<String>, 642 parentColumns: List<String> 643 ): String { 644 return """ 645 Number of child columns in foreign key must match number of parent columns. 646 Child reference has ${childColumns.joinToString(",")} and parent reference has 647 ${parentColumns.joinToString(",")} 648 """ 649 .trim() 650 } 651 652 fun foreignKeyMissingParentEntityInDatabase(parentTable: String, childEntity: String): String { 653 return """ 654 $parentTable table referenced in the foreign keys of $childEntity does not exist in 655 the database. Maybe you forgot to add the referenced entity in the entities list of 656 the @Database annotation?""" 657 .trim() 658 } 659 660 fun foreignKeyMissingIndexInParent( 661 parentEntity: String, 662 parentColumns: List<String>, 663 childEntity: String, 664 childColumns: List<String> 665 ): String { 666 return """ 667 $childEntity has a foreign key (${childColumns.joinToString(",")}) that references 668 $parentEntity (${parentColumns.joinToString(",")}) but $parentEntity does not have 669 a unique index on those columns nor the columns are its primary key. 670 SQLite requires having a unique constraint on referenced parent columns so you must 671 add a unique index to $parentEntity that has 672 (${parentColumns.joinToString(",")}) column(s). 673 """ 674 .trim() 675 } 676 677 fun foreignKeyMissingIndexInChildColumns(childColumns: List<String>): String { 678 return """ 679 (${childColumns.joinToString(",")}) column(s) reference a foreign key but 680 they are not part of an index. This may trigger full table scans whenever parent 681 table is modified so you are highly advised to create an index that covers these 682 columns. 683 """ 684 .trim() 685 } 686 687 fun foreignKeyMissingIndexInChildColumn(childColumn: String): String { 688 return """ 689 $childColumn column references a foreign key but it is not part of an index. This 690 may trigger full table scans whenever parent table is modified so you are highly 691 advised to create an index that covers this column. 692 """ 693 .trim() 694 } 695 696 fun shortcutEntityIsNotInDatabase(database: String, dao: String, entity: String): String { 697 return """ 698 $dao is part of $database but this entity is not in the database. Maybe you forgot 699 to add $entity to the entities section of the @Database? 700 """ 701 .trim() 702 } 703 704 const val MISSING_ROOM_GUAVA_ARTIFACT = 705 "To use Guava features, you must add `guava`" + 706 " artifact from Room as a dependency. androidx.room:room-guava:<version>" 707 708 const val MISSING_ROOM_RXJAVA2_ARTIFACT = 709 "To use RxJava2 features, you must add `rxjava2`" + 710 " artifact from Room as a dependency. androidx.room:room-rxjava2:<version>" 711 712 const val MISSING_ROOM_RXJAVA3_ARTIFACT = 713 "To use RxJava3 features, you must add `rxjava3`" + 714 " artifact from Room as a dependency. androidx.room:room-rxjava3:<version>" 715 716 const val MISSING_ROOM_PAGING_ARTIFACT = 717 "To use PagingSource, you must add `room-paging`" + 718 " artifact from Room as a dependency. androidx.room:room-paging:<version>" 719 720 const val MISSING_ROOM_PAGING_GUAVA_ARTIFACT = 721 "To use ListenableFuturePagingSource, you must " + 722 "add `room-paging-guava` artifact from Room as a dependency. " + 723 "androidx.room:room-paging-guava:<version>" 724 725 const val MISSING_ROOM_PAGING_RXJAVA2_ARTIFACT = 726 "To use RxPagingSource, you must " + 727 "add `room-paging-rxjava2` artifact from Room as a dependency. " + 728 "androidx.room:room-paging-rxjava2:<version>" 729 730 const val MISSING_ROOM_PAGING_RXJAVA3_ARTIFACT = 731 "To use RxPagingSource, you must " + 732 "add `room-paging-rxjava3` artifact from Room as a dependency. " + 733 "androidx.room:room-paging-rxjava3:<version>" 734 735 fun ambiguousConstructor( 736 dataClass: String, 737 paramName: String, 738 matchingProperties: List<String> 739 ): String { 740 return """ 741 Ambiguous constructor. The parameter ($paramName) in $dataClass matches multiple properties: 742 [${matchingProperties.joinToString(",")}]. If you don't want to use this constructor, 743 you can annotate it with @Ignore. If you want Room to use this constructor, you can 744 rename the parameters to exactly match the property name to fix the ambiguity. 745 """ 746 .trim() 747 } 748 749 val MISSING_DATA_CLASS_CONSTRUCTOR = 750 """ 751 Entities and data classes must have a usable public constructor. You can have an empty 752 constructor or a constructor whose parameters match the properties (by name and type). 753 """ 754 .trim() 755 756 val TOO_MANY_DATA_CLASS_CONSTRUCTORS = 757 """ 758 Room cannot pick a constructor since multiple constructors are suitable. Try to annotate 759 unwanted constructors with @Ignore. 760 """ 761 .trim() 762 763 val TOO_MANY_DATA_CLASS_CONSTRUCTORS_CHOOSING_NO_ARG = 764 """ 765 There are multiple good constructors and Room will pick the no-arg constructor. 766 You can use the @Ignore annotation to eliminate unwanted constructors. 767 """ 768 .trim() 769 770 const val PAGING_SPECIFY_DATA_SOURCE_TYPE = 771 "For now, Room only supports PositionalDataSource class." 772 773 const val PAGING_SPECIFY_PAGING_SOURCE_TYPE = 774 "For now, Room only supports PagingSource with Key of" + " type Int." 775 776 const val PAGING_SPECIFY_PAGING_SOURCE_VALUE_TYPE = 777 "For now, Room only supports PagingSource with" + " Value that is not of Collection type." 778 779 fun primaryKeyNull(property: String): String { 780 return "You must annotate primary keys with @NonNull. \"$property\" is nullable. SQLite " + 781 "considers this a " + 782 "bug and Room does not allow it. See SQLite docs for details: " + 783 "https://www.sqlite.org/lang_createtable.html" 784 } 785 786 const val INVALID_COLUMN_NAME = 787 "Invalid column name. Room does not allow using ` or \" in column" + " names" 788 789 const val INVALID_TABLE_NAME = 790 "Invalid table name. Room does not allow using ` or \" in table names" 791 792 const val RAW_QUERY_BAD_PARAMS = 793 "RawQuery functions should have 1 and only 1 parameter with type" + " SupportSQLiteQuery" 794 795 fun parameterCannotBeNullable(parameterName: String) = 796 """ 797 Parameter `$parameterName` cannot be nullable. 798 """ 799 .trimIndent() 800 801 const val RAW_QUERY_BAD_RETURN_TYPE = "RawQuery functions must return a non-void type." 802 803 fun rawQueryBadEntity(typeName: String): String { 804 return """ 805 observedEntities property in RawQuery must either reference a class that is annotated 806 with @Entity or it should reference a data class that either contains 807 @Embedded properties that are annotated with @Entity or @Relation properties. 808 $typeName does not have these properties, did you mean another class? 809 """ 810 .trim() 811 } 812 813 val RAW_QUERY_STRING_PARAMETER_REMOVED = 814 "@RawQuery does not allow passing a string anymore." + 815 " Please use ${RoomTypeNames.RAW_QUERY.canonicalName}." 816 817 const val MISSING_COPY_ANNOTATIONS = 818 "Annotated property getter is missing " + "@AutoValue.CopyAnnotations." 819 820 fun invalidAnnotationTarget(annotationName: String, elementKindName: String): String { 821 return "@$annotationName is not allowed in this $elementKindName." 822 } 823 824 const val INDICES_IN_FTS_ENTITY = "Indices not allowed in FTS Entity." 825 826 const val FOREIGN_KEYS_IN_FTS_ENTITY = "Foreign Keys not allowed in FTS Entity." 827 828 const val MISSING_PRIMARY_KEYS_ANNOTATION_IN_ROW_ID = 829 "The property with column name 'rowid' in " + 830 "an FTS entity must be annotated with @PrimaryKey." 831 832 const val TOO_MANY_PRIMARY_KEYS_IN_FTS_ENTITY = 833 "An FTS entity can only have a single primary key." 834 835 const val INVALID_FTS_ENTITY_PRIMARY_KEY_NAME = 836 "The single primary key property in an FTS entity " + 837 "must either be named 'rowid' or must be annotated with @ColumnInfo(name = \"rowid\")" 838 839 const val INVALID_FTS_ENTITY_PRIMARY_KEY_AFFINITY = 840 "The single @PrimaryKey annotated property in an " + 841 "FTS entity must be of INTEGER affinity." 842 843 fun missingLanguageIdProperty(columnName: String) = 844 "The specified 'languageid' column: \"$columnName\", was not found." 845 846 const val INVALID_FTS_ENTITY_LANGUAGE_ID_AFFINITY = 847 "The 'languageid' property must be of INTEGER " + "affinity." 848 849 fun missingNotIndexedProperty(missingNotIndexedColumns: List<String>) = 850 "Non-existent columns are specified to be not indexed in notIndexed: " + 851 missingNotIndexedColumns.joinToString(",") 852 853 const val INVALID_FTS_ENTITY_PREFIX_SIZES = 854 "Prefix sizes to index must non-zero positive values." 855 856 const val FTS_EXTERNAL_CONTENT_CANNOT_FIND_ENTITY = "Cannot find external content entity class." 857 858 fun externalContentNotAnEntity(className: String) = 859 "External content entity referenced in " + 860 "a Fts4 annotation must be a @Entity class. $className is not an entity" 861 862 fun missingFtsContentProperty( 863 ftsClassName: String, 864 columnName: String, 865 contentClassName: String 866 ) = 867 "External Content FTS Entity '$ftsClassName' has declared property with column name " + 868 "'$columnName' that was not found in the external content entity " + 869 "'$contentClassName'." 870 871 fun missingExternalContentEntity(ftsClassName: String, contentClassName: String) = 872 "External Content FTS Entity '$ftsClassName' has a declared content entity " + 873 "'$contentClassName' that is not present in the same @Database. Maybe you " + 874 "forgot to add it to the entities section of the @Database?" 875 876 fun cannotFindAsEntityProperty(entityName: String) = 877 "Cannot find a column in the entity " + 878 "$entityName that matches with this partial entity property. If you don't wish to use " + 879 "the property then you can annotate it with @Ignore." 880 881 const val INVALID_TARGET_ENTITY_IN_SHORTCUT_FUNCTION = 882 "Target entity declared in @Insert, @Update " + "or @Delete must be annotated with @Entity." 883 884 const val INVALID_RELATION_IN_PARTIAL_ENTITY = "Partial entities cannot have relations." 885 886 fun invalidQueryForSingleColumnArray(returnType: String) = 887 "If a DAO function has a " + 888 "primitive array or an array of String return type, a single column must be returned. " + 889 "Please check the query of the DAO function with the `$returnType` return type." 890 891 val EXPAND_PROJECTION_ALONG_WITH_REMOVE_UNUSED = 892 """ 893 Using @${RewriteQueriesToDropUnusedColumns::class.simpleName} annotation when 894 room.expandProjection compiler flag is enabled will disable expandProjection for queries 895 covered with @${RewriteQueriesToDropUnusedColumns::class.simpleName}. 896 """ 897 .trim() 898 899 fun missingPrimaryKeysInPartialEntityForInsert( 900 partialEntityName: String, 901 primaryKeyNames: List<String> 902 ) = 903 "The partial entity $partialEntityName is missing the primary key properties " + 904 "(${primaryKeyNames.joinToString()}) needed to perform an INSERT. If your single " + 905 "primary key is auto generated then the properties are optional." 906 907 fun missingPrimaryKeysInPartialEntityForUpsert( 908 partialEntityName: String, 909 primaryKeyNames: List<String> 910 ) = 911 "The partial entity $partialEntityName is missing the primary key properties " + 912 "(${primaryKeyNames.joinToString()}) needed to perform an UPSERT. If your single " + 913 "primary key is auto generated then the properties are optional." 914 915 fun missingRequiredColumnsInPartialEntity( 916 partialEntityName: String, 917 missingColumnNames: List<String> 918 ) = 919 "The partial entity $partialEntityName is missing required columns " + 920 "(${missingColumnNames.joinToString()}) needed to perform an INSERT. These are " + 921 "NOT NULL columns without default values." 922 923 fun missingPrimaryKeysInPartialEntityForUpdate( 924 partialEntityName: String, 925 primaryKeyNames: List<String> 926 ) = 927 "The partial entity $partialEntityName is missing the primary key properties " + 928 "(${primaryKeyNames.joinToString()}) needed to perform an UPDATE." 929 930 fun noColumnsInPartialEntity(partialEntityName: String) = 931 "The partial entity $partialEntityName does not have any columns that can be used to " + 932 "perform the query." 933 934 fun cannotFindPreparedQueryResultAdapter(returnType: String, type: QueryType) = 935 StringBuilder() 936 .apply { 937 append("Not sure how to handle query function's return type ($returnType). ") 938 if (type == QueryType.INSERT) { 939 append( 940 "INSERT query functions must either return void " + 941 "or long (the rowid of the inserted row)." 942 ) 943 } else if (type == QueryType.UPDATE) { 944 append( 945 "UPDATE query functions must either return void " + 946 "or int (the number of updated rows)." 947 ) 948 } else if (type == QueryType.DELETE) { 949 append( 950 "DELETE query functions must either return void " + 951 "or int (the number of deleted rows)." 952 ) 953 } 954 } 955 .toString() 956 957 val JDK_VERSION_HAS_BUG = 958 "Current JDK version ${System.getProperty("java.runtime.version") ?: ""} has a bug" + 959 " (https://bugs.openjdk.java.net/browse/JDK-8007720)" + 960 " that prevents Room from being incremental." + 961 " Consider using JDK 11+ or the embedded JDK shipped with Android Studio 3.5+." 962 963 fun invalidChannelType(typeName: String) = 964 "'$typeName' is not supported as a return type. " + 965 "Instead declare return type as ${KotlinTypeNames.FLOW} and use Flow transforming " + 966 "functions that converts the Flow into a Channel." 967 968 fun mismatchedGetter( 969 propertyName: String, 970 ownerType: String, 971 getterType: String, 972 propertyType: String 973 ) = 974 """ 975 $ownerType's $propertyName property has type $propertyType but its getter returns $getterType. 976 This mismatch might cause unexpected $propertyName values in the database when $ownerType 977 is inserted into database. 978 """ 979 .trim() 980 981 fun mismatchedSetter( 982 propertyName: String, 983 ownerType: String, 984 setterType: String, 985 propertyType: String 986 ) = 987 """ 988 $ownerType's $propertyName property has type $propertyType but its setter accepts $setterType. 989 This mismatch might cause unexpected $propertyName values when $ownerType is read from the 990 database. 991 """ 992 .trim() 993 994 const val DATABASE_INVALID_DAO_FUNCTION_RETURN_TYPE = 995 "Abstract database functions must return a @Dao " + "annotated class or interface." 996 997 fun invalidEntityTypeInDatabaseAnnotation(typeName: String): String { 998 return "Invalid Entity type: $typeName. An entity in the database must be a class." 999 } 1000 1001 fun invalidViewTypeInDatabaseAnnotation(typeName: String): String { 1002 return "Invalid View type: $typeName. Views in a database must be a class or an " + 1003 "interface." 1004 } 1005 1006 fun invalidAutoMigrationTypeInDatabaseAnnotation(): String { 1007 return "Invalid AutoMigration type: An auto migration in the database must be " + 1008 "an @AutoMigration annotation." 1009 } 1010 1011 const val EMBEDDED_TYPES_MUST_BE_A_CLASS_OR_INTERFACE = 1012 "The type of an Embedded property must be a " + "class or an interface." 1013 const val RELATION_TYPE_MUST_BE_A_CLASS_OR_INTERFACE = 1014 "Entity type in a Relation must be a class " + "or an interface." 1015 1016 fun shortcutFunctionArgumentMustBeAClass(typeName: String): String { 1017 return "Invalid query argument: $typeName. It must be a class or an interface." 1018 } 1019 1020 const val AUTOMIGRATION_SPEC_MUST_BE_CLASS = "The AutoMigration spec " + "type must be a class." 1021 1022 fun autoMigrationElementMustImplementSpec(spec: String): String { 1023 return "The AutoMigration spec $spec must implement the AutoMigrationSpec interface." 1024 } 1025 1026 // TODO: (b/180389433) If the files don't exist the getSchemaFile() function should return 1027 // null and before calling process 1028 fun autoMigrationToVersionMustBeGreaterThanFrom(to: Int, from: Int) = 1029 if (from > to) { 1030 "Downgrades are not supported in AutoMigration." 1031 } else { 1032 "The versions provided (to: $to, from: $from) are invalid. The To version must" + 1033 " be greater than the From version." 1034 } 1035 1036 fun autoMigrationSchemasNotFound(schemaVersion: Int, schemaOutFolderPath: String): String { 1037 return "Schema '$schemaVersion.json' required for migration was not found at the schema " + 1038 "out folder: $schemaOutFolderPath. Cannot generate auto migrations." 1039 } 1040 1041 fun invalidAutoMigrationSchema(schemaVersion: Int, schemaOutFolderPath: String): String { 1042 return "Found invalid schema file '$schemaVersion.json' at the schema out " + 1043 "folder: $schemaOutFolderPath.\nIf you've modified the file, you might've broken the " + 1044 "JSON format, try deleting the file and re-running the compiler.\n" + 1045 "If you've not modified the file, please file a bug at " + 1046 "https://issuetracker.google.com/issues/new?component=413107&template=1096568 " + 1047 "with a sample app to reproduce the issue." 1048 } 1049 1050 fun newNotNullColumnMustHaveDefaultValue(columnName: String): String { 1051 return "New NOT NULL " + 1052 "column'$columnName' " + 1053 "added with no default value specified. Please specify the default value using " + 1054 "@ColumnInfo." 1055 } 1056 1057 fun columnWithChangedSchemaFound(columnName: String): String { 1058 return "Encountered column '$columnName' with an unsupported schema change at the column " + 1059 "level (e.g. affinity change). These changes are not yet " + 1060 "supported by AutoMigration." 1061 } 1062 1063 fun deletedOrRenamedColumnFound( 1064 className: String?, 1065 columnName: String, 1066 tableName: String 1067 ): String { 1068 return if (className != null) { 1069 """ 1070 AutoMigration Failure in ‘$className’: Column ‘$columnName’ in table ‘$tableName’ has 1071 been either removed or renamed. Please annotate ‘$className’ with the @RenameColumn 1072 or @DeleteColumn annotation to specify the change to be performed: 1073 1) RENAME: 1074 @RenameColumn.Entries( 1075 @RenameColumn( 1076 tableName = "$tableName", 1077 fromColumnName = "$columnName", 1078 toColumnName = <NEW_COLUMN_NAME> 1079 ) 1080 ) 1081 2) DELETE: 1082 @DeleteColumn.Entries( 1083 @DeleteColumn( 1084 tableName = "$tableName", 1085 columnName = "$columnName" 1086 ) 1087 ) 1088 """ 1089 } else { 1090 """ 1091 AutoMigration Failure: Please declare an interface extending 'AutoMigrationSpec', 1092 and annotate with the @RenameColumn or @DeleteColumn annotation to specify the 1093 change to be performed: 1094 1) RENAME: 1095 @RenameColumn.Entries( 1096 @RenameColumn( 1097 tableName = "$tableName", 1098 fromColumnName = "$columnName", 1099 toColumnName = <NEW_COLUMN_NAME> 1100 ) 1101 ) 1102 2) DELETE: 1103 @DeleteColumn.Entries( 1104 @DeleteColumn( 1105 tableName = "$tableName", 1106 columnName = "$columnName" 1107 ) 1108 ) 1109 """ 1110 } 1111 } 1112 1113 fun deletedOrRenamedTableFound(className: String?, tableName: String): String { 1114 return if (className != null) { 1115 """ 1116 AutoMigration Failure in '$className': Table '$tableName' has been either removed or 1117 renamed. Please annotate '$className' with the @RenameTable or @DeleteTable 1118 annotation to specify the change to be performed: 1119 1) RENAME: 1120 @RenameTable.Entries( 1121 @RenameTable( 1122 fromTableName = "$tableName", 1123 toTableName = <NEW_TABLE_NAME> 1124 ) 1125 ) 1126 2) DELETE: 1127 @DeleteTable.Entries( 1128 @DeleteTable( 1129 tableName = "$tableName" 1130 ) 1131 ) 1132 """ 1133 } else { 1134 """ 1135 AutoMigration Failure: Please declare an interface extending 'AutoMigrationSpec', 1136 and annotate with the @RenameTable or @DeleteTable annotation to specify the change 1137 to be performed: 1138 1) RENAME: 1139 @RenameTable.Entries( 1140 @RenameTable( 1141 fromTableName = "$tableName", 1142 toTableName = <NEW_TABLE_NAME> 1143 ) 1144 ) 1145 2) DELETE: 1146 @DeleteTable.Entries( 1147 @DeleteTable( 1148 tableName = "$tableName" 1149 ) 1150 ) 1151 """ 1152 } 1153 } 1154 1155 fun tableRenameError( 1156 className: String, 1157 originalTableName: String, 1158 newTableName: String 1159 ): String { 1160 return "AutoMigration Failure in '$className': The table renamed from " + 1161 "'$originalTableName' to '$newTableName' is " + 1162 "not found in the new version of the database." 1163 } 1164 1165 fun conflictingRenameTableAnnotationsFound(annotations: String): String { 1166 return "Conflicting @RenameTable annotations found: [$annotations]" 1167 } 1168 1169 fun conflictingRenameColumnAnnotationsFound(annotations: String): String { 1170 return "Conflicting @RenameColumn annotations found: [$annotations]" 1171 } 1172 1173 const val AUTO_MIGRATION_FOUND_BUT_EXPORT_SCHEMA_OFF = 1174 "Cannot create auto migrations when " + "exportSchema is false." 1175 1176 const val AUTO_MIGRATION_SCHEMA_IN_FOLDER_NULL = 1177 "Schema import directory was not provided to the" + 1178 " annotation processor so Room cannot read older schemas. To generate auto migrations," + 1179 " you must provide `room.schemaLocation` annotation processor arguments by applying the" + 1180 " Room Gradle plugin (id 'androidx.room') AND set exportSchema to true." 1181 1182 fun tableWithConflictingPrefixFound(tableName: String): String { 1183 return "The new version of the schema contains '$tableName' a table name" + 1184 " with the prefix '_new_', which will cause conflicts for auto migrations. Please use" + 1185 " a different name." 1186 } 1187 1188 const val INNER_CLASS_AUTOMIGRATION_SPEC_MUST_BE_STATIC = 1189 "An inner class AutoMigrationSpec must be" + " static." 1190 1191 const val AUTOMIGRATION_SPEC_MISSING_NOARG_CONSTRUCTOR = 1192 "Classes that are used as " + 1193 "AutoMigrationSpec " + 1194 "implementations must have no-argument public constructors." 1195 1196 const val JVM_NAME_ON_OVERRIDDEN_FUNCTION = 1197 "Using @JvmName annotation on a function or accessor " + 1198 "that will be overridden by Room is not supported. If this is important for your use " + 1199 "case, please file a bug at $ISSUE_TRACKER_LINK with details." 1200 1201 fun ambiguousColumn( 1202 columnName: String, 1203 location: AmbiguousColumnLocation, 1204 typeName: String? 1205 ): String { 1206 val (locationDesc, recommendation) = 1207 when (location) { 1208 AmbiguousColumnLocation.MAP_INFO -> { 1209 "in the @MapInfo" to "update @MapInfo" 1210 } 1211 AmbiguousColumnLocation.DATA_CLASS -> { 1212 checkNotNull(typeName) 1213 "in the object '$typeName'" to "use @ColumnInfo" 1214 } 1215 AmbiguousColumnLocation.ENTITY -> { 1216 checkNotNull(typeName) 1217 "in the entity '$typeName'" to 1218 "use a new data class / data class with " + "@ColumnInfo'" 1219 } 1220 } 1221 return "The column '$columnName' $locationDesc is ambiguous and cannot be properly " + 1222 "resolved. Please alias the column and $recommendation. Otherwise there is a risk of " + 1223 "the query returning invalid values. You can suppress this warning by annotating " + 1224 "the function with @SuppressWarnings(RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT)." 1225 } 1226 1227 enum class AmbiguousColumnLocation { 1228 MAP_INFO, 1229 DATA_CLASS, 1230 ENTITY, 1231 } 1232 1233 const val NONNULL_VOID = 1234 "Invalid non-null declaration of 'Void', should be nullable. The 'Void' " + 1235 "class represents a placeholder type that is uninstantiable and 'null' is always returned." 1236 1237 fun nullableCollectionOrArrayReturnTypeInDaoFunction( 1238 typeName: String, 1239 returnType: String 1240 ): String { 1241 return "The nullable `$returnType` ($typeName) return type in a DAO function is " + 1242 "meaningless because Room will instead return an empty `$returnType` if no rows are " + 1243 "returned from the query." 1244 } 1245 1246 fun nullableComponentInDaoFunctionReturnType(typeName: String): String { 1247 return "The DAO function return type ($typeName) with the nullable type argument " + 1248 "is meaningless because for now Room will never put a null value in a result." 1249 } 1250 1251 const val EXPORTING_SCHEMA_TO_RESOURCES = 1252 "Schema export is set to be outputted as a resource" + 1253 " (i.e. room.exportSchemaResource is set to true), this means Room will write the current" + 1254 " schema version file into the produced JAR. Such flag must only be used for generating" + 1255 " the schema file and extracting it from the JAR but not for production builds, otherwise" + 1256 " the schema file will end up in the final artifact which is typically not desired. This" + 1257 " warning serves as a reminder to use room.exportSchemaResource cautiously." 1258 1259 const val INVALID_GRADLE_PLUGIN_AND_SCHEMA_LOCATION_OPTION = 1260 "The Room Gradle plugin " + 1261 "(id 'androidx.room') cannot be used with an explicit use of the annotation processor" + 1262 "option `room.schemaLocation`, please remove the configuration of the option and " + 1263 "configure the schema location via the plugin project extension: " + 1264 "`room { schemaDirectory(...) }`." 1265 1266 const val INVALID_DATABASE_VERSION = "Database version must be greater than 0" 1267 1268 const val JAVA_CODEGEN_ON_NON_ANDROID_TARGET = 1269 "Cannot generate Java targeting a non-Android " + 1270 "platform. To generate Java, you must only have Android as a target platform. " + 1271 "To process a non-Android target platform, you must enable Kotlin code " + 1272 "generation in KSP." 1273 1274 const val INVALID_BLOCKING_DAO_FUNCTION_NON_ANDROID = 1275 "Only suspend functions are allowed in DAOs" + 1276 " declared in source sets targeting non-Android platforms." 1277 1278 val INVALID_KOTLIN_CODE_GEN_IN_JAVAC = 1279 "${Context.BooleanProcessorOptions.GENERATE_KOTLIN.argName} can only be enabled in KSP." 1280 1281 const val RAW_QUERY_NOT_SUPPORTED_ON_NON_ANDROID = 1282 "@RawQuery annotated DAO functions are currently not supported in source sets targeting " + 1283 "non-Android platforms." 1284 1285 const val MISSING_CONSTRUCTED_BY_ANNOTATION = 1286 "The @Database class must be annotated with @ConstructedBy since the source is targeting " + 1287 "non-Android platforms." 1288 1289 const val INVALID_CONSTRUCTED_BY_CLASS = "The @ConstructedBy 'value' must be a valid class." 1290 1291 const val INVALID_CONSTRUCTED_BY_NOT_OBJECT = 1292 "The @ConstructedBy definition must be an 'object' declaration." 1293 1294 const val INVALID_CONSTRUCTED_BY_NOT_EXPECT = 1295 "The @ConstructedBy definition must be an 'expect' declaration." 1296 1297 fun invalidConstructedBySuperInterface(expected: String) = 1298 "The @ConstructedBy definition must implement a single interface of type '$expected'." 1299 } 1300