This commit is contained in:
thosakwe 2017-07-14 19:03:39 -04:00
parent 85b032d4f0
commit 7298b4638b
9 changed files with 390 additions and 246 deletions

View file

@ -1,4 +1,3 @@
import 'package:charcode/charcode.dart';
import 'package:meta/meta.dart';
import 'package:intl/intl.dart';
import 'package:string_scanner/string_scanner.dart';

View file

@ -0,0 +1,7 @@
# 1.0.0-alpha+1
* Closed #12. `insertX` and `updateX` now use `rc.camelCase`, instead of `rc.snakeCase`.
* Closed #13. Added `limit` and `offset` properties to `XQuery`.
* Closed #14. Refined the `or` method (it now takes an `XQueryWhere`), and removed `and` and `not`.
* Closed #16. Added `sortAscending` and `sortDescending` to `XQuery`.
* Closed #17. `delete` now uses `toSql` from `XQuery`.
* Closed #18. `XQuery` now supports `union` and `unionAll`.

View file

@ -12,7 +12,12 @@ import 'package:source_gen/source_gen.dart';
import 'build_context.dart';
import 'postgres_build_context.dart';
const List<String> RELATIONS = const ['and', 'or', 'not'];
const List<String> RELATIONS = const ['or'];
const List<String> RESTRICTORS = const ['limit', 'offset'];
const Map<String, String> SORT_MODES = const {
'Descending': 'DESC',
'Ascending': 'ASC'
};
// TODO: HasOne, HasMany, BelongsTo
class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
@ -95,26 +100,51 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var clazz = new ClassBuilder(ctx.queryClassName);
// Add constructor + field
var PostgreSQLConnection = new TypeBuilder('PostgreSQLConnection');
var connection = reference('connection');
// Add or + not
// Add _unions
clazz.addField(varFinal('_unions',
value: map({}),
type: new TypeBuilder('Map',
genericTypes: [ctx.queryClassBuilder, lib$core.bool])));
var unions = <String, bool>{'union': false, 'unionAll': true};
unions.forEach((name, all) {
var meth = new MethodBuilder(name, returnType: lib$core.$void);
meth.addPositional(parameter('query', [ctx.queryClassBuilder]));
meth.addStatement(
literal(all).asAssign(reference('_unions')[reference('query')]));
clazz.addMethod(meth);
});
// Add _sortMode
clazz.addField(varField('_sortKey', type: lib$core.String));
clazz.addField(varField('_sortMode', type: lib$core.String));
SORT_MODES.keys.forEach((sort) {
var m = new MethodBuilder('sort$sort', returnType: lib$core.$void);
m.addPositional(parameter('key', [lib$core.String]));
m.addStatement(literal(sort).asAssign(reference('_sortMode')));
m.addStatement(reference('key').asAssign(reference('_sortKey')));
clazz.addMethod(m);
});
// Add limit, offset
for (var restrictor in RESTRICTORS) {
clazz.addField(varField(restrictor, type: lib$core.int));
}
// Add and, or, not
for (var relation in RELATIONS) {
clazz.addField(varFinal('_$relation',
type: new TypeBuilder('List', genericTypes: [lib$core.String]),
type: new TypeBuilder('List', genericTypes: [ctx.whereClassBuilder]),
value: list([])));
var relationMethod =
new MethodBuilder(relation, returnType: lib$core.$void);
relationMethod.addPositional(
parameter('other', [new TypeBuilder(ctx.queryClassName)]));
var otherWhere = reference('other').property('where');
var compiled = reference('compiled');
relationMethod.addStatement(varField('compiled',
value: otherWhere.invoke('toWhereClause', [],
namedArguments: {'keyword': literal(false)})));
relationMethod.addStatement(ifThen(compiled.notEquals(literal(null)), [
reference('_$relation').invoke('add', [compiled])
]));
relationMethod
.addPositional(parameter('selector', [ctx.whereClassBuilder]));
relationMethod.addStatement(
reference('_$relation').invoke('add', [reference('selector')]));
clazz.addMethod(relationMethod);
}
@ -159,24 +189,35 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
clazz.addMethod(
new MethodBuilder('getAll',
returnType: new TypeBuilder('Stream',
genericTypes: [new TypeBuilder(ctx.modelClassName)]),
returns: new TypeBuilder(ctx.queryClassName)
genericTypes: [ctx.modelClassBuilder]),
returns: ctx.queryClassBuilder
.newInstance([]).invoke('get', [connection]))
..addPositional(parameter('connection', [PostgreSQLConnection])),
..addPositional(
parameter('connection', [ctx.postgreSQLConnectionBuilder])),
asStatic: true);
return clazz;
}
MethodBuilder buildToSqlMethod(PostgresBuildContext ctx) {
// TODO: Bake relations into SQL queries
// TODO: Bake relationships into SQL queries
var meth = new MethodBuilder('toSql', returnType: lib$core.String);
meth.addStatement(varField('buf',
value: lib$core.StringBuffer
.newInstance([literal('SELECT * FROM "${ctx.tableName}"')])));
meth.addPositional(parameter('prefix', [lib$core.String]).asOptional());
var buf = reference('buf');
meth.addStatement(
varField('buf', value: lib$core.StringBuffer.newInstance([])));
// Write prefix, or default to SELECT
var prefix = reference('prefix');
meth.addStatement(buf.invoke('write', [
prefix
.notEquals(literal(null))
.ternary(prefix, literal('SELECT * FROM "${ctx.tableName}"'))
]));
meth.addStatement(varField('whereClause',
value: reference('where').invoke('toWhereClause', [])));
var buf = reference('buf');
var whereClause = reference('whereClause');
meth.addStatement(ifThen(whereClause.notEquals(literal(null)), [
@ -184,29 +225,73 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
]));
for (var relation in RELATIONS) {
var ref = reference('_$relation');
var ref = reference('_$relation'),
x = reference('x'),
whereClause = reference('whereClause');
var upper = relation.toUpperCase();
var joined = ref.invoke('join', [literal(',')]);
var closure = new MethodBuilder.closure();
closure.addPositional(parameter('x'));
closure.addStatement(varField('whereClause',
value: x.invoke('toWhereClause', [],
namedArguments: {'keyword': literal(false)})));
closure.addStatement(ifThen(whereClause.notEquals(literal(null)), [
buf.invoke('write', [literal(' $upper (') + whereClause + literal(')')])
]));
meth.addStatement(ifThen(ref.property('isNotEmpty'), [
buf.invoke('write', [literal(' $upper (') + joined + literal(')')])
meth.addStatement(ref.invoke('forEach', [closure]));
}
var ifNoPrefix = ifThen(reference('prefix').equals(literal(null)));
for (var restrictor in RESTRICTORS) {
var ref = reference(restrictor);
var upper = restrictor.toUpperCase();
ifNoPrefix.addStatement(ifThen(ref.notEquals(literal(null)), [
buf.invoke('write', [literal(' $upper ') + ref.invoke('toString', [])])
]));
}
meth.addStatement(buf.invoke('write', [literal(';')]));
meth.addStatement(buf.invoke('toString', []).asReturn());
var sortMode = reference('_sortMode');
SORT_MODES.forEach((k, sort) {
ifNoPrefix.addStatement(ifThen(sortMode.equals(literal(k)), [
buf.invoke('write', [
literal(' ORDER BY "') + reference('_sortKey') + literal('" $sort')
])
]));
});
// Add unions
var unionClosure = new MethodBuilder.closure();
unionClosure.addPositional(parameter('query'));
unionClosure.addPositional(parameter('all'));
unionClosure.addStatement(buf.invoke('write', [literal(' UNION')]));
unionClosure.addStatement(ifThen(reference('all'), [
buf.invoke('write', [literal(' ALL')])
]));
unionClosure.addStatement(buf.invoke('write', [literal(' (')]));
unionClosure.addStatement(varField('sql',
value: reference('query').invoke('toSql', []).invoke(
'replaceAll', [literal(';'), literal('')])));
unionClosure
.addStatement(buf.invoke('write', [reference('sql') + literal(')')]));
ifNoPrefix
.addStatement(reference('_unions').invoke('forEach', [unionClosure]));
ifNoPrefix.addStatement(buf.invoke('write', [literal(';')]));
meth.addStatement(ifNoPrefix);
meth.addStatement(buf.invoke('toString', []).asReturn());
return meth;
}
MethodBuilder buildParseRowMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('parseRow',
returnType: new TypeBuilder(ctx.modelClassName));
var meth = new MethodBuilder('parseRow', returnType: ctx.modelClassBuilder);
meth.addPositional(parameter('row', [lib$core.List]));
//meth.addStatement(lib$core.print.call(
// [literal('ROW MAP: ') + reference('row').invoke('toString', [])]));
var row = reference('row');
var DATE_YMD_HMS = reference('DATE_YMD_HMS');
// We want to create a Map using the SQL row.
Map<String, ExpressionBuilder> data = {};
@ -217,18 +302,9 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var name = ctx.resolveFieldName(field.name);
var rowKey = row[literal(i++)];
/* if (field.type.isAssignableTo(ctx.dateTimeType)) {
// TODO: Handle DATE and not just DATETIME
data[name] = DATE_YMD_HMS.invoke('parse', [rowKey]);
} else
*/
if (field.name == 'id' && ctx.shimmed.containsKey('id')) {
data[name] = rowKey.invoke('toString', []);
} /* else if (field.type.isAssignableTo(ctx.typeProvider.boolType)) {
// TODO: Find out what date is returned as
data[name] = rowKey.equals(literal(1));
}*/
else
} else
data[name] = rowKey;
});
@ -244,7 +320,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
});
// Then, call a .fromJson() constructor
meth.addStatement(new TypeBuilder(ctx.modelClassName)
meth.addStatement(ctx.modelClassBuilder
.newInstance([map(data)], constructor: 'fromJson').asReturn());
return meth;
@ -266,12 +342,12 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildGetMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('get',
returnType: new TypeBuilder('Stream',
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
returnType:
new TypeBuilder('Stream', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
var streamController = new TypeBuilder('StreamController',
genericTypes: [new TypeBuilder(ctx.modelClassName)]);
genericTypes: [ctx.modelClassBuilder]);
meth.addStatement(varField('ctrl',
type: streamController, value: streamController.newInstance([])));
@ -283,11 +359,11 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildGetOneMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('getOne',
returnType: new TypeBuilder('Future',
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
returnType:
new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional(parameter('id', [lib$core.int]));
meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
meth.addStatement(reference('connection').invoke('query', [
literal('SELECT * FROM "${ctx.tableName}" WHERE "id" = @id;')
], namedArguments: {
@ -360,10 +436,10 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildUpdateMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('update',
returnType: new TypeBuilder('Stream',
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
returnType:
new TypeBuilder('Stream', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
_addAllNamed(meth, ctx);
var buf = new StringBuffer('UPDATE "${ctx.tableName}" SET (');
@ -409,7 +485,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var substitutionValues = _buildSubstitutionValues(ctx);
var ctrlType = new TypeBuilder('StreamController',
genericTypes: [new TypeBuilder(ctx.modelClassName)]);
genericTypes: [ctx.modelClassBuilder]);
meth.addStatement(varField('ctrl', value: ctrlType.newInstance([])));
var result = _executeQuery(
$buf.invoke('toString', []) + literal(buf2.toString()),
@ -421,46 +497,23 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildDeleteMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('delete',
returnType: new TypeBuilder('Stream',
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
returnType:
new TypeBuilder('Stream', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
var buf = reference('buf'), whereClause = reference('whereClause');
meth.addStatement(varField('buf',
value: lib$core.StringBuffer
.newInstance([literal('DELETE FROM "${ctx.tableName}"')])));
meth.addStatement(varField('whereClause',
value: reference('where').invoke('toWhereClause', [])));
var ifStmt = ifThen(whereClause.notEquals(literal(null)), [
buf.invoke('write', [literal(' ') + whereClause])
]);
meth.addStatement(ifStmt);
for (var relation in RELATIONS) {
var ref = reference('_$relation');
var upper = relation.toUpperCase();
ifStmt.addStatement(ifThen(ref.property('isNotEmpty'), [
buf.invoke('write', [
literal(' $upper (') +
ref.invoke('join', [literal(', ')]) +
literal(')')
])
]));
}
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
var litBuf = new StringBuffer();
_addReturning(litBuf, ctx);
meth.addStatement(buf.invoke('write', [literal(litBuf.toString())]));
var streamController = new TypeBuilder('StreamController',
genericTypes: [new TypeBuilder(ctx.modelClassName)]);
genericTypes: [ctx.modelClassBuilder]);
meth.addStatement(varField('ctrl',
type: streamController, value: streamController.newInstance([])));
var future =
reference('connection').invoke('query', [buf.invoke('toString', [])]);
var future = reference('connection').invoke('query', [
reference('toSql').call([literal('DELETE FROM "${ctx.tableName}"')]) +
literal(litBuf.toString())
]);
_invokeStreamClosure(future, meth);
return meth;
@ -469,11 +522,11 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildDeleteOneMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('deleteOne',
modifier: MethodModifier.asAsync,
returnType: new TypeBuilder('Future',
genericTypes: [new TypeBuilder(ctx.modelClassName)]))
returnType:
new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]))
..addPositional(parameter('id', [lib$core.int]))
..addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
var id = reference('id');
var connection = reference('connection');
@ -498,10 +551,10 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildInsertMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('insert',
modifier: MethodModifier.asAsync,
returnType: new TypeBuilder('Future',
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
returnType:
new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
// Add all named params
_addAllNamed(meth, ctx);
@ -554,22 +607,21 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildInsertModelMethod(PostgresBuildContext ctx) {
var rc = new ReCase(ctx.modelClassName);
var meth = new MethodBuilder('insert${rc.pascalCase}',
returnType: new TypeBuilder('Future',
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
returnType:
new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
meth.addPositional(
parameter(rc.snakeCase, [new TypeBuilder(ctx.modelClassName)]));
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
meth.addPositional(parameter(rc.camelCase, [ctx.modelClassBuilder]));
Map<String, ExpressionBuilder> args = {};
var ref = reference(rc.snakeCase);
var ref = reference(rc.camelCase);
ctx.fields.forEach((f) {
if (f.name != 'id') args[f.name] = ref.property(f.name);
});
meth.addStatement(new TypeBuilder(ctx.queryClassName)
meth.addStatement(ctx.queryClassBuilder
.invoke('insert', [reference('connection')], namedArguments: args)
.asReturn());
@ -579,19 +631,18 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildUpdateModelMethod(PostgresBuildContext ctx) {
var rc = new ReCase(ctx.modelClassName);
var meth = new MethodBuilder('update${rc.pascalCase}',
returnType: new TypeBuilder('Future',
genericTypes: [new TypeBuilder(ctx.modelClassName)]));
returnType:
new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')]));
meth.addPositional(
parameter(rc.snakeCase, [new TypeBuilder(ctx.modelClassName)]));
parameter('connection', [ctx.postgreSQLConnectionBuilder]));
meth.addPositional(parameter(rc.camelCase, [ctx.modelClassBuilder]));
// var query = new XQuery();
var ref = reference(rc.snakeCase);
var ref = reference(rc.camelCase);
var query = reference('query');
meth.addStatement(varField('query',
value: new TypeBuilder(ctx.queryClassName).newInstance([])));
meth.addStatement(
varField('query', value: ctx.queryClassBuilder.newInstance([])));
// query.where.id.equals(x.id);
meth.addStatement(query.property('where').property('id').invoke('equals', [

View file

@ -4,11 +4,16 @@ import 'package:analyzer/src/generated/resolver.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_serialize_generator/context.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart';
class PostgresBuildContext extends BuildContext {
DartType _dateTimeTypeCache;
LibraryElement _libraryCache;
TypeProvider _typeProviderCache;
TypeBuilder _modelClassBuilder,
_queryClassBuilder,
_whereClassBuilder,
_postgresqlConnectionBuilder;
final Map<String, Column> columnInfo = {};
final Map<String, IndexType> indices = {};
final Map<String, Relationship> relationships = {};
@ -28,6 +33,18 @@ class PostgresBuildContext extends BuildContext {
final List<FieldElement> fields = [], relationshipFields = [];
TypeBuilder get modelClassBuilder =>
_modelClassBuilder ??= new TypeBuilder(modelClassName);
TypeBuilder get queryClassBuilder =>
_queryClassBuilder ??= new TypeBuilder(queryClassName);
TypeBuilder get whereClassBuilder =>
_whereClassBuilder ??= new TypeBuilder(whereClassName);
TypeBuilder get postgreSQLConnectionBuilder =>
_postgresqlConnectionBuilder ??= new TypeBuilder('PostgreSQLConnection');
Map<String, String> get aliases => raw.aliases;
Map<String, bool> get shimmed => raw.shimmed;

View file

@ -1,5 +1,5 @@
name: angel_orm_generator
version: 1.0.0-alpha
version: 1.0.0-alpha+1
description: Code generators for Angel's ORM.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/orm

View file

@ -1,4 +1,3 @@
import 'dart:io';
import 'package:angel_orm/angel_orm.dart';
import 'package:postgres/postgres.dart';
import 'package:test/test.dart';
@ -84,10 +83,32 @@ main() {
expect(car.recalledAt, isNull);
});
test('and clause', () async {
test('union', () async {
var query1 = new CarQuery()..where.make.like('%Fer%');
var query2 = new CarQuery()..where.familyFriendly.equals(true);
var query3 = new CarQuery()..where.description.equals('Submarine');
query1
..union(query2)
..unionAll(query3);
print(query1.toSql());
var cars = await query1.get(connection).toList();
expect(cars, hasLength(1));
});
test('or clause', () async {
var query = new CarQuery()
..where.make.like('Fer%')
..and(new CarQuery()..where.familyFriendly.equals(true));
..or(new CarQueryWhere()
..familyFriendly.equals(true)
..make.equals('Honda'));
print(query.toSql());
var cars = await query.get(connection).toList();
expect(cars, hasLength(1));
});
test('limit obeyed', () async {
var query = new CarQuery()..limit = 0;
print(query.toSql());
var cars = await query.get(connection).toList();
expect(cars, isEmpty);
@ -108,6 +129,8 @@ main() {
test('delete stream', () async {
var query = new CarQuery()..where.make.equals('Ferrari');
query.or(new CarQueryWhere()..familyFriendly.equals(true));
print(query.toSql('DELETE FROM "cars"'));
var cars = await query.delete(connection).toList();
expect(cars, hasLength(1));
expect(cars.first.toJson(), ferrari.toJson());

View file

@ -11,51 +11,79 @@ import 'package:postgres/postgres.dart';
import 'author.dart';
class AuthorQuery {
final List<String> _and = [];
final Map<AuthorQuery, bool> _unions = {};
final List<String> _or = [];
String _sortKey;
final List<String> _not = [];
String _sortMode;
int limit;
int offset;
final List<AuthorQueryWhere> _or = [];
final AuthorQueryWhere where = new AuthorQueryWhere();
void and(AuthorQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_and.add(compiled);
}
void union(AuthorQuery query) {
_unions[query] = false;
}
void or(AuthorQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_or.add(compiled);
}
void unionAll(AuthorQuery query) {
_unions[query] = true;
}
void not(AuthorQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_not.add(compiled);
}
void sortDescending(String key) {
_sortMode = 'Descending';
_sortKey = key;
}
String toSql() {
var buf = new StringBuffer('SELECT * FROM "authors"');
void sortAscending(String key) {
_sortMode = 'Ascending';
_sortKey = key;
}
void or(AuthorQueryWhere selector) {
_or.add(selector);
}
String toSql([String prefix]) {
var buf = new StringBuffer();
buf.write(prefix != null ? prefix : 'SELECT * FROM "authors"');
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
}
if (_and.isNotEmpty) {
buf.write(' AND (' + _and.join(',') + ')');
_or.forEach((x) {
var whereClause = x.toWhereClause(keyword: false);
if (whereClause != null) {
buf.write(' OR (' + whereClause + ')');
}
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(',') + ')');
});
if (prefix == null) {
if (limit != null) {
buf.write(' LIMIT ' + limit.toString());
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(',') + ')');
if (offset != null) {
buf.write(' OFFSET ' + offset.toString());
}
if (_sortMode == 'Descending') {
buf.write(' ORDER BY "' + _sortKey + '" DESC');
}
if (_sortMode == 'Ascending') {
buf.write(' ORDER BY "' + _sortKey + '" ASC');
}
_unions.forEach((query, all) {
buf.write(' UNION');
if (all) {
buf.write(' ALL');
}
buf.write(' (');
var sql = query.toSql().replaceAll(';', '');
buf.write(sql + ')');
});
buf.write(';');
}
return buf.toString();
}
@ -108,23 +136,11 @@ class AuthorQuery {
}
Stream<Author> delete(PostgreSQLConnection connection) {
var buf = new StringBuffer('DELETE FROM "authors"');
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
if (_and.isNotEmpty) {
buf.write(' AND (' + _and.join(', ') + ')');
}
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(', ') + ')');
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(', ') + ')');
}
}
buf.write(' RETURNING "id", "name", "created_at", "updated_at";');
StreamController<Author> ctrl = new StreamController<Author>();
connection.query(buf.toString()).then((rows) {
connection
.query(toSql('DELETE FROM "authors"') +
' RETURNING "id", "name", "created_at", "updated_at";')
.then((rows) {
rows.map(parseRow).forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);

View file

@ -12,51 +12,79 @@ import 'book.dart';
import 'author.orm.g.dart';
class BookQuery {
final List<String> _and = [];
final Map<BookQuery, bool> _unions = {};
final List<String> _or = [];
String _sortKey;
final List<String> _not = [];
String _sortMode;
int limit;
int offset;
final List<BookQueryWhere> _or = [];
final BookQueryWhere where = new BookQueryWhere();
void and(BookQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_and.add(compiled);
}
void union(BookQuery query) {
_unions[query] = false;
}
void or(BookQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_or.add(compiled);
}
void unionAll(BookQuery query) {
_unions[query] = true;
}
void not(BookQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_not.add(compiled);
}
void sortDescending(String key) {
_sortMode = 'Descending';
_sortKey = key;
}
String toSql() {
var buf = new StringBuffer('SELECT * FROM "books"');
void sortAscending(String key) {
_sortMode = 'Ascending';
_sortKey = key;
}
void or(BookQueryWhere selector) {
_or.add(selector);
}
String toSql([String prefix]) {
var buf = new StringBuffer();
buf.write(prefix != null ? prefix : 'SELECT * FROM "books"');
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
}
if (_and.isNotEmpty) {
buf.write(' AND (' + _and.join(',') + ')');
_or.forEach((x) {
var whereClause = x.toWhereClause(keyword: false);
if (whereClause != null) {
buf.write(' OR (' + whereClause + ')');
}
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(',') + ')');
});
if (prefix == null) {
if (limit != null) {
buf.write(' LIMIT ' + limit.toString());
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(',') + ')');
if (offset != null) {
buf.write(' OFFSET ' + offset.toString());
}
if (_sortMode == 'Descending') {
buf.write(' ORDER BY "' + _sortKey + '" DESC');
}
if (_sortMode == 'Ascending') {
buf.write(' ORDER BY "' + _sortKey + '" ASC');
}
_unions.forEach((query, all) {
buf.write(' UNION');
if (all) {
buf.write(' ALL');
}
buf.write(' (');
var sql = query.toSql().replaceAll(';', '');
buf.write(sql + ')');
});
buf.write(';');
}
return buf.toString();
}
@ -110,23 +138,11 @@ class BookQuery {
}
Stream<Book> delete(PostgreSQLConnection connection) {
var buf = new StringBuffer('DELETE FROM "books"');
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
if (_and.isNotEmpty) {
buf.write(' AND (' + _and.join(', ') + ')');
}
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(', ') + ')');
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(', ') + ')');
}
}
buf.write(' RETURNING "id", "name", "created_at", "updated_at";');
StreamController<Book> ctrl = new StreamController<Book>();
connection.query(buf.toString()).then((rows) {
connection
.query(toSql('DELETE FROM "books"') +
' RETURNING "id", "name", "created_at", "updated_at";')
.then((rows) {
rows.map(parseRow).forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);

View file

@ -11,51 +11,79 @@ import 'package:postgres/postgres.dart';
import 'car.dart';
class CarQuery {
final List<String> _and = [];
final Map<CarQuery, bool> _unions = {};
final List<String> _or = [];
String _sortKey;
final List<String> _not = [];
String _sortMode;
int limit;
int offset;
final List<CarQueryWhere> _or = [];
final CarQueryWhere where = new CarQueryWhere();
void and(CarQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_and.add(compiled);
}
void union(CarQuery query) {
_unions[query] = false;
}
void or(CarQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_or.add(compiled);
}
void unionAll(CarQuery query) {
_unions[query] = true;
}
void not(CarQuery other) {
var compiled = other.where.toWhereClause(keyword: false);
if (compiled != null) {
_not.add(compiled);
}
void sortDescending(String key) {
_sortMode = 'Descending';
_sortKey = key;
}
String toSql() {
var buf = new StringBuffer('SELECT * FROM "cars"');
void sortAscending(String key) {
_sortMode = 'Ascending';
_sortKey = key;
}
void or(CarQueryWhere selector) {
_or.add(selector);
}
String toSql([String prefix]) {
var buf = new StringBuffer();
buf.write(prefix != null ? prefix : 'SELECT * FROM "cars"');
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
}
if (_and.isNotEmpty) {
buf.write(' AND (' + _and.join(',') + ')');
_or.forEach((x) {
var whereClause = x.toWhereClause(keyword: false);
if (whereClause != null) {
buf.write(' OR (' + whereClause + ')');
}
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(',') + ')');
});
if (prefix == null) {
if (limit != null) {
buf.write(' LIMIT ' + limit.toString());
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(',') + ')');
if (offset != null) {
buf.write(' OFFSET ' + offset.toString());
}
if (_sortMode == 'Descending') {
buf.write(' ORDER BY "' + _sortKey + '" DESC');
}
if (_sortMode == 'Ascending') {
buf.write(' ORDER BY "' + _sortKey + '" ASC');
}
_unions.forEach((query, all) {
buf.write(' UNION');
if (all) {
buf.write(' ALL');
}
buf.write(' (');
var sql = query.toSql().replaceAll(';', '');
buf.write(sql + ')');
});
buf.write(';');
}
return buf.toString();
}
@ -120,24 +148,11 @@ class CarQuery {
}
Stream<Car> delete(PostgreSQLConnection connection) {
var buf = new StringBuffer('DELETE FROM "cars"');
var whereClause = where.toWhereClause();
if (whereClause != null) {
buf.write(' ' + whereClause);
if (_and.isNotEmpty) {
buf.write(' AND (' + _and.join(', ') + ')');
}
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(', ') + ')');
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(', ') + ')');
}
}
buf.write(
' RETURNING "id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at";');
StreamController<Car> ctrl = new StreamController<Car>();
connection.query(buf.toString()).then((rows) {
connection
.query(toSql('DELETE FROM "cars"') +
' RETURNING "id", "make", "description", "family_friendly", "recalled_at", "created_at", "updated_at";')
.then((rows) {
rows.map(parseRow).forEach(ctrl.add);
ctrl.close();
}).catchError(ctrl.addError);