Thanks for taking the time to contribute to drift!
Feel free to post any questions, bug reports or feature requests by creating an issue. In any case, taking the time to provide some context on
- what you were trying to do
- what you would have expected to happen
- what actually happened
most certainly helps to resolve the issue quickly.
All kinds of pull requests are absolutely appreciated! Before working on bigger changes, it can be helpful to create an issue describing your plans to help coordination.
When working on drift, its recommended to fork the develop
branch and also target that
branch for PRs. When possible, we only use the latest_release
branch to reflect the state
that's been released to pub.
If you have any question about drift internals that you feel are not explained well enough, you're most welcome to create an issue or chat via gitter.
The project is divided into multiple modules:
drift/
: Contains common APIs that will run on all platforms.backends
: Common helper classes to make implementing backends easier. The idea is that a backend only needs to know how to run prepared statements. This lets us port the library to different database libraries without much trouble.web.dart
: Experimental web implementation, built withsql.js
.native.dart
: FFI-based implementation around thesqlite3
package.- This is the biggest package, see the concepts section below on how drift works and what it contains.
moor_flutter/
: Contains a Flutter implementation for the database.drift_dev/
: Creates table, database and dao classes from the table structure and compiled queries.sqlparser/
: Contains an SQL parser and analyzer that is mostly independent of drift, but used by the generator for compiled custom queries.
For each user-defined class that inherits from Table
and appears in a @UseMoor
or @UseDao
annotation,
we generate three classes:
- A class that inherits from
TableInfo
(we call this the "table class"). It contains a structural representation of the table, which includes columns (including name, type, constraints...), the primary key and so on. The idea is that, if we have aTableInfo
instance, we can create all kinds of SQL statements. - A class to represent a fully loaded row of a table. We call this a "data class" and it inherits from
DataClass
. - A class to represent partial data (e.g. for inserts or updates, where not all columns are set). This class was introduced in moor 1.5 and is called a "companion".
This approach lets us write a higher-level api that uses the generated TableInfo
classes to know what columns to
write. For instance, the Migrator
can write CREATE TABLE
statements from these classes, an UpdateStatement
will
write UPDATE
statements and so on. To write the query, we construct a GenerationContext
, which contains a string
buffer to write the query, keeps track of the introduced variables and so on. The idea is that everything that can
appear anywhere in an SQL statement inherits from Component
(for instance, Query
, Expression
, Variable
, Where
,
OrderBy
). We can then recursively create the query by calling Component.writeInto
for all subparts of a component.
This query is then sent to a QueryExecutor
, which is responsible for executing it and returning its result. The
QueryExecutor
is the only part that is platform specific, everything else is pure Dart that doesn't import any
restricted libraries.
A DatabaseConnectionUser
is the central piece of a drift database instance. It contains an SqlTypeSystem
(responsible
for mapping simple Dart objects from and to SQL), the QueryExecutor
discussed above and a StreamQueryStore
(responsible for keeping active queries and re-running them when a table updates). It is also the super class of
GeneratedDatabase
and DatabaseAccessor
, which are the classes a @UseMoor
and @UseDao
class inherits from.
Finally, the QueryEngine
is a mixin in DatabaseConnectionUser
that provides the select
, update
, delete
methods
used to construct common queries.
We have an analyzer plugin to support IDE features like auto-complete, navigation, syntax highlighting, outline and folding to users. Normally, analyzer plugins are discovered and loaded by the analysis server, which makes them very annoying to debug.
However, we found a way to run the plugin in isolation, which makes debugging much 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:
- In
drift/tools/analyzer_plugin/bin/plugin.dart
, setuseDebuggingVariant
to true. - Run
drift_dev/tool/debug_plugin.dart
as a regular Dart VM app (this can be debugged when started from an IDE). - (optional) Make sure the analysis server picks up the updated version of the analysis
plugin by deleting the
~/.dartServer/.plugin_manager
folder. - Open a project that uses the plugin, for instance via
code extras/plugin_example
.
More details are available under extras/plugin_example/README.md
.
To debug the builder, run pub run build_runner generate-build-script
in the drift
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.
Minor changes will be published directly, no special steps are necessary. For major updates that span multiple versions, we should follow these steps
- Changelogs: The changelog of
drift_dev
should only mention changes to the generator, most changes shuold be indrift/CHANGELOG.md
. Generator changes should also be copied into that file. - Make sure each package has the correct dependencies:
drift_dev
version1.x
should depend ondrift
1.x
as well to ensure users will alwayspub get
drift packages that are compatible with each other. - Comment out the
dependency_overrides
section indrift
,drift/tool/analyzer_plugin
,moor_flutter
,drift_dev
andsqlparser
. Make sure thatuseDebuggingVariant
is false in the analyzer plugin. - Create an annotated tag and a GitHub release for each package published.
drift
anddrift_dev
can be merged into a single GitHub release. - Publish packages in this order to avoid scoring penalties caused by versions not existing:
drift
drift_dev
- (optional)
moor_flutter
The sqlparser
library can be published independently of drift.
We use build_runner
to build the documentation. The readme contains everything
you need to know go get started.