floor icon indicating copy to clipboard operation
floor copied to clipboard

Return null on scalar queries such as "Select count(*) from ..."

Open wzhou-grid opened this issue 3 years ago • 10 comments

The following query always returns null:

@Query('SELECT COUNT(*) from Area')
  Future<int?> total();

I have tested other scalar queries, such as

@Query('SELECT MAX(Id) from Area')
  Future<int?> getMax();

still got null. very strange.

wzhou-grid avatar Jun 18 '21 03:06 wzhou-grid

I have the same problem. The following query

@Query('SELECT MAX(createdAt), MAX(updatedAt) from people')
  Future<List<DateTime>> getOldestDates();

generates following code in database.g.dart

 await _queryAdapter
      .queryNoReturn('SELECT MAX(createdAt), MAX(updatedAt) from people');

which has a return type of void

evandrmb avatar Jul 26 '21 19:07 evandrmb

What about removing ? from Future generic type?

@Query('SELECT COUNT(*) from Area')
  Future<int> total();

I suppose COUNT(*) always returns number and never returns null

Sanshain avatar Aug 01 '21 21:08 Sanshain

What about removing ? from Future generic type?

@Query('SELECT COUNT(*) from Area')
  Future<int> total();

I suppose COUNT(*) always returns number and never returns null

He can't. If you do that and try to run flutter pub run build_runner build it will fail with the following warning:

Queries returning single elements might return null. Make the method return a Future of a nullable type e.g. Future<int?>`

evandrmb avatar Aug 04 '21 12:08 evandrmb

What about max (id) when db is empty?

ayalma avatar Aug 31 '21 12:08 ayalma

This is badly needed feature. I have several tables I'd like to know if they are empty or not. My workaround is to have raw queries in the AppDatabase class, something along the line of:

  Future<int> areaCount() async {
    final result = await database.rawQuery("SELECT COUNT(`id`) AS cnt FROM Area");

    return result.isEmpty ? 0 : result[0]['cnt'] as int? ?? 0;
  }

But this is sooo ugly and might not be robust! I think count is common enough to be supported along with CRUD.

MrCsabaToth avatar Nov 10 '21 20:11 MrCsabaToth

This feature will help https://github.com/vitusortner/floor/issues/200

MrCsabaToth avatar Nov 10 '21 20:11 MrCsabaToth

What about max (id) when db is empty?

No matter how you spin it around the code generator portion needs to recognize and support what you want to do. And that's what is missing right now.

MrCsabaToth avatar Nov 11 '21 23:11 MrCsabaToth

stranger still with DAO @Query('SELECT COUNT(*) FROM MyItem') Future<int?> getAllCount(); @Query('SELECT * FROM MyItem') Future<List<MyItem>> findAllItems();

int? count=await database.myItemDAO.getAllCount(); print('db-size' + count.toString()); ==> returns null

List<MyItem> myItems= await database.myItemDAO.findAllItems(); print('db-size' + myItems.length.toString()); ==> returns the actual count

Nathagamelle avatar Nov 30 '21 21:11 Nathagamelle

stranger still with DAO @query('SELECT COUNT(*) FROM MyItem') Future<int?> getAllCount(); @query('SELECT * FROM MyItem') Future<List> findAllItems();

int? count=await database.myItemDAO.getAllCount(); print('db-size' + count.toString()); ==> returns null

List myItems= await database.myItemDAO.findAllItems(); print('db-size' + myItems.length.toString()); ==> returns the actual count

That length on the findAllItems result is very resource intensive especially with a larger table. First I would say just for this purpose you may want @query('SELECT id FROM MyItem') instead of @query('SELECT * FROM MyItem') just to decrease the bandwidth. But then you'd rather want to do what I advised and place a dedicated function for the purpose into your AppDatabase descendant class:

  Future<int> myItemCount() async {
    final result = await database.rawQuery("SELECT COUNT(`id`) AS cnt FROM MyItem");

    return result.isEmpty ? 0 : result[0]['cnt'] as int? ?? 0;
  }

MrCsabaToth avatar Dec 01 '21 18:12 MrCsabaToth

The auto-generated code for SELECT COUNT(*) is _queryAdapter.queryNoReturn, so you won't get any result.

dao code:

@Query('SELECT COUNT(*) FROM notification WHERE groupUid = :groupUid AND createTime >= :timestamp AND hasRead IS FALSE')
 Future<int?> getUnReadNotificationCountByGroupId(String groupUid, int timestamp);

My solution: Modify the auto-generated database.g.dart code:

@override
  Future<int?> getUnReadNotificationCountByGroupId(String groupUid, int timestamp) async {
    return _queryAdapter.query(
        'SELECT COUNT(*) FROM notification WHERE groupUid = ?1 AND createTime >= ?2 AND hasRead IS FALSE',
        mapper: (Map<String, Object?> row) => row['COUNT(*)'] as int,
        arguments: [groupUid, timestamp]);
  }

The raw sql result is a map: {'COUNT(*)': value}, so you just write the mapper to get the value is ok.

debbiefu avatar Mar 09 '22 08:03 debbiefu