Status Update
Comments
cu...@google.com <cu...@google.com>
sh...@gmail.com <sh...@gmail.com> #2
This is indeed a long standing issue in Room and will likely be more relevant with the map / multimap return type. I think we can show a warning to start but we'll also take a deeper look to see how we can solve the duplicate column name issue.
As a workaround you can always use a column alias and a POJO class with matching field name.
kc...@google.com <kc...@google.com> #3
Branch: androidx-main
commit 894b0ff2c1b095a46803975f400a561eb974ea28
Author: Daniel Santiago Rivera <danysantiago@google.com>
Date: Fri Oct 29 10:56:31 2021
Duplicate column resolution heuristic algorithm
AmbiguousColumnResolver contains an algorithm to map query result columns to data objects (POJOs) columns. We call data object columns 'mapping' and in a multimap query where there might be multiple data objects we refer to the list of all their columns 'mappings'. The algorithm uses a grouping / neighboring strategy to assign the duplicate columns to their right data objects matching their name along with taking into account the the nearby columns since in a star-projected query all columns coming from a table will appear in the result before the next table. Room will generate code that uses the algorithm at runtime if the query has a star-projection, if not then Room will use the algorithm during compile-time since the columns result order is known and fixed.
The algorithm does not solve all cases, specifically those where one of the data objects has a single column which is the duplicate column. For those situation Room will warn the user so that they alias the duplicate column. For other odd cases, Room will behave as it used to, picking the first result column that matches with the data object column.
Bug: 201306012
Bug: 212279118
Test: AmbiguousColumnResolverTest
Relnote: Room will now attempt to resolve ambiguous columns in a multimap query. This allows for JOINs with tables containing same-name tables to be correctly mapped to a result data object.
Change-Id: I4b444b042245a334cc3f362f3239721ce0b6bd1e
M room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
M room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
A room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AmbiguousColumnResolverTest.kt
M room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
M room/room-common/api/current.txt
M room/room-compiler/src/main/kotlin/androidx/room/vo/Warning.kt
M room/room-runtime/api/restricted_current.txt
A room/room-common/src/main/java/androidx/room/AmbiguousColumnResolver.kt
M room/room-common/api/public_plus_experimental_current.txt
M room/room-common/build.gradle
M room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
M room/room-common/src/main/java/androidx/room/RoomWarnings.kt
M room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt
M room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaImmutableMultimapQueryResultAdapter.kt
M room/room-runtime/src/main/java/androidx/room/util/CursorUtil.kt
M room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
A room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/AmbiguousColumnIndexAdapter.kt
M room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
M room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ImmutableMapQueryResultAdapter.kt
A room/room-common/src/test/java/androidx/room/AmbiguousColumnResolverTest.kt
M room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MapQueryResultAdapter.kt
M room/room-common/api/restricted_current.txt
ko...@gmail.com <ko...@gmail.com> #4
@Entity
data class RoomSubTrip(
@PrimaryKey val id: String,
val startDate: Long,
val endDate: Long?,
val estimatedActivity: UserActivity,
val tripId: String,
)
@Entity
data class RoomSubTripSensorData(
@PrimaryKey(autoGenerate = true)
val id: Int = 0,
val tripId: String,
val subTripId: String,
val recording: String
)
@Transaction
@Query(
"SELECT * FROM RoomSubTrip JOIN RoomSubTripSensorData " +
"ON RoomSubTrip.id = RoomSubTripSensorData.subTripId "
)
fun getSubTripWithSensorRecordings(): Map<RoomSubTrip, List<RoomSubTripSensorData>>
This return map with RoomSubTrip with id from RoomSubTripSensorData.id
ga...@gtempaccount.com <ga...@gtempaccount.com> #5
Sorry, the fix for this issue is in Room 2.5.0 and not 2.4.x, we are working on getting a stable release of 2.5.0 soon.
oy...@gmail.com <oy...@gmail.com> #6
Need to know if I should wait for the update or refactor my code.
ga...@gtempaccount.com <ga...@gtempaccount.com> #7
Room 2.5.0-rc01 was recently released, the next release will be 2.5.0. Unfortunately the US holidays are around the corner and the next androidx release will be early next year.
ap...@google.com <ap...@google.com> #8
Room 2.5.1, this issue still exists when selecting using a join to filter data but omitting the joined data in the result. E.g. when Entity1
has column named id
and Entity2
also has id
, and I use a @Query
that returns a List<Entity1>
with a simple join in order to filter only those of Entity1
who have a relation to certain Entity2
. The resulting List<Entity1>
contains instances of Entity1
whose ids are wrongly assigned to those of their respective joined Entity2
. And that is regardless whether @RewriteQueriesToDropUnusedColumns
is being used or not.
ap...@google.com <ap...@google.com> #9
#8, can you provide a sample app that reproduces that error? Seeing the query + entity1/2 should be enough. thanks.
ap...@google.com <ap...@google.com> #10
I'm also still seeing this issue in Room 2.6.0. I'm able to produce it using the query from the "
@Query(
"SELECT * FROM book " +
"INNER JOIN loan ON loan.book_id = book.id " +
"INNER JOIN user ON user.id = loan.user_id " +
"WHERE user.name LIKE :userName"
)
fun findBooksBorrowedByNameSync(userName: String): List<Book>
I created these entities to go with it:
@Entity
data class Book(
@PrimaryKey(autoGenerate = true) val id: Int,
val title: String,
)
@Entity
data class Loan(
@PrimaryKey(autoGenerate = true) val id: Int,
@ColumnInfo(name = "book_id") val bookId: Int,
@ColumnInfo(name = "user_id") val userId: Int,
)
@Entity
data class User(
@PrimaryKey(autoGenerate = true) val id: Int,
val name: String,
)
When testing it with this
@Test
fun returnsCorrectIds() {
val users = (1..5).map { User(id = 0, name = "User $it") }
userDao.insert(users)
val books = (1..5).map { Book(id = 0, title = "Book $it") }
bookDao.insert(books)
val insertedBooks = bookDao.getAll()
assertThat(insertedBooks).contains(Book(id = 4, title = "Book 4")) // Book 4 has id 4
loanDao.insert(Loan(id = 0, bookId = 4, userId = 2))
val borrowedBooks = bookDao.findBooksBorrowedByNameSync("User 2")
assertThat(borrowedBooks).containsExactly(Book(id = 4, title = "Book 4"))
}
the last assertion here fails:
value of: iterable.onlyElement()
expected: Book(id=4, title=Book 4)
but was : Book(id=2, title=Book 4)
So the Books that are returned all have the User's ID in the id
field. (If Book
had name
instead of a title
it would also return the User's name
as the Book's name
.) If I change the order of the two joins, then the returned Books have the ID of the Loan.
ap...@google.com <ap...@google.com> #11
Branch: androidx-master-dev
commit 10435ab81f5e20d69a4780eb7f1f713ac9762961
Author: Curtis Belmonte <curtislb@google.com>
Date: Wed Oct 16 14:33:20 2019
Update biometric workaround to use model prefixes
Makes the existing device-specific workaround to fall back to
FingerprintDialogFragment more general by updating it to expect device
model prefixes, rather than exact string device models to match against.
This allows us to cover device variants with different model suffixes.
Test: Manually, using the biometric support demo app.
Bug: 142150327
Change-Id: I3a109b8bdcf8a4bf59597cb99bf6b702734166cb
M biometric/res/values/devices.xml
M biometric/src/main/java/androidx/biometric/Utils.java
an...@google.com <an...@google.com> #12
Release Track: (
Created: 2019-10-17 01:13:42.291290+00:00
Changes: aosp/1145123, aosp/1145121, aosp/1145122, aosp/1145119
cu...@google.com <cu...@google.com> #13
ba...@gmail.com <ba...@gmail.com> #14
kc...@google.com <kc...@google.com> #15
ba...@gmail.com <ba...@gmail.com> #16
oy...@gmail.com <oy...@gmail.com> #17
In the list the following devices are blacklisted:
- Samsung Galaxy S8/S8+
- Samsung Galaxy S9/S9+
- Samsung Galaxy S10/S10+
- Samsung Galaxy Note 8
- Samsung Galaxy Note 9
- Samsung Galaxy Note 10/10+
- Samsung Galaxy A20/A20e
But, by doing some quick research, I found that some other models also includes face recognition:
- Samsung Galaxy S10e
- Samsung Galaxy A8/A8+
- Samsung Galaxy A9
- Samsung Galaxy A10
- Samsung Galaxy A40
- Samsung Galaxy A50
- Samsung Galaxy A70
Probably many others includes face recognition as well. I can't rely on this blacklisting if you have to add every device to that list.
da...@gmail.com <da...@gmail.com> #18
This is ridiculous. I use Iris all the time on my S8 and it's much preferred over Fingerprint or Face. Why is Google always trying to fix what ain't broke. There is irony in the fact that they even removed Fingerprint sensor from Pixel 4, yet that is the default now?
...and they wonder why Android gets a bad rap compared to IOS. Infuriating and backwards policies they force upon users/developers.
ev...@willowtreeapps.com <ev...@willowtreeapps.com> #19
That is a Samsung policy, please complain to them.
ga...@gmail.com <ga...@gmail.com> #20
1. Fingerprint API could handle Face ID (especially because it is one or the other), instead now you cannot login on Pixel 4 when an app is still on Fingerprint APIs
2. They wrote a new API instead for Face (again, there was no reason for it) AND intentionally blocked Samsung face tech out of it by categorizing them as "unsupported weak biometrics"
Samsung now says they will offer Iris on Android 10 but first you always get a fingerprint dialog and then you get a button or something within that dialog to go to Iris. Pretty UX, don't expect seamless logins. Even if your phone unlock is on Iris, you still get fingerprint every time.
sh...@gmail.com <sh...@gmail.com> #21
oy...@gmail.com <oy...@gmail.com> #22
A user with Samsung A70 (which is not blacklisted by Google) gets the UserNotAuthenticatedException because he has face recognition set as preferred biometrics, and tries to log into our app. You can't blacklist every Samsung device! You have to fix this another way!
ab...@gmail.com <ab...@gmail.com> #23
[Deleted User] <[Deleted User]> #24
[Deleted User] <[Deleted User]> #25
Setting preferred method to fingerprint works great.
Description
The long-term fix is to work with OEMs to resolve this issue, which is not the scope of this bug.
In the short term, we need to maintain a list of misbehaving devices, for which authenticate(CryptoObject) will temporarily invoke FingerprintManager (via FingerprintDialog/FingerprintHelper)
The purpose of this bug is to implement a whitelist to fallback to fingerprint. Once that's done, we'll add devices to the list to the next available release.