fix lists + double

This commit is contained in:
Tobe O 2019-01-26 23:14:20 -05:00
parent 50c4b394c7
commit 24fb936a86
24 changed files with 295 additions and 91 deletions

View file

@ -1,3 +1,7 @@
# 2.0.0-dev.19
* Implement cast-based `double` support.
* Finish `ListSqlExpressionBuilder`.
# 2.0.0-dev.18 # 2.0.0-dev.18
* Add `ListSqlExpressionBuilder` (still in development). * Add `ListSqlExpressionBuilder` (still in development).

View file

@ -36,6 +36,7 @@ String sanitizeExpression(String unsafe) {
abstract class SqlExpressionBuilder<T> { abstract class SqlExpressionBuilder<T> {
final Query query; final Query query;
final String columnName; final String columnName;
String _cast;
bool _isProperty = false; bool _isProperty = false;
String _substitution; String _substitution;
@ -75,7 +76,10 @@ class NumericSqlExpressionBuilder<T extends num>
String compile() { String compile() {
if (_raw != null) return _raw; if (_raw != null) return _raw;
if (_value == null) return null; if (_value == null) return null;
return '$_op $_value'; var v = _value.toString();
if (T == double) v = 'CAST ("$v" as decimal)';
if (_cast != null) v = 'CAST ($v AS $_cast)';
return '$_op $v';
} }
operator <(T value) => _change('<', value); operator <(T value) => _change('<', value);
@ -306,6 +310,7 @@ class BooleanSqlExpressionBuilder extends SqlExpressionBuilder<bool> {
if (_raw != null) return _raw; if (_raw != null) return _raw;
if (_value == null) return null; if (_value == null) return null;
var v = _value ? 'TRUE' : 'FALSE'; var v = _value ? 'TRUE' : 'FALSE';
if (_cast != null) v = 'CAST ($v AS $_cast)';
return '$_op $v'; return '$_op $v';
} }
@ -555,17 +560,16 @@ class JsonSqlExpressionBuilderProperty {
throw StateError( throw StateError(
'$nameString is already typed as $_typed, and cannot be changed.'); '$nameString is already typed as $_typed, and cannot be changed.');
} else { } else {
_typed = value().._isProperty = true; _typed = value()
.._cast = 'text'
.._isProperty = true;
return _typed as T; return _typed as T;
} }
} }
String get nameString { String get nameString {
if (isInt) { var n = isInt ? name : "'$name'";
return '(${builder.columnName}->>$name)::jsonb'; return '${builder.columnName}::jsonb->>$n';
} else {
return "(${builder.columnName}->>'$name')::jsonb";
}
} }
void isNotNull() { void isNotNull() {

View file

@ -7,6 +7,10 @@ bool isAscii(int ch) => ch >= $nul && ch <= $del;
/// A base class for objects that compile to SQL queries, typically within an ORM. /// A base class for objects that compile to SQL queries, typically within an ORM.
abstract class QueryBase<T> { abstract class QueryBase<T> {
/// Casts to perform when querying the database.
Map<String, String> get casts => {};
/// Values to insert into a prepared statement.
final Map<String, dynamic> substitutionValues = {}; final Map<String, dynamic> substitutionValues = {};
/// The list of fields returned by this query. /// The list of fields returned by this query.
@ -15,15 +19,18 @@ abstract class QueryBase<T> {
List<String> get fields; List<String> get fields;
/// A String of all [fields], joined by a comma (`,`). /// A String of all [fields], joined by a comma (`,`).
String get fieldSet => fields.join(', '); String get fieldSet => fields.map((k) {
var cast = casts[k];
return cast == null ? k : 'CAST ($k AS $cast)';
}).join(', ');
String compile( String compile(Set<String> trampoline,
{bool includeTableName: false, String preamble, bool withFields: true}); {bool includeTableName: false, String preamble, bool withFields: true});
T deserialize(List row); T deserialize(List row);
Future<List<T>> get(QueryExecutor executor) async { Future<List<T>> get(QueryExecutor executor) async {
var sql = compile(); var sql = compile(Set());
return executor return executor
.query(sql, substitutionValues) .query(sql, substitutionValues)
.then((it) => it.map(deserialize).toList()); .then((it) => it.map(deserialize).toList());
@ -187,52 +194,77 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
String _joinAlias() => 'a${_joins.length}'; String _joinAlias() => 'a${_joins.length}';
String _compileJoin(tableName, Set<String> trampoline) {
if (tableName is String) return tableName;
if (tableName is Query) {
var c = tableName.compile(trampoline);
if (c == null) return c;
return '($c)';
}
throw ArgumentError.value(
tableName, 'tableName', 'must be a String or Query');
}
/// Execute an `INNER JOIN` against another table. /// Execute an `INNER JOIN` against another table.
void join(String tableName, String localKey, String foreignKey, void join(tableName, String localKey, String foreignKey,
{String op: '=', List<String> additionalFields: const []}) { {String op: '=',
_joins.add(new JoinBuilder( List<String> additionalFields: const [],
JoinType.inner, this, tableName, localKey, foreignKey, Set<String> trampoline}) {
_joins.add(new JoinBuilder(JoinType.inner, this,
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
op: op, alias: _joinAlias(), additionalFields: additionalFields)); op: op, alias: _joinAlias(), additionalFields: additionalFields));
} }
/// Execute a `LEFT JOIN` against another table. /// Execute a `LEFT JOIN` against another table.
void leftJoin(String tableName, String localKey, String foreignKey, void leftJoin(tableName, String localKey, String foreignKey,
{String op: '=', List<String> additionalFields: const []}) { {String op: '=',
_joins.add(new JoinBuilder( List<String> additionalFields: const [],
JoinType.left, this, tableName, localKey, foreignKey, Set<String> trampoline}) {
_joins.add(new JoinBuilder(JoinType.left, this,
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
op: op, alias: _joinAlias(), additionalFields: additionalFields)); op: op, alias: _joinAlias(), additionalFields: additionalFields));
} }
/// Execute a `RIGHT JOIN` against another table. /// Execute a `RIGHT JOIN` against another table.
void rightJoin(String tableName, String localKey, String foreignKey, void rightJoin(tableName, String localKey, String foreignKey,
{String op: '=', List<String> additionalFields: const []}) { {String op: '=',
_joins.add(new JoinBuilder( List<String> additionalFields: const [],
JoinType.right, this, tableName, localKey, foreignKey, Set<String> trampoline}) {
_joins.add(new JoinBuilder(JoinType.right, this,
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
op: op, alias: _joinAlias(), additionalFields: additionalFields)); op: op, alias: _joinAlias(), additionalFields: additionalFields));
} }
/// Execute a `FULL OUTER JOIN` against another table. /// Execute a `FULL OUTER JOIN` against another table.
void fullOuterJoin(String tableName, String localKey, String foreignKey, void fullOuterJoin(tableName, String localKey, String foreignKey,
{String op: '=', List<String> additionalFields: const []}) { {String op: '=',
_joins.add(new JoinBuilder( List<String> additionalFields: const [],
JoinType.full, this, tableName, localKey, foreignKey, Set<String> trampoline}) {
_joins.add(new JoinBuilder(JoinType.full, this,
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
op: op, alias: _joinAlias(), additionalFields: additionalFields)); op: op, alias: _joinAlias(), additionalFields: additionalFields));
} }
/// Execute a `SELF JOIN`. /// Execute a `SELF JOIN`.
void selfJoin(String tableName, String localKey, String foreignKey, void selfJoin(tableName, String localKey, String foreignKey,
{String op: '=', List<String> additionalFields: const []}) { {String op: '=',
_joins.add(new JoinBuilder( List<String> additionalFields: const [],
JoinType.self, this, tableName, localKey, foreignKey, Set<String> trampoline}) {
_joins.add(new JoinBuilder(JoinType.self, this,
_compileJoin(tableName, trampoline ?? Set()), localKey, foreignKey,
op: op, alias: _joinAlias(), additionalFields: additionalFields)); op: op, alias: _joinAlias(), additionalFields: additionalFields));
} }
@override @override
String compile( String compile(Set<String> trampoline,
{bool includeTableName: false, {bool includeTableName: false,
String preamble, String preamble,
bool withFields: true, bool withFields: true,
String fromQuery}) { String fromQuery}) {
if (!trampoline.add(tableName)) {
return null;
}
includeTableName = includeTableName || _joins.isNotEmpty; includeTableName = includeTableName || _joins.isNotEmpty;
var b = new StringBuffer(preamble ?? 'SELECT'); var b = new StringBuffer(preamble ?? 'SELECT');
b.write(' '); b.write(' ');
@ -241,8 +273,12 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
if (fields == null) { if (fields == null) {
f = ['*']; f = ['*'];
} else { } else {
f = new List<String>.from( f = new List<String>.from(fields.map((s) {
fields.map((s) => includeTableName ? '$tableName.$s' : s)); var ss = includeTableName ? '$tableName.$s' : s;
var cast = casts[s];
if (cast != null) ss = 'CAST ($ss AS $cast)';
return ss;
}));
_joins.forEach((j) { _joins.forEach((j) {
f f
..add(j.fieldName) ..add(j.fieldName)
@ -256,7 +292,10 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
// No joins if it's not a select. // No joins if it's not a select.
if (preamble == null) { if (preamble == null) {
if (_crossJoin != null) b.write(' CROSS JOIN $_crossJoin'); if (_crossJoin != null) b.write(' CROSS JOIN $_crossJoin');
for (var join in _joins) b.write(' ${join.compile()}'); for (var join in _joins) {
var c = join.compile();
if (c != null) b.write(' $c');
}
} }
var whereClause = var whereClause =
@ -276,7 +315,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
} }
Future<List<T>> delete(QueryExecutor executor) { Future<List<T>> delete(QueryExecutor executor) {
var sql = compile(preamble: 'DELETE', withFields: false); var sql = compile(Set(), preamble: 'DELETE', withFields: false);
if (_joins.isEmpty) { if (_joins.isEmpty) {
return executor return executor
@ -305,7 +344,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
} else { } else {
// TODO: How to do this in a non-Postgres DB? // TODO: How to do this in a non-Postgres DB?
var returning = fields.map(adornWithTableName).join(', '); var returning = fields.map(adornWithTableName).join(', ');
var sql = compile(); var sql = compile(Set());
sql = 'WITH $tableName as ($insertion RETURNING $returning) ' + sql; sql = 'WITH $tableName as ($insertion RETURNING $returning) ' + sql;
return executor return executor
.query(sql, substitutionValues) .query(sql, substitutionValues)
@ -326,7 +365,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
if (_limit != null) updateSql.write(' LIMIT $_limit'); if (_limit != null) updateSql.write(' LIMIT $_limit');
var returning = fields.map(adornWithTableName).join(', '); var returning = fields.map(adornWithTableName).join(', ');
var sql = compile(); var sql = compile(Set());
sql = 'WITH $tableName as ($updateSql RETURNING $returning) ' + sql; sql = 'WITH $tableName as ($updateSql RETURNING $returning) ' + sql;
return executor return executor
@ -480,10 +519,12 @@ class Union<T> extends QueryBase<T> {
T deserialize(List row) => left.deserialize(row); T deserialize(List row) => left.deserialize(row);
@override @override
String compile( String compile(Set<String> trampoline,
{bool includeTableName: false, String preamble, bool withFields: true}) { {bool includeTableName: false, String preamble, bool withFields: true}) {
var selector = all == true ? 'UNION ALL' : 'UNION'; var selector = all == true ? 'UNION ALL' : 'UNION';
return '(${left.compile(includeTableName: includeTableName)}) $selector (${right.compile(includeTableName: includeTableName)})'; var t1 = Set<String>.from(trampoline);
var t2 = Set<String>.from(trampoline);
return '(${left.compile(t1, includeTableName: includeTableName)}) $selector (${right.compile(t2, includeTableName: includeTableName)})';
} }
} }
@ -495,7 +536,10 @@ class JoinBuilder {
final List<String> additionalFields; final List<String> additionalFields;
JoinBuilder(this.type, this.from, this.to, this.key, this.value, JoinBuilder(this.type, this.from, this.to, this.key, this.value,
{this.op: '=', this.alias, this.additionalFields: const []}); {this.op: '=', this.alias, this.additionalFields: const []}) {
assert(to != null,
'computation of this join threw an error, and returned null.');
}
String get fieldName { String get fieldName {
var right = '$to.$value'; var right = '$to.$value';
@ -510,6 +554,7 @@ class JoinBuilder {
} }
String compile() { String compile() {
if (to == null) return null;
var b = new StringBuffer(); var b = new StringBuffer();
var left = '${from.tableName}.$key'; var left = '${from.tableName}.$key';
var right = fieldName; var right = fieldName;

View file

@ -1,5 +1,5 @@
name: angel_orm name: angel_orm
version: 2.0.0-dev.18 version: 2.0.0-dev.19
description: Runtime support for Angel's ORM. Includes base classes for queries. description: Runtime support for Angel's ORM. Includes base classes for queries.
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/orm homepage: https://github.com/angel-dart/orm

View file

@ -1,3 +1,7 @@
# 2.0.0-dev.5
* Implement cast-based `double` support.
* Finish `ListSqlExpressionBuilder`.
# 2.0.0-dev.4 # 2.0.0-dev.4
* List generation support. * List generation support.

View file

@ -11,6 +11,14 @@ import 'package:source_gen/source_gen.dart';
import 'orm_build_context.dart'; import 'orm_build_context.dart';
var floatTypes = [
ColumnType.decimal,
ColumnType.float,
ColumnType.numeric,
ColumnType.real,
const ColumnType('double precision'),
];
Builder ormBuilder(BuilderOptions options) { Builder ormBuilder(BuilderOptions options) {
return new SharedPartBuilder([ return new SharedPartBuilder([
new OrmGenerator( new OrmGenerator(
@ -68,6 +76,28 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
]); ]);
}); });
// Override casts so that we can cast doubles
clazz.methods.add(Method((b) {
b
..name = 'casts'
..annotations.add(refer('override'))
..type = MethodType.getter
..body = Block((b) {
var args = <String, Expression>{};
for (var field in ctx.effectiveFields) {
var name = ctx.buildContext.resolveFieldName(field.name);
var type = ctx.columns[field.name]?.type;
if (type == null) continue;
if (floatTypes.contains(type)) {
args[name] = literalString('text');
}
}
b.addExpression(literalMap(args).returned);
});
}));
// Add values // Add values
clazz.fields.add(new Field((b) { clazz.fields.add(new Field((b) {
var type = refer('${rc.pascalCase}QueryValues'); var type = refer('${rc.pascalCase}QueryValues');
@ -158,6 +188,10 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
expr = refer('json') expr = refer('json')
.property('decode') .property('decode')
.call([expr.asA(refer('String'))]).asA(type); .call([expr.asA(refer('String'))]).asA(type);
} else if (floatTypes.contains(ctx.columns[field.name]?.type)) {
expr = refer('double')
.property('parse')
.call([expr.property('toString').call([])]);
} else if (fType is InterfaceType && fType.element.isEnum) { } else if (fType is InterfaceType && fType.element.isEnum) {
expr = type.property('values').index(expr.asA(refer('int'))); expr = type.property('values').index(expr.asA(refer('int')));
} else } else
@ -222,7 +256,18 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// If there are any relations, we need some overrides. // If there are any relations, we need some overrides.
clazz.constructors.add(new Constructor((b) { clazz.constructors.add(new Constructor((b) {
b b
..optionalParameters.add(Parameter((b) => b
..named = true
..name = 'trampoline'
..type = TypeReference((b) => b
..symbol = 'Set'
..types.add(refer('String')))))
..body = new Block((b) { ..body = new Block((b) {
b.statements.addAll([
Code('trampoline ??= Set();'),
Code('trampoline.add(tableName);'),
]);
// Add a constructor that initializes _where // Add a constructor that initializes _where
b.addExpression( b.addExpression(
refer('_where') refer('_where')
@ -248,16 +293,10 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
var foreignQueryType = var foreignQueryType =
foreign.buildContext.modelClassNameRecase.pascalCase + foreign.buildContext.modelClassNameRecase.pascalCase +
'Query'; 'Query';
var compiledSubquery = refer(foreignQueryType)
.newInstance([])
.property('compile')
.call([]);
joinArgs.insert( joinArgs.insert(
0, 0,
literalString('(') refer(foreignQueryType).newInstance(
.operatorAdd(compiledSubquery) [], {'trampoline': refer('trampoline')}));
.operatorAdd(literalString(')')));
} else { } else {
joinArgs.insert(0, literalString(foreign.tableName)); joinArgs.insert(0, literalString(foreign.tableName));
} }
@ -580,11 +619,13 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
for (var field in ctx.effectiveFields) { for (var field in ctx.effectiveFields) {
var fType = field.type; var fType = field.type;
var name = ctx.buildContext.resolveFieldName(field.name); var name = ctx.buildContext.resolveFieldName(field.name);
var type = ctx.columns[field.name]?.type?.name; var type = ctx.columns[field.name]?.type;
if (type == null) continue; if (type == null) continue;
if (const TypeChecker.fromRuntime(List) if (const TypeChecker.fromRuntime(List)
.isAssignableFromType(fType)) { .isAssignableFromType(fType)) {
args[name] = literalString(type); args[name] = literalString(type.name);
} else if (floatTypes.contains(type)) {
args[name] = literalString(type.name);
} }
} }
@ -612,6 +653,10 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
value = refer('json') value = refer('json')
.property('decode') .property('decode')
.call([value.asA(refer('String'))]).asA(refer('List')); .call([value.asA(refer('String'))]).asA(refer('List'));
} else if (floatTypes.contains(ctx.columns[field.name]?.type)) {
value = refer('double')
.property('parse')
.call([value.asA(refer('String'))]);
} else { } else {
value = value.asA(type); value = value.asA(type);
} }
@ -631,6 +676,8 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
} else if (const TypeChecker.fromRuntime(List) } else if (const TypeChecker.fromRuntime(List)
.isAssignableFromType(fType)) { .isAssignableFromType(fType)) {
value = refer('json').property('encode').call([value]); value = refer('json').property('encode').call([value]);
} else if (floatTypes.contains(ctx.columns[field.name]?.type)) {
value = value.property('toString').call([]);
} }
b b

View file

@ -1,5 +1,5 @@
name: angel_orm_generator name: angel_orm_generator
version: 2.0.0-dev.4 version: 2.0.0-dev.5
description: Code generators for Angel's ORM. Generates query builder classes. description: Code generators for Angel's ORM. Generates query builder classes.
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/orm homepage: https://github.com/angel-dart/orm

View file

@ -54,7 +54,7 @@ main() {
test('select one', () async { test('select one', () async {
var query = new BookQuery(); var query = new BookQuery();
query.where.id.equals(int.parse(deathlyHallows.id)); query.where.id.equals(int.parse(deathlyHallows.id));
print(query.compile()); print(query.compile(Set()));
var book = await query.getOne(executor); var book = await query.getOne(executor);
print(book.toJson()); print(book.toJson());
@ -71,7 +71,7 @@ main() {
var query = new BookQuery() var query = new BookQuery()
..where.name.equals('Goblet of Fire') ..where.name.equals('Goblet of Fire')
..orWhere((w) => w.authorId.equals(int.parse(jkRowling.id))); ..orWhere((w) => w.authorId.equals(int.parse(jkRowling.id)));
print(query.compile()); print(query.compile(Set()));
var books = await query.get(executor); var books = await query.get(executor);
expect(books, hasLength(1)); expect(books, hasLength(1));
@ -95,7 +95,7 @@ main() {
query1 query1
..union(query2) ..union(query2)
..unionAll(query3); ..unionAll(query3);
print(query1.compile()); print(query1.compile(Set()));
var books = await query1.get(executor); var books = await query1.get(executor);
expect(books, hasLength(1)); expect(books, hasLength(1));
@ -120,7 +120,7 @@ main() {
test('delete stream', () async { test('delete stream', () async {
var query = new BookQuery()..where.name.equals(deathlyHallows.name); var query = new BookQuery()..where.name.equals(deathlyHallows.name);
print(query.compile()); print(query.compile(Set()));
var books = await query.delete(executor); var books = await query.delete(executor);
expect(books, hasLength(1)); expect(books, hasLength(1));

View file

@ -28,7 +28,7 @@ main() {
test('can fetch one foot', () async { test('can fetch one foot', () async {
var footQuery = new FootQuery() var footQuery = new FootQuery()
..values.legId = int.parse(originalLeg.id) ..values.legId = int.parse(originalLeg.id)
..values.nToes = 5; ..values.nToes = 5.64;
var legQuery = new LegQuery()..where.id.equals(int.parse(originalLeg.id)); var legQuery = new LegQuery()..where.id.equals(int.parse(originalLeg.id));
var foot = await footQuery.insert(executor); var foot = await footQuery.insert(executor);
var leg = await legQuery.getOne(executor); var leg = await legQuery.getOne(executor);
@ -57,7 +57,7 @@ main() {
test('sets foot on update', () async { test('sets foot on update', () async {
var footQuery = new FootQuery() var footQuery = new FootQuery()
..values.legId = int.parse(originalLeg.id) ..values.legId = int.parse(originalLeg.id)
..values.nToes = 5; ..values.nToes = 5.64;
var legQuery = new LegQuery() var legQuery = new LegQuery()
..where.id.equals(int.parse(originalLeg.id)) ..where.id.equals(int.parse(originalLeg.id))
..values.copyFrom(originalLeg.copyWith(name: 'Right')); ..values.copyFrom(originalLeg.copyWith(name: 'Right'));
@ -73,7 +73,7 @@ main() {
test('sets foot on delete', () async { test('sets foot on delete', () async {
var footQuery = new FootQuery() var footQuery = new FootQuery()
..values.legId = int.parse(originalLeg.id) ..values.legId = int.parse(originalLeg.id)
..values.nToes = 5; ..values.nToes = 5.64;
var legQuery = new LegQuery()..where.id.equals(int.parse(originalLeg.id)); var legQuery = new LegQuery()..where.id.equals(int.parse(originalLeg.id));
var foot = await footQuery.insert(executor); var foot = await footQuery.insert(executor);
var leg = await legQuery.deleteOne(executor); var leg = await legQuery.deleteOne(executor);

View file

@ -1,4 +1,4 @@
CREATE TEMPORARY TABLE "user_roles" ( CREATE TEMPORARY TABLE "role_users" (
"id" serial PRIMARY KEY, "id" serial PRIMARY KEY,
"user_id" int NOT NULL, "user_id" int NOT NULL,
"role_id" int NOT NULL, "role_id" int NOT NULL,

View file

@ -28,7 +28,9 @@ class AuthorMigration extends Migration {
// ************************************************************************** // **************************************************************************
class AuthorQuery extends Query<Author, AuthorQueryWhere> { class AuthorQuery extends Query<Author, AuthorQueryWhere> {
AuthorQuery() { AuthorQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new AuthorQueryWhere(this); _where = new AuthorQueryWhere(this);
} }
@ -37,6 +39,11 @@ class AuthorQuery extends Query<Author, AuthorQueryWhere> {
AuthorQueryWhere _where; AuthorQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'authors'; return 'authors';

View file

@ -30,7 +30,9 @@ class BookMigration extends Migration {
// ************************************************************************** // **************************************************************************
class BookQuery extends Query<Book, BookQueryWhere> { class BookQuery extends Query<Book, BookQueryWhere> {
BookQuery() { BookQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new BookQueryWhere(this); _where = new BookQueryWhere(this);
leftJoin('authors', 'author_id', 'id', leftJoin('authors', 'author_id', 'id',
additionalFields: const ['name', 'created_at', 'updated_at']); additionalFields: const ['name', 'created_at', 'updated_at']);
@ -43,6 +45,11 @@ class BookQuery extends Query<Book, BookQueryWhere> {
BookQueryWhere _where; BookQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'books'; return 'books';

View file

@ -31,7 +31,9 @@ class CarMigration extends Migration {
// ************************************************************************** // **************************************************************************
class CarQuery extends Query<Car, CarQueryWhere> { class CarQuery extends Query<Car, CarQueryWhere> {
CarQuery() { CarQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new CarQueryWhere(this); _where = new CarQueryWhere(this);
} }
@ -40,6 +42,11 @@ class CarQuery extends Query<Car, CarQueryWhere> {
CarQueryWhere _where; CarQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'cars'; return 'cars';

View file

@ -27,7 +27,9 @@ class CustomerMigration extends Migration {
// ************************************************************************** // **************************************************************************
class CustomerQuery extends Query<Customer, CustomerQueryWhere> { class CustomerQuery extends Query<Customer, CustomerQueryWhere> {
CustomerQuery() { CustomerQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new CustomerQueryWhere(this); _where = new CustomerQueryWhere(this);
} }
@ -36,6 +38,11 @@ class CustomerQuery extends Query<Customer, CustomerQueryWhere> {
CustomerQueryWhere _where; CustomerQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'customers'; return 'customers';

View file

@ -9,5 +9,7 @@ part 'foot.g.dart';
@serializable @serializable
@Orm(tableName: 'feet') @Orm(tableName: 'feet')
class _Foot extends Model { class _Foot extends Model {
int legId, nToes; int legId;
double nToes;
} }

View file

@ -12,7 +12,7 @@ class FootMigration extends Migration {
schema.create('feet', (table) { schema.create('feet', (table) {
table.serial('id')..primaryKey(); table.serial('id')..primaryKey();
table.integer('leg_id'); table.integer('leg_id');
table.integer('n_toes'); table.declare('n_toes', new ColumnType('decimal'));
table.timeStamp('created_at'); table.timeStamp('created_at');
table.timeStamp('updated_at'); table.timeStamp('updated_at');
}); });
@ -29,7 +29,9 @@ class FootMigration extends Migration {
// ************************************************************************** // **************************************************************************
class FootQuery extends Query<Foot, FootQueryWhere> { class FootQuery extends Query<Foot, FootQueryWhere> {
FootQuery() { FootQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new FootQueryWhere(this); _where = new FootQueryWhere(this);
} }
@ -38,6 +40,11 @@ class FootQuery extends Query<Foot, FootQueryWhere> {
FootQueryWhere _where; FootQueryWhere _where;
@override
get casts {
return {'n_toes': 'text'};
}
@override @override
get tableName { get tableName {
return 'feet'; return 'feet';
@ -63,7 +70,7 @@ class FootQuery extends Query<Foot, FootQueryWhere> {
var model = new Foot( var model = new Foot(
id: row[0].toString(), id: row[0].toString(),
legId: (row[1] as int), legId: (row[1] as int),
nToes: (row[2] as int), nToes: double.parse(row[2].toString()),
createdAt: (row[3] as DateTime), createdAt: (row[3] as DateTime),
updatedAt: (row[4] as DateTime)); updatedAt: (row[4] as DateTime));
return model; return model;
@ -79,7 +86,7 @@ class FootQueryWhere extends QueryWhere {
FootQueryWhere(FootQuery query) FootQueryWhere(FootQuery query)
: id = new NumericSqlExpressionBuilder<int>(query, 'id'), : id = new NumericSqlExpressionBuilder<int>(query, 'id'),
legId = new NumericSqlExpressionBuilder<int>(query, 'leg_id'), legId = new NumericSqlExpressionBuilder<int>(query, 'leg_id'),
nToes = new NumericSqlExpressionBuilder<int>(query, 'n_toes'), nToes = new NumericSqlExpressionBuilder<double>(query, 'n_toes'),
createdAt = new DateTimeSqlExpressionBuilder(query, 'created_at'), createdAt = new DateTimeSqlExpressionBuilder(query, 'created_at'),
updatedAt = new DateTimeSqlExpressionBuilder(query, 'updated_at'); updatedAt = new DateTimeSqlExpressionBuilder(query, 'updated_at');
@ -87,7 +94,7 @@ class FootQueryWhere extends QueryWhere {
final NumericSqlExpressionBuilder<int> legId; final NumericSqlExpressionBuilder<int> legId;
final NumericSqlExpressionBuilder<int> nToes; final NumericSqlExpressionBuilder<double> nToes;
final DateTimeSqlExpressionBuilder createdAt; final DateTimeSqlExpressionBuilder createdAt;
@ -102,7 +109,7 @@ class FootQueryWhere extends QueryWhere {
class FootQueryValues extends MapQueryValues { class FootQueryValues extends MapQueryValues {
@override @override
get casts { get casts {
return {}; return {'n_toes': 'decimal'};
} }
int get id { int get id {
@ -115,11 +122,11 @@ class FootQueryValues extends MapQueryValues {
} }
set legId(int value) => values['leg_id'] = value; set legId(int value) => values['leg_id'] = value;
int get nToes { double get nToes {
return (values['n_toes'] as int); return double.parse((values['n_toes'] as String));
} }
set nToes(int value) => values['n_toes'] = value; set nToes(double value) => values['n_toes'] = value.toString();
DateTime get createdAt { DateTime get createdAt {
return (values['created_at'] as DateTime); return (values['created_at'] as DateTime);
} }
@ -153,7 +160,7 @@ class Foot extends _Foot {
final int legId; final int legId;
@override @override
final int nToes; final double nToes;
@override @override
final DateTime createdAt; final DateTime createdAt;
@ -164,7 +171,7 @@ class Foot extends _Foot {
Foot copyWith( Foot copyWith(
{String id, {String id,
int legId, int legId,
int nToes, double nToes,
DateTime createdAt, DateTime createdAt,
DateTime updatedAt}) { DateTime updatedAt}) {
return new Foot( return new Foot(
@ -203,7 +210,7 @@ abstract class FootSerializer {
return new Foot( return new Foot(
id: map['id'] as String, id: map['id'] as String,
legId: map['leg_id'] as int, legId: map['leg_id'] as int,
nToes: map['n_toes'] as int, nToes: map['n_toes'] as double,
createdAt: map['created_at'] != null createdAt: map['created_at'] != null
? (map['created_at'] is DateTime ? (map['created_at'] is DateTime
? (map['created_at'] as DateTime) ? (map['created_at'] as DateTime)

View file

@ -29,7 +29,9 @@ class FruitMigration extends Migration {
// ************************************************************************** // **************************************************************************
class FruitQuery extends Query<Fruit, FruitQueryWhere> { class FruitQuery extends Query<Fruit, FruitQueryWhere> {
FruitQuery() { FruitQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new FruitQueryWhere(this); _where = new FruitQueryWhere(this);
} }
@ -38,6 +40,11 @@ class FruitQuery extends Query<Fruit, FruitQueryWhere> {
FruitQueryWhere _where; FruitQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'fruits'; return 'fruits';

View file

@ -28,7 +28,9 @@ class HasCarMigration extends Migration {
// ************************************************************************** // **************************************************************************
class HasCarQuery extends Query<HasCar, HasCarQueryWhere> { class HasCarQuery extends Query<HasCar, HasCarQueryWhere> {
HasCarQuery() { HasCarQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new HasCarQueryWhere(this); _where = new HasCarQueryWhere(this);
} }
@ -37,6 +39,11 @@ class HasCarQuery extends Query<HasCar, HasCarQueryWhere> {
HasCarQueryWhere _where; HasCarQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'has_cars'; return 'has_cars';

View file

@ -26,7 +26,9 @@ class HasMapMigration extends Migration {
// ************************************************************************** // **************************************************************************
class HasMapQuery extends Query<HasMap, HasMapQueryWhere> { class HasMapQuery extends Query<HasMap, HasMapQueryWhere> {
HasMapQuery() { HasMapQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new HasMapQueryWhere(this); _where = new HasMapQueryWhere(this);
} }
@ -35,6 +37,11 @@ class HasMapQuery extends Query<HasMap, HasMapQueryWhere> {
HasMapQueryWhere _where; HasMapQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'has_maps'; return 'has_maps';

View file

@ -28,7 +28,9 @@ class LegMigration extends Migration {
// ************************************************************************** // **************************************************************************
class LegQuery extends Query<Leg, LegQueryWhere> { class LegQuery extends Query<Leg, LegQueryWhere> {
LegQuery() { LegQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new LegQueryWhere(this); _where = new LegQueryWhere(this);
leftJoin('feet', 'id', 'leg_id', additionalFields: const [ leftJoin('feet', 'id', 'leg_id', additionalFields: const [
'leg_id', 'leg_id',
@ -43,6 +45,11 @@ class LegQuery extends Query<Leg, LegQueryWhere> {
LegQueryWhere _where; LegQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'legs'; return 'legs';

View file

@ -31,7 +31,9 @@ class OrderMigration extends Migration {
// ************************************************************************** // **************************************************************************
class OrderQuery extends Query<Order, OrderQueryWhere> { class OrderQuery extends Query<Order, OrderQueryWhere> {
OrderQuery() { OrderQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new OrderQueryWhere(this); _where = new OrderQueryWhere(this);
leftJoin('customers', 'customer_id', 'id', leftJoin('customers', 'customer_id', 'id',
additionalFields: const ['created_at', 'updated_at']); additionalFields: const ['created_at', 'updated_at']);
@ -42,6 +44,11 @@ class OrderQuery extends Query<Order, OrderQueryWhere> {
OrderQueryWhere _where; OrderQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'orders'; return 'orders';

View file

@ -28,9 +28,11 @@ class TreeMigration extends Migration {
// ************************************************************************** // **************************************************************************
class TreeQuery extends Query<Tree, TreeQueryWhere> { class TreeQuery extends Query<Tree, TreeQueryWhere> {
TreeQuery() { TreeQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new TreeQueryWhere(this); _where = new TreeQueryWhere(this);
leftJoin('(' + new FruitQuery().compile() + ')', 'id', 'tree_id', leftJoin(new FruitQuery(trampoline: trampoline), 'id', 'tree_id',
additionalFields: const [ additionalFields: const [
'tree_id', 'tree_id',
'common_name', 'common_name',
@ -44,6 +46,11 @@ class TreeQuery extends Query<Tree, TreeQueryWhere> {
TreeQueryWhere _where; TreeQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'trees'; return 'trees';

View file

@ -65,7 +65,9 @@ class RoleMigration extends Migration {
// ************************************************************************** // **************************************************************************
class UserQuery extends Query<User, UserQueryWhere> { class UserQuery extends Query<User, UserQueryWhere> {
UserQuery() { UserQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new UserQueryWhere(this); _where = new UserQueryWhere(this);
} }
@ -74,6 +76,11 @@ class UserQuery extends Query<User, UserQueryWhere> {
UserQueryWhere _where; UserQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'users'; return 'users';
@ -192,7 +199,9 @@ class UserQueryValues extends MapQueryValues {
} }
class RoleUserQuery extends Query<RoleUser, RoleUserQueryWhere> { class RoleUserQuery extends Query<RoleUser, RoleUserQueryWhere> {
RoleUserQuery() { RoleUserQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new RoleUserQueryWhere(this); _where = new RoleUserQueryWhere(this);
leftJoin('roles', 'role_id', 'id', leftJoin('roles', 'role_id', 'id',
additionalFields: const ['name', 'created_at', 'updated_at']); additionalFields: const ['name', 'created_at', 'updated_at']);
@ -210,6 +219,11 @@ class RoleUserQuery extends Query<RoleUser, RoleUserQueryWhere> {
RoleUserQueryWhere _where; RoleUserQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'role_users'; return 'role_users';
@ -319,7 +333,9 @@ class RoleUserQueryValues extends MapQueryValues {
} }
class RoleQuery extends Query<Role, RoleQueryWhere> { class RoleQuery extends Query<Role, RoleQueryWhere> {
RoleQuery() { RoleQuery({Set<String> trampoline}) {
trampoline ??= Set();
trampoline.add(tableName);
_where = new RoleQueryWhere(this); _where = new RoleQueryWhere(this);
} }
@ -328,6 +344,11 @@ class RoleQuery extends Query<Role, RoleQueryWhere> {
RoleQueryWhere _where; RoleQueryWhere _where;
@override
get casts {
return {};
}
@override @override
get tableName { get tableName {
return 'roles'; return 'roles';

View file

@ -82,7 +82,7 @@ main() {
var query2 = new CarQuery()..where.familyFriendly.isTrue; var query2 = new CarQuery()..where.familyFriendly.isTrue;
var query3 = new CarQuery()..where.description.equals('Submarine'); var query3 = new CarQuery()..where.description.equals('Submarine');
var union = query1.union(query2).unionAll(query3); var union = query1.union(query2).unionAll(query3);
print(union.compile()); print(union.compile(Set()));
var cars = await union.get(connection); var cars = await union.get(connection);
expect(cars, hasLength(1)); expect(cars, hasLength(1));
}); });
@ -93,14 +93,14 @@ main() {
..orWhere((where) => where ..orWhere((where) => where
..familyFriendly.isTrue ..familyFriendly.isTrue
..make.equals('Honda')); ..make.equals('Honda'));
print(query.compile()); print(query.compile(Set()));
var cars = await query.get(connection); var cars = await query.get(connection);
expect(cars, hasLength(1)); expect(cars, hasLength(1));
}); });
test('limit obeyed', () async { test('limit obeyed', () async {
var query = new CarQuery()..limit(0); var query = new CarQuery()..limit(0);
print(query.compile()); print(query.compile(Set()));
var cars = await query.get(connection); var cars = await query.get(connection);
expect(cars, isEmpty); expect(cars, isEmpty);
}); });
@ -126,7 +126,7 @@ main() {
var query = new CarQuery() var query = new CarQuery()
..where.make.equals('Ferrari東') ..where.make.equals('Ferrari東')
..orWhere((w) => w.familyFriendly.isTrue); ..orWhere((w) => w.familyFriendly.isTrue);
print(query.compile(preamble: 'DELETE FROM "cars"')); print(query.compile(Set(), preamble: 'DELETE FROM "cars"'));
var cars = await query.delete(connection); var cars = await query.delete(connection);
expect(cars, hasLength(1)); expect(cars, hasLength(1));