Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0.0] Exception on 'SELECT *, MAX(...) FROM ...' query #506

Closed
proninyaroslav opened this issue Mar 9, 2021 · 16 comments
Closed

[1.0.0] Exception on 'SELECT *, MAX(...) FROM ...' query #506

proninyaroslav opened this issue Mar 9, 2021 · 16 comments

Comments

@proninyaroslav
Copy link

proninyaroslav commented Mar 9, 2021

It's easier to understand this from an example. I tried to make a simplified version of what is used in my project. Let's say we have the following null-safety code:

// foo.dart

@entity
class Foo {
  @primaryKey
  final int id;

  Foo({required this.id});
}
// foo_dao.dart

@dao
abstract class FooDao {
  @Query('SELECT *, MAX(id) FROM Foo')
  Future<List<Foo>> getMax();
}

database.g.dart will generate code like this for the getMax() method:

// database.g.dart

  @override
  Future<List<Foo>> getMax() async {
    return _queryAdapter.queryList('SELECT *, MAX(id) FROM Foo',
        mapper: (Map<String, Object?> row) => Foo(id: row['id'] as int));
  }

But there will be an exception when calling the method if the DB is empty. SELECT *, MAX(id) FROM Foo query is an important condition for reproducing the error. If you write just SELECT * FROM Foo, there will be no error.

type 'Null' is not a subtype of type 'int' in type cast
package:example/database.g.dart 110:65              _$FooDao.getMax.<fn>
package:floor/src/adapter/query_adapter.dart 40:36  QueryAdapter.queryList.<fn>
dart:_internal                                      ListIterable.toList
package:floor/src/adapter/query_adapter.dart 40:43  QueryAdapter.queryList
@proninyaroslav
Copy link
Author

I modified the example to make it even easier.

@proninyaroslav proninyaroslav changed the title [1.0.0] Problem with 'SELECT *, MAX(...) FROM ...' query [1.0.0] Exception on 'SELECT *, MAX(...) FROM ...' query Mar 9, 2021
@proninyaroslav
Copy link
Author

@vitusortner
By the way, it's likely that this code didn't work even before the migration to null safety. Unfortunately I didn't write a test for it, and in the old version of floor it silently set all entity fields to null. But with the advent of null safety, such assignment is denied for non-null types. Quite helpful :D

@vitusortner
Copy link
Collaborator

I started investigating this issue. Does it only happen when the database is empty?

@proninyaroslav
Copy link
Author

Yes.

@proninyaroslav
Copy link
Author

proninyaroslav commented Mar 14, 2021

I modified the example. The database is not empty:

test('Get max', () async {
    await taskDao.insertTask(Task(null, 'test'));
    print(await taskDao.getMax());
  });

Output:

[Task{id: 1, message: test}]
✓ Get max

The database is empty:

test('Get max', () async {
    print(await taskDao.getMax());
  });

Output:

type 'Null' is not a subtype of type 'String' in type cast
package:example/database.g.dart 152:52              _$TaskDao.getMax.<fn>
package:floor/src/adapter/query_adapter.dart 40:36  QueryAdapter.queryList.<fn>
dart:_internal                                      ListIterable.toList
package:floor/src/adapter/query_adapter.dart 40:43  QueryAdapter.queryList

@proninyaroslav
Copy link
Author

@vitusortner
1.0.1 version still has this problem.

@proninyaroslav
Copy link
Author

@vitusortner
Hi. Any ETA for issue resolving?

@mqus
Copy link
Collaborator

mqus commented Apr 22, 2021

I looked at it briefly and I'm not sure what you're expecting the query to return, purely from an sql viewpoint. Afaik, on an empty database, this query will normally return no rows, but due to the max() function will return a single (aggregated) row, where all other columns are set to null. If the returned type (in this case Foo) can't set its values to null, thats when you get the error. As this is specified behaviour by SQL itself, I don't see the bug here.

@proninyaroslav
Copy link
Author

@mqus
So, what to do in this situation? I can’t give up the list here, just like I can’t make all entity fields as nullable (this is at least silly)

@mqus
Copy link
Collaborator

mqus commented Apr 22, 2021

I don't think this would work either way because the ´MAX(id)´ will not get mapped either way(as you can see in the generated code). Where should it even be mapped to?

Maybe you could explain your situation(what do you have and what do you want to achieve) from a higher level and we could start from there.

I want to add that we sadly do not yet support arbitrary return values for queries, so returning the maximum might require workarounds.

@proninyaroslav
Copy link
Author

@mqus
The situation is to get all entities with the highest priority, the priority can be duplicated (therefore, a list is needed). It's probably easier to solve this problem on the Dart side by doing sorting or searching through the list.

@mqus
Copy link
Collaborator

mqus commented Apr 23, 2021

What you are trying to do should be easy with floor and needs no workarounds. The query you could try is
SELECT * FROM mytable WHERE prio = (SELECT MAX(prio) FROM mytable).

(You'll have to replace prio and mytable with your actual priority column and the name of your entity)

(Updated: I have tried this in sqlite and it works as expected: no output in an empty table and only the rows with the maximum prio if entries are there).

@proninyaroslav
Copy link
Author

Yes, it works.

@proninyaroslav
Copy link
Author

Alternate query, that works:

SELECT *, MAX(id) FROM Foo GROUP BY id

@mqus
Copy link
Collaborator

mqus commented May 10, 2021

I don't think this query does what you think it does. It is semantically completely different.

@mqus
Copy link
Collaborator

mqus commented Jul 10, 2021

Issue seems to be solved, so I'm closing this. Reopen if you disagree :)

@mqus mqus closed this as completed Jul 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants