Merge branch 'develop' into beta

This commit is contained in:
Simon Binder 2019-10-01 20:39:44 +02:00
commit 20cac9fefd
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
131 changed files with 1956 additions and 501 deletions

View File

@ -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

View File

@ -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`

View File

@ -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

View File

@ -4,3 +4,5 @@ $enable-rounded: false;
$enable-shadows: false;
$secondary: #4CAF50;
$code-color: #dc3545;

View File

@ -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 >}}

View File

@ -29,3 +29,9 @@ At the moment, moor supports these options:
* `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.
* `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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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`
## 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!

View File

@ -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

114
docs/content/en/v2/index.md Normal file
View File

@ -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

85
docs/package-lock.json generated
View File

@ -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": {

View File

@ -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"
}
}

View File

@ -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

View File

@ -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

4
moor/.gitignore vendored
View File

@ -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

View File

@ -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.

View File

@ -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();

View File

@ -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.

View File

@ -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> {

View File

@ -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

View File

@ -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);

View File

@ -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';

View File

@ -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});

View File

@ -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';

View File

@ -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';

View File

@ -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));
});
});
}

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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);

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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 {

View File

@ -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'));
});
}

View File

@ -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';

View File

@ -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(

View File

@ -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';

View File

@ -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);

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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'"));
});
}

View File

@ -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;

View File

@ -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();

View File

@ -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';

View File

@ -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';

View File

@ -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', () {

View File

@ -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);

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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');
}

View File

@ -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');

View File

@ -3,11 +3,10 @@ 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:
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:

View File

@ -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`.

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -0,0 +1,6 @@
targets:
$default:
builders:
moor_generator:
options:
compact_query_methods: true

View File

@ -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

View File

@ -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,
});
}

View File

@ -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"

View File

@ -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

View File

@ -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) {

View File

@ -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';

View File

@ -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) {

View File

@ -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) {

View File

@ -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,
));

View File

@ -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(

View File

@ -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,

View File

@ -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);
}

View File

@ -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 ||

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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());
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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;

View File

@ -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) {

View File

@ -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';

View File

@ -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

View File

@ -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 {

View File

@ -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