mirror of https://github.com/AMT-Cheif/drift.git
Custom primary keys
This commit is contained in:
parent
83f12a71b6
commit
2e7c079e4d
|
@ -1,5 +1,7 @@
|
||||||
import 'package:sally/sally.dart';
|
import 'package:sally/sally.dart';
|
||||||
|
|
||||||
|
part 'example.g.dart';
|
||||||
|
|
||||||
// Define tables that can model a database of recipes.
|
// Define tables that can model a database of recipes.
|
||||||
|
|
||||||
@DataClassName('Category')
|
@DataClassName('Category')
|
||||||
|
@ -22,7 +24,6 @@ class Ingredients extends Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
class IngredientInRecipes extends Table {
|
class IngredientInRecipes extends Table {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get tableName => 'recipe_ingredients';
|
String get tableName => 'recipe_ingredients';
|
||||||
|
|
||||||
|
@ -30,8 +31,22 @@ class IngredientInRecipes extends Table {
|
||||||
@override
|
@override
|
||||||
Set<Column> get primaryKey => {recipe, ingredient};
|
Set<Column> get primaryKey => {recipe, ingredient};
|
||||||
|
|
||||||
IntColumn get recipe => integer().autoIncrement()();
|
IntColumn get recipe => integer()();
|
||||||
IntColumn get ingredient => integer().autoIncrement()();
|
IntColumn get ingredient => integer()();
|
||||||
|
|
||||||
IntColumn get amountInGrams => integer().named('amount')();
|
IntColumn get amountInGrams => integer().named('amount')();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UseSally(tables: [Categories, Recipes, Ingredients, IngredientInRecipes])
|
||||||
|
class Database extends _$Database {
|
||||||
|
Database(QueryExecutor e) : super(e);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get schemaVersion => 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
MigrationStrategy get migration => MigrationStrategy(onFinished: () async {
|
||||||
|
// populate data
|
||||||
|
await into(categories).insert(Category(description: 'Sweets'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,349 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'example.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// SallyGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
class Category {
|
||||||
|
final int id;
|
||||||
|
final String description;
|
||||||
|
Category({this.id, this.description});
|
||||||
|
factory Category.fromData(Map<String, dynamic> data, GeneratedDatabase db) {
|
||||||
|
final intType = db.typeSystem.forDartType<int>();
|
||||||
|
final stringType = db.typeSystem.forDartType<String>();
|
||||||
|
return Category(
|
||||||
|
id: intType.mapFromDatabaseResponse(data['id']),
|
||||||
|
description: stringType.mapFromDatabaseResponse(data['description']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Category copyWith({int id, String description}) => Category(
|
||||||
|
id: id ?? this.id,
|
||||||
|
description: description ?? this.description,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
int get hashCode => (id.hashCode) * 31 + description.hashCode;
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is Category && other.id == id && other.description == description);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $CategoriesTable extends Categories
|
||||||
|
implements TableInfo<Categories, Category> {
|
||||||
|
final GeneratedDatabase _db;
|
||||||
|
$CategoriesTable(this._db);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get id =>
|
||||||
|
GeneratedIntColumn('id', false, hasAutoIncrement: true);
|
||||||
|
@override
|
||||||
|
GeneratedTextColumn get description => GeneratedTextColumn(
|
||||||
|
'description',
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [id, description];
|
||||||
|
@override
|
||||||
|
Categories get asDslTable => this;
|
||||||
|
@override
|
||||||
|
String get $tableName => 'categories';
|
||||||
|
@override
|
||||||
|
bool validateIntegrity(Category instance, bool isInserting) =>
|
||||||
|
id.isAcceptableValue(instance.id, isInserting) &&
|
||||||
|
description.isAcceptableValue(instance.description, isInserting);
|
||||||
|
@override
|
||||||
|
Set<GeneratedColumn> get $primaryKey => null;
|
||||||
|
@override
|
||||||
|
Category map(Map<String, dynamic> data) {
|
||||||
|
return Category.fromData(data, _db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Variable> entityToSql(Category d) {
|
||||||
|
final map = <String, Variable>{};
|
||||||
|
if (d.id != null) {
|
||||||
|
map['id'] = Variable<int, IntType>(d.id);
|
||||||
|
}
|
||||||
|
if (d.description != null) {
|
||||||
|
map['description'] = Variable<String, StringType>(d.description);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Recipe {
|
||||||
|
final int id;
|
||||||
|
final String title;
|
||||||
|
final String instructions;
|
||||||
|
final int category;
|
||||||
|
Recipe({this.id, this.title, this.instructions, this.category});
|
||||||
|
factory Recipe.fromData(Map<String, dynamic> data, GeneratedDatabase db) {
|
||||||
|
final intType = db.typeSystem.forDartType<int>();
|
||||||
|
final stringType = db.typeSystem.forDartType<String>();
|
||||||
|
return Recipe(
|
||||||
|
id: intType.mapFromDatabaseResponse(data['id']),
|
||||||
|
title: stringType.mapFromDatabaseResponse(data['title']),
|
||||||
|
instructions: stringType.mapFromDatabaseResponse(data['instructions']),
|
||||||
|
category: intType.mapFromDatabaseResponse(data['category']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Recipe copyWith({int id, String title, String instructions, int category}) =>
|
||||||
|
Recipe(
|
||||||
|
id: id ?? this.id,
|
||||||
|
title: title ?? this.title,
|
||||||
|
instructions: instructions ?? this.instructions,
|
||||||
|
category: category ?? this.category,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
(((id.hashCode) * 31 + title.hashCode) * 31 + instructions.hashCode) *
|
||||||
|
31 +
|
||||||
|
category.hashCode;
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is Recipe &&
|
||||||
|
other.id == id &&
|
||||||
|
other.title == title &&
|
||||||
|
other.instructions == instructions &&
|
||||||
|
other.category == category);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $RecipesTable extends Recipes implements TableInfo<Recipes, Recipe> {
|
||||||
|
final GeneratedDatabase _db;
|
||||||
|
$RecipesTable(this._db);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get id =>
|
||||||
|
GeneratedIntColumn('id', false, hasAutoIncrement: true);
|
||||||
|
@override
|
||||||
|
GeneratedTextColumn get title =>
|
||||||
|
GeneratedTextColumn('title', false, maxTextLength: 16);
|
||||||
|
@override
|
||||||
|
GeneratedTextColumn get instructions => GeneratedTextColumn(
|
||||||
|
'instructions',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get category => GeneratedIntColumn(
|
||||||
|
'category',
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [id, title, instructions, category];
|
||||||
|
@override
|
||||||
|
Recipes get asDslTable => this;
|
||||||
|
@override
|
||||||
|
String get $tableName => 'recipes';
|
||||||
|
@override
|
||||||
|
bool validateIntegrity(Recipe instance, bool isInserting) =>
|
||||||
|
id.isAcceptableValue(instance.id, isInserting) &&
|
||||||
|
title.isAcceptableValue(instance.title, isInserting) &&
|
||||||
|
instructions.isAcceptableValue(instance.instructions, isInserting) &&
|
||||||
|
category.isAcceptableValue(instance.category, isInserting);
|
||||||
|
@override
|
||||||
|
Set<GeneratedColumn> get $primaryKey => null;
|
||||||
|
@override
|
||||||
|
Recipe map(Map<String, dynamic> data) {
|
||||||
|
return Recipe.fromData(data, _db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Variable> entityToSql(Recipe d) {
|
||||||
|
final map = <String, Variable>{};
|
||||||
|
if (d.id != null) {
|
||||||
|
map['id'] = Variable<int, IntType>(d.id);
|
||||||
|
}
|
||||||
|
if (d.title != null) {
|
||||||
|
map['title'] = Variable<String, StringType>(d.title);
|
||||||
|
}
|
||||||
|
if (d.instructions != null) {
|
||||||
|
map['instructions'] = Variable<String, StringType>(d.instructions);
|
||||||
|
}
|
||||||
|
if (d.category != null) {
|
||||||
|
map['category'] = Variable<int, IntType>(d.category);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Ingredient {
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
final int caloriesPer100g;
|
||||||
|
Ingredient({this.id, this.name, this.caloriesPer100g});
|
||||||
|
factory Ingredient.fromData(Map<String, dynamic> data, GeneratedDatabase db) {
|
||||||
|
final intType = db.typeSystem.forDartType<int>();
|
||||||
|
final stringType = db.typeSystem.forDartType<String>();
|
||||||
|
return Ingredient(
|
||||||
|
id: intType.mapFromDatabaseResponse(data['id']),
|
||||||
|
name: stringType.mapFromDatabaseResponse(data['name']),
|
||||||
|
caloriesPer100g: intType.mapFromDatabaseResponse(data['calories']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ingredient copyWith({int id, String name, int caloriesPer100g}) => Ingredient(
|
||||||
|
id: id ?? this.id,
|
||||||
|
name: name ?? this.name,
|
||||||
|
caloriesPer100g: caloriesPer100g ?? this.caloriesPer100g,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
((id.hashCode) * 31 + name.hashCode) * 31 + caloriesPer100g.hashCode;
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is Ingredient &&
|
||||||
|
other.id == id &&
|
||||||
|
other.name == name &&
|
||||||
|
other.caloriesPer100g == caloriesPer100g);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $IngredientsTable extends Ingredients
|
||||||
|
implements TableInfo<Ingredients, Ingredient> {
|
||||||
|
final GeneratedDatabase _db;
|
||||||
|
$IngredientsTable(this._db);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get id =>
|
||||||
|
GeneratedIntColumn('id', false, hasAutoIncrement: true);
|
||||||
|
@override
|
||||||
|
GeneratedTextColumn get name => GeneratedTextColumn(
|
||||||
|
'name',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get caloriesPer100g => GeneratedIntColumn(
|
||||||
|
'calories',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [id, name, caloriesPer100g];
|
||||||
|
@override
|
||||||
|
Ingredients get asDslTable => this;
|
||||||
|
@override
|
||||||
|
String get $tableName => 'ingredients';
|
||||||
|
@override
|
||||||
|
bool validateIntegrity(Ingredient instance, bool isInserting) =>
|
||||||
|
id.isAcceptableValue(instance.id, isInserting) &&
|
||||||
|
name.isAcceptableValue(instance.name, isInserting) &&
|
||||||
|
caloriesPer100g.isAcceptableValue(instance.caloriesPer100g, isInserting);
|
||||||
|
@override
|
||||||
|
Set<GeneratedColumn> get $primaryKey => null;
|
||||||
|
@override
|
||||||
|
Ingredient map(Map<String, dynamic> data) {
|
||||||
|
return Ingredient.fromData(data, _db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Variable> entityToSql(Ingredient d) {
|
||||||
|
final map = <String, Variable>{};
|
||||||
|
if (d.id != null) {
|
||||||
|
map['id'] = Variable<int, IntType>(d.id);
|
||||||
|
}
|
||||||
|
if (d.name != null) {
|
||||||
|
map['name'] = Variable<String, StringType>(d.name);
|
||||||
|
}
|
||||||
|
if (d.caloriesPer100g != null) {
|
||||||
|
map['calories'] = Variable<int, IntType>(d.caloriesPer100g);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IngredientInRecipe {
|
||||||
|
final int recipe;
|
||||||
|
final int ingredient;
|
||||||
|
final int amountInGrams;
|
||||||
|
IngredientInRecipe({this.recipe, this.ingredient, this.amountInGrams});
|
||||||
|
factory IngredientInRecipe.fromData(
|
||||||
|
Map<String, dynamic> data, GeneratedDatabase db) {
|
||||||
|
final intType = db.typeSystem.forDartType<int>();
|
||||||
|
return IngredientInRecipe(
|
||||||
|
recipe: intType.mapFromDatabaseResponse(data['recipe']),
|
||||||
|
ingredient: intType.mapFromDatabaseResponse(data['ingredient']),
|
||||||
|
amountInGrams: intType.mapFromDatabaseResponse(data['amount']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
IngredientInRecipe copyWith(
|
||||||
|
{int recipe, int ingredient, int amountInGrams}) =>
|
||||||
|
IngredientInRecipe(
|
||||||
|
recipe: recipe ?? this.recipe,
|
||||||
|
ingredient: ingredient ?? this.ingredient,
|
||||||
|
amountInGrams: amountInGrams ?? this.amountInGrams,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
((recipe.hashCode) * 31 + ingredient.hashCode) * 31 +
|
||||||
|
amountInGrams.hashCode;
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is IngredientInRecipe &&
|
||||||
|
other.recipe == recipe &&
|
||||||
|
other.ingredient == ingredient &&
|
||||||
|
other.amountInGrams == amountInGrams);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $IngredientInRecipesTable extends IngredientInRecipes
|
||||||
|
implements TableInfo<IngredientInRecipes, IngredientInRecipe> {
|
||||||
|
final GeneratedDatabase _db;
|
||||||
|
$IngredientInRecipesTable(this._db);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get recipe => GeneratedIntColumn(
|
||||||
|
'recipe',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get ingredient => GeneratedIntColumn(
|
||||||
|
'ingredient',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get amountInGrams => GeneratedIntColumn(
|
||||||
|
'amount',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [recipe, ingredient, amountInGrams];
|
||||||
|
@override
|
||||||
|
IngredientInRecipes get asDslTable => this;
|
||||||
|
@override
|
||||||
|
String get $tableName => 'recipe_ingredients';
|
||||||
|
@override
|
||||||
|
bool validateIntegrity(IngredientInRecipe instance, bool isInserting) =>
|
||||||
|
recipe.isAcceptableValue(instance.recipe, isInserting) &&
|
||||||
|
ingredient.isAcceptableValue(instance.ingredient, isInserting) &&
|
||||||
|
amountInGrams.isAcceptableValue(instance.amountInGrams, isInserting);
|
||||||
|
@override
|
||||||
|
Set<GeneratedColumn> get $primaryKey => {recipe, ingredient};
|
||||||
|
@override
|
||||||
|
IngredientInRecipe map(Map<String, dynamic> data) {
|
||||||
|
return IngredientInRecipe.fromData(data, _db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Variable> entityToSql(IngredientInRecipe d) {
|
||||||
|
final map = <String, Variable>{};
|
||||||
|
if (d.recipe != null) {
|
||||||
|
map['recipe'] = Variable<int, IntType>(d.recipe);
|
||||||
|
}
|
||||||
|
if (d.ingredient != null) {
|
||||||
|
map['ingredient'] = Variable<int, IntType>(d.ingredient);
|
||||||
|
}
|
||||||
|
if (d.amountInGrams != null) {
|
||||||
|
map['amount'] = Variable<int, IntType>(d.amountInGrams);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _$Database extends GeneratedDatabase {
|
||||||
|
_$Database(QueryExecutor e) : super(const SqlTypeSystem.withDefaults(), e);
|
||||||
|
$CategoriesTable get categories => $CategoriesTable(this);
|
||||||
|
$RecipesTable get recipes => $RecipesTable(this);
|
||||||
|
$IngredientsTable get ingredients => $IngredientsTable(this);
|
||||||
|
$IngredientInRecipesTable get ingredientInRecipes =>
|
||||||
|
$IngredientInRecipesTable(this);
|
||||||
|
@override
|
||||||
|
List<TableInfo> get allTables =>
|
||||||
|
[categories, recipes, ingredients, ingredientInRecipes];
|
||||||
|
}
|
|
@ -40,10 +40,6 @@ class ColumnBuilder<Builder, ResultColumn> {
|
||||||
/// `IntColumn get id = integer((c) => c.named('user_id'))`.
|
/// `IntColumn get id = integer((c) => c.named('user_id'))`.
|
||||||
Builder named(String name) => null;
|
Builder named(String name) => null;
|
||||||
|
|
||||||
@Deprecated('Ignored by the generator. Please override primaryKey in your '
|
|
||||||
'table class instead')
|
|
||||||
Builder primaryKey() => null;
|
|
||||||
|
|
||||||
/// Marks this column as nullable. Nullable columns should not appear in a
|
/// Marks this column as nullable. Nullable columns should not appear in a
|
||||||
/// primary key. Columns are non-null by default.
|
/// primary key. Columns are non-null by default.
|
||||||
Builder nullable() => null;
|
Builder nullable() => null;
|
||||||
|
|
|
@ -17,8 +17,23 @@ abstract class Table {
|
||||||
@visibleForOverriding
|
@visibleForOverriding
|
||||||
String get tableName => null;
|
String get tableName => null;
|
||||||
|
|
||||||
/// In the future, you can override this to specify a custom primary key. This
|
/// Override this to specify custom primary keys:
|
||||||
/// is not supported by sally at the moment.
|
/// ```dart
|
||||||
|
/// class IngredientInRecipes extends Table {
|
||||||
|
/// @override
|
||||||
|
/// Set<Column> get primaryKey => {recipe, ingredient};
|
||||||
|
///
|
||||||
|
/// IntColumn get recipe => integer().autoIncrement()();
|
||||||
|
/// IntColumn get ingredient => integer().autoIncrement()();
|
||||||
|
///
|
||||||
|
/// IntColumn get amountInGrams => integer().named('amount')();
|
||||||
|
///}
|
||||||
|
/// ```
|
||||||
|
/// The getter must return a set literal using the `=>` syntax so that the
|
||||||
|
/// sally generator can understand the code.
|
||||||
|
/// Also, please not that it's an error to have a
|
||||||
|
/// [IntColumnBuilder.autoIncrement] column and a custom primary key.
|
||||||
|
/// Writing such table in sql will throw at runtime.
|
||||||
@visibleForOverriding
|
@visibleForOverriding
|
||||||
Set<Column> get primaryKey => null;
|
Set<Column> get primaryKey => null;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,10 @@ import 'package:sally/src/runtime/structure/table_info.dart';
|
||||||
typedef Future<void> OnCreate(Migrator m);
|
typedef Future<void> OnCreate(Migrator m);
|
||||||
typedef Future<void> OnUpgrade(Migrator m, int from, int to);
|
typedef Future<void> OnUpgrade(Migrator m, int from, int to);
|
||||||
|
|
||||||
|
/// Signature of a function that's called after a migration has finished and the
|
||||||
|
/// database is ready to be used. Useful to populate data.
|
||||||
|
typedef Future<void> OnMigrationFinished();
|
||||||
|
|
||||||
Future<void> _defaultOnCreate(Migrator m) => m.createAllTables();
|
Future<void> _defaultOnCreate(Migrator m) => m.createAllTables();
|
||||||
Future<void> _defaultOnUpdate(Migrator m, int from, int to) async =>
|
Future<void> _defaultOnUpdate(Migrator m, int from, int to) async =>
|
||||||
throw Exception("You've bumped the schema version for your sally database "
|
throw Exception("You've bumped the schema version for your sally database "
|
||||||
|
@ -21,9 +25,15 @@ class MigrationStrategy {
|
||||||
/// happened at a lower [GeneratedDatabase.schemaVersion].
|
/// happened at a lower [GeneratedDatabase.schemaVersion].
|
||||||
final OnUpgrade onUpgrade;
|
final OnUpgrade onUpgrade;
|
||||||
|
|
||||||
|
/// Executes after the database is ready and all migrations ran, but before
|
||||||
|
/// any other queries will be executed, making this method suitable to
|
||||||
|
/// populate data.
|
||||||
|
final OnMigrationFinished onFinished;
|
||||||
|
|
||||||
MigrationStrategy({
|
MigrationStrategy({
|
||||||
this.onCreate = _defaultOnCreate,
|
this.onCreate = _defaultOnCreate,
|
||||||
this.onUpgrade = _defaultOnUpdate,
|
this.onUpgrade = _defaultOnUpdate,
|
||||||
|
this.onFinished,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +69,19 @@ class Migrator {
|
||||||
if (i < table.$columns.length - 1) sql.write(', ');
|
if (i < table.$columns.length - 1) sql.write(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (table.$primaryKey != null) {
|
||||||
|
sql.write(', PRIMARY KEY (');
|
||||||
|
final pkList = table.$primaryKey.toList(growable: false);
|
||||||
|
for (var i = 0; i < pkList.length; i++) {
|
||||||
|
final column = pkList[i];
|
||||||
|
|
||||||
|
sql.write(column.$name);
|
||||||
|
|
||||||
|
if (i != pkList.length - 1) sql.write(', ');
|
||||||
|
}
|
||||||
|
sql.write(')');
|
||||||
|
}
|
||||||
|
|
||||||
sql.write(');');
|
sql.write(');');
|
||||||
|
|
||||||
return issueCustomQuery(sql.toString());
|
return issueCustomQuery(sql.toString());
|
||||||
|
|
|
@ -27,7 +27,15 @@ class Categories extends Table {
|
||||||
TextColumn get description => text().named('desc')();
|
TextColumn get description => text().named('desc')();
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseSally(tables: [TodosTable, Categories, Users])
|
class SharedTodos extends Table {
|
||||||
|
IntColumn get todo => integer()();
|
||||||
|
IntColumn get user => integer()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {todo, user};
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseSally(tables: [TodosTable, Categories, Users, SharedTodos])
|
||||||
class TodoDb extends _$TodoDb {
|
class TodoDb extends _$TodoDb {
|
||||||
TodoDb(QueryExecutor e) : super(e);
|
TodoDb(QueryExecutor e) : super(e);
|
||||||
|
|
||||||
|
|
|
@ -64,10 +64,8 @@ class $TodosTableTable extends TodosTable
|
||||||
GeneratedIntColumn get id =>
|
GeneratedIntColumn get id =>
|
||||||
GeneratedIntColumn('id', false, hasAutoIncrement: true);
|
GeneratedIntColumn('id', false, hasAutoIncrement: true);
|
||||||
@override
|
@override
|
||||||
GeneratedTextColumn get title => GeneratedTextColumn(
|
GeneratedTextColumn get title =>
|
||||||
'title',
|
GeneratedTextColumn('title', true, minTextLength: 4, maxTextLength: 16);
|
||||||
true,
|
|
||||||
);
|
|
||||||
@override
|
@override
|
||||||
GeneratedTextColumn get content => GeneratedTextColumn(
|
GeneratedTextColumn get content => GeneratedTextColumn(
|
||||||
'content',
|
'content',
|
||||||
|
@ -98,7 +96,7 @@ class $TodosTableTable extends TodosTable
|
||||||
targetDate.isAcceptableValue(instance.targetDate, isInserting) &&
|
targetDate.isAcceptableValue(instance.targetDate, isInserting) &&
|
||||||
category.isAcceptableValue(instance.category, isInserting);
|
category.isAcceptableValue(instance.category, isInserting);
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
Set<GeneratedColumn> get $primaryKey => null;
|
||||||
@override
|
@override
|
||||||
TodoEntry map(Map<String, dynamic> data) {
|
TodoEntry map(Map<String, dynamic> data) {
|
||||||
return TodoEntry.fromData(data, _db);
|
return TodoEntry.fromData(data, _db);
|
||||||
|
@ -173,7 +171,7 @@ class $CategoriesTable extends Categories
|
||||||
id.isAcceptableValue(instance.id, isInserting) &&
|
id.isAcceptableValue(instance.id, isInserting) &&
|
||||||
description.isAcceptableValue(instance.description, isInserting);
|
description.isAcceptableValue(instance.description, isInserting);
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
Set<GeneratedColumn> get $primaryKey => null;
|
||||||
@override
|
@override
|
||||||
Category map(Map<String, dynamic> data) {
|
Category map(Map<String, dynamic> data) {
|
||||||
return Category.fromData(data, _db);
|
return Category.fromData(data, _db);
|
||||||
|
@ -231,10 +229,8 @@ class $UsersTable extends Users implements TableInfo<Users, User> {
|
||||||
GeneratedIntColumn get id =>
|
GeneratedIntColumn get id =>
|
||||||
GeneratedIntColumn('id', false, hasAutoIncrement: true);
|
GeneratedIntColumn('id', false, hasAutoIncrement: true);
|
||||||
@override
|
@override
|
||||||
GeneratedTextColumn get name => GeneratedTextColumn(
|
GeneratedTextColumn get name =>
|
||||||
'name',
|
GeneratedTextColumn('name', false, minTextLength: 6, maxTextLength: 32);
|
||||||
false,
|
|
||||||
);
|
|
||||||
@override
|
@override
|
||||||
GeneratedBoolColumn get isAwesome => GeneratedBoolColumn(
|
GeneratedBoolColumn get isAwesome => GeneratedBoolColumn(
|
||||||
'is_awesome',
|
'is_awesome',
|
||||||
|
@ -252,7 +248,7 @@ class $UsersTable extends Users implements TableInfo<Users, User> {
|
||||||
name.isAcceptableValue(instance.name, isInserting) &&
|
name.isAcceptableValue(instance.name, isInserting) &&
|
||||||
isAwesome.isAcceptableValue(instance.isAwesome, isInserting);
|
isAwesome.isAcceptableValue(instance.isAwesome, isInserting);
|
||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => <GeneratedColumn>{};
|
Set<GeneratedColumn> get $primaryKey => null;
|
||||||
@override
|
@override
|
||||||
User map(Map<String, dynamic> data) {
|
User map(Map<String, dynamic> data) {
|
||||||
return User.fromData(data, _db);
|
return User.fromData(data, _db);
|
||||||
|
@ -274,11 +270,79 @@ class $UsersTable extends Users implements TableInfo<Users, User> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SharedTodo {
|
||||||
|
final int todo;
|
||||||
|
final int user;
|
||||||
|
SharedTodo({this.todo, this.user});
|
||||||
|
factory SharedTodo.fromData(Map<String, dynamic> data, GeneratedDatabase db) {
|
||||||
|
final intType = db.typeSystem.forDartType<int>();
|
||||||
|
return SharedTodo(
|
||||||
|
todo: intType.mapFromDatabaseResponse(data['todo']),
|
||||||
|
user: intType.mapFromDatabaseResponse(data['user']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SharedTodo copyWith({int todo, int user}) => SharedTodo(
|
||||||
|
todo: todo ?? this.todo,
|
||||||
|
user: user ?? this.user,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
int get hashCode => (todo.hashCode) * 31 + user.hashCode;
|
||||||
|
@override
|
||||||
|
bool operator ==(other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is SharedTodo && other.todo == todo && other.user == user);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $SharedTodosTable extends SharedTodos
|
||||||
|
implements TableInfo<SharedTodos, SharedTodo> {
|
||||||
|
final GeneratedDatabase _db;
|
||||||
|
$SharedTodosTable(this._db);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get todo => GeneratedIntColumn(
|
||||||
|
'todo',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
GeneratedIntColumn get user => GeneratedIntColumn(
|
||||||
|
'user',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [todo, user];
|
||||||
|
@override
|
||||||
|
SharedTodos get asDslTable => this;
|
||||||
|
@override
|
||||||
|
String get $tableName => 'shared_todos';
|
||||||
|
@override
|
||||||
|
bool validateIntegrity(SharedTodo instance, bool isInserting) =>
|
||||||
|
todo.isAcceptableValue(instance.todo, isInserting) &&
|
||||||
|
user.isAcceptableValue(instance.user, isInserting);
|
||||||
|
@override
|
||||||
|
Set<GeneratedColumn> get $primaryKey => {todo, user};
|
||||||
|
@override
|
||||||
|
SharedTodo map(Map<String, dynamic> data) {
|
||||||
|
return SharedTodo.fromData(data, _db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Variable> entityToSql(SharedTodo d) {
|
||||||
|
final map = <String, Variable>{};
|
||||||
|
if (d.todo != null) {
|
||||||
|
map['todo'] = Variable<int, IntType>(d.todo);
|
||||||
|
}
|
||||||
|
if (d.user != null) {
|
||||||
|
map['user'] = Variable<int, IntType>(d.user);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract class _$TodoDb extends GeneratedDatabase {
|
abstract class _$TodoDb extends GeneratedDatabase {
|
||||||
_$TodoDb(QueryExecutor e) : super(const SqlTypeSystem.withDefaults(), e);
|
_$TodoDb(QueryExecutor e) : super(const SqlTypeSystem.withDefaults(), e);
|
||||||
$TodosTableTable get todosTable => $TodosTableTable(this);
|
$TodosTableTable get todosTable => $TodosTableTable(this);
|
||||||
$CategoriesTable get categories => $CategoriesTable(this);
|
$CategoriesTable get categories => $CategoriesTable(this);
|
||||||
$UsersTable get users => $UsersTable(this);
|
$UsersTable get users => $UsersTable(this);
|
||||||
|
$SharedTodosTable get sharedTodos => $SharedTodosTable(this);
|
||||||
@override
|
@override
|
||||||
List<TableInfo> get allTables => [todosTable, categories, users];
|
List<TableInfo> get allTables => [todosTable, categories, users, sharedTodos];
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ void main() {
|
||||||
test('creates all tables', () async {
|
test('creates all tables', () async {
|
||||||
await Migrator(db, mockQueryExecutor).createAllTables();
|
await Migrator(db, mockQueryExecutor).createAllTables();
|
||||||
|
|
||||||
// should create todos, categories and users table
|
// should create todos, categories, users and shared_todos table
|
||||||
verify(mockQueryExecutor.call('CREATE TABLE IF NOT EXISTS todos '
|
verify(mockQueryExecutor.call('CREATE TABLE IF NOT EXISTS todos '
|
||||||
'(id INTEGER PRIMARY KEY AUTOINCREMENT, title VARCHAR NULL, '
|
'(id INTEGER PRIMARY KEY AUTOINCREMENT, title VARCHAR NULL, '
|
||||||
'content VARCHAR NOT NULL, target_date INTEGER NULL, '
|
'content VARCHAR NOT NULL, target_date INTEGER NULL, '
|
||||||
|
@ -29,6 +29,9 @@ void main() {
|
||||||
verify(mockQueryExecutor.call('CREATE TABLE IF NOT EXISTS users '
|
verify(mockQueryExecutor.call('CREATE TABLE IF NOT EXISTS users '
|
||||||
'(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR NOT NULL, '
|
'(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR NOT NULL, '
|
||||||
'is_awesome BOOLEAN NOT NULL CHECK (is_awesome in (0, 1)));'));
|
'is_awesome BOOLEAN NOT NULL CHECK (is_awesome in (0, 1)));'));
|
||||||
|
|
||||||
|
verify(mockQueryExecutor.call('CREATE TABLE IF NOT EXISTS shared_todos '
|
||||||
|
'(todo INTEGER NOT NULL, user INTEGER NOT NULL, PRIMARY KEY (todo, user));'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('creates individual tables', () async {
|
test('creates individual tables', () async {
|
||||||
|
|
|
@ -293,7 +293,7 @@ Please note that a workaround for most on this list exists with custom statement
|
||||||
### Planned for the future
|
### Planned for the future
|
||||||
These aren't sorted by priority. If you have more ideas or want some features happening soon,
|
These aren't sorted by priority. If you have more ideas or want some features happening soon,
|
||||||
let us know by creating an issue!
|
let us know by creating an issue!
|
||||||
- Specify primary keys
|
- Specify custom primary keys ✔️
|
||||||
- Support an simplified update that doesn't need an explicit where based on the primary key
|
- Support an simplified update that doesn't need an explicit where based on the primary key
|
||||||
- Simple `COUNT(*)` operations (group operations will be much more complicated)
|
- Simple `COUNT(*)` operations (group operations will be much more complicated)
|
||||||
- Support default values and expressions
|
- Support default values and expressions
|
||||||
|
|
|
@ -21,6 +21,7 @@ class FlutterQueryExecutor extends QueryExecutor {
|
||||||
final bool logStatements;
|
final bool logStatements;
|
||||||
|
|
||||||
Database _db;
|
Database _db;
|
||||||
|
bool _hadMigration = false;
|
||||||
|
|
||||||
FlutterQueryExecutor({@required this.path, this.logStatements})
|
FlutterQueryExecutor({@required this.path, this.logStatements})
|
||||||
: _inDbPath = false;
|
: _inDbPath = false;
|
||||||
|
@ -46,14 +47,25 @@ class FlutterQueryExecutor extends QueryExecutor {
|
||||||
resolvedPath,
|
resolvedPath,
|
||||||
version: databaseInfo.schemaVersion,
|
version: databaseInfo.schemaVersion,
|
||||||
onCreate: (db, version) {
|
onCreate: (db, version) {
|
||||||
|
_hadMigration = true;
|
||||||
return databaseInfo.handleDatabaseCreation(
|
return databaseInfo.handleDatabaseCreation(
|
||||||
executor: (sql) => db.execute(sql),
|
executor: (sql) => db.execute(sql),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onUpgrade: (db, from, to) {
|
onUpgrade: (db, from, to) {
|
||||||
|
_hadMigration = true;
|
||||||
return databaseInfo.handleDatabaseVersionChange(
|
return databaseInfo.handleDatabaseVersionChange(
|
||||||
executor: (sql) => db.execute(sql), from: from, to: to);
|
executor: (sql) => db.execute(sql), from: from, to: to);
|
||||||
},
|
},
|
||||||
|
onOpen: (db) async {
|
||||||
|
_db = db;
|
||||||
|
// the openDatabase future will resolve later, so we can get an instance
|
||||||
|
// where we can send the queries from the onFinished operation;
|
||||||
|
final fn = databaseInfo.migration.onFinished;
|
||||||
|
if (fn != null && _hadMigration) {
|
||||||
|
await fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -17,7 +17,11 @@ class SpecifiedTable {
|
||||||
final Set<SpecifiedColumn> primaryKey;
|
final Set<SpecifiedColumn> primaryKey;
|
||||||
|
|
||||||
const SpecifiedTable(
|
const SpecifiedTable(
|
||||||
{this.fromClass, this.columns, this.sqlName, this.dartTypeName, this.primaryKey});
|
{this.fromClass,
|
||||||
|
this.columns,
|
||||||
|
this.sqlName,
|
||||||
|
this.dartTypeName,
|
||||||
|
this.primaryKey});
|
||||||
}
|
}
|
||||||
|
|
||||||
String tableInfoNameForTableClass(ClassElement fromClass) =>
|
String tableInfoNameForTableClass(ClassElement fromClass) =>
|
||||||
|
|
|
@ -68,35 +68,48 @@ class TableParser extends ParserBase {
|
||||||
return tableName;
|
return tableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<SpecifiedColumn> _readPrimaryKey(ClassElement element, List<SpecifiedColumn> columns) {
|
Set<SpecifiedColumn> _readPrimaryKey(
|
||||||
|
ClassElement element, List<SpecifiedColumn> columns) {
|
||||||
final primaryKeyGetter = element.getGetter('primaryKey');
|
final primaryKeyGetter = element.getGetter('primaryKey');
|
||||||
if (primaryKeyGetter == null) {
|
if (primaryKeyGetter == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ast = generator.loadElementDeclaration(primaryKeyGetter).node as MethodDeclaration;
|
final ast = generator.loadElementDeclaration(primaryKeyGetter).node
|
||||||
|
as MethodDeclaration;
|
||||||
final body = ast.body;
|
final body = ast.body;
|
||||||
if (body is! ExpressionFunctionBody) {
|
if (body is! ExpressionFunctionBody) {
|
||||||
generator.errors.add(SallyError(affectedElement: primaryKeyGetter, message: 'This must return a set literal using the => syntax!'));
|
generator.errors.add(SallyError(
|
||||||
|
affectedElement: primaryKeyGetter,
|
||||||
|
message: 'This must return a set literal using the => syntax!'));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final expression = (body as ExpressionFunctionBody).expression;
|
final expression = (body as ExpressionFunctionBody).expression;
|
||||||
// set expressions {x, y} are parsed as map literals whose values are an empty
|
// set expressions {x, y} are sometimes parsed as map literals whose values
|
||||||
// identifier {x: , y: }. yeah.
|
// are an empty identifier {x: , y: }, but sometimes as proper set literal.
|
||||||
|
// this is probably due to backwards compatibility.
|
||||||
// todo should we support MapLiteral2 to support the experiments discussed there?
|
// todo should we support MapLiteral2 to support the experiments discussed there?
|
||||||
if (expression is! MapLiteral) {
|
|
||||||
generator.errors.add(SallyError(affectedElement: primaryKeyGetter, message: 'This must return a set literal!'));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final mapLiteral = expression as MapLiteral;
|
|
||||||
|
|
||||||
final parsedPrimaryKey = <SpecifiedColumn>{};
|
final parsedPrimaryKey = <SpecifiedColumn>{};
|
||||||
|
|
||||||
for (var entry in mapLiteral.entries) {
|
if (expression is MapLiteral) {
|
||||||
|
for (var entry in expression.entries) {
|
||||||
final key = entry.key as Identifier;
|
final key = entry.key as Identifier;
|
||||||
final column = columns.singleWhere((column) => column.dartGetterName == key.name);
|
final column =
|
||||||
|
columns.singleWhere((column) => column.dartGetterName == key.name);
|
||||||
parsedPrimaryKey.add(column);
|
parsedPrimaryKey.add(column);
|
||||||
}
|
}
|
||||||
|
} else if (expression is SetLiteral) {
|
||||||
|
for (var entry in expression.elements) {
|
||||||
|
final column = columns.singleWhere(
|
||||||
|
(column) => column.dartGetterName == (entry as Identifier).name);
|
||||||
|
parsedPrimaryKey.add(column);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generator.errors.add(SallyError(
|
||||||
|
affectedElement: primaryKeyGetter,
|
||||||
|
message: 'This must return a set literal!'));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return parsedPrimaryKey;
|
return parsedPrimaryKey;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import 'package:sally_generator/src/writer/database_writer.dart';
|
||||||
import 'package:source_gen/source_gen.dart';
|
import 'package:source_gen/source_gen.dart';
|
||||||
|
|
||||||
class SallyGenerator extends GeneratorForAnnotation<UseSally> {
|
class SallyGenerator extends GeneratorForAnnotation<UseSally> {
|
||||||
final Map<String, ParsedLibraryResult> _astForLibs = {};
|
//final Map<String, ParsedLibraryResult> _astForLibs = {};
|
||||||
final ErrorStore errors = ErrorStore();
|
final ErrorStore errors = ErrorStore();
|
||||||
|
|
||||||
TableParser tableParser;
|
TableParser tableParser;
|
||||||
|
@ -24,11 +24,12 @@ class SallyGenerator extends GeneratorForAnnotation<UseSally> {
|
||||||
final Map<DartType, SpecifiedTable> _foundTables = {};
|
final Map<DartType, SpecifiedTable> _foundTables = {};
|
||||||
|
|
||||||
ElementDeclarationResult loadElementDeclaration(Element element) {
|
ElementDeclarationResult loadElementDeclaration(Element element) {
|
||||||
final result = _astForLibs.putIfAbsent(element.library.name, () {
|
/*final result = _astForLibs.putIfAbsent(element.library.name, () {
|
||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
return ParsedLibraryResultImpl.tmp(element.library);
|
return ParsedLibraryResultImpl.tmp(element.library);
|
||||||
});
|
});*/
|
||||||
|
// ignore: deprecated_member_use
|
||||||
|
final result = ParsedLibraryResultImpl.tmp(element.library);
|
||||||
return result.getElementDeclaration(element);
|
return result.getElementDeclaration(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +62,20 @@ class SallyGenerator extends GeneratorForAnnotation<UseSally> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (errors.errors.isNotEmpty) {
|
||||||
|
print('Warning: There were some errors whily running sally_generator:');
|
||||||
|
|
||||||
|
for (var error in errors.errors) {
|
||||||
|
print(error.message);
|
||||||
|
|
||||||
|
if (error.affectedElement != null) {
|
||||||
|
final span = spanForElement(error.affectedElement);
|
||||||
|
print('${span.start.toolString}\n${span.highlight()}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errors.errors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (_foundTables.isEmpty) return '';
|
if (_foundTables.isEmpty) return '';
|
||||||
|
|
||||||
final specifiedDb =
|
final specifiedDb =
|
||||||
|
|
|
@ -44,14 +44,7 @@ class TableWriter {
|
||||||
..write('@override\nString get \$tableName => \'${table.sqlName}\';\n');
|
..write('@override\nString get \$tableName => \'${table.sqlName}\';\n');
|
||||||
|
|
||||||
_writeValidityCheckMethod(buffer);
|
_writeValidityCheckMethod(buffer);
|
||||||
|
_writePrimaryKeyOverride(buffer);
|
||||||
// write primary key getter: Set<Column> get $primaryKey => <GeneratedColumn>{id};
|
|
||||||
final primaryKeyColumns = table.primaryKey.map((c) => c.dartGetterName);
|
|
||||||
buffer
|
|
||||||
..write(
|
|
||||||
'@override\nSet<GeneratedColumn> get \$primaryKey => <GeneratedColumn>{')
|
|
||||||
..write(primaryKeyColumns.join(', '))
|
|
||||||
..write('};\n');
|
|
||||||
|
|
||||||
_writeMappingMethod(buffer);
|
_writeMappingMethod(buffer);
|
||||||
_writeReverseMappingMethod(buffer);
|
_writeReverseMappingMethod(buffer);
|
||||||
|
@ -91,8 +84,17 @@ class TableWriter {
|
||||||
final isNullable = column.nullable;
|
final isNullable = column.nullable;
|
||||||
final additionalParams = <String, String>{};
|
final additionalParams = <String, String>{};
|
||||||
|
|
||||||
if (column.hasAI) {
|
for (var feature in column.features) {
|
||||||
|
if (feature is AutoIncrement) {
|
||||||
additionalParams['hasAutoIncrement'] = 'true';
|
additionalParams['hasAutoIncrement'] = 'true';
|
||||||
|
} else if (feature is LimitingTextLength) {
|
||||||
|
if (feature.minLength != null) {
|
||||||
|
additionalParams['minTextLength'] = feature.minLength.toString();
|
||||||
|
}
|
||||||
|
if (feature.maxLength != null) {
|
||||||
|
additionalParams['maxTextLength'] = feature.maxLength.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @override
|
// @override
|
||||||
|
@ -133,4 +135,24 @@ class TableWriter {
|
||||||
|
|
||||||
buffer..write(validationCode)..write(';\n');
|
buffer..write(validationCode)..write(';\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _writePrimaryKeyOverride(StringBuffer buffer) {
|
||||||
|
buffer.write('@override\nSet<GeneratedColumn> get \$primaryKey => ');
|
||||||
|
if (table.primaryKey == null) {
|
||||||
|
buffer.write('null;');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.write('{');
|
||||||
|
final pkList = table.primaryKey.toList();
|
||||||
|
for (var i = 0; i < pkList.length; i++) {
|
||||||
|
final pk = pkList[i];
|
||||||
|
|
||||||
|
buffer.write(pk.dartGetterName);
|
||||||
|
if (i != pkList.length - 1) {
|
||||||
|
buffer.write(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.write('};\n');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,10 @@ void main() async {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('parses custom primary keys', () {
|
test('parses custom primary keys', () {
|
||||||
final table = TableParser(generator).parse(testLib.getType('CustomPrimaryKey'));
|
final table =
|
||||||
|
TableParser(generator).parse(testLib.getType('CustomPrimaryKey'));
|
||||||
|
|
||||||
expect(table.primaryKey, containsAll(table.columns));
|
expect(table.primaryKey, containsAll(table.columns));
|
||||||
|
expect(table.columns.any((column) => column.hasAI), isFalse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue