Add multi-package example

This commit is contained in:
Simon Binder 2024-01-31 00:26:36 +01:00
parent b58e97f99c
commit af55bd0f92
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
29 changed files with 1130 additions and 0 deletions

View File

@ -26,6 +26,8 @@ drift features:
- The [migration] example makes use of advanced schema migrations and shows how
to test migrations between different database schemas by using drift's
[dedicated tooling][migration tooling] for this purpose.
- There's an example showing how to share drift database definitions between a
[server and a client][multi_package] in different packages.
- [Another example][with_built_value] shows how to use drift-generated code in
other builders (here, `built_value`).
@ -53,3 +55,5 @@ Additional patterns are also shown and explained on this website:
[migration]: https://github.com/simolus3/drift/tree/develop/examples/migrations_example
[migration tooling]: {{ '../Migrations/tests.md#verifying-migrations' | pageUrl }}
[with_built_value]: https://github.com/simolus3/drift/tree/develop/examples/with_built_value
[multi_package]: https://github.com/simolus3/drift/tree/develop/examples/multi_package

View File

@ -7,6 +7,7 @@ This collection of examples demonstrates how to use some advanced drift features
- `migrations_example`: Example showing to how to generate test utilities to verify schema migration.
- `modular`: Example using drift's upcoming modular generation mode.
- `with_built_value`: Configure `build_runner` so that drift-generated classes can be used by `build_runner`.
- `multi_package`: This example shows how to share drift definitions between packages.
These two examples exist to highlight a feature of `package:drift/web.dart` and `package:drift/web/workers.dart`.
However, the setup shown here is now a core drift feature thanks to `WasmDatabase.open`, which means that this

View File

@ -0,0 +1,32 @@
This example shows how to use drift declarations across packages.
It is structured as follows:
- `shared/` contains table definitions. This package does not define a database
on its own (although that could be useful for testing), instead it declares
tables used by the server and the client.
- `server/` is a simple shelf server using Postgres with drift.
- `client/` is a simple CLI client using a local sqlite3 database
while also communicating with the server.
As the main point of this example is to demonstrate how the build
setup could look like, the client and server are kept minimal.
To fully build the code, `build_runner run` needs to be run in all three
packages.
However, after making changes to the database code in one of the packages, only
that package needs to be rebuilt.
## Starting
To run the server, first start a postgres database server:
```
docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres postgres
```
Then, run the example by starting a server and a client:
```
dart run server/bin/server.dart
dart run client/bin/client.dart
```

View File

@ -0,0 +1,3 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

View File

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,33 @@
import 'dart:convert';
import 'package:client/database.dart';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:http/http.dart' as http;
import 'package:shared/tables.dart';
void main(List<String> arguments) async {
final database = ClientDatabase(NativeDatabase.memory());
final client = http.Client();
// Fetch posts from server and save them in the local database.
final fromServer =
await client.get(Uri.parse('http://localhost:8080/posts/latest'));
await database.batch((batch) {
final entries = json.decode(fromServer.body) as List;
for (final entry in entries) {
final post = Post.fromJson(entry['post']);
final user = User.fromJson(entry['author']);
batch.insert(database.posts, post);
batch.insert(database.users, user, onConflict: DoUpdate((old) => user));
}
});
final localPosts = await database.locallySavedPosts;
print('Saved local posts: $localPosts');
await database.close();
}

View File

@ -0,0 +1,25 @@
targets:
$default:
builders:
drift_dev:
# disable drift's default builder, we're using the modular setup
# instead.
enabled: false
# Instead, enable drift_dev:analyzer and drift_dev:modular manually:
drift_dev:analyzer:
enabled: true
options: &options
# Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/
store_date_time_values_as_text: true
named_parameters: true
sql:
dialects:
- sqlite
options:
version: "3.45"
modules: [fts5]
drift_dev:modular:
enabled: true
# We use yaml anchors to give the two builders the same options
options: *options

View File

@ -0,0 +1,17 @@
import 'package:drift/drift.dart';
import 'database.drift.dart';
@DriftDatabase(include: {'package:shared/shared.drift'})
class ClientDatabase extends $ClientDatabase {
ClientDatabase(super.e);
@override
int get schemaVersion => 1;
Future<int> get locallySavedPosts async {
final count = countAll();
final query = selectOnly(posts)..addColumns([count]);
return query.map((row) => row.read(count)!).getSingle();
}
}

View File

