serverpod / serverpod

Serverpod is a next-generation app and web server, explicitly built for the Flutter and Dart ecosystem.
BSD 3-Clause "New" or "Revised" License
2.41k stars 221 forks source link

Allow raw queries to be parsed as Dart types #701

Open lukehutch opened 1 year ago

lukehutch commented 1 year ago

Is your feature request related to a problem? Please describe.

Currently raw queries in Serverpod take the form

List<List<dynamic>> result = await session.db.query('SELECT * FROM profiles WHERE ...');

and return type List<List<dynamic>> of rows and columns.

I need to execute a query that is a bit too exotic for the expression builder syntax (specifically I need to use SELECT * FROM profiles WHERE name NOT IN (SELECT name FROM blockednames)).

However, I want to automatically deserialize the results to type Profile (the type serialized to the profiles table), rather than be given a simple row/column result set.

As far as I can see, there's no way to do this right now. Could a mechanism be added for this? e.g.

List<Profile> profiles = await session.db.queryTyped<Profile>('SELECT * FROM profiles WHERE ...');
fischerscode commented 1 year ago

An alternative syntax could be (await session.db.query('SELECT * FROM profiles WHERE ...')).deserialize([typeColumn1, typeColumn2, ...],serializationManager).

I'd allow selecting only some columns, using GROUP BY and joins. With dart3 it might even be possible to further enhance it to List<(ColumnType1, ColumnType2)> result = (await session.db.query('SELECT * FROM profiles WHERE ...')).deserialize<(ColumnType1, ColumnType2)>(serializationManager).

Untested implementation using an extension:

/// Licensed by "The Unlicense" (https://choosealicense.com/licenses/unlicense/)
extension on List<List<dynamic>> {
  List<List<dynamic>> deserialize(
      List<Type> types, SerializationManager serializationManager) {
    return map(
      (row) => List.generate(
        types.length,
        (index) => serializationManager.deserialize(row[index], types[index]),
      ),
    ).toList();
  }
}

edit: Use something like this to acquire nullable types.

  Type getType<T>() => T;

  var type = getType<int?>();