Started to integrate the sqlparser into moor generator

This commit is contained in:
Simon Binder 2019-06-28 23:41:27 +02:00
parent 1479a0d850
commit 40a4ebdadf
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
22 changed files with 247 additions and 1826 deletions

View File

@ -1,788 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'example.dart';
// **************************************************************************
// MoorGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps
class Category extends DataClass implements Insertable<Category> {
final int id;
final String description;
Category({@required this.id, this.description});
factory Category.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return Category(
id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']),
description: stringType
.mapFromDatabaseResponse(data['${effectivePrefix}description']),
);
}
factory Category.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return Category(
id: serializer.fromJson<int>(json['id']),
description: serializer.fromJson<String>(json['description']),
);
}
@override
Map<String, dynamic> toJson(
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return {
'id': serializer.toJson<int>(id),
'description': serializer.toJson<String>(description),
};
}
@override
T createCompanion<T extends UpdateCompanion<Category>>(bool nullToAbsent) {
return CategoriesCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
description: description == null && nullToAbsent
? const Value.absent()
: Value(description),
) as T;
}
Category copyWith({int id, String description}) => Category(
id: id ?? this.id,
description: description ?? this.description,
);
@override
String toString() {
return (StringBuffer('Category(')
..write('id: $id, ')
..write('description: $description')
..write(')'))
.toString();
}
@override
int get hashCode => $mrjf($mrjc($mrjc(0, id.hashCode), description.hashCode));
@override
bool operator ==(other) =>
identical(this, other) ||
(other is Category && other.id == id && other.description == description);
}
class CategoriesCompanion extends UpdateCompanion<Category> {
final Value<int> id;
final Value<String> description;
const CategoriesCompanion({
this.id = const Value.absent(),
this.description = const Value.absent(),
});
}
class $CategoriesTable extends Categories
with TableInfo<$CategoriesTable, Category> {
final GeneratedDatabase _db;
final String _alias;
$CategoriesTable(this._db, [this._alias]);
final VerificationMeta _idMeta = const VerificationMeta('id');
GeneratedIntColumn _id;
@override
GeneratedIntColumn get id => _id ??= _constructId();
GeneratedIntColumn _constructId() {
return GeneratedIntColumn('id', $tableName, false, hasAutoIncrement: true);
}
final VerificationMeta _descriptionMeta =
const VerificationMeta('description');
GeneratedTextColumn _description;
@override
GeneratedTextColumn get description =>
_description ??= _constructDescription();
GeneratedTextColumn _constructDescription() {
return GeneratedTextColumn(
'description',
$tableName,
true,
);
}
@override
List<GeneratedColumn> get $columns => [id, description];
@override
$CategoriesTable get asDslTable => this;
@override
String get $tableName => _alias ?? 'categories';
@override
final String actualTableName = 'categories';
@override
VerificationContext validateIntegrity(CategoriesCompanion d,
{bool isInserting = false}) {
final context = VerificationContext();
if (d.id.present) {
context.handle(_idMeta, id.isAcceptableValue(d.id.value, _idMeta));
} else if (id.isRequired && isInserting) {
context.missing(_idMeta);
}
if (d.description.present) {
context.handle(_descriptionMeta,
description.isAcceptableValue(d.description.value, _descriptionMeta));
} else if (description.isRequired && isInserting) {
context.missing(_descriptionMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
Category map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
return Category.fromData(data, _db, prefix: effectivePrefix);
}
@override
Map<String, Variable> entityToSql(CategoriesCompanion d) {
final map = <String, Variable>{};
if (d.id.present) {
map['id'] = Variable<int, IntType>(d.id.value);
}
if (d.description.present) {
map['description'] = Variable<String, StringType>(d.description.value);
}
return map;
}
@override
$CategoriesTable createAlias(String alias) {
return $CategoriesTable(_db, alias);
}
}
class Recipe extends DataClass implements Insertable<Recipe> {
final int id;
final String title;
final String instructions;
final int category;
Recipe(
{@required this.id,
@required this.title,
@required this.instructions,
this.category});
factory Recipe.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return Recipe(
id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']),
title:
stringType.mapFromDatabaseResponse(data['${effectivePrefix}title']),
instructions: stringType
.mapFromDatabaseResponse(data['${effectivePrefix}instructions']),
category:
intType.mapFromDatabaseResponse(data['${effectivePrefix}category']),
);
}
factory Recipe.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return Recipe(
id: serializer.fromJson<int>(json['id']),
title: serializer.fromJson<String>(json['title']),
instructions: serializer.fromJson<String>(json['instructions']),
category: serializer.fromJson<int>(json['category']),
);
}
@override
Map<String, dynamic> toJson(
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return {
'id': serializer.toJson<int>(id),
'title': serializer.toJson<String>(title),
'instructions': serializer.toJson<String>(instructions),
'category': serializer.toJson<int>(category),
};
}
@override
T createCompanion<T extends UpdateCompanion<Recipe>>(bool nullToAbsent) {
return RecipesCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
title:
title == null && nullToAbsent ? const Value.absent() : Value(title),
instructions: instructions == null && nullToAbsent
? const Value.absent()
: Value(instructions),
category: category == null && nullToAbsent
? const Value.absent()
: Value(category),
) as T;
}
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
String toString() {
return (StringBuffer('Recipe(')
..write('id: $id, ')
..write('title: $title, ')
..write('instructions: $instructions, ')
..write('category: $category')
..write(')'))
.toString();
}
@override
int get hashCode => $mrjf($mrjc(
$mrjc(
$mrjc($mrjc(0, id.hashCode), title.hashCode), instructions.hashCode),
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 RecipesCompanion extends UpdateCompanion<Recipe> {
final Value<int> id;
final Value<String> title;
final Value<String> instructions;
final Value<int> category;
const RecipesCompanion({
this.id = const Value.absent(),
this.title = const Value.absent(),
this.instructions = const Value.absent(),
this.category = const Value.absent(),
});
}
class $RecipesTable extends Recipes with TableInfo<$RecipesTable, Recipe> {
final GeneratedDatabase _db;
final String _alias;
$RecipesTable(this._db, [this._alias]);
final VerificationMeta _idMeta = const VerificationMeta('id');
GeneratedIntColumn _id;
@override
GeneratedIntColumn get id => _id ??= _constructId();
GeneratedIntColumn _constructId() {
return GeneratedIntColumn('id', $tableName, false, hasAutoIncrement: true);
}
final VerificationMeta _titleMeta = const VerificationMeta('title');
GeneratedTextColumn _title;
@override
GeneratedTextColumn get title => _title ??= _constructTitle();
GeneratedTextColumn _constructTitle() {
return GeneratedTextColumn('title', $tableName, false, maxTextLength: 16);
}
final VerificationMeta _instructionsMeta =
const VerificationMeta('instructions');
GeneratedTextColumn _instructions;
@override
GeneratedTextColumn get instructions =>
_instructions ??= _constructInstructions();
GeneratedTextColumn _constructInstructions() {
return GeneratedTextColumn(
'instructions',
$tableName,
false,
);
}
final VerificationMeta _categoryMeta = const VerificationMeta('category');
GeneratedIntColumn _category;
@override
GeneratedIntColumn get category => _category ??= _constructCategory();
GeneratedIntColumn _constructCategory() {
return GeneratedIntColumn(
'category',
$tableName,
true,
);
}
@override
List<GeneratedColumn> get $columns => [id, title, instructions, category];
@override
$RecipesTable get asDslTable => this;
@override
String get $tableName => _alias ?? 'recipes';
@override
final String actualTableName = 'recipes';
@override
VerificationContext validateIntegrity(RecipesCompanion d,
{bool isInserting = false}) {
final context = VerificationContext();
if (d.id.present) {
context.handle(_idMeta, id.isAcceptableValue(d.id.value, _idMeta));
} else if (id.isRequired && isInserting) {
context.missing(_idMeta);
}
if (d.title.present) {
context.handle(
_titleMeta, title.isAcceptableValue(d.title.value, _titleMeta));
} else if (title.isRequired && isInserting) {
context.missing(_titleMeta);
}
if (d.instructions.present) {
context.handle(
_instructionsMeta,
instructions.isAcceptableValue(
d.instructions.value, _instructionsMeta));
} else if (instructions.isRequired && isInserting) {
context.missing(_instructionsMeta);
}
if (d.category.present) {
context.handle(_categoryMeta,
category.isAcceptableValue(d.category.value, _categoryMeta));
} else if (category.isRequired && isInserting) {
context.missing(_categoryMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
Recipe map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
return Recipe.fromData(data, _db, prefix: effectivePrefix);
}
@override
Map<String, Variable> entityToSql(RecipesCompanion d) {
final map = <String, Variable>{};
if (d.id.present) {
map['id'] = Variable<int, IntType>(d.id.value);
}
if (d.title.present) {
map['title'] = Variable<String, StringType>(d.title.value);
}
if (d.instructions.present) {
map['instructions'] = Variable<String, StringType>(d.instructions.value);
}
if (d.category.present) {
map['category'] = Variable<int, IntType>(d.category.value);
}
return map;
}
@override
$RecipesTable createAlias(String alias) {
return $RecipesTable(_db, alias);
}
}
class Ingredient extends DataClass implements Insertable<Ingredient> {
final int id;
final String name;
final int caloriesPer100g;
Ingredient(
{@required this.id, @required this.name, @required this.caloriesPer100g});
factory Ingredient.fromData(Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
final intType = db.typeSystem.forDartType<int>();
final stringType = db.typeSystem.forDartType<String>();
return Ingredient(
id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']),
name: stringType.mapFromDatabaseResponse(data['${effectivePrefix}name']),
caloriesPer100g:
intType.mapFromDatabaseResponse(data['${effectivePrefix}calories']),
);
}
factory Ingredient.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return Ingredient(
id: serializer.fromJson<int>(json['id']),
name: serializer.fromJson<String>(json['name']),
caloriesPer100g: serializer.fromJson<int>(json['caloriesPer100g']),
);
}
@override
Map<String, dynamic> toJson(
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return {
'id': serializer.toJson<int>(id),
'name': serializer.toJson<String>(name),
'caloriesPer100g': serializer.toJson<int>(caloriesPer100g),
};
}
@override
T createCompanion<T extends UpdateCompanion<Ingredient>>(bool nullToAbsent) {
return IngredientsCompanion(
id: id == null && nullToAbsent ? const Value.absent() : Value(id),
name: name == null && nullToAbsent ? const Value.absent() : Value(name),
caloriesPer100g: caloriesPer100g == null && nullToAbsent
? const Value.absent()
: Value(caloriesPer100g),
) as T;
}
Ingredient copyWith({int id, String name, int caloriesPer100g}) => Ingredient(
id: id ?? this.id,
name: name ?? this.name,
caloriesPer100g: caloriesPer100g ?? this.caloriesPer100g,
);
@override
String toString() {
return (StringBuffer('Ingredient(')
..write('id: $id, ')
..write('name: $name, ')
..write('caloriesPer100g: $caloriesPer100g')
..write(')'))
.toString();
}
@override
int get hashCode => $mrjf($mrjc(
$mrjc($mrjc(0, id.hashCode), name.hashCode), caloriesPer100g.hashCode));
@override
bool operator ==(other) =>
identical(this, other) ||
(other is Ingredient &&
other.id == id &&
other.name == name &&
other.caloriesPer100g == caloriesPer100g);
}
class IngredientsCompanion extends UpdateCompanion<Ingredient> {
final Value<int> id;
final Value<String> name;
final Value<int> caloriesPer100g;
const IngredientsCompanion({
this.id = const Value.absent(),
this.name = const Value.absent(),
this.caloriesPer100g = const Value.absent(),
});
}
class $IngredientsTable extends Ingredients
with TableInfo<$IngredientsTable, Ingredient> {
final GeneratedDatabase _db;
final String _alias;
$IngredientsTable(this._db, [this._alias]);
final VerificationMeta _idMeta = const VerificationMeta('id');
GeneratedIntColumn _id;
@override
GeneratedIntColumn get id => _id ??= _constructId();
GeneratedIntColumn _constructId() {
return GeneratedIntColumn('id', $tableName, false, hasAutoIncrement: true);
}
final VerificationMeta _nameMeta = const VerificationMeta('name');
GeneratedTextColumn _name;
@override
GeneratedTextColumn get name => _name ??= _constructName();
GeneratedTextColumn _constructName() {
return GeneratedTextColumn(
'name',
$tableName,
false,
);
}
final VerificationMeta _caloriesPer100gMeta =
const VerificationMeta('caloriesPer100g');
GeneratedIntColumn _caloriesPer100g;
@override
GeneratedIntColumn get caloriesPer100g =>
_caloriesPer100g ??= _constructCaloriesPer100g();
GeneratedIntColumn _constructCaloriesPer100g() {
return GeneratedIntColumn(
'calories',
$tableName,
false,
);
}
@override
List<GeneratedColumn> get $columns => [id, name, caloriesPer100g];
@override
$IngredientsTable get asDslTable => this;
@override
String get $tableName => _alias ?? 'ingredients';
@override
final String actualTableName = 'ingredients';
@override
VerificationContext validateIntegrity(IngredientsCompanion d,
{bool isInserting = false}) {
final context = VerificationContext();
if (d.id.present) {
context.handle(_idMeta, id.isAcceptableValue(d.id.value, _idMeta));
} else if (id.isRequired && isInserting) {
context.missing(_idMeta);
}
if (d.name.present) {
context.handle(
_nameMeta, name.isAcceptableValue(d.name.value, _nameMeta));
} else if (name.isRequired && isInserting) {
context.missing(_nameMeta);
}
if (d.caloriesPer100g.present) {
context.handle(
_caloriesPer100gMeta,
caloriesPer100g.isAcceptableValue(
d.caloriesPer100g.value, _caloriesPer100gMeta));
} else if (caloriesPer100g.isRequired && isInserting) {
context.missing(_caloriesPer100gMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
Ingredient map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
return Ingredient.fromData(data, _db, prefix: effectivePrefix);
}
@override
Map<String, Variable> entityToSql(IngredientsCompanion d) {
final map = <String, Variable>{};
if (d.id.present) {
map['id'] = Variable<int, IntType>(d.id.value);
}
if (d.name.present) {
map['name'] = Variable<String, StringType>(d.name.value);
}
if (d.caloriesPer100g.present) {
map['calories'] = Variable<int, IntType>(d.caloriesPer100g.value);
}
return map;
}
@override
$IngredientsTable createAlias(String alias) {
return $IngredientsTable(_db, alias);
}
}
class IngredientInRecipe extends DataClass
implements Insertable<IngredientInRecipe> {
final int recipe;
final int ingredient;
final int amountInGrams;
IngredientInRecipe(
{@required this.recipe,
@required this.ingredient,
@required this.amountInGrams});
factory IngredientInRecipe.fromData(
Map<String, dynamic> data, GeneratedDatabase db,
{String prefix}) {
final effectivePrefix = prefix ?? '';
final intType = db.typeSystem.forDartType<int>();
return IngredientInRecipe(
recipe: intType.mapFromDatabaseResponse(data['${effectivePrefix}recipe']),
ingredient:
intType.mapFromDatabaseResponse(data['${effectivePrefix}ingredient']),
amountInGrams:
intType.mapFromDatabaseResponse(data['${effectivePrefix}amount']),
);
}
factory IngredientInRecipe.fromJson(Map<String, dynamic> json,
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return IngredientInRecipe(
recipe: serializer.fromJson<int>(json['recipe']),
ingredient: serializer.fromJson<int>(json['ingredient']),
amountInGrams: serializer.fromJson<int>(json['amountInGrams']),
);
}
@override
Map<String, dynamic> toJson(
{ValueSerializer serializer = const ValueSerializer.defaults()}) {
return {
'recipe': serializer.toJson<int>(recipe),
'ingredient': serializer.toJson<int>(ingredient),
'amountInGrams': serializer.toJson<int>(amountInGrams),
};
}
@override
T createCompanion<T extends UpdateCompanion<IngredientInRecipe>>(
bool nullToAbsent) {
return IngredientInRecipesCompanion(
recipe:
recipe == null && nullToAbsent ? const Value.absent() : Value(recipe),
ingredient: ingredient == null && nullToAbsent
? const Value.absent()
: Value(ingredient),
amountInGrams: amountInGrams == null && nullToAbsent
? const Value.absent()
: Value(amountInGrams),
) as T;
}
IngredientInRecipe copyWith(
{int recipe, int ingredient, int amountInGrams}) =>
IngredientInRecipe(
recipe: recipe ?? this.recipe,
ingredient: ingredient ?? this.ingredient,
amountInGrams: amountInGrams ?? this.amountInGrams,
);
@override
String toString() {
return (StringBuffer('IngredientInRecipe(')
..write('recipe: $recipe, ')
..write('ingredient: $ingredient, ')
..write('amountInGrams: $amountInGrams')
..write(')'))
.toString();
}
@override
int get hashCode => $mrjf($mrjc(
$mrjc($mrjc(0, recipe.hashCode), ingredient.hashCode),
amountInGrams.hashCode));
@override
bool operator ==(other) =>
identical(this, other) ||
(other is IngredientInRecipe &&
other.recipe == recipe &&
other.ingredient == ingredient &&
other.amountInGrams == amountInGrams);
}
class IngredientInRecipesCompanion extends UpdateCompanion<IngredientInRecipe> {
final Value<int> recipe;
final Value<int> ingredient;
final Value<int> amountInGrams;
const IngredientInRecipesCompanion({
this.recipe = const Value.absent(),
this.ingredient = const Value.absent(),
this.amountInGrams = const Value.absent(),
});
}
class $IngredientInRecipesTable extends IngredientInRecipes
with TableInfo<$IngredientInRecipesTable, IngredientInRecipe> {
final GeneratedDatabase _db;
final String _alias;
$IngredientInRecipesTable(this._db, [this._alias]);
final VerificationMeta _recipeMeta = const VerificationMeta('recipe');
GeneratedIntColumn _recipe;
@override
GeneratedIntColumn get recipe => _recipe ??= _constructRecipe();
GeneratedIntColumn _constructRecipe() {
return GeneratedIntColumn(
'recipe',
$tableName,
false,
);
}
final VerificationMeta _ingredientMeta = const VerificationMeta('ingredient');
GeneratedIntColumn _ingredient;
@override
GeneratedIntColumn get ingredient => _ingredient ??= _constructIngredient();
GeneratedIntColumn _constructIngredient() {
return GeneratedIntColumn(
'ingredient',
$tableName,
false,
);
}
final VerificationMeta _amountInGramsMeta =
const VerificationMeta('amountInGrams');
GeneratedIntColumn _amountInGrams;
@override
GeneratedIntColumn get amountInGrams =>
_amountInGrams ??= _constructAmountInGrams();
GeneratedIntColumn _constructAmountInGrams() {
return GeneratedIntColumn(
'amount',
$tableName,
false,
);
}
@override
List<GeneratedColumn> get $columns => [recipe, ingredient, amountInGrams];
@override
$IngredientInRecipesTable get asDslTable => this;
@override
String get $tableName => _alias ?? 'recipe_ingredients';
@override
final String actualTableName = 'recipe_ingredients';
@override
VerificationContext validateIntegrity(IngredientInRecipesCompanion d,
{bool isInserting = false}) {
final context = VerificationContext();
if (d.recipe.present) {
context.handle(
_recipeMeta, recipe.isAcceptableValue(d.recipe.value, _recipeMeta));
} else if (recipe.isRequired && isInserting) {
context.missing(_recipeMeta);
}
if (d.ingredient.present) {
context.handle(_ingredientMeta,
ingredient.isAcceptableValue(d.ingredient.value, _ingredientMeta));
} else if (ingredient.isRequired && isInserting) {
context.missing(_ingredientMeta);
}
if (d.amountInGrams.present) {
context.handle(
_amountInGramsMeta,
amountInGrams.isAcceptableValue(
d.amountInGrams.value, _amountInGramsMeta));
} else if (amountInGrams.isRequired && isInserting) {
context.missing(_amountInGramsMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {recipe, ingredient};
@override
IngredientInRecipe map(Map<String, dynamic> data, {String tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null;
return IngredientInRecipe.fromData(data, _db, prefix: effectivePrefix);
}
@override
Map<String, Variable> entityToSql(IngredientInRecipesCompanion d) {
final map = <String, Variable>{};
if (d.recipe.present) {
map['recipe'] = Variable<int, IntType>(d.recipe.value);
}
if (d.ingredient.present) {
map['ingredient'] = Variable<int, IntType>(d.ingredient.value);
}
if (d.amountInGrams.present) {
map['amount'] = Variable<int, IntType>(d.amountInGrams.value);
}
return map;
}
@override
$IngredientInRecipesTable createAlias(String alias) {
return $IngredientInRecipesTable(_db, alias);
}
}
abstract class _$Database extends GeneratedDatabase {
_$Database(QueryExecutor e) : super(const SqlTypeSystem.withDefaults(), e);
$CategoriesTable _categories;
$CategoriesTable get categories => _categories ??= $CategoriesTable(this);
$RecipesTable _recipes;
$RecipesTable get recipes => _recipes ??= $RecipesTable(this);
$IngredientsTable _ingredients;
$IngredientsTable get ingredients => _ingredients ??= $IngredientsTable(this);
$IngredientInRecipesTable _ingredientInRecipes;
$IngredientInRecipesTable get ingredientInRecipes =>
_ingredientInRecipes ??= $IngredientInRecipesTable(this);
@override
List<TableInfo> get allTables =>
[categories, recipes, ingredients, ingredientInRecipes];
}

View File

@ -28,6 +28,7 @@ class UseMoor {
/// Optionally, a list of queries. Moor will generate matching methods for the
/// variables and return types.
// todo better documentation
@experimental
final List<Sql> queries;
/// Use this class as an annotation to inform moor_generator that a database
@ -64,6 +65,7 @@ class UseDao {
/// The tables accessed by this DAO.
final List<Type> tables;
// todo better documentation
@experimental
final List<Sql> queries;
const UseDao({@required this.tables, this.queries});

View File

@ -52,7 +52,15 @@ class TableWithoutPK extends Table {
RealColumn get someFloat => real()();
}
@UseMoor(tables: [TodosTable, Categories, Users, SharedTodos, TableWithoutPK])
@UseMoor(
tables: [TodosTable, Categories, Users, SharedTodos, TableWithoutPK],
queries: [
Sql(
'allTodosWithCategory',
'SELECT t.*, c.id as catId, c."desc" as catDesc '
'FROM todos t INNER JOIN categories c ON c.id = t.category'),
],
)
class TodoDb extends _$TodoDb {
TodoDb(QueryExecutor e) : super(e);

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,13 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:moor_generator/src/model/specified_table.dart';
import 'package:moor_generator/src/model/sql_query.dart';
class SpecifiedDatabase {
final ClassElement fromClass;
final List<SpecifiedTable> tables;
final List<DartType> daos;
final List<SqlQuery> queries;
SpecifiedDatabase(this.fromClass, this.tables, this.daos);
SpecifiedDatabase(this.fromClass, this.tables, this.daos, this.queries);
}

View File

@ -0,0 +1,34 @@
import 'package:moor_generator/src/model/specified_column.dart';
import 'package:moor_generator/src/model/specified_table.dart';
abstract class SqlQuery {
final String name;
SqlQuery(this.name);
}
class SqlSelectQuery extends SqlQuery {
final List<SpecifiedTable> readsFrom;
final InferredResultSet resultSet;
SqlSelectQuery(String name, this.readsFrom, this.resultSet) : super(name);
}
class InferredResultSet {
/// If the result columns of a SELECT statement exactly match one table, we
/// can just use the data class generated for that table. Otherwise, we'd have
/// to create another class.
// todo implement this check
final SpecifiedTable matchingTable;
final List<ResultColumn> columns;
InferredResultSet(this.matchingTable, this.columns);
}
class ResultColumn {
final String name;
final ColumnType type;
final bool nullable;
ResultColumn(this.name, this.type, this.nullable);
}

View File

@ -13,6 +13,9 @@ import 'package:moor_generator/src/parser/table_parser.dart';
import 'package:moor_generator/src/writer/database_writer.dart';
import 'package:source_gen/source_gen.dart';
import 'model/sql_query.dart';
import 'parser/sql/sql_parser.dart';
class MoorGenerator extends GeneratorForAnnotation<UseMoor> {
//final Map<String, ParsedLibraryResult> _astForLibs = {};
final ErrorStore errors = ErrorStore();
@ -47,11 +50,13 @@ class MoorGenerator extends GeneratorForAnnotation<UseMoor> {
.listValue
.map((obj) => obj.toTypeValue())
.toList();
final queries = annotation.peek('queries')?.listValue ?? [];
tableParser ??= TableParser(this);
columnParser ??= ColumnParser(this);
final tablesForThisDb = <SpecifiedTable>[];
var resolvedQueries = <SqlQuery>[];
for (var table in tableTypes) {
if (!tableTypeChecker.isAssignableFrom(table.element)) {
@ -67,7 +72,7 @@ class MoorGenerator extends GeneratorForAnnotation<UseMoor> {
}
if (errors.errors.isNotEmpty) {
print('Warning: There were some errors whily running moor_generator:');
print('Warning: There were some errors while running moor_generator:');
for (var error in errors.errors) {
print(error.message);
@ -80,10 +85,17 @@ class MoorGenerator extends GeneratorForAnnotation<UseMoor> {
errors.errors.clear();
}
if (queries.isNotEmpty) {
final parser = SqlParser(options, tablesForThisDb, queries)..parse();
errors.errors.addAll(parser.errors);
resolvedQueries = parser.foundQueries;
}
if (_foundTables.isEmpty) return '';
final specifiedDb =
SpecifiedDatabase(element as ClassElement, tablesForThisDb, daoTypes);
final specifiedDb = SpecifiedDatabase(
element as ClassElement, tablesForThisDb, daoTypes, resolvedQueries);
final buffer = StringBuffer()
..write('// ignore_for_file: unnecessary_brace_in_string_interps\n');

View File

@ -0,0 +1,26 @@
import 'package:sqlparser/sqlparser.dart';
/// An AST-visitor that walks sql statements and finds all tables referenced in
/// them.
class AffectedTablesVisitor extends RecursiveVisitor<void> {
final Set<Table> foundTables = {};
@override
void visitReference(Reference e) {
final column = e.resolved as Column;
if (column is TableColumn) {
foundTables.add(column.table);
}
visitChildren(e);
}
@override
void visitQueryable(Queryable e) {
if (e is TableReference) {
foundTables.add(e.resolved as Table);
}
visitChildren(e);
}
}

View File

@ -0,0 +1,126 @@
import 'package:analyzer/dart/constant/value.dart';
import 'package:moor_generator/src/errors.dart';
import 'package:moor_generator/src/model/specified_column.dart';
import 'package:moor_generator/src/model/specified_table.dart';
import 'package:moor_generator/src/model/sql_query.dart';
import 'package:moor_generator/src/options.dart';
import 'package:sqlparser/sqlparser.dart' hide ResultColumn;
import 'affected_tables_visitor.dart';
class SqlParser {
final MoorOptions options;
final List<SpecifiedTable> tables;
final List<DartObject> definedQueries;
SqlEngine _engine;
final Map<Table, SpecifiedTable> _engineTablesToSpecified = {};
final List<SqlQuery> foundQueries = [];
final List<MoorError> errors = [];
SqlParser(this.options, this.tables, this.definedQueries);
void _spawnEngine() {
_engine = SqlEngine();
tables.map(_extractStructure).forEach(_engine.registerTable);
}
/// Convert a [SpecifiedTable] from moor into something that can be understood
/// by the sqlparser library.
Table _extractStructure(SpecifiedTable table) {
final columns = <TableColumn>[];
for (var specified in table.columns) {
final type = _resolveForColumnType(specified.type)
.withNullable(specified.nullable);
columns.add(TableColumn(specified.name.name, type));
}
final engineTable = Table(name: table.sqlName, resolvedColumns: columns);
_engineTablesToSpecified[engineTable] = table;
return engineTable;
}
ResolvedType _resolveForColumnType(ColumnType type) {
switch (type) {
case ColumnType.integer:
return const ResolvedType(type: BasicType.int);
case ColumnType.text:
return const ResolvedType(type: BasicType.text);
case ColumnType.boolean:
return const ResolvedType(type: BasicType.int, hint: IsBoolean());
case ColumnType.datetime:
return const ResolvedType(type: BasicType.int, hint: IsDateTime());
case ColumnType.blob:
return const ResolvedType(type: BasicType.blob);
case ColumnType.real:
return const ResolvedType(type: BasicType.real);
}
throw StateError('cant happen');
}
ColumnType _resolvedToMoor(ResolvedType type) {
switch (type.type) {
case BasicType.nullType:
return ColumnType.text;
case BasicType.int:
if (type.hint is IsBoolean) {
return ColumnType.boolean;
} else if (type.hint is IsDateTime) {
return ColumnType.datetime;
}
return ColumnType.integer;
case BasicType.real:
return ColumnType.real;
case BasicType.text:
return ColumnType.text;
case BasicType.blob:
return ColumnType.blob;
}
throw StateError('Unexpected type: $type');
}
void parse() {
_spawnEngine();
for (var query in definedQueries) {
final name = query.getField('name').toStringValue();
final sql = query.getField('query').toStringValue();
final context = _engine.analyze(sql);
for (var error in context.errors) {
errors.add(MoorError(
message: 'The sql query $sql is invalid: ${error.message}',
));
}
final root = context.root;
if (root is SelectStatement) {
_handleSelect(name, root, context);
} else {
throw StateError('Unexpected sql');
}
}
}
void _handleSelect(
String queryName, SelectStatement stmt, AnalysisContext ctx) {
final tableFinder = AffectedTablesVisitor();
stmt.accept(tableFinder);
final foundTables = tableFinder.foundTables;
final moorTables = foundTables.map((t) => _engineTablesToSpecified[t]);
final resultColumns = stmt.resolvedColumns;
final moorColumns = <ResultColumn>[];
for (var column in resultColumns) {
final type = ctx.typeOf(column).type;
moorColumns
.add(ResultColumn(column.name, _resolvedToMoor(type), type.nullable));
}
final resultSet = InferredResultSet(null, moorColumns);
foundQueries.add(SqlSelectQuery(queryName, moorTables.toList(), resultSet));
}
}

View File

@ -21,7 +21,8 @@ dependencies:
build_config: '>=0.3.1 <1.0.0'
moor: ^1.4.0
meta: '>= 1.0.0 <2.0.0'
sqlparser:
path: ../sqlparser
dev_dependencies:
test: ^1.6.0
test_api: ^0.2.0

View File

@ -11,7 +11,9 @@ class TableColumn extends Column {
final String name;
final ResolvedType type;
const TableColumn(this.name, this.type);
Table table;
TableColumn(this.name, this.type);
}
class ExpressionColumn extends Column {

View File

@ -23,5 +23,9 @@ class Table with ResultSet, VisibleToChildren {
@override
final List<TableColumn> resolvedColumns;
Table({@required this.name, this.resolvedColumns});
Table({@required this.name, this.resolvedColumns}) {
for (var column in resolvedColumns) {
column.table = this;
}
}
}

View File

@ -23,6 +23,7 @@ class ColumnResolver extends RecursiveVisitor<void> {
availableColumns.addAll(select.statement.resolvedColumns);
},
isJoin: (join) {
_handle(join.primary, availableColumns);
for (var query in join.joins.map((j) => j.query)) {
_handle(query, availableColumns);
}
@ -52,6 +53,7 @@ class ColumnResolver extends RecursiveVisitor<void> {
relevantNode: resultColumn,
));
});
usedColumns.addAll(tableResolver.resultSet.resolvedColumns);
} else {
// we have a * column, that would be all available columns

View File

@ -18,6 +18,7 @@ part 'expressions/subquery.dart';
part 'expressions/variables.dart';
part 'statements/select.dart';
part 'statements/statement.dart';
abstract class AstNode {
/// The parent of this node, or null if this is the root node. Will be set

View File

@ -1,6 +1,6 @@
part of '../ast.dart';
class SelectStatement extends AstNode with ResultSet {
class SelectStatement extends Statement with ResultSet {
final bool distinct;
final List<ResultColumn> columns;
final List<Queryable> from;

View File

@ -0,0 +1,3 @@
part of '../ast.dart';
abstract class Statement extends AstNode {}

View File

@ -37,7 +37,7 @@ class SqlEngine {
// todo error handling from scanner
final parser = Parser(tokens);
return parser.select();
return parser.statement();
}
AnalysisContext analyze(String sql) {

View File

@ -92,6 +92,12 @@ class Parser {
_error(message);
}
Statement statement() {
final stmt = select();
_matchOne(TokenType.semicolon);
return stmt;
}
/// Parses a [SelectStatement], or returns null if there is no select token
/// after the current position.
///

View File

@ -112,6 +112,9 @@ class Scanner {
case ':':
_addToken(TokenType.colon);
break;
case ';':
_addToken(TokenType.semicolon);
break;
case 'x':
if (_match("'")) {

View File

@ -76,6 +76,7 @@ enum TokenType {
limit,
offset,
semicolon,
eof,
}

View File

@ -12,9 +12,9 @@ void main() {
});
test('correctly resolves return columns', () {
final id = const TableColumn('id', ResolvedType(type: BasicType.int));
final id = TableColumn('id', const ResolvedType(type: BasicType.int));
final content =
const TableColumn('content', ResolvedType(type: BasicType.text));
TableColumn('content', const ResolvedType(type: BasicType.text));
final demoTable = Table(
name: 'demo',

View File

@ -11,9 +11,9 @@ Map<String, ResolveResult> _types = {
};
void main() {
final id = const TableColumn('id', ResolvedType(type: BasicType.int));
final id = TableColumn('id', const ResolvedType(type: BasicType.int));
final content =
const TableColumn('content', ResolvedType(type: BasicType.text));
TableColumn('content', const ResolvedType(type: BasicType.text));
final demoTable = Table(
name: 'demo',