mirror of https://github.com/AMT-Cheif/drift.git
Merge branch 'develop' into beta
This commit is contained in:
commit
20cac9fefd
|
@ -13,4 +13,5 @@ task:
|
|||
analyze_script: tool/analyze.sh
|
||||
test_moor_script: tool/test_moor.sh
|
||||
test_sqlparser_script: tool/test_sqlparser.sh
|
||||
# format_coverage_script: tool/upload_coverage.sh
|
||||
test_moor_generator_script: tool/test_generator.sh
|
||||
format_coverage_script: tool/upload_coverage.sh
|
|
@ -65,6 +65,28 @@ used to construct common queries.
|
|||
|
||||
## Workflows
|
||||
|
||||
### Debugging the analyzer plugin
|
||||
The analyzer plugin (branded as "SQL IDE" to users) can be run in isolation, which makes
|
||||
debugging easier. Note: Port 9999 has to be free for this to work, but you can change the
|
||||
port defined in the two files below.
|
||||
|
||||
To debug the plugin, do the following:
|
||||
1. In `moor/tools/analyzer_plugin/bin/plugin.dart`, set `useDebuggingVariant` to true.
|
||||
2. Run `moor_generator/lib/plugin.dart` as a regular Dart VM app (this can be debugged).
|
||||
3. (optional) Make sure the analysis server picks up the updated version of the analysis
|
||||
plugin by deleting the `~/.dartServer/.plugin_manager` folder.
|
||||
4. Open a project that uses the plugin, for instance via `code extras/plugin_example`.
|
||||
|
||||
More details are available under `extras/plugin_example/README.md`.
|
||||
|
||||
### Debugging the builder
|
||||
|
||||
To debug the builder, run `pub run build_runner generate-build-script` in the `moor`
|
||||
subdirectory (or any other directory you want to use as an input). This will generate
|
||||
a `.dart_tool/build/entrypoint/build.dart`. That file can be run and debugged as a
|
||||
regular Dart VM app. Be sure to pass something like `build -v` as program arguments
|
||||
and use the input package as a working directory.
|
||||
|
||||
### Releasing to pub
|
||||
Minor changes will be published directly, no special steps are necessary. For major
|
||||
updates that span multiple versions, we should follow these steps
|
||||
|
@ -77,7 +99,8 @@ updates that span multiple versions, we should follow these steps
|
|||
on moor `1.x` as well to ensure users will always `pub get` moor packages that are compatible
|
||||
with each other.
|
||||
3. Comment out the `dependency_overrides` section in `moor`, `moor/tool/analyzer_plugin`, `moor_flutter`,
|
||||
`moor_generator` and `sqlparser`.
|
||||
`moor_generator` and `sqlparser`. Make sure that `useDebuggingVariant` is false in the
|
||||
analyzer plugin.
|
||||
4. Publish packages in this order to avoid scoring penalties caused by versions not existing:
|
||||
1. `moor`
|
||||
2. `moor_generator`
|
||||
|
|
|
@ -4,7 +4,7 @@ We use [Docsy](https://github.com/google/docsy), a Hugo theme for this website.
|
|||
[here](https://www.docsy.dev/docs/getting-started/).
|
||||
|
||||
To work on the documentation, first cd into this directory, then run `git submodule update --init --recursive` and
|
||||
`npm install`.
|
||||
`npm install`. To update the dependencies, run `git pull --recurse-submodules`-
|
||||
|
||||
## Running the website locally
|
||||
After the setup, it's just a simple
|
||||
|
|
|
@ -3,4 +3,6 @@ $enable-gradients: false;
|
|||
$enable-rounded: false;
|
||||
$enable-shadows: false;
|
||||
|
||||
$secondary: #4CAF50;
|
||||
$secondary: #4CAF50;
|
||||
|
||||
$code-color: #dc3545;
|
|
@ -32,17 +32,19 @@ that allows you run fluent queries on your data.
|
|||
[Get started now]({{< ref "/docs/Getting started/_index.md" >}})
|
||||
{{% /blocks/feature %}}
|
||||
|
||||
{{% blocks/feature icon="fas fa-database" title="Prefer SQL? Moor got you covered!" url="https://moor.simonbinder.eu/queries/custom" %}}
|
||||
Moor contains a powerful sql parser and analyzer, allowing it to create typesafe APIs for all your sql queries. All queries are
|
||||
{{% blocks/feature icon="fas fa-database" title="Prefer SQL? Moor got you covered!" %}}
|
||||
Moor contains a powerful sql parser and analyzer, allowing it to create typesafe APIs for all your sql queries. All sql queries are
|
||||
validated and analyzed during build-time, so moor can provide hints about potential errors quickly and generate efficient mapping
|
||||
code.
|
||||
|
||||
[Learn more]({{< ref "/docs/Using SQL/_index.md" >}})
|
||||
{{% /blocks/feature %}}
|
||||
|
||||
|
||||
{{% blocks/feature icon="fas fa-star" title="And much more!" %}}
|
||||
Moor can also provide auto-updating streams emitting new results when the underlying data changes.
|
||||
Moor makes dealing with transactions easy (no special parameter to pass around everywhere), lets
|
||||
your write modular database code with DAOs and much more.
|
||||
Moor provides auto-updating `Streams` for all your queries, makes dealing with transactions and migrations easy
|
||||
and lets your write modular database code with DAOs. We even have a [sql IDE](too) builtin to the project
|
||||
When using moor, working with databases in Dart is fun!
|
||||
|
||||
{{% /blocks/feature %}}
|
||||
{{< /blocks/section >}}
|
||||
|
|
|
@ -28,4 +28,10 @@ At the moment, moor supports these options:
|
|||
constructor that takes a `Map<String, dynamic>`.
|
||||
* `override_hash_and_equals_in_result_sets`: boolean. When moor generates another class
|
||||
to hold the result of generated select queries, this flag controls whether moor should
|
||||
override `operator ==` and `hashCode` in those classes.
|
||||
override `operator ==` and `hashCode` in those classes.
|
||||
* `compact_query_methods`: For queries declared on a `@UseMoor` or `@UseDao` annotation, moor
|
||||
will generate three methods: A base method returning a `Selectable` and then two helper
|
||||
methods returning a `Stream` or a `Future`. As the `Selectable` class contains its own methods
|
||||
to convert it to a `Stream` and `Future`, the two later methods only exist for backwards
|
||||
compatibility. When this flag is enabled, moor won't write them at all. This will be the only
|
||||
option in moor 3.0
|
|
@ -13,7 +13,7 @@ how to get started. You can watch it [here](https://youtu.be/zpWsedYMczM).
|
|||
|
||||
|
||||
## Adding the dependency
|
||||
First, let's add moor to your project's `pubspec.yaml`.
|
||||
First, lets add moor to your project's `pubspec.yaml`.
|
||||
At the moment, the current version of `moor_flutter` is [![Flutter version](https://img.shields.io/pub/v/moor_flutter.svg)](https://pub.dartlang.org/packages/moor_flutter) and the current version of `moor_generator` is [![Generator version](https://img.shields.io/pub/v/moor_generator.svg)](https://pub.dartlang.org/packages/moor_generator)
|
||||
|
||||
```yaml
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
---
|
||||
title: "Getting started with sql"
|
||||
weight: 5
|
||||
description: Learn how to get started with the SQL version of moor, or how to migrate an existing project to moor.
|
||||
---
|
||||
|
||||
The regular [getting started guide]({{< relref "_index.md" >}}) explains how to get started with moor by
|
||||
declaring both tables and queries in Dart. This version will focus on how to use moor with SQL instead.
|
||||
|
||||
## Adding the dependency
|
||||
First, lets add moor to your project's `pubspec.yaml`.
|
||||
At the moment, the current version of `moor_flutter` is [![Flutter version](https://img.shields.io/pub/v/moor_flutter.svg)](https://pub.dartlang.org/packages/moor_flutter) and the current version of `moor_generator` is [![Generator version](https://img.shields.io/pub/v/moor_generator.svg)](https://pub.dartlang.org/packages/moor_generator)
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
moor_flutter: # use the latest version
|
||||
|
||||
dev_dependencies:
|
||||
moor_generator: # use the latest version
|
||||
build_runner:
|
||||
```
|
||||
|
||||
The `moor_flutter` package will execute sql at runtime, while the
|
||||
`moor_generator` will generate typesafe Dart based on your SQL queries.
|
||||
|
||||
## Declaring tables and queries
|
||||
|
||||
To declare tables and queries in sql, create a file called `tables.moor`
|
||||
next to your Dart files (for instance in `lib/database/tables.moor`).
|
||||
|
||||
You can put the `CREATE TABLE` statements for your queries in there.
|
||||
The following example creates two tables to model a todo-app. If you're
|
||||
migrating an existing project to moor, you would put the `CREATE TABLE`
|
||||
for your tables in there.
|
||||
```sql
|
||||
-- this is the tables.moor file
|
||||
CREATE TABLE todos (
|
||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT,
|
||||
body TEXT,
|
||||
category INT REFERENCES categories (id)
|
||||
);
|
||||
|
||||
CREATE TABLE categories (
|
||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
description TEXT,
|
||||
) AS Category; -- see the explanation on "AS Category" below
|
||||
|
||||
/* after declaring your tables, you can put queries in here. Just
|
||||
write the name of the query, a colon (:) and the SQL: */
|
||||
todosInCategory: SELECT * FROM todos WHERE category = ?;
|
||||
|
||||
/* Here's a more complex query: It counts the amount of entries per
|
||||
category, including those entries which aren't in any category at all. */
|
||||
countEntries:
|
||||
SELECT
|
||||
c.desc,
|
||||
(SELECT COUNT(*) FROM todos WHERE category = c.id) AS amount
|
||||
FROM categories c
|
||||
UNION ALL
|
||||
SELECT null, (SELECT COUNT(*) FROM todos WHERE category IS NULL)
|
||||
```
|
||||
|
||||
{{% alert title="On that AS Category" %}}
|
||||
Moor will generate Dart classes for your tables, and the name of those
|
||||
classes is based on the table name. By default, moor just strips away
|
||||
the trailing `s` from your table. That works for most cases, but in some
|
||||
(like the `categories` table above), it doesn't. We'd like to have a
|
||||
`Category` class (and not `Categorie`) generated, so we tell moor to
|
||||
generate a different name with the `AS <name>` declaration at the end.
|
||||
{{% /alert %}}
|
||||
|
||||
## Generating matching code
|
||||
|
||||
After you declared the tables, lets generate some Dart code to actually
|
||||
run them. Moor needs to know which tables are used in a database, so we
|
||||
have to write a small Dart class that moor will then read. Lets create
|
||||
a file called `database.dart` next to the `tables.moor` file you wrote
|
||||
in the previous step.
|
||||
|
||||
```dart
|
||||
import 'package:moor_flutter/moor_flutter.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
@UseMoor(
|
||||
include: {'tables.moor'},
|
||||
)
|
||||
class AppDb extends _$AppDb {
|
||||
AppDb() : super(FlutterQueryExecutor.inDatabaseFolder('app.db'));
|
||||
|
||||
@override
|
||||
int get schemaVersion => 1;
|
||||
}
|
||||
```
|
||||
|
||||
To generate the `database.g.dart` file which contains the `_$AppDb`
|
||||
superclass, run `flutter pub run build_runner build` on the command
|
||||
line.
|
||||
|
||||
## What moor generates
|
||||
|
||||
Let's take a look at what moor generated during the build:
|
||||
|
||||
- Generated data classes (`Todo` and `Category`) - these hold a single
|
||||
row from the respective table.
|
||||
- Companion versions of these classes. Those are only relevant when
|
||||
using the Dart apis of moor, you can [learn more here]({{< relref "writing_queries.md#inserts" >}}).
|
||||
- A `CountEntriesResult` class, it holds the result rows when running the
|
||||
`countEntries` query.
|
||||
- A `_$AppDb` superclass. It takes care of creating the tables when
|
||||
the database file is first opened. It also contains typesafe methods
|
||||
for the queries declared in the `tables.moor` file:
|
||||
- a `Selectable<Todo> todosInCategory(int)` method, which runs the
|
||||
`todosInCategory` query declared above. Moor 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.
|
||||
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()`).
|
||||
- a `Selectable<CountEntriesResult> countEntries()` method, which runs
|
||||
the other query when used.
|
||||
|
||||
By the way, you can also put insert, update and delete statements in
|
||||
a `.moor` file - moor will generate matching code for them as well.
|
||||
|
||||
## Learning more
|
||||
|
||||
Know that you know how to use moor together with sql, here are some
|
||||
further guides to help you learn more:
|
||||
|
||||
- The [SQL IDE]({{< relref "../Using SQL/sql_ide.md" >}}) that provides feedback on sql queries right in your editor.
|
||||
- [Transactions]({{< relref "../transactions.md" >}})
|
||||
- [Schema migrations]({{< relref "../Advanced Features/migrations.md" >}})
|
||||
- Writing [queries]({{< relref "writing_queries.md" >}}) and
|
||||
[expressions]({{< relref "expressions.md" >}}) in Dart
|
||||
- A more [in-depth guide]({{< relref "../Using SQL/moor_files.md" >}})
|
||||
on `moor` files, which explains `import` statements and the Dart-SQL interop.
|
|
@ -1,12 +1,78 @@
|
|||
---
|
||||
title: Dart VM
|
||||
description: An upcoming version will have a version for the Dart VM
|
||||
title: Dart VM (using ffi)
|
||||
description: Experimental version of moor using `dart:ffi`
|
||||
---
|
||||
|
||||
An upcoming version of moor will have first class support for the Dart VM,
|
||||
so you can use moor on Desktop Flutter applications or Dart apps.
|
||||
## Warnings and supported platforms
|
||||
|
||||
We're going to use the `dart:ffi` feature for that, which itself is an
|
||||
experimental state at the moment. We already have a version of moor that
|
||||
runs on the Dart VM (see [#76](https://github.com/simolus3/moor/issues/76))
|
||||
and we're going to release it when `dart:ffi` becomes stable.
|
||||
Please note that `dart:ffi` is in "preview" at the moment and that there will be breaking
|
||||
changes. Using the `moor_ffi` package on non-stable Dart or Flutter versions can break.
|
||||
Also, please don't use the package for production apps yet.
|
||||
|
||||
At the moment, `moor_ffi` supports iOS, macOS and Android out of the box. Most Linux
|
||||
Distros have sqlite available as a shared library, those are supported as well.
|
||||
|
||||
If you're shipping apps for Windows and Linux, it is recommended that you bundle a
|
||||
`sqlite3.so` and `sqlite3.dll` file with your app. You can then make `moor_ffi`
|
||||
support your setup by running this code before opening the database:
|
||||
|
||||
```dart
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:moor_ffi/open_helper.dart';
|
||||
|
||||
void main() {
|
||||
open.overrideFor(OperatingSystem.linux, _openOnLinux);
|
||||
|
||||
final db = Database.memory();
|
||||
db.close();
|
||||
}
|
||||
|
||||
DynamicLibrary _openOnLinux() {
|
||||
final script = File(Platform.script.toFilePath());
|
||||
final libraryNextToScript = File('${script.path}/sqlite3.so');
|
||||
return DynamicLibrary.open(libraryNextToScript.path);
|
||||
}
|
||||
// _openOnWindows could be implemented similarly by opening `sqlite3.dll`
|
||||
|
||||
```
|
||||
|
||||
## Migrating from moor_flutter to moor_ffi
|
||||
|
||||
__Again__: If you're only looking for Android and iOS support, `moor_flutter` is the
|
||||
right library to use.
|
||||
|
||||
1. Adapt your `pubspec.yaml`: You can remove the `moor_flutter` dependency and instead
|
||||
add both the `moor` and `moor_ffi` dependencies:
|
||||
```yaml
|
||||
dependencies:
|
||||
moor: ^2.0.0
|
||||
moor_ffi: ^0.0.1
|
||||
dev_dependencies:
|
||||
moor_generator: ^2.0.0
|
||||
```
|
||||
Note: If you were using `FlutterQueryExecutor.inDatabasesFolder`, you should also depend
|
||||
on `path_provider`. For desktop support of that library, see [this readme](https://github.com/google/flutter-desktop-embedding/tree/master/plugins/flutter_plugins).
|
||||
2. Adapt your imports:
|
||||
- In the file where you created a `FlutterQueryExecutor`, replace the `moor_flutter` import
|
||||
with `package:moor_ffi/moor_ffi.dart`.
|
||||
- In all other files where you might have import `moor_flutter`, just import `package:moor/moor.dart`.
|
||||
3. Replace the executor. This code:
|
||||
```dart
|
||||
FlutterQueryExecutor.inDatabaseFolder(path: 'db.sqlite')
|
||||
```
|
||||
can now be written as
|
||||
```dart
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
LazyDatabase(() async {
|
||||
final dbFolder = await getApplicationDocumentsDirectory();
|
||||
final file = File(j.join(dbFolder.path, 'db.sqlite'));
|
||||
return VmDatabase(file);
|
||||
})
|
||||
```
|
||||
__Important warning__: `FlutterQueryExecutor.inDatabaseFolder` may use a different folder on Android, which
|
||||
can cause data loss. This documentation will provide a better migration guide once `moor_ffi` is stable.
|
||||
Please create an issue if you need guidance on this soon.
|
|
@ -10,9 +10,10 @@ aliases:
|
|||
Moor files are a new feature that lets you write all your database code in SQL - moor will generate typesafe APIs for them.
|
||||
|
||||
## Getting started
|
||||
To use this feature, lets create two files: `database.dart` and `tables.moor`. The Dart file is pretty straightforward:
|
||||
To use this feature, lets create two files: `database.dart` and `tables.moor`. The Dart file only contains the minimum code
|
||||
to setup the database:
|
||||
```dart
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:moor_flutter/moor_flutter.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
|
@ -47,13 +48,14 @@ deleteById: DELETE FROM todos WHERE id = :id;
|
|||
watchAllTodos: SELECT * FROM todos;
|
||||
```
|
||||
|
||||
After running the build runner, moor will write the `database.g.dart`
|
||||
After running the build runner with `flutter pub run build_runner build`,
|
||||
moor will write the `database.g.dart`
|
||||
file which contains the `_$MoorDb` superclass. Let's take a look at
|
||||
what we got:
|
||||
|
||||
- Generated data classes (`Todo` and `Category`), and companion versions
|
||||
for inserts (see [Dart Interop](#dart-interop) for info). By default,
|
||||
we strip a trailing "s" from the table name for the class. That's why
|
||||
moor strips a trailing "s" from the table name for the class. That's why
|
||||
we used `AS Category` on the second table - it would have been called
|
||||
`Categorie` otherwise.
|
||||
- Methods to run the queries:
|
||||
|
@ -111,11 +113,13 @@ All tables reachable from the other file will then also be visible in
|
|||
the current file and to the database that `includes` it. Importing
|
||||
Dart files into a moor file will also work - then, all the tables
|
||||
declared via Dart tables can be used inside queries.
|
||||
We support both relative imports and the `package:` imports you
|
||||
know from Dart.
|
||||
|
||||
## Dart interop
|
||||
Moor files work perfectly together with moor's existing Dart API:
|
||||
|
||||
- you can write Dart queries for moor files:
|
||||
- you can write Dart queries for tables declared in a moor file:
|
||||
```dart
|
||||
Future<void> insert(TodosCompanion companion) async {
|
||||
await into(todos).insert(companion);
|
||||
|
@ -126,8 +130,10 @@ Future<void> insert(TodosCompanion companion) async {
|
|||
- generated methods for queries can be used in transactions, they work
|
||||
together with auto-updating queries, etc.
|
||||
|
||||
You can make most of both SQL and Dart by "Dart Templates", which is a
|
||||
Dart expression that gets inlined to a query. To use them, declare a
|
||||
### Dart components in SQL
|
||||
|
||||
You can make most of both SQL and Dart with "Dart Templates", which is a
|
||||
Dart expression that gets inlined to a query at runtime. To use them, declare a
|
||||
$-variable in a query:
|
||||
```sql
|
||||
_filterTodos: SELECT * FROM todos WHERE $predicate;
|
||||
|
@ -139,10 +145,25 @@ Stream<List<Todo>> watchInCategory(int category) {
|
|||
return _filterTodos(todos.category.equals(category)).watch();
|
||||
}
|
||||
```
|
||||
This feature works for
|
||||
This lets you write a single SQL query and dynamically apply a predicate at runtime!
|
||||
This feature also works for
|
||||
|
||||
- expressions
|
||||
- single ordering terms: `SELECT * FROM todos ORDER BY $term, id ASC`
|
||||
will generate a method taking an `OrderingTerm`.
|
||||
- whole order-by clauses: `SELECT * FROM todos ORDER BY $order`
|
||||
- limit clauses: `SELECT * FROM todos LIMIT $limit`
|
||||
- limit clauses: `SELECT * FROM todos LIMIT $limit`
|
||||
|
||||
## Supported statements
|
||||
At the moment, the following statements can appear in a `.moor` file.
|
||||
|
||||
- `import 'other.moor'`: Import all tables and queries declared in the other file
|
||||
into the current file.
|
||||
- DDL statements (`CREATE TABLE`): Declares a table. We don't currently support indices and views,
|
||||
[#162](https://github.com/simolus3/moor/issues/162) tracks support for that.
|
||||
- Query statements: We support `INSERT`, `SELECT`, `UPDATE` and `DELETE` statements.
|
||||
|
||||
All imports must come before DDL statements, and those must come before the named queries.
|
||||
|
||||
If you need support for another statement, or if moor rejects a query you think is valid, please
|
||||
create an issue!
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
title: "Experimental IDE"
|
||||
weight: 5
|
||||
description: Get real-time feedback as you type sql
|
||||
---
|
||||
|
||||
Moor ships with an experimental analyzer plugin that provides real-time feedback on errors,
|
||||
hints, folding and outline.
|
||||
|
||||
## Features
|
||||
|
||||
At the moment, the IDE supports
|
||||
|
||||
- auto-complete to suggest matching keywords as you type
|
||||
- warnings and errors for your queries
|
||||
- navigation (Ctrl click on a reference to see where a column or table is declared)
|
||||
- an outline view highlighting tables and queries
|
||||
- folding inside `CREATE TABLE` statements and import-blocks
|
||||
|
||||
We would very much like to support syntax highlighting, but sadly VS Code doesn't support
|
||||
that. Please upvote [this issue](https://github.com/microsoft/vscode/issues/585) to help
|
||||
us here.
|
||||
|
||||
## Setup
|
||||
To use the plugin, you need a supported editor (see below).
|
||||
|
||||
First, tell the Dart analysis server to run the moor plugin. Create a file called
|
||||
`analysis_options.yaml` in your project root, next to your pubspec. It should contain
|
||||
this section:
|
||||
```yaml
|
||||
analyzer:
|
||||
plugins:
|
||||
- moor
|
||||
```
|
||||
|
||||
Then, follow the steps for the IDE you want to use.
|
||||
|
||||
### Using with VS Code
|
||||
|
||||
Make sure that your project depends on moor 2.0 or later. Then
|
||||
|
||||
1. In the preferences, make sure that the `dart.analyzeAngularTemplates` option is
|
||||
set to true. Contrary to its name, that flag turns on the plugin system, so you
|
||||
don't need to worry about angular.
|
||||
2. Tell Dart Code to analyze moor files as well. Add this to your `settings.json`:
|
||||
```json
|
||||
"dart.additionalAnalyzerFileExtensions": [
|
||||
"moor"
|
||||
]
|
||||
```
|
||||
3. Finally, close and reopen your IDE so that the analysis server is restarted. The analysis server will
|
||||
then load the moor plugin and start providing analysis results for `.moor` files. Loading the plugin
|
||||
can take some time (around a minute for the first time).
|
||||
|
||||
### Other IDEs
|
||||
|
||||
Unfortunately, we can't support IntelliJ and Android Studio yet. Please vote on
|
||||
[this issue](https://youtrack.jetbrains.com/issue/WEB-41424) to help us here!
|
||||
|
||||
If you're looking for support for an other IDE that uses the Dart analysis server,
|
||||
please create an issue. We can probably make that happen.
|
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
|
@ -0,0 +1,114 @@
|
|||
---
|
||||
title: Version 2.0
|
||||
layout: home
|
||||
---
|
||||
|
||||
{{< blocks/cover title="Moor 2.0: Supercharged SQL for Dart" image_anchor="top" height="min" color="indigo" >}}
|
||||
<div class="mx-auto">
|
||||
<p class="lead mt-5">
|
||||
Learn everything about Dart-SQL interop, the SQL IDE, experimental ffi support and all things new in moor
|
||||
</p>
|
||||
|
||||
<a class="btn btn-lg btn-primary mr-3 mb-4" href="{{< relref "/docs" >}}">
|
||||
Get started <i class="fas fa-arrow-alt-circle-right ml-2"></i>
|
||||
</a>
|
||||
<a class="btn btn-lg btn-secondary mr-3 mb-4" href="{{< relref "/docs/Getting started/starting_with_sql.md" >}}">
|
||||
Migrate an existing project <i class="fas fa-code ml-2 "></i>
|
||||
</a>
|
||||
</div>
|
||||
{{< /blocks/cover >}}
|
||||
|
||||
{{% blocks/lead color="blue" %}}
|
||||
## Generator overhaul
|
||||
|
||||
The rewritten compiler is faster than ever, supports more SQL features and gives you
|
||||
more flexibility when writing database code.
|
||||
|
||||
[Check the updated documentation]({{< ref "../docs/Using SQL/moor_files.md" >}})
|
||||
{{% /blocks/lead %}}
|
||||
|
||||
{{< blocks/section color="light" >}}
|
||||
{{% blocks/feature icon="fas fa-database" title="Pure SQL API" %}}
|
||||
The new `.moor` files have been updated and can now hold both `CREATE TABLE` statements
|
||||
and queries you define. Moor will then generate typesafe Dart APIs based on your tables
|
||||
and statements.
|
||||
|
||||
[Get started with SQL and moor]({{< ref "../docs/Getting started/starting_with_sql.md" >}})
|
||||
{{% /blocks/feature %}}
|
||||
{{% blocks/feature icon="fas fa-plus" title="Analyzer improvements" %}}
|
||||
We now support more advanced features like compound select statements and window functions,
|
||||
including detailed static analysis and lints. The updated type inference engine provides
|
||||
better results on complex expressions. We also generate simpler methods for queries that only
|
||||
return one column.
|
||||
{{% /blocks/feature %}}
|
||||
{{% blocks/feature icon="fas fa-code-branch" title="Dart-SQL interop" %}}
|
||||
Declare tables in Dart, write your queries in SQL. Or do it the other way around. Or do it all in Dart.
|
||||
Or all in SQL. Moor makes writing database code fun without taking control over your code.
|
||||
For maximum flexibilty, moor lets you inline Dart expressions into SQL and use the best of both
|
||||
worlds.
|
||||
{{% /blocks/feature %}}
|
||||
|
||||
{{< /blocks/section >}}
|
||||
|
||||
{{% blocks/lead color="green" %}}
|
||||
## Builtin SQL IDE
|
||||
|
||||
Moor 2.0 expands the previous sql parser and analyzer, providing real-time feedback on your
|
||||
SQL queries as you type. Moor plugs right into the Dart analysis server, so you don't have
|
||||
to install any additional extensions.
|
||||
|
||||
[Learn more about the IDE]({{< ref "../docs/Using SQL/sql_ide.md" >}})
|
||||
{{% /blocks/lead %}}
|
||||
|
||||
{{< blocks/section color="dark" >}}
|
||||
{{% blocks/feature icon="fa-lightbulb" title="Quickfixes" %}}
|
||||
![](quickfix.png)
|
||||
|
||||
Moor lets you write query code faster with helpful actions.
|
||||
{{% /blocks/feature %}}
|
||||
{{% blocks/feature icon="fas fa-exclamation-circle" title="Smart warnings" %}}
|
||||
![](warning.png)
|
||||
|
||||
Moor analyzes statements as you write them and reports errors right away.
|
||||
This helps you identify problems fast, without having to open your app.
|
||||
{{% /blocks/feature %}}
|
||||
{{% blocks/feature icon="fas fa-info-circle" title="Structure view" %}}
|
||||
![](outline.png)
|
||||
|
||||
Moor provides an outline of your tables and queries for a better overview.
|
||||
{{% /blocks/feature %}}
|
||||
|
||||
{{< /blocks/section >}}
|
||||
|
||||
{{% blocks/lead color="purple" %}}
|
||||
## And much, much more
|
||||
|
||||
Moor 2.0 contains a set of optimizations and makes common tasks simpler
|
||||
{{% /blocks/lead %}}
|
||||
|
||||
{{< blocks/section color="light" >}}
|
||||
{{% blocks/feature icon="fa-lightbulb" title="New database helpers" %}}
|
||||
New utils to load database from assets or to perform additional work before creating a database.
|
||||
{{% /blocks/feature %}}
|
||||
{{% blocks/feature icon="fas fa-exclamation" title="Removed deprecated features" %}}
|
||||
We removed a whole bunch of deprecated apis that made it harder to develop new features.
|
||||
|
||||
[Read the changelog for details](https://pub.dev/packages/moor#-changelog-tab-)
|
||||
{{% /blocks/feature %}}
|
||||
{{% blocks/feature icon="fas fa-bolt" title="Experimental `dart:ffi` bindings" %}}
|
||||
The new [moor_ffi](https://pub.dev/packages/moor_ffi) package brings moor to the desktop and is up to 500x faster than the old
|
||||
implementation.
|
||||
|
||||
_Please not that the package is still in preview_
|
||||
{{% /blocks/feature %}}
|
||||
|
||||
{{< /blocks/section >}}
|
||||
|
||||
{{< blocks/section color="dark" type="section" >}}
|
||||
## Try moor now
|
||||
|
||||
- To get started with moor, follow our [getting started guide]({{< ref "../docs/Getting started/_index.md" >}}) here.
|
||||
- To get started with SQL in moor, or to migrate an existing project to moor, follow our
|
||||
[migration guide]({{< ref "../docs/Getting started/starting_with_sql.md" >}})
|
||||
|
||||
{{< /blocks/section >}}
|
Binary file not shown.
After Width: | Height: | Size: 9.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -118,45 +118,24 @@
|
|||
"dev": true
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "9.4.6",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.6.tgz",
|
||||
"integrity": "sha512-Yp51mevbOEdxDUy5WjiKtpQaecqYq9OqZSL04rSoCiry7Tc5I9FEyo3bfxiTJc1DfHeKwSFCUYbBAiOQ2VGfiw==",
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.1.tgz",
|
||||
"integrity": "sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.4.1",
|
||||
"caniuse-lite": "^1.0.30000929",
|
||||
"browserslist": "^4.6.3",
|
||||
"caniuse-lite": "^1.0.30000980",
|
||||
"chalk": "^2.4.2",
|
||||
"normalize-range": "^0.1.2",
|
||||
"num2fraction": "^1.2.2",
|
||||
"postcss": "^7.0.13",
|
||||
"postcss-value-parser": "^3.3.1"
|
||||
"postcss": "^7.0.17",
|
||||
"postcss-value-parser": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"version": "7.0.14",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz",
|
||||
"integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==",
|
||||
"version": "7.0.18",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.18.tgz",
|
||||
"integrity": "sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
|
@ -288,14 +267,14 @@
|
|||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz",
|
||||
"integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==",
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz",
|
||||
"integrity": "sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30000929",
|
||||
"electron-to-chromium": "^1.3.103",
|
||||
"node-releases": "^1.1.3"
|
||||
"caniuse-lite": "^1.0.30000989",
|
||||
"electron-to-chromium": "^1.3.247",
|
||||
"node-releases": "^1.1.29"
|
||||
}
|
||||
},
|
||||
"cache-base": {
|
||||
|
@ -328,9 +307,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30000932",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000932.tgz",
|
||||
"integrity": "sha512-4bghJFItvzz8m0T3lLZbacmEY9X1Z2AtIzTr7s7byqZIOumASfr4ynDx7rtm0J85nDmx8vsgR6vnaSoeU8Oh0A==",
|
||||
"version": "1.0.30000997",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000997.tgz",
|
||||
"integrity": "sha512-BQLFPIdj2ntgBNWp9Q64LGUIEmvhKkzzHhUHR3CD5A9Lb7ZKF20/+sgadhFap69lk5XmK1fTUleDclaRFvgVUA==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
|
@ -559,9 +538,9 @@
|
|||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.108",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.108.tgz",
|
||||
"integrity": "sha512-/QI4hMpAh48a1Sea6PALGv+kuVne9A2EWGd8HrWHMdYhIzGtbhVVHh6heL5fAzGaDnZuPyrlWJRl8WPm4RyiQQ==",
|
||||
"version": "1.3.270",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.270.tgz",
|
||||
"integrity": "sha512-426qbfgLn0hVE4pDxok2dcAhA3u5lwXlBg2+i6VWQJvnMZNgevkC6s/qr91YH/avVMKXKwxnR5iBznpivg210A==",
|
||||
"dev": true
|
||||
},
|
||||
"error-ex": {
|
||||
|
@ -1872,9 +1851,9 @@
|
|||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.3.tgz",
|
||||
"integrity": "sha512-6VrvH7z6jqqNFY200kdB6HdzkgM96Oaj9v3dqGfgp6mF+cHmU4wyQKZ2/WPDRVoR0Jz9KqbamaBN0ZhdUaysUQ==",
|
||||
"version": "1.1.33",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.33.tgz",
|
||||
"integrity": "sha512-I0V30bWQEoHb+10W8oedVoUrdjW5wIkYm0w7vvcrPO95pZY738m1k77GF5sO0vKg5eXYg9oGtrMAETbgZGm11A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^5.3.0"
|
||||
|
@ -2171,9 +2150,9 @@
|
|||
}
|
||||
},
|
||||
"postcss-value-parser": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
|
||||
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz",
|
||||
"integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-hrtime": {
|
||||
|
@ -2311,9 +2290,9 @@
|
|||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
|
||||
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"set-blocking": {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"homepage": "https://github.com/bep/tech-doc-hugo#readme",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^9.4.6",
|
||||
"autoprefixer": "^9.6.1",
|
||||
"postcss-cli": "^5.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,37 +7,40 @@ Currently, we support
|
|||
- folding
|
||||
- (very, very limited) autocomplete
|
||||
- some quickfixes to make columns nullable or non-null
|
||||
- navigation for references in sql queries
|
||||
|
||||
## Setup
|
||||
To use this plugin, you'll need to perform these steps once. It is assumed that you
|
||||
To debug this plugin, you'll need to perform these steps once. It is assumed that you
|
||||
have already cloned the `moor` repository.
|
||||
|
||||
1. Clone https://github.com/simolus3/Dart-Code and checkout the
|
||||
`use-custom-file-endings` branch.
|
||||
2. Run `npm install` and `npm run build` to verify that everything is working.
|
||||
3. Open the forked Dart-Code repo in your regular VS Code installation.
|
||||
4. Press `F5` to run the plugin in another editor instance. All subsequent
|
||||
steps need to be completed in that editor.
|
||||
5. In the settings of that editor, change `dart.additionalAnalyzerFileExtensions`
|
||||
1. Make sure you run version `3.5.0` or later of the Dart extension in VS Code.
|
||||
2. In the editor settings, change `dart.additionalAnalyzerFileExtensions`
|
||||
to include `moor` files:
|
||||
```json
|
||||
{
|
||||
"dart.additionalAnalyzerFileExtensions": ["moor"]
|
||||
}
|
||||
```
|
||||
6. Close that editor.
|
||||
7. Uncomment the plugin lines in `analysis_options.yaml`
|
||||
3. Uncomment the plugin lines in `analysis_options.yaml`
|
||||
|
||||
## Running
|
||||
After you completed the setup, these steps will open an editor instance that runs the plugin.
|
||||
1. chdir into `moor_generator` and run `lib/plugin.dart`. You can run that file from an IDE if
|
||||
you need debugging capabilities, but starting it from the command line is fine. Keep that
|
||||
script running.
|
||||
2. Re-open the "inner" editor with the custom Dart plugin
|
||||
2. Open this folder in the editor that runs the custom Dart plugin. Wait ~15s, you should start
|
||||
to see some log entries in the output of step 1. As soon as they appear, the plugin is ready
|
||||
to go.
|
||||
2. Open this folder in the code instance
|
||||
3. Wait ~15s, you should start to see some log entries in the output of step 1.
|
||||
As soon as they appear, the plugin is ready to go.
|
||||
|
||||
_Note_: `lib/plugin.dart` doesn't support multiple clients. Whenever you close or reload the
|
||||
editor, that script needs to be restarted as well. That script should also be running before
|
||||
starting the analysis server.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the plugin doesn't start properly, you can
|
||||
|
||||
1. make sure it was picked up by the analysis server: You set the `dart.analyzerDiagnosticsPort`
|
||||
to any port and see some basic information under the "plugins" tab of the website started.
|
||||
2. When setting `dart.analyzerInstrumentationLogFile`, the analysis server will write the
|
||||
exception that caused the plugin to stop
|
|
@ -1,5 +1,8 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
|
||||
# The source-controlled version of this file should always have the plugin commented out. Our development version with
|
||||
# the websocket proxy causes analysis errors when to plugin doesn't run
|
||||
|
||||
#analyzer:
|
||||
# plugins:
|
||||
# - moor
|
||||
# - moor
|
|
@ -31,6 +31,10 @@ doc/api/
|
|||
android/
|
||||
ios/
|
||||
|
||||
# coverage
|
||||
test/.test_coverage.dart
|
||||
coverage_badge.svg
|
||||
|
||||
### Intellij ###
|
||||
.idea/**/*
|
||||
# End of https://www.gitignore.io/api/dart,intellij
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
## 2.0.0
|
||||
This is the first major update after the initial release and moor and we have a lot to cover:
|
||||
The `.moor` files can now have their own imports and queries, you can embed Dart in sql queries
|
||||
`.moor` files can now have their own imports and queries, you can embed Dart in sql queries
|
||||
using the new templates feature and we have a prototype of a pure-Dart SQL IDE ready.
|
||||
Finally, we also removed a variety of deprecated features. See the breaking changes
|
||||
section to learn what components are affected and what alternatives are available.
|
||||
|
@ -28,7 +28,7 @@ Stream<User> loadUserById(int id) {
|
|||
}
|
||||
```
|
||||
|
||||
Moor files can also import other moor files by using an `import 'other.moor';'` statement at the
|
||||
Moor files can also import other moor files by using an `import 'other.moor';` statement at the
|
||||
top. Then, all tables defined in `other.moor` will also be available to the current file.
|
||||
|
||||
Moor takes Dart and SQL interop even further with the new "Dart in SQL templates". You can define
|
||||
|
@ -49,12 +49,15 @@ And finally, we now generate better query code when queries only return a single
|
|||
generating a whole new class for that, we simply return the value directly.
|
||||
|
||||
#### Experimental ffi support
|
||||
TODO: Describe ffi port
|
||||
We released an experimental version of moor built on top of `dart:ffi`. It works
|
||||
cross-platform and is much, much faster than `moor_flutter`. It you want to try
|
||||
it out, read the docs [here](https://moor.simonbinder.eu/docs/other-engines/vm/).
|
||||
|
||||
### Minor changes
|
||||
- a `Constant<String>` can now be written to SQL, it used to throw before. This is useful
|
||||
if you need default values for strings columns.
|
||||
- new `LazyDatabase` when you want to construct a database asynchronously (for instance, if
|
||||
if you need default values for strings columns. This also works for `BLOBS`
|
||||
(`Constant<Uint8List>`).
|
||||
- new `LazyDatabase` for when you want to construct a database asynchronously (for instance, if
|
||||
you first need to find a file before you can open a database).
|
||||
|
||||
### Breaking changes
|
||||
|
@ -85,6 +88,7 @@ TODO: Describe ffi port
|
|||
adopted to `_watchAllUsers`. The `generate_private_watch_methods` builder option, which
|
||||
backported this fix to older versions, has thus been removed.
|
||||
- Removed `InsertStatement.insertOrReplace`. Use `insert(data, orReplace: true)` instead.
|
||||
- Removed the diff util and `MoorAnimatedList`. Use a third party library for that.
|
||||
|
||||
## 1.7.2
|
||||
- Fixed a race condition that caused the database to be opened multiple times on slower devices.
|
||||
|
|
|
@ -168,6 +168,7 @@ class _TransactionExecutor extends TransactionExecutor
|
|||
Future<void> send() async {
|
||||
if (_sendOnCommit != null) {
|
||||
await runCustom(_sendOnCommit, const []);
|
||||
_db.delegate.isInTransaction = false;
|
||||
}
|
||||
|
||||
_sendCalled.complete();
|
||||
|
|
|
@ -121,7 +121,7 @@ abstract class Selectable<T> {
|
|||
return watch().transform(singleElements());
|
||||
}
|
||||
|
||||
/// Maps this selectable by using [mapper].
|
||||
/// Maps this selectable by the [mapper] function.
|
||||
///
|
||||
/// Each entry emitted by this [Selectable] will be transformed by the
|
||||
/// [mapper] and then emitted to the selectable returned.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:convert/convert.dart';
|
||||
|
||||
part 'custom_type.dart';
|
||||
|
||||
/// A type that can be mapped from Dart to sql. The generic type parameter here
|
||||
|
@ -118,12 +120,14 @@ class BlobType extends SqlType<Uint8List> {
|
|||
mapFromDatabaseResponse(response) => response as Uint8List;
|
||||
|
||||
@override
|
||||
String mapToSqlConstant(content) {
|
||||
throw UnimplementedError("Blobs can't be mapped to sql literals");
|
||||
String mapToSqlConstant(Uint8List content) {
|
||||
// BLOB literals are string literals containing hexadecimal data and
|
||||
// preceded by a single "x" or "X" character. Example: X'53514C697465'
|
||||
return "x'${hex.encode(content)}'";
|
||||
}
|
||||
|
||||
@override
|
||||
mapToSqlVariable(content) => content;
|
||||
mapToSqlVariable(Uint8List content) => content;
|
||||
}
|
||||
|
||||
class RealType extends SqlType<double> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: moor
|
||||
description: Moor is a safe and reactive persistence library for Dart applications
|
||||
version: 1.7.2
|
||||
version: 2.0.0
|
||||
repository: https://github.com/simolus3/moor
|
||||
homepage: https://moor.simonbinder.eu/
|
||||
issue_tracker: https://github.com/simolus3/moor/issues
|
||||
|
@ -13,6 +13,7 @@ environment:
|
|||
|
||||
dependencies:
|
||||
meta: ^1.0.0
|
||||
convert: ^2.1.1
|
||||
collection: ^1.0.0
|
||||
synchronized: ^2.1.0
|
||||
pedantic: ^1.0.0
|
||||
|
@ -22,17 +23,13 @@ dev_dependencies:
|
|||
build_runner: '>=1.3.0 <2.0.0'
|
||||
build_test: ^0.10.8
|
||||
test: ^1.6.4
|
||||
test_api: '>=0.2.0 <1.0.0'
|
||||
test_core: '>=0.2.0 <1.0.0'
|
||||
mockito: ^4.1.0
|
||||
grinder: ^0.8.3
|
||||
coverage: ^0.12.4
|
||||
coverage: any # will be determined by test_coverage
|
||||
test_coverage: ^0.3.0
|
||||
|
||||
dependency_overrides:
|
||||
moor_generator:
|
||||
path: ../moor_generator
|
||||
sqlparser:
|
||||
path: ../sqlparser
|
||||
# Temporarily use my fork because it can collect coverage when running tests with the test runner
|
||||
coverage:
|
||||
git: https://github.com/simolus3/coverage.git
|
||||
path: ../sqlparser
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final nullable = GeneratedDateTimeColumn('name', null, true);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
@ -29,6 +29,32 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
test('custom select reads values', () async {
|
||||
final time = DateTime(2019, 10, 1);
|
||||
final unix = time.millisecondsSinceEpoch ~/ 1000;
|
||||
|
||||
when(executor.runSelect(any, any)).thenAnswer((i) {
|
||||
return Future.value([
|
||||
<String, dynamic>{
|
||||
'bool': true,
|
||||
'int': 3,
|
||||
'double': 3.14,
|
||||
'dateTime': unix,
|
||||
'blob': Uint8List.fromList([1, 2, 3]),
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
final rows = await db.customSelectQuery('').get();
|
||||
final row = rows.single;
|
||||
|
||||
expect(row.readBool('bool'), isTrue);
|
||||
expect(row.readInt('int'), 3);
|
||||
expect(row.readDouble('double'), 3.14);
|
||||
expect(row.readDateTime('dateTime'), time);
|
||||
expect(row.readBlob('blob'), Uint8List.fromList([1, 2, 3]));
|
||||
});
|
||||
|
||||
test('custom update informs stream queries', () async {
|
||||
await db.customUpdate('UPDATE tbl SET a = ?',
|
||||
variables: [Variable.withString('hi')], updates: {db.users});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
import 'data/utils/mocks.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
import 'package:mockito/mockito.dart';
|
||||
import 'package:moor/backends.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockDelegate extends Mock implements DatabaseDelegate {}
|
||||
|
||||
class _MockUserDb extends Mock implements GeneratedDatabase {}
|
||||
|
||||
class _MockDynamicVersionDelegate extends Mock
|
||||
implements DynamicVersionDelegate {}
|
||||
|
||||
class _MockTransactionDelegate extends Mock
|
||||
implements SupportedTransactionDelegate {}
|
||||
|
||||
void main() {
|
||||
_MockDelegate delegate;
|
||||
setUp(() {
|
||||
delegate = _MockDelegate();
|
||||
|
||||
when(delegate.isOpen).thenAnswer((_) => Future.value(true));
|
||||
when(delegate.runSelect(any, any))
|
||||
.thenAnswer((_) => Future.value(QueryResult.fromRows([])));
|
||||
when(delegate.runUpdate(any, any)).thenAnswer((_) => Future.value(3));
|
||||
when(delegate.runInsert(any, any)).thenAnswer((_) => Future.value(4));
|
||||
when(delegate.runCustom(any, any)).thenAnswer((_) => Future.value());
|
||||
when(delegate.runBatched(any)).thenAnswer((_) => Future.value());
|
||||
});
|
||||
|
||||
group('delegates queries', () {
|
||||
void _runTests(bool sequential) {
|
||||
test('when sequential = $sequential', () async {
|
||||
final db = DelegatedDatabase(delegate, isSequential: sequential);
|
||||
|
||||
await db.doWhenOpened((_) async {
|
||||
expect(await db.runSelect(null, null), isEmpty);
|
||||
expect(await db.runUpdate(null, null), 3);
|
||||
expect(await db.runInsert(null, null), 4);
|
||||
await db.runCustom(null);
|
||||
await db.runBatched(null);
|
||||
});
|
||||
|
||||
verifyInOrder([
|
||||
delegate.isOpen,
|
||||
delegate.runSelect(null, null),
|
||||
delegate.runUpdate(null, null),
|
||||
delegate.runInsert(null, null),
|
||||
delegate.runCustom(null, []),
|
||||
delegate.runBatched(null),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
_runTests(false);
|
||||
_runTests(true);
|
||||
});
|
||||
|
||||
group('migrations', () {
|
||||
DelegatedDatabase db;
|
||||
_MockUserDb userDb;
|
||||
setUp(() {
|
||||
userDb = _MockUserDb();
|
||||
when(userDb.schemaVersion).thenReturn(3);
|
||||
|
||||
when(delegate.isOpen).thenAnswer((_) => Future.value(false));
|
||||
db = DelegatedDatabase(delegate)..databaseInfo = userDb;
|
||||
|
||||
when(userDb.handleDatabaseCreation(executor: anyNamed('executor')))
|
||||
.thenAnswer((i) async {
|
||||
final executor = i.namedArguments.values.single as SqlExecutor;
|
||||
await executor('created', []);
|
||||
});
|
||||
|
||||
when(userDb.handleDatabaseVersionChange(
|
||||
executor: anyNamed('executor'),
|
||||
from: anyNamed('from'),
|
||||
to: anyNamed('to'),
|
||||
)).thenAnswer((i) async {
|
||||
final executor = i.namedArguments[#executor] as SqlExecutor;
|
||||
final from = i.namedArguments[#from] as int;
|
||||
final to = i.namedArguments[#to] as int;
|
||||
await executor('upgraded', [from, to]);
|
||||
});
|
||||
});
|
||||
|
||||
test('when the database does not support versions', () async {
|
||||
when(delegate.versionDelegate).thenReturn(const NoVersionDelegate());
|
||||
await db.doWhenOpened((_) async {});
|
||||
|
||||
verify(delegate.open(userDb));
|
||||
verifyNever(delegate.runCustom(any, any));
|
||||
});
|
||||
|
||||
test('when the database supports versions at opening', () async {
|
||||
when(delegate.versionDelegate)
|
||||
.thenReturn(OnOpenVersionDelegate(() => Future.value(3)));
|
||||
await db.doWhenOpened((_) async {});
|
||||
|
||||
verify(delegate.open(userDb));
|
||||
verifyNever(delegate.runCustom(any, any));
|
||||
});
|
||||
|
||||
test('when the database supports dynamic version', () async {
|
||||
final version = _MockDynamicVersionDelegate();
|
||||
when(version.schemaVersion).thenAnswer((_) => Future.value(3));
|
||||
|
||||
when(delegate.versionDelegate).thenReturn(version);
|
||||
await db.doWhenOpened((_) async {});
|
||||
|
||||
verify(delegate.open(userDb));
|
||||
verifyNever(delegate.runCustom(any, any));
|
||||
verify(version.schemaVersion);
|
||||
verify(version.setSchemaVersion(3));
|
||||
});
|
||||
|
||||
test('handles database creations', () async {
|
||||
when(delegate.versionDelegate)
|
||||
.thenReturn(OnOpenVersionDelegate(() => Future.value(0)));
|
||||
await db.doWhenOpened((_) async {});
|
||||
|
||||
verify(delegate.runCustom('created', []));
|
||||
});
|
||||
|
||||
test('handles database upgrades', () async {
|
||||
when(delegate.versionDelegate)
|
||||
.thenReturn(OnOpenVersionDelegate(() => Future.value(1)));
|
||||
await db.doWhenOpened((_) async {});
|
||||
|
||||
verify(delegate.runCustom('upgraded', [1, 3]));
|
||||
});
|
||||
});
|
||||
|
||||
group('transactions', () {
|
||||
DelegatedDatabase db;
|
||||
|
||||
setUp(() {
|
||||
db = DelegatedDatabase(delegate, isSequential: true);
|
||||
});
|
||||
|
||||
test('when the delegate does not support transactions', () async {
|
||||
when(delegate.transactionDelegate)
|
||||
.thenReturn(const NoTransactionDelegate());
|
||||
await db.doWhenOpened((_) async {
|
||||
final transaction = db.beginTransaction();
|
||||
await transaction.doWhenOpened((e) async {
|
||||
await e.runSelect(null, null);
|
||||
|
||||
await transaction.send();
|
||||
});
|
||||
});
|
||||
|
||||
verifyInOrder([
|
||||
delegate.runCustom('BEGIN TRANSACTION', []),
|
||||
delegate.runSelect(null, null),
|
||||
delegate.runCustom('COMMIT TRANSACTION', []),
|
||||
]);
|
||||
});
|
||||
|
||||
test('when the database supports transactions', () async {
|
||||
final transaction = _MockTransactionDelegate();
|
||||
when(transaction.startTransaction(any)).thenAnswer((i) {
|
||||
(i.positionalArguments.single as Function(QueryDelegate))(delegate);
|
||||
});
|
||||
|
||||
when(delegate.transactionDelegate).thenReturn(transaction);
|
||||
|
||||
await db.doWhenOpened((_) async {
|
||||
final transaction = db.beginTransaction();
|
||||
await transaction.doWhenOpened((e) async {
|
||||
await e.runSelect(null, null);
|
||||
|
||||
await transaction.send();
|
||||
});
|
||||
});
|
||||
|
||||
verify(transaction.startTransaction(any));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:moor/src/runtime/expressions/expression.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
typedef Expression<int, IntType> _Extractor(
|
||||
Expression<DateTime, DateTimeType> d);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:moor/moor.dart' as moor;
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:moor/moor.dart' as moor;
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/src/runtime/components/component.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
@ -18,6 +18,7 @@ void main() {
|
|||
test('generates insert statements', () async {
|
||||
await db.into(db.todosTable).insert(const TodosTableCompanion(
|
||||
content: Value('Implement insert statements'),
|
||||
title: Value.absent(),
|
||||
));
|
||||
|
||||
verify(executor.runInsert('INSERT INTO todos (content) VALUES (?)',
|
||||
|
@ -111,16 +112,21 @@ void main() {
|
|||
verify(streamQueries.handleTableUpdates({db.users}));
|
||||
});
|
||||
|
||||
test('enforces data integrity', () {
|
||||
expect(
|
||||
db.into(db.todosTable).insert(
|
||||
test('enforces data integrity', () async {
|
||||
InvalidDataException exception;
|
||||
try {
|
||||
await db.into(db.todosTable).insert(
|
||||
const TodosTableCompanion(
|
||||
// not declared as nullable in table definition
|
||||
content: Value(null),
|
||||
),
|
||||
),
|
||||
throwsA(const TypeMatcher<InvalidDataException>()),
|
||||
);
|
||||
);
|
||||
fail('inserting invalid data did not throw');
|
||||
} on InvalidDataException catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
|
||||
expect(exception.toString(), startsWith('InvalidDataException'));
|
||||
});
|
||||
|
||||
test('reports auto-increment id', () async {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:moor/src/runtime/components/join.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
||||
|
@ -113,4 +114,19 @@ void main() {
|
|||
verify(executor.runSelect(
|
||||
argThat(contains('WHERE t.id < ? ORDER BY t.title ASC')), [3]));
|
||||
});
|
||||
|
||||
test('injects custom error message when a table is used multiple times',
|
||||
() async {
|
||||
when(executor.runSelect(any, any)).thenAnswer((_) => Future.error('nah'));
|
||||
|
||||
MoorWrappedException wrappedException;
|
||||
try {
|
||||
await db.select(db.todosTable).join([crossJoin(db.todosTable)]).get();
|
||||
fail('expected this to throw');
|
||||
} on MoorWrappedException catch (e) {
|
||||
wrappedException = e;
|
||||
}
|
||||
|
||||
expect(wrappedException.toString(), contains('possible cause'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/tables/custom_tables.dart';
|
||||
import '../data/utils/mocks.dart';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
@ -17,7 +17,7 @@ void main() {
|
|||
|
||||
group('Migrations', () {
|
||||
test('creates all tables', () async {
|
||||
await Migrator(db, mockQueryExecutor).createAllTables();
|
||||
await db.handleDatabaseCreation(executor: mockQueryExecutor);
|
||||
|
||||
// should create todos, categories, users and shared_todos table
|
||||
verify(mockQueryExecutor.call(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/data_class.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'data/tables/todos.dart';
|
||||
|
||||
final someDate = DateTime(2019, 06, 08);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:moor/src/runtime/executor/stream_queries.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
|
|
@ -14,7 +14,7 @@ test/data/utils/mocks.dart 22:20 Mo
|
|||
package:mockito/src/mock.dart 128:22 MockExecutor.noSuchMethod
|
||||
*/
|
||||
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('maps without transormation', () {
|
||||
const type = BlobType();
|
||||
final data = Uint8List.fromList(List.generate(256, (i) => i));
|
||||
|
||||
expect(type.mapToSqlVariable(data), data);
|
||||
expect(type.mapFromDatabaseResponse(data), data);
|
||||
});
|
||||
|
||||
test('writes blob literals', () {
|
||||
const type = BlobType();
|
||||
const hex = '67656E6572616C206B656E6F626921';
|
||||
final data = Uint8List.fromList(utf8.encode('general kenobi!'));
|
||||
|
||||
expect(type.mapToSqlConstant(data), equalsIgnoringCase("x'$hex'"));
|
||||
});
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/moor.dart' as moor;
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const _exampleUnixSqlite = 1550172560;
|
||||
const _exampleUnixMillis = 1550172560000;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor/moor.dart' as moor;
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final type = const moor.RealType();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'data/tables/todos.dart';
|
||||
import 'data/utils/mocks.dart';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
import '../data/utils/mocks.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:moor/src/utils/single_transformer.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('transforms simple values', () {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:moor/src/web/binary_string_conversion.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final data = Uint8List(256 * 2);
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:grinder/grinder_sdk.dart';
|
||||
import 'package:coverage/coverage.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
// First, generate the build script, see
|
||||
// https://github.com/dart-lang/build/blob/3208cfe94c475ed3e1ec44c227aadaddaeac263d/build_runner/bin/build_runner.dart#L65
|
||||
Pub.run('build_runner', arguments: ['generate-build-script']);
|
||||
|
||||
// Next, run the test script in another dart process that has the vm services
|
||||
// enabled.
|
||||
final tests = join(File.fromUri(Platform.script).parent.path, 'tester.dart');
|
||||
final coverage = await runAndCollect(tests, onExit: true, printOutput: true);
|
||||
|
||||
File('coverage.json').writeAsStringSync(json.encode(coverage));
|
||||
}
|
|
@ -2,28 +2,31 @@ import 'dart:io';
|
|||
|
||||
import 'package:coverage/coverage.dart';
|
||||
|
||||
// note that this script will be run from the parent directory (the root of the
|
||||
// moor repo)
|
||||
Future main() async {
|
||||
Directory.current = Directory.current.parent;
|
||||
// the lcov file generated by test_coverage has wrong file paths
|
||||
final moorLcov = File('moor/coverage/lcov.info').readAsLinesSync();
|
||||
final moorWithFixesPaths = moorLcov.map((line) {
|
||||
if (line.startsWith('SF:')) {
|
||||
final path = line.split(':')[1];
|
||||
return 'SF:moor/$path';
|
||||
} else {
|
||||
return line;
|
||||
}
|
||||
}).toList();
|
||||
|
||||
final resolver = Resolver(
|
||||
packagesPath: 'moor/.packages',
|
||||
);
|
||||
|
||||
final coverage = await parseCoverage([
|
||||
File('moor/coverage.json'),
|
||||
File('sqlparser/coverage.json'),
|
||||
], 1);
|
||||
final sqlCoverage = await parseCoverage([File('sqlparser/coverage.json')], 1);
|
||||
final resolver = Resolver(packagesPath: 'sqlparser/.packages');
|
||||
|
||||
// report coverage for the moor and moor_generator package
|
||||
final lcov = await LcovFormatter(
|
||||
resolver,
|
||||
reportOn: [
|
||||
'moor/lib/',
|
||||
'moor_generator/lib',
|
||||
'sqlparser/lib',
|
||||
],
|
||||
reportOn: ['sqlparser/lib'],
|
||||
basePath: '.',
|
||||
).format(coverage);
|
||||
).format(sqlCoverage);
|
||||
|
||||
File('lcov.info').writeAsStringSync(lcov);
|
||||
// ignore: prefer_interpolation_to_compose_strings
|
||||
final output = moorWithFixesPaths.join('\n') + '\n' + lcov;
|
||||
File('lcov.info').writeAsStringSync(output);
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import 'package:test_core/src/executable.dart' as test;
|
||||
import '../.dart_tool/build/entrypoint/build.dart' as builder;
|
||||
|
||||
void main() async {
|
||||
print('inside test file');
|
||||
// Run the build runner
|
||||
await builder.main(['build', '--delete-conflicting-outputs']);
|
||||
print('done with build runner, now starting with tests');
|
||||
// Run tests
|
||||
await test.main([]);
|
||||
print('done, script should terminate after coverage is collected');
|
||||
}
|
|
@ -1,23 +1,28 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:isolate';
|
||||
|
||||
//import 'package:moor_generator/plugin.dart';
|
||||
import 'package:moor_generator/plugin.dart' as plugin;
|
||||
import 'package:web_socket_channel/io.dart';
|
||||
|
||||
const useProxyPlugin = true;
|
||||
const useDebuggingVariant = false;
|
||||
|
||||
void main(List<String> args, SendPort sendPort) {
|
||||
PluginProxy(sendPort).start();
|
||||
// start(args, sendPort);
|
||||
if (useDebuggingVariant) {
|
||||
_PluginProxy(sendPort).start();
|
||||
} else {
|
||||
plugin.start(args, sendPort);
|
||||
}
|
||||
}
|
||||
|
||||
class PluginProxy {
|
||||
// Used during development. See CONTRIBUTING.md in the moor repo on how to debug
|
||||
// the plugin.
|
||||
class _PluginProxy {
|
||||
final SendPort sendToAnalysisServer;
|
||||
|
||||
ReceivePort _receive;
|
||||
IOWebSocketChannel _channel;
|
||||
|
||||
PluginProxy(this.sendToAnalysisServer);
|
||||
_PluginProxy(this.sendToAnalysisServer);
|
||||
|
||||
void start() async {
|
||||
_channel = IOWebSocketChannel.connect('ws://localhost:9999');
|
||||
|
|
|
@ -3,14 +3,13 @@ version: 1.0.0
|
|||
description: This pubspec is a part of moor and determines the version of the moor analyzer to load
|
||||
|
||||
dependencies:
|
||||
# moor_generator:
|
||||
web_socket_channel: ^1.0.15
|
||||
moor_generator:
|
||||
web_socket_channel: ^1.0.15
|
||||
|
||||
# To work on this plugin, you need to add the absolute paths here. Relative paths aren't supported yet
|
||||
# https://github.com/dart-lang/sdk/issues/35281
|
||||
# These overrides are only needed when working on the plugin with useDebuggingVariant = false (not recommended)
|
||||
|
||||
#dependency_overrides:
|
||||
# moor_generator:
|
||||
# path: /home/simon/IdeaProjects/moor/moor_generator
|
||||
# sqlparser:
|
||||
# path: /home/simon/IdeaProjects/moor/sqlparser
|
||||
# moor_generator:
|
||||
# path: /home/simon/IdeaProjects/moor/moor_generator
|
||||
# sqlparser:
|
||||
# path: /home/simon/IdeaProjects/moor/sqlparser
|
|
@ -96,5 +96,4 @@ In all other project files that use moor apis (e.g. a `Value` class for companio
|
|||
|
||||
Finally, replace usages of `FlutterQueryExecutor` with `VmDatabase`.
|
||||
|
||||
Note that, at the moment, there is no direct counterpart for `FlutterQueryExecutor.inDatabasePath`
|
||||
and that the async API using a background isolate is not available for moor yet.
|
||||
Note that, at the moment, there is no direct counterpart for `FlutterQueryExecutor.inDatabasePath`.
|
|
@ -7,13 +7,18 @@ class VmDatabase extends DelegatedDatabase {
|
|||
|
||||
/// Creates a database that will store its result in the [file], creating it
|
||||
/// if it doesn't exist.
|
||||
factory VmDatabase(File file, {bool logStatements = false}) {
|
||||
return VmDatabase._(_VmDelegate(file), logStatements);
|
||||
///
|
||||
/// If [background] is enabled (defaults to false), the database will be
|
||||
/// opened on a background isolate. This is much slower, but reduces work on
|
||||
/// the UI thread.
|
||||
factory VmDatabase(File file,
|
||||
{bool logStatements = false, bool background = false}) {
|
||||
return VmDatabase._(_VmDelegate(file, background), logStatements);
|
||||
}
|
||||
|
||||
/// Creates a database won't persist its changes on disk.
|
||||
/// Creates an in-memory database won't persist its changes on disk.
|
||||
factory VmDatabase.memory({bool logStatements = false}) {
|
||||
return VmDatabase._(_VmDelegate(null), logStatements);
|
||||
return VmDatabase._(_VmDelegate(null, false), logStatements);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,8 +26,9 @@ class _VmDelegate extends DatabaseDelegate {
|
|||
BaseDatabase _db;
|
||||
|
||||
final File file;
|
||||
final bool background;
|
||||
|
||||
_VmDelegate(this.file);
|
||||
_VmDelegate(this.file, this.background);
|
||||
|
||||
@override
|
||||
final TransactionDelegate transactionDelegate = const NoTransactionDelegate();
|
||||
|
@ -34,10 +40,18 @@ class _VmDelegate extends DatabaseDelegate {
|
|||
Future<bool> get isOpen => Future.value(_db != null);
|
||||
|
||||
@override
|
||||
Future<void> open([GeneratedDatabase db]) {
|
||||
Future<void> open([GeneratedDatabase db]) async {
|
||||
if (file != null) {
|
||||
_db = Database.openFile(file);
|
||||
if (background) {
|
||||
_db = await IsolateDb.openFile(file);
|
||||
} else {
|
||||
_db = Database.openFile(file);
|
||||
}
|
||||
} else {
|
||||
assert(
|
||||
!background,
|
||||
"moor_ffi doesn't support in-memory databases on a background "
|
||||
'isolate');
|
||||
_db = Database.memory();
|
||||
}
|
||||
versionDelegate = _VmVersionDelegate(_db);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: moor_ffi
|
||||
description: "Experimental sqlite bindings using dart:ffi"
|
||||
description: "Provides experimental sqlite bindings using dart:ffi, including a moor executor"
|
||||
version: 0.0.1
|
||||
author: Simon Binder <oss@simonbinder.eu>
|
||||
homepage: https://github.com/simolus3/moor/tree/develop/moor_ffi
|
||||
|
@ -10,6 +10,7 @@ environment:
|
|||
|
||||
dependencies:
|
||||
moor: ">=1.7.0 <2.1.0"
|
||||
collection: ^1.0.0
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.6.0
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
## 2.0.0
|
||||
See the changelog of [moor](https://pub.dev/packages/moor#-changelog-tab-) for details,
|
||||
or check out an overview of new features [here](https://moor.simonbinder.eu/v2])
|
||||
|
||||
## 1.7.0
|
||||
- Support custom columns via type converters. See the [docs](https://moor.simonbinder.eu/type_converters)
|
||||
for details on how to use this feature.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
targets:
|
||||
$default:
|
||||
builders:
|
||||
moor_generator:
|
||||
options:
|
||||
compact_query_methods: true
|
|
@ -42,6 +42,15 @@ class EntryWithCategory {
|
|||
tables: [Todos, Categories],
|
||||
queries: {
|
||||
'_resetCategory': 'UPDATE todos SET category = NULL WHERE category = ?',
|
||||
'_categoriesWithCount': '''
|
||||
SELECT
|
||||
c.id,
|
||||
c.desc,
|
||||
(SELECT COUNT(*) FROM todos WHERE category = c.id) AS amount
|
||||
FROM categories c
|
||||
UNION ALL
|
||||
SELECT null, null, (SELECT COUNT(*) FROM todos WHERE category IS NULL)
|
||||
''',
|
||||
},
|
||||
)
|
||||
class Database extends _$Database {
|
||||
|
@ -89,26 +98,15 @@ class Database extends _$Database {
|
|||
}
|
||||
|
||||
Stream<List<CategoryWithCount>> categoriesWithCount() {
|
||||
// select all categories and load how many associated entries there are for
|
||||
// each category
|
||||
return customSelectQuery(
|
||||
'SELECT c.id, c.desc, '
|
||||
'(SELECT COUNT(*) FROM todos WHERE category = c.id) AS amount '
|
||||
'FROM categories c '
|
||||
'UNION ALL SELECT null, null, '
|
||||
'(SELECT COUNT(*) FROM todos WHERE category IS NULL)',
|
||||
readsFrom: {todos, categories},
|
||||
).watch().map((rows) {
|
||||
// when we have the result set, map each row to the data class
|
||||
return rows.map((row) {
|
||||
final hasId = row.data['id'] != null;
|
||||
// the _categoriesWithCount method has been generated automatically based
|
||||
// on the query declared in the @UseMoor annotation
|
||||
return _categoriesWithCount().map((row) {
|
||||
final hasId = row.id != null;
|
||||
final category =
|
||||
hasId ? Category(id: row.id, description: row.desc) : null;
|
||||
|
||||
return CategoryWithCount(
|
||||
hasId ? Category.fromData(row.data, this) : null,
|
||||
row.readInt('amount'),
|
||||
);
|
||||
}).toList();
|
||||
});
|
||||
return CategoryWithCount(category, row.amount);
|
||||
}).watch();
|
||||
}
|
||||
|
||||
/// Watches all entries in the given [category]. If the category is null, all
|
||||
|
|
|
@ -6,7 +6,7 @@ part of 'database.dart';
|
|||
// MoorGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: unnecessary_brace_in_string_interps
|
||||
// ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this
|
||||
class TodoEntry extends DataClass implements Insertable<TodoEntry> {
|
||||
final int id;
|
||||
final String content;
|
||||
|
@ -89,18 +89,16 @@ class TodoEntry extends DataClass implements Insertable<TodoEntry> {
|
|||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(
|
||||
$mrjc(
|
||||
$mrjc($mrjc(0, id.hashCode), content.hashCode), targetDate.hashCode),
|
||||
category.hashCode));
|
||||
int get hashCode => $mrjf($mrjc(id.hashCode,
|
||||
$mrjc(content.hashCode, $mrjc(targetDate.hashCode, category.hashCode))));
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
identical(this, other) ||
|
||||
(other is TodoEntry &&
|
||||
other.id == id &&
|
||||
other.content == content &&
|
||||
other.targetDate == targetDate &&
|
||||
other.category == category);
|
||||
other.id == this.id &&
|
||||
other.content == this.content &&
|
||||
other.targetDate == this.targetDate &&
|
||||
other.category == this.category);
|
||||
}
|
||||
|
||||
class TodosCompanion extends UpdateCompanion<TodoEntry> {
|
||||
|
@ -114,6 +112,24 @@ class TodosCompanion extends UpdateCompanion<TodoEntry> {
|
|||
this.targetDate = const Value.absent(),
|
||||
this.category = const Value.absent(),
|
||||
});
|
||||
TodosCompanion.insert({
|
||||
this.id = const Value.absent(),
|
||||
@required String content,
|
||||
this.targetDate = const Value.absent(),
|
||||
this.category = const Value.absent(),
|
||||
}) : content = Value(content);
|
||||
TodosCompanion copyWith(
|
||||
{Value<int> id,
|
||||
Value<String> content,
|
||||
Value<DateTime> targetDate,
|
||||
Value<int> category}) {
|
||||
return TodosCompanion(
|
||||
id: id ?? this.id,
|
||||
content: content ?? this.content,
|
||||
targetDate: targetDate ?? this.targetDate,
|
||||
category: category ?? this.category,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class $TodosTable extends Todos with TableInfo<$TodosTable, TodoEntry> {
|
||||
|
@ -125,7 +141,8 @@ class $TodosTable extends Todos with TableInfo<$TodosTable, TodoEntry> {
|
|||
@override
|
||||
GeneratedIntColumn get id => _id ??= _constructId();
|
||||
GeneratedIntColumn _constructId() {
|
||||
return GeneratedIntColumn('id', $tableName, false, hasAutoIncrement: true);
|
||||
return GeneratedIntColumn('id', $tableName, false,
|
||||
hasAutoIncrement: true, declaredAsPrimaryKey: true);
|
||||
}
|
||||
|
||||
final VerificationMeta _contentMeta = const VerificationMeta('content');
|
||||
|
@ -287,11 +304,13 @@ class Category extends DataClass implements Insertable<Category> {
|
|||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc($mrjc(0, id.hashCode), description.hashCode));
|
||||
int get hashCode => $mrjf($mrjc(id.hashCode, description.hashCode));
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
identical(this, other) ||
|
||||
(other is Category && other.id == id && other.description == description);
|
||||
(other is Category &&
|
||||
other.id == this.id &&
|
||||
other.description == this.description);
|
||||
}
|
||||
|
||||
class CategoriesCompanion extends UpdateCompanion<Category> {
|
||||
|
@ -301,6 +320,16 @@ class CategoriesCompanion extends UpdateCompanion<Category> {
|
|||
this.id = const Value.absent(),
|
||||
this.description = const Value.absent(),
|
||||
});
|
||||
CategoriesCompanion.insert({
|
||||
this.id = const Value.absent(),
|
||||
@required String description,
|
||||
}) : description = Value(description);
|
||||
CategoriesCompanion copyWith({Value<int> id, Value<String> description}) {
|
||||
return CategoriesCompanion(
|
||||
id: id ?? this.id,
|
||||
description: description ?? this.description,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class $CategoriesTable extends Categories
|
||||
|
@ -313,7 +342,8 @@ class $CategoriesTable extends Categories
|
|||
@override
|
||||
GeneratedIntColumn get id => _id ??= _constructId();
|
||||
GeneratedIntColumn _constructId() {
|
||||
return GeneratedIntColumn('id', $tableName, false, hasAutoIncrement: true);
|
||||
return GeneratedIntColumn('id', $tableName, false,
|
||||
hasAutoIncrement: true, declaredAsPrimaryKey: true);
|
||||
}
|
||||
|
||||
final VerificationMeta _descriptionMeta =
|
||||
|
@ -388,19 +418,40 @@ abstract class _$Database extends GeneratedDatabase {
|
|||
$TodosTable get todos => _todos ??= $TodosTable(this);
|
||||
$CategoriesTable _categories;
|
||||
$CategoriesTable get categories => _categories ??= $CategoriesTable(this);
|
||||
Future<int> _resetCategory(
|
||||
int var1,
|
||||
{@Deprecated('No longer needed with Moor 1.6 - see the changelog for details')
|
||||
QueryEngine operateOn}) {
|
||||
return (operateOn ?? this).customUpdate(
|
||||
Future<int> _resetCategory(int var1) {
|
||||
return customUpdate(
|
||||
'UPDATE todos SET category = NULL WHERE category = ?',
|
||||
variables: [
|
||||
Variable.withInt(var1),
|
||||
],
|
||||
variables: [Variable.withInt(var1)],
|
||||
updates: {todos},
|
||||
);
|
||||
}
|
||||
|
||||
CategoriesWithCountResult _rowToCategoriesWithCountResult(QueryRow row) {
|
||||
return CategoriesWithCountResult(
|
||||
id: row.readInt('id'),
|
||||
desc: row.readString('desc'),
|
||||
amount: row.readInt('amount'),
|
||||
);
|
||||
}
|
||||
|
||||
Selectable<CategoriesWithCountResult> _categoriesWithCount() {
|
||||
return customSelectQuery(
|
||||
'SELECT\n c.id,\n c.desc,\n (SELECT COUNT(*) FROM todos WHERE category = c.id) AS amount\n FROM categories c\n UNION ALL\n SELECT null, null, (SELECT COUNT(*) FROM todos WHERE category IS NULL)',
|
||||
variables: [],
|
||||
readsFrom: {categories, todos}).map(_rowToCategoriesWithCountResult);
|
||||
}
|
||||
|
||||
@override
|
||||
List<TableInfo> get allTables => [todos, categories];
|
||||
}
|
||||
|
||||
class CategoriesWithCountResult {
|
||||
final int id;
|
||||
final String desc;
|
||||
final int amount;
|
||||
CategoriesWithCountResult({
|
||||
this.id,
|
||||
this.desc,
|
||||
this.amount,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,20 +1,6 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.10"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.2"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -50,13 +36,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -67,13 +46,6 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -109,13 +81,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0+1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -198,13 +163,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.5.0"
|
||||
sdks:
|
||||
dart: ">=2.4.0 <3.0.0"
|
||||
dart: ">=2.2.2 <3.0.0"
|
||||
flutter: ">=1.2.1 <2.0.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: moor_flutter
|
||||
description: Flutter implementation of moor, a safe and reactive persistence library for Dart applications
|
||||
version: 1.7.0
|
||||
version: 2.0.0
|
||||
repository: https://github.com/simolus3/moor
|
||||
homepage: https://moor.simonbinder.eu/
|
||||
issue_tracker: https://github.com/simolus3/moor/issues
|
||||
|
@ -12,7 +12,7 @@ environment:
|
|||
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
moor: ^1.7.0
|
||||
moor: ^2.0.0
|
||||
sqflite: ^1.1.6+5
|
||||
meta: ^1.0.0
|
||||
path: ^1.0.0
|
||||
|
|
|
@ -179,7 +179,7 @@ class ColumnParser {
|
|||
sqlType: columnType);
|
||||
}
|
||||
|
||||
return SpecifiedColumn(
|
||||
final column = SpecifiedColumn(
|
||||
type: columnType,
|
||||
dartGetterName: getter.name.name,
|
||||
name: name,
|
||||
|
@ -189,6 +189,10 @@ class ColumnParser {
|
|||
features: foundFeatures,
|
||||
defaultArgument: foundDefaultExpression?.toSource(),
|
||||
typeConverter: converter);
|
||||
|
||||
final declaration =
|
||||
ColumnDeclaration(column, base.step.file, element, null);
|
||||
return column..declaration = declaration;
|
||||
}
|
||||
|
||||
ColumnType _startMethodToColumnType(String startMethod) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:moor/sqlite_keywords.dart';
|
||||
import 'package:moor_generator/src/analyzer/errors.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart';
|
||||
import 'package:moor_generator/src/model/specified_column.dart';
|
||||
import 'package:moor_generator/src/model/specified_db_classes.dart';
|
||||
import 'package:moor_generator/src/model/specified_table.dart';
|
||||
|
|
|
@ -19,6 +19,7 @@ class TableParser {
|
|||
dartTypeName: _readDartTypeName(element),
|
||||
primaryKey: await _readPrimaryKey(element, columns),
|
||||
);
|
||||
table.declaration = TableDeclaration(table, base.step.file, element, null);
|
||||
|
||||
var index = 0;
|
||||
for (var converter in table.converters) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/type_mapping.dart';
|
||||
import 'package:moor_generator/src/model/specified_column.dart';
|
||||
import 'package:moor_generator/src/model/specified_table.dart';
|
||||
|
@ -10,8 +12,9 @@ import 'package:sqlparser/sqlparser.dart';
|
|||
class CreateTableReader {
|
||||
/// The AST of this `CREATE TABLE` statement.
|
||||
final CreateTableStatement stmt;
|
||||
final Step step;
|
||||
|
||||
CreateTableReader(this.stmt);
|
||||
CreateTableReader(this.stmt, this.step);
|
||||
|
||||
SpecifiedTable extractTable(TypeMapper mapper) {
|
||||
final table = SchemaFromCreateTable().read(stmt);
|
||||
|
@ -68,6 +71,10 @@ class CreateTableReader {
|
|||
typeConverter: converter,
|
||||
);
|
||||
|
||||
final declaration =
|
||||
ColumnDeclaration(parsed, step.file, null, column.definition);
|
||||
parsed.declaration = declaration;
|
||||
|
||||
foundColumns[column.name] = parsed;
|
||||
if (isPrimaryKey) {
|
||||
primaryKey.add(parsed);
|
||||
|
@ -89,7 +96,7 @@ class CreateTableReader {
|
|||
}
|
||||
}
|
||||
|
||||
return SpecifiedTable(
|
||||
final specifiedTable = SpecifiedTable(
|
||||
fromClass: null,
|
||||
columns: foundColumns.values.toList(),
|
||||
sqlName: table.name,
|
||||
|
@ -101,6 +108,10 @@ class CreateTableReader {
|
|||
// we take care of writing the primary key ourselves
|
||||
overrideDontWriteConstraints: true,
|
||||
);
|
||||
|
||||
return specifiedTable
|
||||
..declaration =
|
||||
TableDeclaration(specifiedTable, step.file, null, table.definition);
|
||||
}
|
||||
|
||||
UsedTypeConverter _readTypeConverter(MappedBy mapper) {
|
||||
|
|
|
@ -26,7 +26,7 @@ class MoorParser {
|
|||
step.inlineDartResolver.importStatements.add(importStmt.importedFile);
|
||||
importStatements.add(importStmt);
|
||||
} else if (parsedStmt is CreateTableStatement) {
|
||||
createdReaders.add(CreateTableReader(parsedStmt));
|
||||
createdReaders.add(CreateTableReader(parsedStmt, step));
|
||||
} else if (parsedStmt is DeclaredStatement) {
|
||||
queryDeclarations.add(DeclaredMoorQuery.fromStatement(parsedStmt));
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ class MoorParser {
|
|||
|
||||
for (var error in result.errors) {
|
||||
step.reportError(ErrorInMoorFile(
|
||||
severity: Severity.error,
|
||||
span: error.token.span,
|
||||
message: error.message,
|
||||
));
|
||||
|
|
|
@ -83,6 +83,11 @@ class Task {
|
|||
|
||||
parsed.resolvedImports = <ImportStatement, FoundFile>{};
|
||||
for (var import in parsed.imports) {
|
||||
if (import.importedFile == null) {
|
||||
// invalid import statement, this can happen as the user is typing
|
||||
continue;
|
||||
}
|
||||
|
||||
final found = session.resolve(file, import.importedFile);
|
||||
if (!await backend.exists(found.uri)) {
|
||||
step.reportError(ErrorInMoorFile(
|
||||
|
|
|
@ -103,7 +103,7 @@ class _LintingVisitor extends RecursiveVisitor<void> {
|
|||
.any((t) => t.name.toUpperCase() == c.name.name.toUpperCase()));
|
||||
|
||||
if (notPresent.isNotEmpty) {
|
||||
final msg = notPresent.join(', ');
|
||||
final msg = notPresent.map((c) => c.name.name).join(', ');
|
||||
|
||||
linter.lints.add(AnalysisError(
|
||||
type: AnalysisErrorType.other,
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/file_graph.dart';
|
||||
import 'package:moor_generator/src/model/specified_column.dart';
|
||||
import 'package:moor_generator/src/model/specified_table.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
class BaseDeclaration {
|
||||
final FoundFile declarationFile;
|
||||
|
||||
/// If the column was declared in Dart, contains an enclosing getter element
|
||||
/// that declared the column
|
||||
final Element dartDeclaration;
|
||||
|
||||
/// If the column was declared in a moor file, contains the ast node that
|
||||
/// contains the column definition
|
||||
final AstNode moorDeclaration;
|
||||
|
||||
BaseDeclaration(
|
||||
this.declarationFile, this.dartDeclaration, this.moorDeclaration);
|
||||
}
|
||||
|
||||
/// Column declaration that is used as a metadata on a [Column] so that the
|
||||
/// analysis plugin can know where a referenced column was declared and provide
|
||||
/// navigation hints.
|
||||
class ColumnDeclaration extends BaseDeclaration {
|
||||
/// The moor version of the declared column.
|
||||
final SpecifiedColumn column;
|
||||
|
||||
ColumnDeclaration(this.column, FoundFile declarationFile,
|
||||
Element dartDeclaration, AstNode moorDeclaration)
|
||||
: super(declarationFile, dartDeclaration, moorDeclaration);
|
||||
}
|
||||
|
||||
/// Meta information set on a [Table] so that the analysis plugin can know where
|
||||
/// a referenced table was declared and provide navigation hints.
|
||||
class TableDeclaration extends BaseDeclaration {
|
||||
final SpecifiedTable table;
|
||||
|
||||
TableDeclaration(this.table, FoundFile declarationFile,
|
||||
Element dartDeclaration, AstNode moorDeclaration)
|
||||
: super(declarationFile, dartDeclaration, moorDeclaration);
|
||||
}
|
|
@ -20,7 +20,7 @@ class QueryHandler {
|
|||
Iterable<FoundVariable> get _foundVariables =>
|
||||
_foundElements.whereType<FoundVariable>();
|
||||
|
||||
SelectStatement get _select => context.root as SelectStatement;
|
||||
BaseSelectStatement get _select => context.root as BaseSelectStatement;
|
||||
|
||||
QueryHandler(this.name, this.context, this.mapper);
|
||||
|
||||
|
@ -39,7 +39,7 @@ class QueryHandler {
|
|||
|
||||
SqlQuery _mapToMoor() {
|
||||
final root = context.root;
|
||||
if (root is SelectStatement) {
|
||||
if (root is BaseSelectStatement) {
|
||||
return _handleSelect();
|
||||
} else if (root is UpdateStatement ||
|
||||
root is DeleteStatement ||
|
||||
|
|
|
@ -4,6 +4,8 @@ import 'package:moor_generator/src/model/sql_query.dart';
|
|||
import 'package:moor_generator/src/utils/type_converter_hint.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
import 'meta/declarations.dart';
|
||||
|
||||
/// Converts tables and types between the moor_generator and the sqlparser
|
||||
/// library.
|
||||
class TypeMapper {
|
||||
|
@ -19,10 +21,15 @@ class TypeMapper {
|
|||
: null;
|
||||
final type = resolveForColumnType(specified.type, overrideHint: hint)
|
||||
.withNullable(specified.nullable);
|
||||
columns.add(TableColumn(specified.name.name, type));
|
||||
|
||||
final column = TableColumn(specified.name.name, type);
|
||||
column.setMeta<ColumnDeclaration>(specified.declaration);
|
||||
|
||||
columns.add(column);
|
||||
}
|
||||
|
||||
final engineTable = Table(name: table.sqlName, resolvedColumns: columns);
|
||||
engineTable.setMeta<TableDeclaration>(table.declaration);
|
||||
_engineTablesToSpecified[engineTable] = table;
|
||||
return engineTable;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@ part of 'moor_builder.dart';
|
|||
class MoorOptions {
|
||||
final bool generateFromJsonStringConstructor;
|
||||
final bool overrideHashAndEqualsInResultSets;
|
||||
final bool compactQueryMethods;
|
||||
|
||||
MoorOptions(this.generateFromJsonStringConstructor,
|
||||
this.overrideHashAndEqualsInResultSets);
|
||||
this.overrideHashAndEqualsInResultSets, this.compactQueryMethods);
|
||||
|
||||
factory MoorOptions.fromBuilder(Map<String, dynamic> config) {
|
||||
final writeFromString =
|
||||
|
@ -14,10 +15,15 @@ class MoorOptions {
|
|||
final overrideInResultSets =
|
||||
config['override_hash_and_equals_in_result_sets'] as bool ?? false;
|
||||
|
||||
return MoorOptions(writeFromString, overrideInResultSets);
|
||||
final compactQueryMethods =
|
||||
config['compact_query_methods'] as bool ?? false;
|
||||
|
||||
return MoorOptions(
|
||||
writeFromString, overrideInResultSets, compactQueryMethods);
|
||||
}
|
||||
|
||||
const MoorOptions.defaults()
|
||||
: generateFromJsonStringConstructor = false,
|
||||
overrideHashAndEqualsInResultSets = false;
|
||||
overrideHashAndEqualsInResultSets = false,
|
||||
compactQueryMethods = false;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// ignore_for_file: implementation_imports
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/src/dart/analysis/file_state.dart';
|
||||
import 'package:analyzer/src/dart/analysis/driver.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/file_graph.dart';
|
||||
import 'package:moor_generator/src/analyzer/session.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/backend/driver_synchronizer.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/backend/file_tracker.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/backend/plugin_backend.dart';
|
||||
|
||||
|
@ -20,10 +22,10 @@ class MoorDriver implements AnalysisDriverGeneric {
|
|||
/// unsaved files.
|
||||
final FileContentOverlay contentOverlay;
|
||||
final ResourceProvider _resourceProvider;
|
||||
final DriverSynchronizer _synchronizer = DriverSynchronizer();
|
||||
|
||||
/* late final */ MoorSession session;
|
||||
StreamSubscription _fileChangeSubscription;
|
||||
bool _isWorking = false;
|
||||
|
||||
MoorDriver(this._tracker, this._scheduler, this.dartDriver,
|
||||
this.contentOverlay, this._resourceProvider) {
|
||||
|
@ -68,11 +70,9 @@ class MoorDriver implements AnalysisDriverGeneric {
|
|||
|
||||
@override
|
||||
Future<void> performWork() async {
|
||||
if (_isWorking) return;
|
||||
|
||||
_isWorking = true;
|
||||
|
||||
try {
|
||||
if (_synchronizer.hasPausedWork) {
|
||||
await _synchronizer.resumePaused();
|
||||
} else {
|
||||
final mostImportantFile = _tracker.fileWithHighestPriority;
|
||||
if (mostImportantFile.file?.isAnalyzed ?? false) {
|
||||
Logger.root.fine('Blocked attempt to work on fully analyzed file');
|
||||
|
@ -82,15 +82,13 @@ class MoorDriver implements AnalysisDriverGeneric {
|
|||
|
||||
try {
|
||||
final task = session.startTask(backendTask);
|
||||
await task.runTask();
|
||||
await _synchronizer.safeRunTask(task);
|
||||
_tracker.handleTaskCompleted(task);
|
||||
} catch (e, s) {
|
||||
Logger.root.warning(
|
||||
'Error while working on ${mostImportantFile.file.uri}', e, s);
|
||||
_tracker.removePending(mostImportantFile);
|
||||
}
|
||||
} finally {
|
||||
_isWorking = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,6 +102,14 @@ class MoorDriver implements AnalysisDriverGeneric {
|
|||
return file.exists ? file.readAsStringSync() : '';
|
||||
}
|
||||
|
||||
Future<LibraryElement> resolveDart(String path) async {
|
||||
final result = await _synchronizer.useDartDriver(() {
|
||||
return dartDriver.currentSession.getUnitElement(path);
|
||||
});
|
||||
|
||||
return result.element.enclosingElement;
|
||||
}
|
||||
|
||||
bool doesFileExist(String path) {
|
||||
return contentOverlay[path] != null ||
|
||||
_resourceProvider.getFile(path).exists;
|
||||
|
@ -133,6 +139,9 @@ class MoorDriver implements AnalysisDriverGeneric {
|
|||
|
||||
@override
|
||||
AnalysisDriverPriority get workPriority {
|
||||
final overridden = _synchronizer.overriddenPriority;
|
||||
if (overridden != null) return overridden;
|
||||
|
||||
if (_tracker.hasWork) {
|
||||
final mostImportant = _tracker.fileWithHighestPriority;
|
||||
return mostImportant.currentPriority;
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
// ignore: implementation_imports
|
||||
import 'package:analyzer/src/dart/analysis/driver.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/task.dart';
|
||||
import 'driver.dart';
|
||||
|
||||
const _lowestPriority = AnalysisDriverPriority.general;
|
||||
|
||||
/// Analysis in the plugin is performed by two drivers: The [MoorDriver] and the
|
||||
/// builtin [AnalysisDriver] from the analyzer. A [AnalysisDriverScheduler] is
|
||||
/// responsible to make these drivers perform work.
|
||||
///
|
||||
/// We can hit a deadlock in that system when we need to analyze a Dart file as
|
||||
/// part of a moor file, because the flow will look like
|
||||
///
|
||||
/// 1. the scheduler instructs the moor driver to analyze a moor file
|
||||
/// 2. that file contains a Dart import, we wait for the Dart driver to resolve
|
||||
/// that library
|
||||
/// 3. The Dart driver never gets called because the moor driver isn't done yet,
|
||||
/// we hit a deadlock.
|
||||
///
|
||||
/// This class is responsible for resolving those deadlocks by pausing the moor
|
||||
/// driver and and decreasing its priority. This will make the scheduler call
|
||||
/// the dart driver instead and resolve step two. When that's done, we will
|
||||
/// continue our work.
|
||||
class DriverSynchronizer {
|
||||
_WorkUnit _currentUnit;
|
||||
Completer _waitForResume;
|
||||
|
||||
/// Whether analyzing a moor file was paused to resolve a deadlock
|
||||
bool get hasPausedWork => _waitForResume != null;
|
||||
|
||||
/// If this synchronizer [hasPausedWork], returns the changed priority that
|
||||
/// should be reported to the scheduler so that the analysis driver is called
|
||||
/// first. Otherwise returns `null`.
|
||||
AnalysisDriverPriority get overriddenPriority {
|
||||
return hasPausedWork ? _lowestPriority : null;
|
||||
}
|
||||
|
||||
Future<T> useDartDriver<T>(Future<T> Function() action) {
|
||||
assert(_currentUnit != null && _waitForResume == null,
|
||||
'Dart driver can only be used as a part of a non-paused task');
|
||||
_waitForResume = Completer();
|
||||
// make safeRunTask or resume complete, so tha work is delegated to the
|
||||
// dart driver
|
||||
_currentUnit._completeCurrentStep();
|
||||
|
||||
return action().then((value) async {
|
||||
await _waitForResume.future;
|
||||
_waitForResume = null;
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> resumePaused() {
|
||||
assert(hasPausedWork);
|
||||
|
||||
_waitForResume.complete();
|
||||
return _currentUnit._currentCompleter.future;
|
||||
}
|
||||
|
||||
Future<void> safeRunTask(Task task) {
|
||||
assert(!hasPausedWork, "Can't start a new task, another one was paused");
|
||||
_currentUnit = _WorkUnit()..task = task;
|
||||
|
||||
task.runTask().then((_) => _handleTaskCompleted(task));
|
||||
|
||||
return _currentUnit._currentCompleter.future;
|
||||
}
|
||||
|
||||
void _handleTaskCompleted(Task task) {
|
||||
assert(_currentUnit.task == task, 'Finished an unexpected task');
|
||||
_currentUnit._currentCompleter.complete();
|
||||
}
|
||||
}
|
||||
|
||||
class _WorkUnit extends LinkedListEntry<_WorkUnit> {
|
||||
Task task;
|
||||
Completer _currentCompleter = Completer();
|
||||
|
||||
void _completeCurrentStep() {
|
||||
_currentCompleter.complete();
|
||||
_currentCompleter = Completer();
|
||||
}
|
||||
}
|
|
@ -12,10 +12,16 @@ void setupLogger(MoorPlugin plugin) {
|
|||
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((LogRecord rec) {
|
||||
if (rec.level >= Level.INFO) {
|
||||
final isFatal = rec.level > Level.WARNING;
|
||||
if (rec.level >= Level.WARNING) {
|
||||
// when we send analysis errors, some tooling prompts users to create an
|
||||
// issue on the Dart SDK repo for that. We're responsible for the problem
|
||||
// though, so tell the user to not annoy the Dart Team with this.
|
||||
final message = 'PLEASE DO NOT REPORT THIS ON dart-lang/sdk! '
|
||||
'This should be reported via https://github.com/simolus3/moor/issues/new '
|
||||
'instead. Message was ${rec.message}, error ${rec.error}';
|
||||
|
||||
final error =
|
||||
PluginErrorParams(isFatal, rec.message, rec.stackTrace.toString());
|
||||
PluginErrorParams(false, message, rec.stackTrace.toString());
|
||||
|
||||
plugin.channel.sendNotification(error.toNotification());
|
||||
}
|
||||
|
|
|
@ -38,9 +38,10 @@ class PluginTask extends BackendTask {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<LibraryElement> resolveDart(Uri uri) {
|
||||
Future<LibraryElement> resolveDart(Uri uri) async {
|
||||
final path = driver.absolutePath(uri, base: entrypoint);
|
||||
return driver.dartDriver.currentSession.getLibraryByUri(path);
|
||||
|
||||
return await driver.resolveDart(path);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:analyzer_plugin/plugin/assist_mixin.dart';
|
|||
import 'package:analyzer_plugin/plugin/completion_mixin.dart';
|
||||
import 'package:analyzer_plugin/plugin/folding_mixin.dart';
|
||||
import 'package:analyzer_plugin/plugin/highlights_mixin.dart';
|
||||
import 'package:analyzer_plugin/plugin/navigation_mixin.dart';
|
||||
import 'package:analyzer_plugin/plugin/outline_mixin.dart';
|
||||
import 'package:analyzer_plugin/plugin/plugin.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
|
||||
|
@ -12,6 +13,7 @@ import 'package:analyzer_plugin/utilities/assist/assist.dart';
|
|||
import 'package:analyzer_plugin/utilities/completion/completion_core.dart';
|
||||
import 'package:analyzer_plugin/utilities/folding/folding.dart';
|
||||
import 'package:analyzer_plugin/utilities/highlights/highlights.dart';
|
||||
import 'package:analyzer_plugin/utilities/navigation/navigation.dart';
|
||||
import 'package:analyzer_plugin/utilities/outline/outline.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/backend/file_tracker.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/services/assists/assist_service.dart';
|
||||
|
@ -19,6 +21,7 @@ import 'package:moor_generator/src/backends/plugin/services/autocomplete.dart';
|
|||
import 'package:moor_generator/src/backends/plugin/services/errors.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/services/folding.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/services/highlights.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/services/navigation.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/services/outline.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/services/requests.dart';
|
||||
|
||||
|
@ -31,7 +34,8 @@ class MoorPlugin extends ServerPlugin
|
|||
HighlightsMixin,
|
||||
FoldingMixin,
|
||||
CompletionMixin,
|
||||
AssistsMixin {
|
||||
AssistsMixin,
|
||||
NavigationMixin {
|
||||
MoorPlugin(ResourceProvider provider) : super(provider) {
|
||||
setupLogger(this);
|
||||
}
|
||||
|
@ -154,7 +158,23 @@ class MoorPlugin extends ServerPlugin
|
|||
final driver = _moorDriverForPath(path);
|
||||
final file = await driver.waitFileParsed(path);
|
||||
|
||||
return MoorAssistRequest(
|
||||
return MoorRequestAtPosition(
|
||||
file, parameters.length, parameters.offset, resourceProvider);
|
||||
}
|
||||
|
||||
@override
|
||||
List<NavigationContributor> getNavigationContributors(String path) {
|
||||
return const [MoorNavigationContributor()];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<NavigationRequest> getNavigationRequest(
|
||||
plugin.AnalysisGetNavigationParams parameters) async {
|
||||
final path = parameters.file;
|
||||
final driver = _moorDriverForPath(path);
|
||||
final file = await driver.waitFileParsed(path);
|
||||
|
||||
return MoorRequestAtPosition(
|
||||
file, parameters.length, parameters.offset, resourceProvider);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ class AssistService implements AssistContributor {
|
|||
|
||||
@override
|
||||
void computeAssists(AssistRequest request, AssistCollector collector) {
|
||||
final moorRequest = request as MoorAssistRequest;
|
||||
final moorRequest = request as MoorRequestAtPosition;
|
||||
|
||||
if (moorRequest.isMoorAndParsed) {
|
||||
final parseResult = moorRequest.parsedMoor.parseResult;
|
||||
|
|
|
@ -28,7 +28,7 @@ class _FoldingVisitor extends RecursiveVisitor<void> {
|
|||
// construct a folding region for import statements
|
||||
final imports = e.imports.toList();
|
||||
if (imports.length > 1) {
|
||||
final first = imports[1].firstPosition;
|
||||
final first = imports.first.firstPosition;
|
||||
final last = imports.last.lastPosition;
|
||||
|
||||
collector.addRegion(first, last - first, FoldingKind.DIRECTIVES);
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
||||
import 'package:analyzer_plugin/utilities/navigation/navigation.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/services/requests.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/utils/ast_to_location.dart';
|
||||
import 'package:moor_generator/src/backends/plugin/utils/span_utils.dart';
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
class MoorNavigationContributor implements NavigationContributor {
|
||||
const MoorNavigationContributor();
|
||||
|
||||
@override
|
||||
void computeNavigation(
|
||||
NavigationRequest request, NavigationCollector collector) {
|
||||
final moorRequest = request as MoorRequestAtPosition;
|
||||
|
||||
final visitor = _NavigationVisitor(moorRequest, collector);
|
||||
if (moorRequest.file.isParsed) {
|
||||
moorRequest.parsedMoor.parsedFile.accept(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _NavigationVisitor extends RecursiveVisitor<void> {
|
||||
final MoorRequestAtPosition request;
|
||||
final NavigationCollector collector;
|
||||
|
||||
_NavigationVisitor(this.request, this.collector);
|
||||
|
||||
void _reportForSpan(SourceSpan span, ElementKind kind, Location target) {
|
||||
final offset = span.start.offset;
|
||||
final length = span.end.offset - offset;
|
||||
|
||||
// The client only wants the navigation target for a single region, but
|
||||
// we always scan the whole file. Only report if there is an intersection
|
||||
if (intersect(span, request.span)) {
|
||||
collector.addRegion(offset, length, kind, target);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitMoorImportStatement(ImportStatement e) {
|
||||
if (request.isMoorAndParsed) {
|
||||
final moor = request.parsedMoor;
|
||||
final resolved = moor.resolvedImports[e];
|
||||
|
||||
if (resolved != null) {
|
||||
final span = e.importString.span;
|
||||
_reportForSpan(
|
||||
span, ElementKind.FILE, Location(resolved.uri.path, 0, 0, 1, 1));
|
||||
}
|
||||
}
|
||||
|
||||
visitChildren(e);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitReference(Reference e) {
|
||||
if (request.isMoorAndAnalyzed) {
|
||||
final resolved = e.resolved;
|
||||
|
||||
if (resolved is Column) {
|
||||
// if we know the declaration because the file was analyzed - use that
|
||||
final declaration = resolved.meta<ColumnDeclaration>();
|
||||
if (declaration != null) {
|
||||
final location = locationOfDeclaration(declaration);
|
||||
_reportForSpan(e.span, ElementKind.FIELD, location);
|
||||
} else if (declaration is ExpressionColumn) {
|
||||
// expression references don't have an explicit declaration, but they
|
||||
// reference an expression that we can target
|
||||
final expr = (declaration as ExpressionColumn).expression;
|
||||
final target = locationOfNode(request.file, expr);
|
||||
_reportForSpan(e.span, ElementKind.LOCAL_VARIABLE, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitChildren(e);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitQueryable(Queryable e) {
|
||||
if (e is TableReference) {
|
||||
final resolved = e.resolved;
|
||||
|
||||
if (resolved is Table) {
|
||||
final declaration = resolved.meta<TableDeclaration>();
|
||||
_reportForSpan(
|
||||
e.span, ElementKind.CLASS, locationOfDeclaration(declaration));
|
||||
}
|
||||
}
|
||||
|
||||
visitChildren(e);
|
||||
}
|
||||
}
|
|
@ -48,7 +48,9 @@ class _OutlineVisitor extends RecursiveVisitor<void> {
|
|||
|
||||
@override
|
||||
void visitColumnDefinition(ColumnDefinition e) {
|
||||
_startElement(ElementKind.FIELD, e.columnName, e)..returnType = e.typeName;
|
||||
// we use parameters instead of returnType because VS Code doesn't show
|
||||
// the return type but we'd really like it to be shown
|
||||
_startElement(ElementKind.FIELD, e.columnName, e)..parameters = e.typeName;
|
||||
|
||||
super.visitChildren(e);
|
||||
collector.endElement();
|
||||
|
|
|
@ -3,14 +3,18 @@ import 'package:analyzer_plugin/utilities/assist/assist.dart';
|
|||
import 'package:analyzer_plugin/utilities/completion/completion_core.dart';
|
||||
import 'package:analyzer_plugin/utilities/folding/folding.dart';
|
||||
import 'package:analyzer_plugin/utilities/highlights/highlights.dart';
|
||||
import 'package:analyzer_plugin/utilities/navigation/navigation.dart';
|
||||
import 'package:analyzer_plugin/utilities/outline/outline.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/file_graph.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
mixin _MoorBaseRequest {
|
||||
FoundFile get file;
|
||||
|
||||
bool get isMoorAndParsed => file.type == FileType.moor && file.isParsed;
|
||||
bool get isMoor => file.type == FileType.moor;
|
||||
bool get isMoorAndParsed => isMoor && file.isParsed;
|
||||
bool get isMoorAndAnalyzed => isMoor && file.isAnalyzed;
|
||||
|
||||
String get path => file.uri.path;
|
||||
|
||||
|
@ -49,7 +53,9 @@ class MoorCompletionRequest extends CompletionRequest with _MoorBaseRequest {
|
|||
MoorCompletionRequest(this.offset, this.resourceProvider, this.file);
|
||||
}
|
||||
|
||||
class MoorAssistRequest extends AssistRequest with _MoorBaseRequest {
|
||||
class MoorRequestAtPosition
|
||||
with _MoorBaseRequest
|
||||
implements AssistRequest, NavigationRequest {
|
||||
@override
|
||||
final FoundFile file;
|
||||
|
||||
|
@ -62,5 +68,14 @@ class MoorAssistRequest extends AssistRequest with _MoorBaseRequest {
|
|||
@override
|
||||
final ResourceProvider resourceProvider;
|
||||
|
||||
MoorAssistRequest(this.file, this.length, this.offset, this.resourceProvider);
|
||||
SourceSpan get span {
|
||||
return SourceSpan(
|
||||
SourceLocation(offset),
|
||||
SourceLocation(offset + length),
|
||||
'x' * length,
|
||||
);
|
||||
}
|
||||
|
||||
MoorRequestAtPosition(
|
||||
this.file, this.length, this.offset, this.resourceProvider);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/file_graph.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart';
|
||||
import 'package:source_gen/source_gen.dart' show spanForElement;
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
Location locationOfNode(FoundFile file, AstNode node) {
|
||||
if (!node.hasSpan) return null;
|
||||
|
||||
final first = node.first.span.start;
|
||||
final last = node.last.span.end;
|
||||
Location _locationForSpan(SourceSpan span, FoundFile file) {
|
||||
final first = span.start;
|
||||
final last = span.end;
|
||||
|
||||
// in [Location], lines and columns are one-indexed, but in [SourceLocation]
|
||||
// they're 0-based.
|
||||
|
@ -18,3 +19,19 @@ Location locationOfNode(FoundFile file, AstNode node) {
|
|||
first.column + 1,
|
||||
);
|
||||
}
|
||||
|
||||
Location locationOfNode(FoundFile file, AstNode node) {
|
||||
if (!node.hasSpan) return null;
|
||||
return _locationForSpan(node.span, file);
|
||||
}
|
||||
|
||||
Location locationOfDeclaration(BaseDeclaration declaration) {
|
||||
if (declaration.dartDeclaration != null) {
|
||||
final span = spanForElement(declaration.dartDeclaration);
|
||||
return _locationForSpan(span, declaration.declarationFile);
|
||||
} else if (declaration.moorDeclaration != null) {
|
||||
return locationOfNode(
|
||||
declaration.declarationFile, declaration.moorDeclaration);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:source_span/source_span.dart';
|
||||
|
||||
bool intersect(SourceSpan a, SourceSpan b) {
|
||||
final startOfFirst = a.start.offset;
|
||||
final endOfFirst = a.end.offset;
|
||||
final startOfSecond = b.start.offset;
|
||||
final endOfSecond = b.end.offset;
|
||||
|
||||
return !(endOfFirst < startOfSecond || startOfFirst > endOfSecond);
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:built_value/built_value.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart';
|
||||
import 'package:moor_generator/src/model/used_type_converter.dart';
|
||||
|
||||
part 'specified_column.g.dart';
|
||||
|
@ -76,6 +77,10 @@ class SpecifiedColumn {
|
|||
/// and in the generated data class that will be generated for each table.
|
||||
final String dartGetterName;
|
||||
|
||||
/// The declaration of this column, contains information about where this
|
||||
/// column was created in source code.
|
||||
ColumnDeclaration declaration;
|
||||
|
||||
/// The sql type of this column
|
||||
final ColumnType type;
|
||||
|
||||
|
@ -155,7 +160,7 @@ class SpecifiedColumn {
|
|||
/// this column.
|
||||
String get sqlTypeName => sqlTypes[type];
|
||||
|
||||
const SpecifiedColumn({
|
||||
SpecifiedColumn({
|
||||
this.type,
|
||||
this.dartGetterName,
|
||||
this.name,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart';
|
||||
import 'package:moor_generator/src/model/specified_column.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:moor_generator/src/model/used_type_converter.dart';
|
||||
|
@ -10,6 +11,8 @@ class SpecifiedTable {
|
|||
/// the table was inferred from a `CREATE TABLE` statement.
|
||||
final ClassElement fromClass;
|
||||
|
||||
TableDeclaration declaration;
|
||||
|
||||
/// If [fromClass] is null, another source to use when determining the name
|
||||
/// of this table in generated Dart code.
|
||||
final String _overriddenName;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import 'package:analyzer/dart/element/type.dart';
|
||||
|
||||
bool isFromMoor(DartType type) {
|
||||
return type.element.library.location.components.first.contains('moor');
|
||||
return type.element?.library?.location?.components?.first?.contains('moor') ??
|
||||
false;
|
||||
}
|
||||
|
||||
bool isColumn(DartType type) {
|
||||
|
|
|
@ -26,6 +26,9 @@ class QueryWriter {
|
|||
MoorOptions get options => scope.writer.options;
|
||||
StringBuffer _buffer;
|
||||
|
||||
bool get _newSelectableMode =>
|
||||
query.declaredInMoorFile || options.compactQueryMethods;
|
||||
|
||||
final Set<String> _writtenMappingMethods;
|
||||
|
||||
QueryWriter(this.query, this.scope, this._writtenMappingMethods) {
|
||||
|
@ -68,7 +71,7 @@ class QueryWriter {
|
|||
|
||||
_writeSelectStatementCreator();
|
||||
|
||||
if (!query.declaredInMoorFile) {
|
||||
if (!_newSelectableMode) {
|
||||
_writeOneTimeReader();
|
||||
_writeStreamReader();
|
||||
}
|
||||
|
@ -79,7 +82,7 @@ class QueryWriter {
|
|||
}
|
||||
|
||||
String _nameOfCreationMethod() {
|
||||
if (query.declaredInMoorFile) {
|
||||
if (_newSelectableMode) {
|
||||
return query.name;
|
||||
} else {
|
||||
return '${query.name}Query';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: moor_generator
|
||||
description: Dev-dependency to generate table and dataclasses together with the moor package.
|
||||
version: 1.7.1
|
||||
version: 2.0.0
|
||||
repository: https://github.com/simolus3/moor
|
||||
homepage: https://moor.simonbinder.eu/
|
||||
issue_tracker: https://github.com/simolus3/moor/issues
|
||||
|
@ -13,7 +13,7 @@ environment:
|
|||
|
||||
dependencies:
|
||||
analyzer: '>=0.36.4 <0.39.0'
|
||||
analyzer_plugin:
|
||||
analyzer_plugin: ^0.1.0
|
||||
collection: ^1.14.0
|
||||
recase: ^2.0.1
|
||||
built_value: ^6.3.0
|
||||
|
@ -22,12 +22,11 @@ dependencies:
|
|||
build: ^1.1.0
|
||||
logging: '>=0.11.0 <1.0.0'
|
||||
build_config: '>=0.3.1 <1.0.0'
|
||||
moor: ^1.7.1
|
||||
moor: ^2.0.0
|
||||
meta: ^1.1.0
|
||||
sqlparser: ^0.2.0
|
||||
dev_dependencies:
|
||||
test: ^1.6.0
|
||||
test_api: ^0.2.0
|
||||
test_core: ^0.2.0
|
||||
build_runner: ^1.6.7
|
||||
built_value_generator: ^6.3.0
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:moor_generator/src/analyzer/dart/parser.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
||||
|
@ -83,6 +84,8 @@ void main() {
|
|||
table.columns.singleWhere((col) => col.name.name == 'id');
|
||||
|
||||
expect(idColumn.name, equals(ColumnName.implicitly('id')));
|
||||
expect(idColumn.declaration.dartDeclaration,
|
||||
const TypeMatcher<PropertyAccessorElement>());
|
||||
});
|
||||
|
||||
test('should use explicit name, if it exists', () async {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
||||
import 'package:test_api/test_api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final content = '''
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue