Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
simolus3 committed Jan 14, 2022
2 parents 6555523 + d6d5271 commit 41472d6
Show file tree
Hide file tree
Showing 32 changed files with 402 additions and 66 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ _Note: Moor has been renamed to drift_
[![Build Status](https://api.cirrus-ci.com/github/simolus3/moor.svg)](https://github.com/simolus3/moor/actions/workflows/main.yml/badge.svg)
[![Chat on Gitter](https://img.shields.io/gitter/room/moor-dart/community)](https://gitter.im/moor-dart/community)

## Proudly Sponsored by [Stream 💙](https://getstream.io/chat/flutter/tutorial/?utm_source=https://github.com/simolus3/moor&utm_medium=github&utm_content=developer&utm_term=flutter)
## Proudly Sponsored by [Stream 💙](https://getstream.io/chat/flutter/tutorial/?utm_source=Github&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Github_Jan2022_FlutterChat&utm_term=moor)

<p align="center">
<table>
<table>
<tbody>
<tr>
<td align="center">
<a href="https://getstream.io/chat/flutter/tutorial/?utm_source=https://github.com/simolus3/moor&utm_medium=github&utm_content=developer&utm_term=flutter" target="_blank"><img width="250px" src="https://stream-blog.s3.amazonaws.com/blog/wp-content/uploads/fc148f0fc75d02841d017bb36e14e388/Stream-logo-with-background-.png"/></a><br/><span><a href="https://getstream.io/chat/flutter/tutorial/?utm_source=https://github.com/simolus3/moor&utm_medium=github&utm_content=developer&utm_term=flutter" target="_blank">Try the Flutter Chat Tutorial &nbsp💬</a></span>
</td>
<a href="https://getstream.io/chat/flutter/tutorial/?utm_source=Github&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Github_Jan2022_FlutterChat&utm_term=moor" target="_blank"><img width="250px" src="https://stream-blog.s3.amazonaws.com/blog/wp-content/uploads/fc148f0fc75d02841d017bb36e14e388/Stream-logo-with-background-.png"/></a><br/><span><a href="https://getstream.io/chat/flutter/tutorial/?utm_source=Github&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Github_Jan2022_FlutterChat&utm_term=moor" target="_blank">Try the Flutter Chat Tutorial &nbsp💬</a></span>
</td>
</tr>
</tbody>
</table>
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/docs/Advanced Features/builder_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ At the moment, drift supports these options:
* `new_sql_code_generation`: Generates SQL statements from the parsed AST instead of replacing substrings. This will also remove
unnecessary whitespace and comments.
If enabling this option breaks your queries, please file an issue!
* `scoped_dart_components`: Generates a function parameter for [Dart placeholders]({{ '../Using SQL/moor_files.md#dart-components-in-sql' | pageUrl }}) in SQL.
* `scoped_dart_components`: Generates a function parameter for [Dart placeholders]({{ '../Using SQL/drift_files.md#dart-components-in-sql' | pageUrl }}) in SQL.
The function has a parameter for each table that is available in the query, making it easier to get aliases right when using
Dart placeholders.
* `null_aware_type_converters`: Consider the type of applied type converters to determine nullability of columns in Dart.
Expand Down
4 changes: 2 additions & 2 deletions docs/pages/docs/Getting started/starting_with_sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ Let's take a look at what drift generated during the build:
- a `Selectable<Todo> todosInCategory(int)` method, which runs the
`todosInCategory` query declared above. Drift has determined that the
type of the variable in that query is `int`, because that's the type
of the `category` column we're comparing it to.
of the `category` column we're comparing it to.
The method returns a `Selectable` to indicate that it can both be
used as a regular query (`Selectable.get` returns a `Future<List<Todo>>`)
or as an auto-updating stream (by using `.watch` instead of `.get()`).
Expand All @@ -169,7 +169,7 @@ further guides to help you learn more:
- [Schema migrations]({{ "../Advanced Features/migrations.md" | pageUrl }})
- Writing [queries]({{ "writing_queries.md" | pageUrl }}) and
[expressions]({{ "../Advanced Features/expressions.md" | pageUrl }}) in Dart
- A more [in-depth guide]({{ "../Using SQL/moor_files.md" | pageUrl }})
- A more [in-depth guide]({{ "../Using SQL/drift_files.md" | pageUrl }})
on `drift` files, which explains `import` statements and the Dart-SQL interop.

{% block "blocks/alert" title="Using the database" %}
Expand Down
10 changes: 5 additions & 5 deletions docs/pages/docs/Using SQL/custom_queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ To use this feature, it's helpful to know how Dart tables are named in sql. For
override `tableName`, the name in sql will be the `snake_case` of the class name. So a Dart table
called `Categories` will be named `categories`, a table called `UserAddressInformation` would be
called `user_address_information`. The same rule applies to column getters without an explicit name.
Tables and columns declared in [Drift files]({{ "moor_files.md" | pageUrl }}) will always have the
Tables and columns declared in [Drift files]({{ "drift_files.md" | pageUrl }}) will always have the
name you specified.
{% endblock %}

You can also use `UPDATE` or `DELETE` statements here. Of course, this feature is also available for
You can also use `UPDATE` or `DELETE` statements here. Of course, this feature is also available for
[daos]({{ "../Advanced Features/daos.md" | pageUrl }}),
and it perfectly integrates with auto-updating streams by analyzing what tables you're reading from or
writing to.
Expand All @@ -60,7 +60,7 @@ writing to.
If you don't want to use the statements with an generated api, you can
still send custom queries by calling `customSelect` for a one-time query or
`customSelectStream` for a query stream that automatically emits a new set of items when
the underlying data changes. Using the todo example introduced in the
the underlying data changes. Using the todo example introduced in the
[getting started guide]({{ "../Getting started/index.md" | pageUrl }}), we can
write this query which will load the amount of todo entries in each category:
```dart
Expand Down Expand Up @@ -90,7 +90,7 @@ Stream<List<CategoryWithCount>> categoriesWithCount() {
```
For custom selects, you should use the `readsFrom` parameter to specify from which tables the query is
reading. When using a `Stream`, drift will be able to know after which updates the stream should emit
items.
items.

You can also bind SQL variables by using question-mark placeholders and the `variables` parameter:

Expand All @@ -104,7 +104,7 @@ Stream<int> amountOfTodosInCategory(int id) {
}
```

Of course, you can also use indexed variables (like `?12`) - for more information on them, see
Of course, you can also use indexed variables (like `?12`) - for more information on them, see
[the sqlite3 documentation](https://sqlite.org/lang_expr.html#varparam).

## Custom update statements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ data:

aliases:
- /docs/using-sql/custom_tables/ # Redirect from outdated "custom tables" page which has been deleted
- /docs/using-sql/moor_files/

template: layouts/docs/single
---

Expand Down Expand Up @@ -174,27 +176,27 @@ CREATE TABLE saved_routes (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
"from" INTEGER NOT NULL REFERENCES coordinates (id),
to INTEGER NOT NULL REFERENCES coordinates (id)
"to" INTEGER NOT NULL REFERENCES coordinates (id)
);

routesWithPoints: SELECT r.id, r.name, f.*, t.* FROM routes r
INNER JOIN coordinates f ON f.id = r."from"
INNER JOIN coordinates t ON t.id = r.to;
INNER JOIN coordinates t ON t.id = r."to";
```

To match the returned column names while avoiding name clashes in Dart, drift
To match the returned column names while avoiding name clashes in Dart, drift
will generate a class having an `id`, `name`, `id1`, `lat`, `long`, `lat1` and
a `long1` field.
Of course, that's not helpful at all - was `lat1` coming from `from` or `to`
Of course, that's not helpful at all - was `lat1` coming from `from` or `to`
again? Let's rewrite the query, this time using nested results:

```sql
routesWithNestedPoints: SELECT r.id, r.name, f.**, t.** FROM routes r
INNER JOIN coordinates f ON f.id = r."from"
INNER JOIN coordinates t ON t.id = r.to;
INNER JOIN coordinates t ON t.id = r."to";
```

As you can see, we can nest a result simply by using the drift-specific
As you can see, we can nest a result simply by using the drift-specific
`table.**` syntax.
For this query, drift will generate the following class:
```dart
Expand All @@ -207,7 +209,7 @@ class RoutesWithNestedPointsResult {
}
```

Great! This class matches our intent much better than the flat result class
Great! This class matches our intent much better than the flat result class
from before.

At the moment, there are some limitations with this approach:
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/v2.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
The rewritten compiler is faster than ever, supports more SQL features and gives you
more flexibility when writing database code.

[Check the updated documentation]({{ "docs/Using SQL/moor_files.md" | pageUrl }})
[Check the updated documentation]({{ "docs/Using SQL/drift_files.md" | pageUrl }})
{% endblock %}
{% endblock %}

Expand Down
7 changes: 5 additions & 2 deletions docs/tool/ci_check.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ Future<void> main() async {
Uri.parse('http://localhost:8080/api/')
],
{'http://localhost:8080/**'},
true,
// todo: Re-enable. Current problem is that we link new pages to their
// final url (under drift.simonbinder.eu) before they're deployed.
false,
UrlSkipper(
'', ['github.com', 'pub.dev', 'api.dart.dev', 'fonts.gstatic.com']),
false,
Expand All @@ -36,7 +38,8 @@ Future<void> main() async {
var hasBrokenLinks = false;

for (final result in results.destinations) {
if (result.isBroken) {
// todo: Remove !result.isExternal after external links work again
if (result.isBroken && !result.isExternal) {
print('Broken: $result (${result.toMap()})');
hasBrokenLinks = true;
}
Expand Down
10 changes: 10 additions & 0 deletions drift/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 1.3.0

- Add the `from(table)` method to generated databases. It can be used to write
common queries more concisely.
- Make `groupConcat` nullable in the Dart API.
- Throw an exception in a `NativeDatabase` when multiple statements are run in
a single call. In previous versions, parts of the SQL string would otherwise
be ignored.
- Close the underlying database when a drift isolate is shut down.

## 1.2.0

- Properly support stream update queries on views.
Expand Down
8 changes: 4 additions & 4 deletions drift/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Drift

## Proudly Sponsored by [Stream 💙](https://getstream.io/chat/flutter/tutorial/?utm_source=https://github.com/simolus3/moor&utm_medium=github&utm_content=developer&utm_term=flutter)
## Proudly Sponsored by [Stream 💙](https://getstream.io/chat/flutter/tutorial/?utm_source=Github&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Github_Jan2022_FlutterChat&utm_term=moor)

<p align="center">
<table>
<table>
<tbody>
<tr>
<td align="center">
<a href="https://getstream.io/chat/flutter/tutorial/?utm_source=https://github.com/simolus3/moor&utm_medium=github&utm_content=developer&utm_term=flutter" target="_blank"><img width="250px" src="https://stream-blog.s3.amazonaws.com/blog/wp-content/uploads/fc148f0fc75d02841d017bb36e14e388/Stream-logo-with-background-.png"/></a><br/><span><a href="https://getstream.io/chat/flutter/tutorial/?utm_source=https://github.com/simolus3/moor&utm_medium=github&utm_content=developer&utm_term=flutter" target="_blank">Try the Flutter Chat Tutorial &nbsp💬</a></span>
</td>
<a href="https://getstream.io/chat/flutter/tutorial/?utm_source=Github&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Github_Jan2022_FlutterChat&utm_term=moor" target="_blank"><img width="250px" src="https://stream-blog.s3.amazonaws.com/blog/wp-content/uploads/fc148f0fc75d02841d017bb36e14e388/Stream-logo-with-background-.png"/></a><br/><span><a href="https://getstream.io/chat/flutter/tutorial/?utm_source=Github&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Github_Jan2022_FlutterChat&utm_term=moor" target="_blank">Try the Flutter Chat Tutorial &nbsp💬</a></span>
</td>
</tr>
</tbody>
</table>
Expand Down
7 changes: 4 additions & 3 deletions drift/lib/src/ffi/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ class _VmDelegate extends DatabaseDelegate {
@override
Future<void> runBatched(BatchedStatements statements) async {
final prepared = [
for (final stmt in statements.statements) _db.prepare(stmt),
for (final stmt in statements.statements)
_db.prepare(stmt, checkNoTail: true),
];

for (final application in statements.arguments) {
Expand All @@ -202,7 +203,7 @@ class _VmDelegate extends DatabaseDelegate {
if (args.isEmpty) {
_db.execute(statement);
} else {
final stmt = _db.prepare(statement);
final stmt = _db.prepare(statement, checkNoTail: true);
stmt.execute(args);
stmt.dispose();
}
Expand All @@ -227,7 +228,7 @@ class _VmDelegate extends DatabaseDelegate {

@override
Future<QueryResult> runSelect(String statement, List<Object?> args) async {
final stmt = _db.prepare(statement);
final stmt = _db.prepare(statement, checkNoTail: true);
final result = stmt.select(args);
stmt.dispose();

Expand Down
2 changes: 1 addition & 1 deletion drift/lib/src/remote/server_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class ServerImplementation implements DriftServer {
@override
Future<void> shutdown() {
if (!_isShuttingDown) {
_done.complete();
_done.complete(connection.executor.close());
_isShuttingDown = true;
}

Expand Down
137 changes: 137 additions & 0 deletions drift/lib/src/runtime/api/connection_user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ abstract class DatabaseConnectionUser {
return executor.ensureOpen(attachedDatabase).then((_) => fn(executor));
}

/// Captures a [table] or view for easier subsequent operations.
///
/// The [TableOrViewOperations] class (or the [TableOperations] extension
/// for tables) provides convenience methods that make common operations
/// easier to write than using the methods from this class directly.
@experimental
TableOrViewOperations<T, D> from<T extends HasResultSet, D>(
ResultSetImplementation<T, D> table) {
return TableOrViewOperations._(this, table);
}

/// Starts an [InsertStatement] for a given table. You can use that statement
/// to write data into the [table] by using [InsertStatement.insert].
InsertStatement<T, D> into<T extends Table, D>(TableInfo<T, D> table) {
Expand Down Expand Up @@ -375,6 +386,10 @@ abstract class DatabaseConnectionUser {
}

/// Executes the custom sql [statement] on the database.
///
/// [statement] should contain exactly one SQL statement. Attempting to run
/// multiple statements with a single [customStatement] may not be fully
/// supported on all platforms.
Future<void> customStatement(String statement, [List<dynamic>? args]) {
final engine = resolvedEngine;

Expand Down Expand Up @@ -539,3 +554,125 @@ abstract class DatabaseConnectionUser {
return buffer.toString();
}
}

/// A capture of a table and a generated database.
///
/// Table operations can be captured with [DatabaseConnectionUser.from], which
/// may make some common operations easier to write:
///
/// - Use `from(table).select()` or `from(table).selectOnly()` instead of
/// `select(table)` or `selectOnly(table)`, respectively.
/// - Use `from(table).insert()` instead of `insert(table)`. You can also use
/// `from(table).insertOne`, or [TableOperations.insertOnConflictUpdate] to
/// insert rows directly.
/// - Use `from(table).update()` instead of `update(table)`. You can also use
/// `from(table).replace()` to replace an existing row.
/// - Use `from(table).delete()`, `from(table).deleteOne()` or
/// `from(table).deleteWhere` to delete rows easily.
@sealed
class TableOrViewOperations<Tbl extends HasResultSet, Row> {
final DatabaseConnectionUser _user;
final ResultSetImplementation<Tbl, Row> _sourceSet;

TableOrViewOperations._(this._user, this._sourceSet);

/// Composes a `SELECT` statement on the captured table or view.
///
/// This is equivalent to calling [DatabaseConnectionUser.select].
SimpleSelectStatement<Tbl, Row> select({bool distinct = false}) {
return _user.select(_sourceSet, distinct: distinct);
}

/// Composes a `SELECT` statement only selecting a subset of columns.
///
/// This is equivalent to calling [DatabaseConnectionUser.selectOnly].
JoinedSelectStatement<Tbl, Row> selectOnly(
{bool distinct = false, bool includeJoinedTableColumns = true}) {
return _user.selectOnly(_sourceSet,
distinct: distinct,
includeJoinedTableColumns: includeJoinedTableColumns);
}
}

/// Additional methods for a [TableOrViewOperations] that are only available on
/// tables.
extension TableOperations<Tbl extends Table, Row>
on TableOrViewOperations<Tbl, Row> {
TableInfo<Tbl, Row> get _table => _sourceSet as TableInfo<Tbl, Row>;

/// Creates an insert statment to be used to compose an insert on the captured
/// table.
///
/// This is equivalent to calling [DatabaseConnectionUser.into] on the
/// captured table. See that method for more information.
InsertStatement<Tbl, Row> insert() => _user.into(_table);

/// Inserts one row into the captured table.
///
/// This is equivalent to calling [InsertStatement.insert] - see that method
/// for more information.
Future<int> insertOne(
Insertable<Row> row, {
InsertMode? mode,
UpsertClause<Tbl, Row>? onConflict,
}) {
return insert().insert(row, mode: mode, onConflict: onConflict);
}

/// Inserts one row into the captured table, replacing an existing row if it
/// exists already.
///
/// Please note that this method is only available on recent sqlite3 versions.
/// See also [InsertStatement.insertOnConflictUpdate].
Future<int> insertOnConflictUpdate(Insertable<Row> row) {
return insert().insertOnConflictUpdate(row);
}

/// Inserts one row into the captured table and returns it, along with auto-
/// generated fields.
///
/// Please note that this method is only available on recent sqlite3 versions.
/// See also [InsertStatement.insertReturning].
Future<Row> insertReturning(
Insertable<Row> row, {
InsertMode? mode,
UpsertClause<Tbl, Row>? onConflict,
}) {
return insert().insertReturning(
row,
mode: mode,
onConflict: onConflict,
);
}

/// Creates a statement to compose an `UPDATE` into the database.
///
/// This is equivalent to calling [DatabaseConnectionUser.update] with the
/// captured table.
UpdateStatement<Tbl, Row> update() => _user.update(_table);

/// Replaces a single row with an update statement.
///
/// See also [UpdateStatement.replace].
Future<void> replace(Insertable<Row> row) {
return update().replace(row);
}

/// Creates a statement to compose a `DELETE` from the database.
///
/// This is equivalent to calling [DatabaseConnectionUser.delete] with the
/// captured table.
DeleteStatement<Tbl, Row> delete() => _user.delete(_table);

/// Deletes the [row] from the captured table.
Future<bool> deleteOne(Insertable<Row> row) async {
return await (delete()..whereSamePrimaryKey(row)).go() != 0;
}

/// Deletes all rows matching the [filter] from the table.
///
/// See also [SingleTableQueryMixin.where].
Future<int> deleteWhere(Expression<bool?> Function(Tbl tbl) filter) {
return (delete()..where(filter)).go();
}
}
Loading

0 comments on commit 41472d6

Please sign in to comment.