@ -0,0 +1,22 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:shared/src/users.drift.dart' as i1;
import 'package:shared/src/posts.drift.dart' as i2;
import 'package:shared/shared.drift.dart' as i3;
import 'package:drift/internal/modular.dart' as i4;
abstract class $ClientDatabase extends i0.GeneratedDatabase {
$ClientDatabase(i0.QueryExecutor e) : super(e);
late final i1.$UsersTable users = i1.$UsersTable(this);
late final i2.Posts posts = i2.Posts(this);
i3.SharedDrift get sharedDrift => i4.ReadDatabaseContainer(this)
.accessor<i3.SharedDrift>(i3.SharedDrift.new);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@override
List<i0.DatabaseSchemaEntity> get allSchemaEntities => [users, posts];
@override
i0.DriftDatabaseOptions get options =>
const i0.DriftDatabaseOptions(storeDateTimeAsText: true);
}

View File

@ -0,0 +1,17 @@
name: client
publish_to: none
environment:
sdk: ^3.2.5
dependencies:
drift: ^2.15.0
http: ^1.2.0
shared:
path: ../shared/
dev_dependencies:
lints: ^2.1.0
test: ^1.24.0
drift_dev: ^2.15.0
build_runner: ^2.4.8

View File

@ -0,0 +1,3 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

View File

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,75 @@
import 'dart:convert';
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift_postgres/drift_postgres.dart';
import 'package:postgres/postgres.dart';
import 'package:server/database.dart';
import 'package:shared/tables.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_router/shelf_router.dart';
// To run this server, first start a local postgres server with
//
// docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres postgres
//
void main(List<String> args) async {
// Use any available host or container IP (usually `0.0.0.0`).
final ip = InternetAddress.anyIPv4;
final database = ServerDatabase(PgDatabase(
endpoint: Endpoint(
host: 'localhost',
database: 'postgres',
username: 'postgres',
password: 'postgres',
),
settings: ConnectionSettings(
// Disable because this example is talking to a local postgres container.
sslMode: SslMode.disable,
),
));
final router = Router()
..post('/post', (Request request) async {
final header = request.headers['Authorization'];
if (header == null || !header.startsWith('Bearer ')) {
return Response.unauthorized('Missing Authorization header');
}
final user =
await database.authenticateUser(header.substring('Bearer '.length));
if (user == null) {
return Response.unauthorized('Invalid token');
}
database.posts.insertOne(PostsCompanion.insert(
author: user.id, content: Value(await request.readAsString())));
return Response(201);
})
..get('/posts/latest', (req) async {
final somePosts = await database.sharedDrift
.allPosts(limit: (_, __) => Limit(10, null))
.get();
return Response.ok(
json.encode([
for (final post in somePosts)
{
'author': post.author,
'post': post.posts,
}
]),
headers: {'Content-Type': 'application/json'},
);
});
// Configure a pipeline that logs requests.
final handler = Pipeline().addMiddleware(logRequests()).addHandler(router);
// For running in containers, we respect the PORT environment variable.
final port = int.parse(Platform.environment['PORT'] ?? '8080');
final server = await serve(handler, ip, port);
print('Server listening on port ${server.port}');
}

View File

@ -0,0 +1,26 @@
targets:
$default:
builders:
drift_dev:
# disable drift's default builder, we're using the modular setup
# instead.
enabled: false
# Instead, enable drift_dev:analyzer and drift_dev:modular manually:
drift_dev:analyzer:
enabled: true
options: &options
# Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/
store_date_time_values_as_text: true
named_parameters: true
sql:
# The server itself will only support postgres
dialects:
- postgres
options:
version: "3.45"
modules: [fts5]
drift_dev:modular:
enabled: true
# We use yaml anchors to give the two builders the same options
options: *options

View File

@ -0,0 +1,43 @@
import 'package:drift/drift.dart';
import 'package:shared/tables.dart';
import 'database.drift.dart';
// Additional table we only need on the server
class ActiveSessions extends Table {
IntColumn get user => integer().references(Users, #id)();
TextColumn get bearerToken => text()();
}
@DriftDatabase(
tables: [ActiveSessions],
include: {'package:shared/shared.drift'},
)
class ServerDatabase extends $ServerDatabase {
ServerDatabase(super.e);
@override
int get schemaVersion => 1;
@override
MigrationStrategy get migration {
return MigrationStrategy(
beforeOpen: (details) async {
if (details.wasCreated) {
await users.insertOne(UsersCompanion.insert(name: 'Demo user'));
await posts.insertOne(
PostsCompanion.insert(author: 1, content: Value('Test post')));
}
},
);
}
Future<User?> authenticateUser(String token) async {
final query = select(users).join(
[innerJoin(activeSessions, activeSessions.user.equalsExp(users.id))]);
query.where(activeSessions.bearerToken.equals(token));
final row = await query.getSingleOrNull();
return row?.readTable(users);
}
}

View File

@ -0,0 +1,222 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:shared/src/users.drift.dart' as i1;
import 'package:shared/src/posts.drift.dart' as i2;
import 'package:server/database.drift.dart' as i3;
import 'package:shared/shared.drift.dart' as i4;
import 'package:drift/internal/modular.dart' as i5;
import 'package:server/database.dart' as i6;
abstract class $ServerDatabase extends i0.GeneratedDatabase {
$ServerDatabase(i0.QueryExecutor e) : super(e);
late final i1.$UsersTable users = i1.$UsersTable(this);
late final i2.Posts posts = i2.Posts(this);
late final i3.$ActiveSessionsTable activeSessions =
i3.$ActiveSessionsTable(this);
i4.SharedDrift get sharedDrift => i5.ReadDatabaseContainer(this)
.accessor<i4.SharedDrift>(i4.SharedDrift.new);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@override
List<i0.DatabaseSchemaEntity> get allSchemaEntities =>
[users, posts, activeSessions];
@override
i0.DriftDatabaseOptions get options =>
const i0.DriftDatabaseOptions(storeDateTimeAsText: true);
}
class $ActiveSessionsTable extends i6.ActiveSessions
with i0.TableInfo<$ActiveSessionsTable, i3.ActiveSession> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$ActiveSessionsTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _userMeta =
const i0.VerificationMeta('user');
@override
late final i0.GeneratedColumn<int> user = i0.GeneratedColumn<int>(
'user', aliasedName, false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
defaultConstraints:
i0.GeneratedColumn.constraintIsAlways('REFERENCES users (id)'));
static const i0.VerificationMeta _bearerTokenMeta =
const i0.VerificationMeta('bearerToken');
@override
late final i0.GeneratedColumn<String> bearerToken =
i0.GeneratedColumn<String>('bearer_token', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
@override
List<i0.GeneratedColumn> get $columns => [user, bearerToken];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'active_sessions';
@override
i0.VerificationContext validateIntegrity(
i0.Insertable<i3.ActiveSession> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('user')) {
context.handle(
_userMeta, user.isAcceptableOrUnknown(data['user']!, _userMeta));
} else if (isInserting) {
context.missing(_userMeta);
}
if (data.containsKey('bearer_token')) {
context.handle(
_bearerTokenMeta,
bearerToken.isAcceptableOrUnknown(
data['bearer_token']!, _bearerTokenMeta));
} else if (isInserting) {
context.missing(_bearerTokenMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => const {};
@override
i3.ActiveSession map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i3.ActiveSession(
user: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}user'])!,
bearerToken: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string, data['${effectivePrefix}bearer_token'])!,
);
}
@override
$ActiveSessionsTable createAlias(String alias) {
return $ActiveSessionsTable(attachedDatabase, alias);
}
}
class ActiveSession extends i0.DataClass
implements i0.Insertable<i3.ActiveSession> {
final int user;
final String bearerToken;
const ActiveSession({required this.user, required this.bearerToken});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['user'] = i0.Variable<int>(user);
map['bearer_token'] = i0.Variable<String>(bearerToken);
return map;
}
i3.ActiveSessionsCompanion toCompanion(bool nullToAbsent) {
return i3.ActiveSessionsCompanion(
user: i0.Value(user),
bearerToken: i0.Value(bearerToken),
);
}
factory ActiveSession.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return ActiveSession(
user: serializer.fromJson<int>(json['user']),
bearerToken: serializer.fromJson<String>(json['bearerToken']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'user': serializer.toJson<int>(user),
'bearerToken': serializer.toJson<String>(bearerToken),
};
}
i3.ActiveSession copyWith({int? user, String? bearerToken}) =>
i3.ActiveSession(
user: user ?? this.user,
bearerToken: bearerToken ?? this.bearerToken,
);
@override
String toString() {
return (StringBuffer('ActiveSession(')
..write('user: $user, ')
..write('bearerToken: $bearerToken')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(user, bearerToken);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i3.ActiveSession &&
other.user == this.user &&
other.bearerToken == this.bearerToken);
}
class ActiveSessionsCompanion extends i0.UpdateCompanion<i3.ActiveSession> {
final i0.Value<int> user;
final i0.Value<String> bearerToken;
final i0.Value<int> rowid;
const ActiveSessionsCompanion({
this.user = const i0.Value.absent(),
this.bearerToken = const i0.Value.absent(),
this.rowid = const i0.Value.absent(),
});
ActiveSessionsCompanion.insert({
required int user,
required String bearerToken,
this.rowid = const i0.Value.absent(),
}) : user = i0.Value(user),
bearerToken = i0.Value(bearerToken);
static i0.Insertable<i3.ActiveSession> custom({
i0.Expression<int>? user,
i0.Expression<String>? bearerToken,
i0.Expression<int>? rowid,
}) {
return i0.RawValuesInsertable({
if (user != null) 'user': user,
if (bearerToken != null) 'bearer_token': bearerToken,
if (rowid != null) 'rowid': rowid,
});
}
i3.ActiveSessionsCompanion copyWith(
{i0.Value<int>? user,
i0.Value<String>? bearerToken,
i0.Value<int>? rowid}) {
return i3.ActiveSessionsCompanion(
user: user ?? this.user,
bearerToken: bearerToken ?? this.bearerToken,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (user.present) {
map['user'] = i0.Variable<int>(user.value);
}
if (bearerToken.present) {
map['bearer_token'] = i0.Variable<String>(bearerToken.value);
}
if (rowid.present) {
map['rowid'] = i0.Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('ActiveSessionsCompanion(')
..write('user: $user, ')
..write('bearerToken: $bearerToken, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}

View File

@ -0,0 +1,23 @@
name: server
publish_to: none
environment:
sdk: ^3.2.5
dependencies:
args: ^2.4.0
drift: ^2.15.0
drift_postgres: ^1.1.0
shelf: ^1.4.0
shelf_router: ^1.1.0
shared:
path: ../shared/
postgres: ^3.0.8
dev_dependencies:
build_runner: ^2.4.8
http: ^1.1.0
lints: ^2.1.0
test: ^1.24.0
drift_dev: ^2.15.0

View File

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

View File

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,28 @@
targets:
$default:
builders:
drift_dev:
# disable drift's default builder, we're using the modular setup
# instead.
enabled: false
# Instead, enable drift_dev:analyzer and drift_dev:modular manually:
drift_dev:analyzer:
enabled: true
options: &options
# Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/
store_date_time_values_as_text: true
named_parameters: true
sql:
# This package will be embedded into a server (using postgres) and a client (using sqlite).
# So we need to generate code for both.
dialects:
- sqlite
- postgres
options:
version: "3.45"
modules: [fts5]
drift_dev:modular:
enabled: true
# We use yaml anchors to give the two builders the same options
options: *options

View File

@ -0,0 +1,11 @@
-- Imports in drift files are transitive, so this is the drift equivalent
-- of exporting files in `src/`.
-- If we only had Dart-defined tables we wanted to expose, a Dart file exporting
-- `users.dart` would have worked as well.
import 'src/posts.drift';
import 'src/users.dart';
allPosts: SELECT posts.**, author.** FROM posts
INNER JOIN users AS author ON author.id = posts.author
LIMIT $limit;

View File

@ -0,0 +1,45 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:drift/internal/modular.dart' as i1;
import 'package:shared/src/posts.drift.dart' as i2;
import 'package:shared/src/users.drift.dart' as i3;
class SharedDrift extends i1.ModularAccessor {
SharedDrift(i0.GeneratedDatabase db) : super(db);
i0.Selectable<AllPostsResult> allPosts({required AllPosts$limit limit}) {
var $arrayStartIndex = 1;
final generatedlimit = $write(
limit(this.posts, alias(this.users, 'author')),
hasMultipleTables: true,
startIndex: $arrayStartIndex);
$arrayStartIndex += generatedlimit.amountOfVariables;
return customSelect(
'SELECT"posts"."author" AS "nested_0.author", "posts"."content" AS "nested_0.content","author"."id" AS "nested_1.id", "author"."name" AS "nested_1.name" FROM posts INNER JOIN users AS author ON author.id = posts.author ${generatedlimit.sql}',
variables: [
...generatedlimit.introducedVariables
],
readsFrom: {
posts,
users,
...generatedlimit.watchedTables,
}).asyncMap((i0.QueryRow row) async => AllPostsResult(
posts: await posts.mapFromRow(row, tablePrefix: 'nested_0'),
author: await users.mapFromRow(row, tablePrefix: 'nested_1'),
));
}
i2.Posts get posts => this.resultSet<i2.Posts>('posts');
i3.$UsersTable get users => this.resultSet<i3.$UsersTable>('users');
}
class AllPostsResult {
final i2.Post posts;
final i3.User author;
AllPostsResult({
required this.posts,
required this.author,
});
}
typedef AllPosts$limit = i0.Limit Function(
i2.Posts posts, i3.$UsersTable author);

View File

@ -0,0 +1,6 @@
import 'users.dart';
CREATE TABLE posts (
author INTEGER NOT NULL REFERENCES users (id),
content TEXT
);

View File

@ -0,0 +1,197 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:shared/src/posts.drift.dart' as i1;
class Posts extends i0.Table with i0.TableInfo<Posts, i1.Post> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
Posts(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _authorMeta =
const i0.VerificationMeta('author');
late final i0.GeneratedColumn<int> author = i0.GeneratedColumn<int>(
'author', aliasedName, false,
type: i0.DriftSqlType.int,
requiredDuringInsert: true,
$customConstraints: 'NOT NULL REFERENCES users(id)');
static const i0.VerificationMeta _contentMeta =
const i0.VerificationMeta('content');
late final i0.GeneratedColumn<String> content = i0.GeneratedColumn<String>(
'content', aliasedName, true,
type: i0.DriftSqlType.string,
requiredDuringInsert: false,
$customConstraints: '');
@override
List<i0.GeneratedColumn> get $columns => [author, content];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'posts';
@override
i0.VerificationContext validateIntegrity(i0.Insertable<i1.Post> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('author')) {
context.handle(_authorMeta,
author.isAcceptableOrUnknown(data['author']!, _authorMeta));
} else if (isInserting) {
context.missing(_authorMeta);
}
if (data.containsKey('content')) {
context.handle(_contentMeta,
content.isAcceptableOrUnknown(data['content']!, _contentMeta));
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => const {};
@override
i1.Post map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.Post(
author: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}author'])!,
content: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}content']),
);
}
@override
Posts createAlias(String alias) {
return Posts(attachedDatabase, alias);
}
@override
bool get dontWriteConstraints => true;
}
class Post extends i0.DataClass implements i0.Insertable<i1.Post> {
final int author;
final String? content;
const Post({required this.author, this.content});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['author'] = i0.Variable<int>(author);
if (!nullToAbsent || content != null) {
map['content'] = i0.Variable<String>(content);
}
return map;
}
i1.PostsCompanion toCompanion(bool nullToAbsent) {
return i1.PostsCompanion(
author: i0.Value(author),
content: content == null && nullToAbsent
? const i0.Value.absent()
: i0.Value(content),
);
}
factory Post.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return Post(
author: serializer.fromJson<int>(json['author']),
content: serializer.fromJson<String?>(json['content']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'author': serializer.toJson<int>(author),
'content': serializer.toJson<String?>(content),
};
}
i1.Post copyWith(
{int? author, i0.Value<String?> content = const i0.Value.absent()}) =>
i1.Post(
author: author ?? this.author,
content: content.present ? content.value : this.content,
);
@override
String toString() {
return (StringBuffer('Post(')
..write('author: $author, ')
..write('content: $content')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(author, content);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.Post &&
other.author == this.author &&
other.content == this.content);
}
class PostsCompanion extends i0.UpdateCompanion<i1.Post> {
final i0.Value<int> author;
final i0.Value<String?> content;
final i0.Value<int> rowid;
const PostsCompanion({
this.author = const i0.Value.absent(),
this.content = const i0.Value.absent(),
this.rowid = const i0.Value.absent(),
});
PostsCompanion.insert({
required int author,
this.content = const i0.Value.absent(),
this.rowid = const i0.Value.absent(),
}) : author = i0.Value(author);
static i0.Insertable<i1.Post> custom({
i0.Expression<int>? author,
i0.Expression<String>? content,
i0.Expression<int>? rowid,
}) {
return i0.RawValuesInsertable({
if (author != null) 'author': author,
if (content != null) 'content': content,
if (rowid != null) 'rowid': rowid,
});
}
i1.PostsCompanion copyWith(
{i0.Value<int>? author,
i0.Value<String?>? content,
i0.Value<int>? rowid}) {
return i1.PostsCompanion(
author: author ?? this.author,
content: content ?? this.content,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (author.present) {
map['author'] = i0.Variable<int>(author.value);
}
if (content.present) {
map['content'] = i0.Variable<String>(content.value);
}
if (rowid.present) {
map['rowid'] = i0.Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('PostsCompanion(')
..write('author: $author, ')
..write('content: $content, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}

View File

@ -0,0 +1,6 @@
import 'package:drift/drift.dart';
class Users extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text()();
}

View File

@ -0,0 +1,174 @@
// ignore_for_file: type=lint
import 'package:drift/drift.dart' as i0;
import 'package:shared/src/users.drift.dart' as i1;
import 'package:shared/src/users.dart' as i2;
class $UsersTable extends i2.Users with i0.TableInfo<$UsersTable, i1.User> {
@override
final i0.GeneratedDatabase attachedDatabase;
final String? _alias;
$UsersTable(this.attachedDatabase, [this._alias]);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints:
i0.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
static const i0.VerificationMeta _nameMeta =
const i0.VerificationMeta('name');
@override
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
'name', aliasedName, false,
type: i0.DriftSqlType.string, requiredDuringInsert: true);
@override
List<i0.GeneratedColumn> get $columns => [id, name];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'users';
@override
i0.VerificationContext validateIntegrity(i0.Insertable<i1.User> instance,
{bool isInserting = false}) {
final context = i0.VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('name')) {
context.handle(
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
} else if (isInserting) {
context.missing(_nameMeta);
}
return context;
}
@override
Set<i0.GeneratedColumn> get $primaryKey => {id};
@override
i1.User map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return i1.User(
id: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!,
name: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
);
}
@override
$UsersTable createAlias(String alias) {
return $UsersTable(attachedDatabase, alias);
}
}
class User extends i0.DataClass implements i0.Insertable<i1.User> {
final int id;
final String name;
const User({required this.id, required this.name});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
map['id'] = i0.Variable<int>(id);
map['name'] = i0.Variable<String>(name);
return map;
}
i1.UsersCompanion toCompanion(bool nullToAbsent) {
return i1.UsersCompanion(
id: i0.Value(id),
name: i0.Value(name),
);
}
factory User.fromJson(Map<String, dynamic> json,
{i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return User(
id: serializer.fromJson<int>(json['id']),
name: serializer.fromJson<String>(json['name']),
);
}
@override
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'name': serializer.toJson<String>(name),
};
}
i1.User copyWith({int? id, String? name}) => i1.User(
id: id ?? this.id,
name: name ?? this.name,
);
@override
String toString() {
return (StringBuffer('User(')
..write('id: $id, ')
..write('name: $name')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, name);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is i1.User && other.id == this.id && other.name == this.name);
}
class UsersCompanion extends i0.UpdateCompanion<i1.User> {
final i0.Value<int> id;
final i0.Value<String> name;
const UsersCompanion({
this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(),
});
UsersCompanion.insert({
this.id = const i0.Value.absent(),
required String name,
}) : name = i0.Value(name);
static i0.Insertable<i1.User> custom({
i0.Expression<int>? id,
i0.Expression<String>? name,
}) {
return i0.RawValuesInsertable({
if (id != null) 'id': id,
if (name != null) 'name': name,
});
}
i1.UsersCompanion copyWith({i0.Value<int>? id, i0.Value<String>? name}) {
return i1.UsersCompanion(
id: id ?? this.id,
name: name ?? this.name,
);
}
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
if (id.present) {
map['id'] = i0.Variable<int>(id.value);
}
if (name.present) {
map['name'] = i0.Variable<String>(name.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('UsersCompanion(')
..write('id: $id, ')
..write('name: $name')
..write(')'))
.toString();
}
}

View File

@ -0,0 +1,3 @@
export 'src/users.dart';
export 'src/users.drift.dart';
export 'src/posts.drift.dart';

View File

@ -0,0 +1,16 @@
name: shared
publish_to: none
environment:
sdk: ^3.2.5
# Add regular dependencies here.
dependencies:
drift: ^2.15.0
# it is important that drift_dev is a regular dependency here.
drift_dev: ^2.15.0
dev_dependencies:
lints: ^2.1.0
test: ^1.24.0
build_runner: ^2.4.8

View File

@ -8,6 +8,7 @@ packages:
- drift_dev
- sqlparser
- examples/*
- examples/multi_package/*
- extras/benchmarks
- extras/drift_devtools_extension
- extras/drift_mariadb