mirror of https://github.com/AMT-Cheif/drift.git
Support custom names for drift views
This commit is contained in:
parent
78a05fdf0b
commit
29303100b8
|
@ -0,0 +1,17 @@
|
||||||
|
// #docregion user
|
||||||
|
class User {
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
User(this.id, this.name);
|
||||||
|
}
|
||||||
|
// #enddocregion user
|
||||||
|
|
||||||
|
// #docregion userwithfriends
|
||||||
|
class UserWithFriends {
|
||||||
|
final User user;
|
||||||
|
final List<User> friends;
|
||||||
|
|
||||||
|
UserWithFriends(this.user, {this.friends = const []});
|
||||||
|
}
|
||||||
|
// #enddocregion userwithfriends
|
|
@ -0,0 +1,22 @@
|
||||||
|
-- #docregion users
|
||||||
|
import 'row_class.dart'; --import for where the row class is defined
|
||||||
|
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL
|
||||||
|
) WITH User; -- This tells drift to use the existing Dart class
|
||||||
|
-- #enddocregion users
|
||||||
|
|
||||||
|
-- #docregion friends
|
||||||
|
-- table to demonstrate a more complex select query below.
|
||||||
|
-- also, remember to add the import for `UserWithFriends` to your drift file.
|
||||||
|
CREATE TABLE friends (
|
||||||
|
user_a INTEGER NOT NULL REFERENCES users(id),
|
||||||
|
user_b INTEGER NOT NULL REFERENCES users(id),
|
||||||
|
PRIMARY KEY (user_a, user_b)
|
||||||
|
);
|
||||||
|
|
||||||
|
allFriendsOf WITH UserWithFriends: SELECT users.** AS user, LIST(
|
||||||
|
SELECT * FROM users a INNER JOIN friends ON user_a = a.id WHERE user_b = users.id OR user_a = users.id
|
||||||
|
) AS friends FROM users WHERE id = :id;
|
||||||
|
-- #enddocregion friends
|
|
@ -0,0 +1,346 @@
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
import 'package:drift/drift.dart' as i0;
|
||||||
|
import 'package:drift_docs/snippets/modular/drift/row_class.dart' as i1;
|
||||||
|
import 'package:drift_docs/snippets/modular/drift/with_existing.drift.dart'
|
||||||
|
as i2;
|
||||||
|
import 'package:drift/internal/modular.dart' as i3;
|
||||||
|
|
||||||
|
class Users extends i0.Table with i0.TableInfo<Users, i1.User> {
|
||||||
|
@override
|
||||||
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
|
final String? _alias;
|
||||||
|
Users(this.attachedDatabase, [this._alias]);
|
||||||
|
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
|
||||||
|
late final i0.GeneratedColumn<int> id = i0.GeneratedColumn<int>(
|
||||||
|
'id', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.int,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
$customConstraints: 'NOT NULL PRIMARY KEY');
|
||||||
|
static const i0.VerificationMeta _nameMeta =
|
||||||
|
const i0.VerificationMeta('name');
|
||||||
|
late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
|
||||||
|
'name', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
$customConstraints: 'NOT NULL');
|
||||||
|
@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(
|
||||||
|
attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!,
|
||||||
|
attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Users createAlias(String alias) {
|
||||||
|
return Users(attachedDatabase, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get dontWriteConstraints => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
i2.UsersCompanion copyWith({i0.Value<int>? id, i0.Value<String>? name}) {
|
||||||
|
return i2.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Friends extends i0.Table with i0.TableInfo<Friends, i2.Friend> {
|
||||||
|
@override
|
||||||
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
|
final String? _alias;
|
||||||
|
Friends(this.attachedDatabase, [this._alias]);
|
||||||
|
static const i0.VerificationMeta _userAMeta =
|
||||||
|
const i0.VerificationMeta('userA');
|
||||||
|
late final i0.GeneratedColumn<int> userA = i0.GeneratedColumn<int>(
|
||||||
|
'user_a', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.int,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
$customConstraints: 'NOT NULL REFERENCES users(id)');
|
||||||
|
static const i0.VerificationMeta _userBMeta =
|
||||||
|
const i0.VerificationMeta('userB');
|
||||||
|
late final i0.GeneratedColumn<int> userB = i0.GeneratedColumn<int>(
|
||||||
|
'user_b', aliasedName, false,
|
||||||
|
type: i0.DriftSqlType.int,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
$customConstraints: 'NOT NULL REFERENCES users(id)');
|
||||||
|
@override
|
||||||
|
List<i0.GeneratedColumn> get $columns => [userA, userB];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
|
@override
|
||||||
|
String get actualTableName => $name;
|
||||||
|
static const String $name = 'friends';
|
||||||
|
@override
|
||||||
|
i0.VerificationContext validateIntegrity(i0.Insertable<i2.Friend> instance,
|
||||||
|
{bool isInserting = false}) {
|
||||||
|
final context = i0.VerificationContext();
|
||||||
|
final data = instance.toColumns(true);
|
||||||
|
if (data.containsKey('user_a')) {
|
||||||
|
context.handle(
|
||||||
|
_userAMeta, userA.isAcceptableOrUnknown(data['user_a']!, _userAMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_userAMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('user_b')) {
|
||||||
|
context.handle(
|
||||||
|
_userBMeta, userB.isAcceptableOrUnknown(data['user_b']!, _userBMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_userBMeta);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<i0.GeneratedColumn> get $primaryKey => {userA, userB};
|
||||||
|
@override
|
||||||
|
i2.Friend map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
|
return i2.Friend(
|
||||||
|
userA: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.int, data['${effectivePrefix}user_a'])!,
|
||||||
|
userB: attachedDatabase.typeMapping
|
||||||
|
.read(i0.DriftSqlType.int, data['${effectivePrefix}user_b'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Friends createAlias(String alias) {
|
||||||
|
return Friends(attachedDatabase, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get customConstraints => const ['PRIMARY KEY(user_a, user_b)'];
|
||||||
|
@override
|
||||||
|
bool get dontWriteConstraints => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Friend extends i0.DataClass implements i0.Insertable<i2.Friend> {
|
||||||
|
final int userA;
|
||||||
|
final int userB;
|
||||||
|
const Friend({required this.userA, required this.userB});
|
||||||
|
@override
|
||||||
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, i0.Expression>{};
|
||||||
|
map['user_a'] = i0.Variable<int>(userA);
|
||||||
|
map['user_b'] = i0.Variable<int>(userB);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2.FriendsCompanion toCompanion(bool nullToAbsent) {
|
||||||
|
return i2.FriendsCompanion(
|
||||||
|
userA: i0.Value(userA),
|
||||||
|
userB: i0.Value(userB),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory Friend.fromJson(Map<String, dynamic> json,
|
||||||
|
{i0.ValueSerializer? serializer}) {
|
||||||
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
|
return Friend(
|
||||||
|
userA: serializer.fromJson<int>(json['user_a']),
|
||||||
|
userB: serializer.fromJson<int>(json['user_b']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||||
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'user_a': serializer.toJson<int>(userA),
|
||||||
|
'user_b': serializer.toJson<int>(userB),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
i2.Friend copyWith({int? userA, int? userB}) => i2.Friend(
|
||||||
|
userA: userA ?? this.userA,
|
||||||
|
userB: userB ?? this.userB,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('Friend(')
|
||||||
|
..write('userA: $userA, ')
|
||||||
|
..write('userB: $userB')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(userA, userB);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is i2.Friend &&
|
||||||
|
other.userA == this.userA &&
|
||||||
|
other.userB == this.userB);
|
||||||
|
}
|
||||||
|
|
||||||
|
class FriendsCompanion extends i0.UpdateCompanion<i2.Friend> {
|
||||||
|
final i0.Value<int> userA;
|
||||||
|
final i0.Value<int> userB;
|
||||||
|
final i0.Value<int> rowid;
|
||||||
|
const FriendsCompanion({
|
||||||
|
this.userA = const i0.Value.absent(),
|
||||||
|
this.userB = const i0.Value.absent(),
|
||||||
|
this.rowid = const i0.Value.absent(),
|
||||||
|
});
|
||||||
|
FriendsCompanion.insert({
|
||||||
|
required int userA,
|
||||||
|
required int userB,
|
||||||
|
this.rowid = const i0.Value.absent(),
|
||||||
|
}) : userA = i0.Value(userA),
|
||||||
|
userB = i0.Value(userB);
|
||||||
|
static i0.Insertable<i2.Friend> custom({
|
||||||
|
i0.Expression<int>? userA,
|
||||||
|
i0.Expression<int>? userB,
|
||||||
|
i0.Expression<int>? rowid,
|
||||||
|
}) {
|
||||||
|
return i0.RawValuesInsertable({
|
||||||
|
if (userA != null) 'user_a': userA,
|
||||||
|
if (userB != null) 'user_b': userB,
|
||||||
|
if (rowid != null) 'rowid': rowid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
i2.FriendsCompanion copyWith(
|
||||||
|
{i0.Value<int>? userA, i0.Value<int>? userB, i0.Value<int>? rowid}) {
|
||||||
|
return i2.FriendsCompanion(
|
||||||
|
userA: userA ?? this.userA,
|
||||||
|
userB: userB ?? this.userB,
|
||||||
|
rowid: rowid ?? this.rowid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, i0.Expression>{};
|
||||||
|
if (userA.present) {
|
||||||
|
map['user_a'] = i0.Variable<int>(userA.value);
|
||||||
|
}
|
||||||
|
if (userB.present) {
|
||||||
|
map['user_b'] = i0.Variable<int>(userB.value);
|
||||||
|
}
|
||||||
|
if (rowid.present) {
|
||||||
|
map['rowid'] = i0.Variable<int>(rowid.value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('FriendsCompanion(')
|
||||||
|
..write('userA: $userA, ')
|
||||||
|
..write('userB: $userB, ')
|
||||||
|
..write('rowid: $rowid')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WithExistingDrift extends i3.ModularAccessor {
|
||||||
|
WithExistingDrift(i0.GeneratedDatabase db) : super(db);
|
||||||
|
i0.Selectable<i1.UserWithFriends> allFriendsOf(int id) {
|
||||||
|
return customSelect(
|
||||||
|
'SELECT"users"."id" AS "nested_0.id", "users"."name" AS "nested_0.name", users.id AS "\$n_0", users.id AS "\$n_1" FROM users WHERE id = ?1',
|
||||||
|
variables: [
|
||||||
|
i0.Variable<int>(id)
|
||||||
|
],
|
||||||
|
readsFrom: {
|
||||||
|
users,
|
||||||
|
friends,
|
||||||
|
}).asyncMap((i0.QueryRow row) async => i1.UserWithFriends(
|
||||||
|
await users.mapFromRow(row, tablePrefix: 'nested_0'),
|
||||||
|
friends: await customSelect(
|
||||||
|
'SELECT * FROM users AS a INNER JOIN friends ON user_a = a.id WHERE user_b = ?1 OR user_a = ?2',
|
||||||
|
variables: [
|
||||||
|
i0.Variable<int>(row.read('\$n_0')),
|
||||||
|
i0.Variable<int>(row.read('\$n_1'))
|
||||||
|
],
|
||||||
|
readsFrom: {
|
||||||
|
users,
|
||||||
|
friends,
|
||||||
|
})
|
||||||
|
.map((i0.QueryRow row) => i1.User(
|
||||||
|
row.read<int>('id'),
|
||||||
|
row.read<String>('name'),
|
||||||
|
))
|
||||||
|
.get(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
i2.Users get users => this.resultSet<i2.Users>('users');
|
||||||
|
i2.Friends get friends => this.resultSet<i2.Friends>('friends');
|
||||||
|
}
|
|
@ -167,6 +167,8 @@ statement before it runs it.
|
||||||
a defined query by appending `WITH YourDartClass` to a `CREATE TABLE` statement.
|
a defined query by appending `WITH YourDartClass` to a `CREATE TABLE` statement.
|
||||||
- Alternatively, you may use `AS DesiredRowClassName` to change the name of the
|
- Alternatively, you may use `AS DesiredRowClassName` to change the name of the
|
||||||
row class generated by drift.
|
row class generated by drift.
|
||||||
|
- Both custom row classes and custom table names also work for views, e.g. with
|
||||||
|
`CREATE VIEW my_view AS DartName AS SELECT ...;`.
|
||||||
- In a column definition, `MAPPED BY` can be used to [apply a converter](#type-converters)
|
- In a column definition, `MAPPED BY` can be used to [apply a converter](#type-converters)
|
||||||
to that column.
|
to that column.
|
||||||
- Similarly, a `JSON KEY` constraint can be used to define the key drift will
|
- Similarly, a `JSON KEY` constraint can be used to define the key drift will
|
||||||
|
@ -356,28 +358,17 @@ With that option, the variable will be inferred to `Preferences` instead of `Str
|
||||||
|
|
||||||
### Existing row classes
|
### Existing row classes
|
||||||
|
|
||||||
|
{% assign existingDrift = "package:drift_docs/snippets/modular/drift/with_existing.drift.excerpt.json" | readString | json_decode %}
|
||||||
|
{% assign rowClassDart = "package:drift_docs/snippets/modular/drift/row_class.dart.excerpt.json" | readString | json_decode %}
|
||||||
|
|
||||||
You can use custom row classes instead of having drift generate one for you.
|
You can use custom row classes instead of having drift generate one for you.
|
||||||
For instance, let's say you had a Dart class defined as
|
For instance, let's say you had a Dart class defined as
|
||||||
|
|
||||||
```dart
|
{% include "blocks/snippet" snippets = rowClassDart name = "user" %}
|
||||||
class User {
|
|
||||||
final int id;
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
User(this.id, this.name);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, you can instruct drift to use that class as a row class as follows:
|
Then, you can instruct drift to use that class as a row class as follows:
|
||||||
|
|
||||||
```sql
|
{% include "blocks/snippet" snippets = existingDrift name = "users" %}
|
||||||
import 'row_class.dart'; --import for where the row class is defined
|
|
||||||
|
|
||||||
CREATE TABLE users (
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
) WITH User; -- This tells drift to use the existing Dart class
|
|
||||||
```
|
|
||||||
|
|
||||||
When using custom row classes defined in another Dart file, you also need to import that file into the file where you define
|
When using custom row classes defined in another Dart file, you also need to import that file into the file where you define
|
||||||
the database.
|
the database.
|
||||||
|
@ -388,32 +379,11 @@ can be added after the name of the query.
|
||||||
|
|
||||||
For instance, let's say we expand the existing Dart code in `row_class.dart` by adding another class:
|
For instance, let's say we expand the existing Dart code in `row_class.dart` by adding another class:
|
||||||
|
|
||||||
```dart
|
{% include "blocks/snippet" snippets = rowClassDart name = "userwithfriends" %}
|
||||||
class UserWithFriends {
|
|
||||||
final User user;
|
|
||||||
final List<User> friends;
|
|
||||||
|
|
||||||
UserWithFriends(this.user, {this.friends = const []});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, we can add a corresponding query using the new class for its rows:
|
Now, we can add a corresponding query using the new class for its rows:
|
||||||
|
|
||||||
```sql
|
{% include "blocks/snippet" snippets = existingDrift name = "friends" %}
|
||||||
-- table to demonstrate a more complex select query below.
|
|
||||||
-- also, remember to add the import for `UserWithFriends` to your drift file.
|
|
||||||
CREATE TABLE friends (
|
|
||||||
user_a INTEGER NOT NULL REFERENCES users(id),
|
|
||||||
user_b INTEGER NOT NULL REFERENCES users(id),
|
|
||||||
PRIMARY KEY (user_a, user_b)
|
|
||||||
);
|
|
||||||
|
|
||||||
allFriendsOf WITH UserWithFriends: SELECT users.**, LIST(
|
|
||||||
SELECT * FROM users a INNER JOIN friends ON user_a = a.id WHERE user_b = users.id
|
|
||||||
UNION ALL
|
|
||||||
SELECT * FROM users b INNER JOIN friends ON user_b = b.id WHERE user_a = users.id
|
|
||||||
) AS friends FROM users WHERE id = :id;
|
|
||||||
```
|
|
||||||
|
|
||||||
The `WITH UserWithFriends` syntax will make drift consider the `UserWithFriends` class.
|
The `WITH UserWithFriends` syntax will make drift consider the `UserWithFriends` class.
|
||||||
For every field in the constructor, drift will check the column from the query and
|
For every field in the constructor, drift will check the column from the query and
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
## 3.35.0-dev
|
||||||
|
|
||||||
|
- Drift extensions: Allow custom class names for `CREATE VIEW` statements.
|
||||||
|
|
||||||
## 0.34.1
|
## 0.34.1
|
||||||
|
|
||||||
- Allow selecting from virtual tables using the table-valued function
|
- Allow selecting from virtual tables using the table-valued function
|
||||||
|
|
|
@ -2294,27 +2294,30 @@ class Parser {
|
||||||
supportAs ? const [TokenType.as, TokenType.$with] : [TokenType.$with];
|
supportAs ? const [TokenType.as, TokenType.$with] : [TokenType.$with];
|
||||||
|
|
||||||
if (enableDriftExtensions && (_match(types))) {
|
if (enableDriftExtensions && (_match(types))) {
|
||||||
final first = _previous;
|
return _startedDriftTableName(_previous);
|
||||||
final useExisting = _previous.type == TokenType.$with;
|
|
||||||
final name =
|
|
||||||
_consumeIdentifier('Expected the name for the data class').identifier;
|
|
||||||
String? constructorName;
|
|
||||||
|
|
||||||
if (_matchOne(TokenType.dot)) {
|
|
||||||
constructorName = _consumeIdentifier(
|
|
||||||
'Expected name of the constructor to use after the dot')
|
|
||||||
.identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DriftTableName(
|
|
||||||
useExistingDartClass: useExisting,
|
|
||||||
overriddenDataClassName: name,
|
|
||||||
constructorName: constructorName,
|
|
||||||
)..setSpan(first, _previous);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DriftTableName _startedDriftTableName(Token first) {
|
||||||
|
final useExisting = _previous.type == TokenType.$with;
|
||||||
|
final name =
|
||||||
|
_consumeIdentifier('Expected the name for the data class').identifier;
|
||||||
|
String? constructorName;
|
||||||
|
|
||||||
|
if (_matchOne(TokenType.dot)) {
|
||||||
|
constructorName = _consumeIdentifier(
|
||||||
|
'Expected name of the constructor to use after the dot')
|
||||||
|
.identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DriftTableName(
|
||||||
|
useExistingDartClass: useExisting,
|
||||||
|
overriddenDataClassName: name,
|
||||||
|
constructorName: constructorName,
|
||||||
|
)..setSpan(first, _previous);
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a "CREATE TRIGGER" statement, assuming that the create token has
|
/// Parses a "CREATE TRIGGER" statement, assuming that the create token has
|
||||||
/// already been consumed.
|
/// already been consumed.
|
||||||
CreateTriggerStatement? _createTrigger() {
|
CreateTriggerStatement? _createTrigger() {
|
||||||
|
@ -2409,17 +2412,33 @@ class Parser {
|
||||||
final ifNotExists = _ifNotExists();
|
final ifNotExists = _ifNotExists();
|
||||||
final name = _consumeIdentifier('Expected a name for this view');
|
final name = _consumeIdentifier('Expected a name for this view');
|
||||||
|
|
||||||
// Don't allow the "AS ClassName" syntax for views since it causes an
|
DriftTableName? driftTableName;
|
||||||
// ambiguity with the regular view syntax.
|
var skippedToSelect = false;
|
||||||
final driftTableName = _driftTableName(supportAs: false);
|
|
||||||
|
|
||||||
List<String>? columnNames;
|
if (enableDriftExtensions) {
|
||||||
if (_matchOne(TokenType.leftParen)) {
|
if (_check(TokenType.$with)) {
|
||||||
columnNames = _columnNames();
|
driftTableName = _driftTableName();
|
||||||
_consume(TokenType.rightParen, 'Expected closing bracket');
|
} else if (_matchOne(TokenType.as)) {
|
||||||
|
// This can either be a data class name or the beginning of the select
|
||||||
|
if (_check(TokenType.identifier)) {
|
||||||
|
// It's a data class name
|
||||||
|
driftTableName = _startedDriftTableName(_previous);
|
||||||
|
} else {
|
||||||
|
// No, we'll expect the SELECT next.
|
||||||
|
skippedToSelect = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_consume(TokenType.as, 'Expected AS SELECT');
|
List<String>? columnNames;
|
||||||
|
if (!skippedToSelect) {
|
||||||
|
if (_matchOne(TokenType.leftParen)) {
|
||||||
|
columnNames = _columnNames();
|
||||||
|
_consume(TokenType.rightParen, 'Expected closing bracket');
|
||||||
|
}
|
||||||
|
|
||||||
|
_consume(TokenType.as, 'Expected AS SELECT');
|
||||||
|
}
|
||||||
|
|
||||||
final query = _fullSelect();
|
final query = _fullSelect();
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
|
|
|
@ -428,6 +428,7 @@ class NodeSqlBuilder extends AstVisitor<void, void> {
|
||||||
_ifNotExists(e.ifNotExists);
|
_ifNotExists(e.ifNotExists);
|
||||||
|
|
||||||
identifier(e.viewName);
|
identifier(e.viewName);
|
||||||
|
e.driftTableName?.accept(this, arg);
|
||||||
|
|
||||||
if (e.columns != null) {
|
if (e.columns != null) {
|
||||||
symbol('(', spaceBefore: true);
|
symbol('(', spaceBefore: true);
|
||||||
|
|
|
@ -36,6 +36,25 @@ void main() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parses a CREATE VIEW statement with a custom Dart name', () {
|
||||||
|
testStatement(
|
||||||
|
'CREATE VIEW my_view AS DartClass AS SELECT 1',
|
||||||
|
CreateViewStatement(
|
||||||
|
viewName: 'my_view',
|
||||||
|
query: SelectStatement(
|
||||||
|
columns: [
|
||||||
|
ExpressionResultColumn(expression: NumericLiteral(1)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
driftTableName: DriftTableName(
|
||||||
|
overriddenDataClassName: 'DartClass',
|
||||||
|
useExistingDartClass: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
driftMode: true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('parses a complex CREATE View statement', () {
|
test('parses a complex CREATE View statement', () {
|
||||||
testStatement(
|
testStatement(
|
||||||
'CREATE VIEW IF NOT EXISTS my_complex_view (ids, name, count, type) AS '
|
'CREATE VIEW IF NOT EXISTS my_complex_view (ids, name, count, type) AS '
|
||||||
|
|
|
@ -107,6 +107,14 @@ CREATE VIEW my_view (foo, bar) AS SELECT * FROM t1;
|
||||||
testFormat('''
|
testFormat('''
|
||||||
CREATE VIEW my_view AS SELECT * FROM t1;
|
CREATE VIEW my_view AS SELECT * FROM t1;
|
||||||
''');
|
''');
|
||||||
|
|
||||||
|
testFormat('''
|
||||||
|
CREATE VIEW my_view AS Foo (foo, bar) AS SELECT * FROM t1;
|
||||||
|
''');
|
||||||
|
|
||||||
|
testFormat('''
|
||||||
|
CREATE VIEW my_view WITH Foo.constr (foo, bar) AS SELECT * FROM t1;
|
||||||
|
''');
|
||||||
});
|
});
|
||||||
|
|
||||||
group('table', () {
|
group('table', () {
|
||||||
|
|
Loading…
Reference in New Issue