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:meta/meta.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:string_scanner/string_scanner.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 'build_context.dart';
import 'postgres_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 // TODO: HasOne, HasMany, BelongsTo
class PostgresORMGenerator extends GeneratorForAnnotation<ORM> { class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
@ -95,26 +100,51 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var clazz = new ClassBuilder(ctx.queryClassName); var clazz = new ClassBuilder(ctx.queryClassName);
// Add constructor + field // Add constructor + field
var PostgreSQLConnection = new TypeBuilder('PostgreSQLConnection');
var connection = reference('connection'); 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) { for (var relation in RELATIONS) {
clazz.addField(varFinal('_$relation', clazz.addField(varFinal('_$relation',
type: new TypeBuilder('List', genericTypes: [lib$core.String]), type: new TypeBuilder('List', genericTypes: [ctx.whereClassBuilder]),
value: list([]))); value: list([])));
var relationMethod = var relationMethod =
new MethodBuilder(relation, returnType: lib$core.$void); new MethodBuilder(relation, returnType: lib$core.$void);
relationMethod.addPositional( relationMethod
parameter('other', [new TypeBuilder(ctx.queryClassName)])); .addPositional(parameter('selector', [ctx.whereClassBuilder]));
var otherWhere = reference('other').property('where'); relationMethod.addStatement(
var compiled = reference('compiled'); reference('_$relation').invoke('add', [reference('selector')]));
relationMethod.addStatement(varField('compiled',
value: otherWhere.invoke('toWhereClause', [],
namedArguments: {'keyword': literal(false)})));
relationMethod.addStatement(ifThen(compiled.notEquals(literal(null)), [
reference('_$relation').invoke('add', [compiled])
]));
clazz.addMethod(relationMethod); clazz.addMethod(relationMethod);
} }
@ -159,24 +189,35 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
clazz.addMethod( clazz.addMethod(
new MethodBuilder('getAll', new MethodBuilder('getAll',
returnType: new TypeBuilder('Stream', returnType: new TypeBuilder('Stream',
genericTypes: [new TypeBuilder(ctx.modelClassName)]), genericTypes: [ctx.modelClassBuilder]),
returns: new TypeBuilder(ctx.queryClassName) returns: ctx.queryClassBuilder
.newInstance([]).invoke('get', [connection])) .newInstance([]).invoke('get', [connection]))
..addPositional(parameter('connection', [PostgreSQLConnection])), ..addPositional(
parameter('connection', [ctx.postgreSQLConnectionBuilder])),
asStatic: true); asStatic: true);
return clazz; return clazz;
} }
MethodBuilder buildToSqlMethod(PostgresBuildContext ctx) { 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); var meth = new MethodBuilder('toSql', returnType: lib$core.String);
meth.addStatement(varField('buf', meth.addPositional(parameter('prefix', [lib$core.String]).asOptional());
value: lib$core.StringBuffer var buf = reference('buf');
.newInstance([literal('SELECT * FROM "${ctx.tableName}"')]))); 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', meth.addStatement(varField('whereClause',
value: reference('where').invoke('toWhereClause', []))); value: reference('where').invoke('toWhereClause', [])));
var buf = reference('buf');
var whereClause = reference('whereClause'); var whereClause = reference('whereClause');
meth.addStatement(ifThen(whereClause.notEquals(literal(null)), [ meth.addStatement(ifThen(whereClause.notEquals(literal(null)), [
@ -184,29 +225,73 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
])); ]));
for (var relation in RELATIONS) { for (var relation in RELATIONS) {
var ref = reference('_$relation'); var ref = reference('_$relation'),
x = reference('x'),
whereClause = reference('whereClause');
var upper = relation.toUpperCase(); 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'), [ meth.addStatement(ref.invoke('forEach', [closure]));
buf.invoke('write', [literal(' $upper (') + joined + literal(')')]) }
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(';')])); var sortMode = reference('_sortMode');
meth.addStatement(buf.invoke('toString', []).asReturn());
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; return meth;
} }
MethodBuilder buildParseRowMethod(PostgresBuildContext ctx) { MethodBuilder buildParseRowMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('parseRow', var meth = new MethodBuilder('parseRow', returnType: ctx.modelClassBuilder);
returnType: new TypeBuilder(ctx.modelClassName));
meth.addPositional(parameter('row', [lib$core.List])); meth.addPositional(parameter('row', [lib$core.List]));
//meth.addStatement(lib$core.print.call( //meth.addStatement(lib$core.print.call(
// [literal('ROW MAP: ') + reference('row').invoke('toString', [])])); // [literal('ROW MAP: ') + reference('row').invoke('toString', [])]));
var row = reference('row'); var row = reference('row');
var DATE_YMD_HMS = reference('DATE_YMD_HMS');
// We want to create a Map using the SQL row. // We want to create a Map using the SQL row.
Map<String, ExpressionBuilder> data = {}; Map<String, ExpressionBuilder> data = {};
@ -217,18 +302,9 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var name = ctx.resolveFieldName(field.name); var name = ctx.resolveFieldName(field.name);
var rowKey = row[literal(i++)]; 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')) { if (field.name == 'id' && ctx.shimmed.containsKey('id')) {
data[name] = rowKey.invoke('toString', []); data[name] = rowKey.invoke('toString', []);
} /* else if (field.type.isAssignableTo(ctx.typeProvider.boolType)) { } else
// TODO: Find out what date is returned as
data[name] = rowKey.equals(literal(1));
}*/
else
data[name] = rowKey; data[name] = rowKey;
}); });
@ -244,7 +320,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
}); });
// Then, call a .fromJson() constructor // Then, call a .fromJson() constructor
meth.addStatement(new TypeBuilder(ctx.modelClassName) meth.addStatement(ctx.modelClassBuilder
.newInstance([map(data)], constructor: 'fromJson').asReturn()); .newInstance([map(data)], constructor: 'fromJson').asReturn());
return meth; return meth;
@ -266,12 +342,12 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildGetMethod(PostgresBuildContext ctx) { MethodBuilder buildGetMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('get', var meth = new MethodBuilder('get',
returnType: new TypeBuilder('Stream', returnType:
genericTypes: [new TypeBuilder(ctx.modelClassName)])); new TypeBuilder('Stream', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional( meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')])); parameter('connection', [ctx.postgreSQLConnectionBuilder]));
var streamController = new TypeBuilder('StreamController', var streamController = new TypeBuilder('StreamController',
genericTypes: [new TypeBuilder(ctx.modelClassName)]); genericTypes: [ctx.modelClassBuilder]);
meth.addStatement(varField('ctrl', meth.addStatement(varField('ctrl',
type: streamController, value: streamController.newInstance([]))); type: streamController, value: streamController.newInstance([])));
@ -283,11 +359,11 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildGetOneMethod(PostgresBuildContext ctx) { MethodBuilder buildGetOneMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('getOne', var meth = new MethodBuilder('getOne',
returnType: new TypeBuilder('Future', returnType:
genericTypes: [new TypeBuilder(ctx.modelClassName)])); new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional(parameter('id', [lib$core.int])); meth.addPositional(parameter('id', [lib$core.int]));
meth.addPositional( meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')])); parameter('connection', [ctx.postgreSQLConnectionBuilder]));
meth.addStatement(reference('connection').invoke('query', [ meth.addStatement(reference('connection').invoke('query', [
literal('SELECT * FROM "${ctx.tableName}" WHERE "id" = @id;') literal('SELECT * FROM "${ctx.tableName}" WHERE "id" = @id;')
], namedArguments: { ], namedArguments: {
@ -360,10 +436,10 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildUpdateMethod(PostgresBuildContext ctx) { MethodBuilder buildUpdateMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('update', var meth = new MethodBuilder('update',
returnType: new TypeBuilder('Stream', returnType:
genericTypes: [new TypeBuilder(ctx.modelClassName)])); new TypeBuilder('Stream', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional( meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')])); parameter('connection', [ctx.postgreSQLConnectionBuilder]));
_addAllNamed(meth, ctx); _addAllNamed(meth, ctx);
var buf = new StringBuffer('UPDATE "${ctx.tableName}" SET ('); var buf = new StringBuffer('UPDATE "${ctx.tableName}" SET (');
@ -409,7 +485,7 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
var substitutionValues = _buildSubstitutionValues(ctx); var substitutionValues = _buildSubstitutionValues(ctx);
var ctrlType = new TypeBuilder('StreamController', var ctrlType = new TypeBuilder('StreamController',
genericTypes: [new TypeBuilder(ctx.modelClassName)]); genericTypes: [ctx.modelClassBuilder]);
meth.addStatement(varField('ctrl', value: ctrlType.newInstance([]))); meth.addStatement(varField('ctrl', value: ctrlType.newInstance([])));
var result = _executeQuery( var result = _executeQuery(
$buf.invoke('toString', []) + literal(buf2.toString()), $buf.invoke('toString', []) + literal(buf2.toString()),
@ -421,46 +497,23 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildDeleteMethod(PostgresBuildContext ctx) { MethodBuilder buildDeleteMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('delete', var meth = new MethodBuilder('delete',
returnType: new TypeBuilder('Stream', returnType:
genericTypes: [new TypeBuilder(ctx.modelClassName)])); new TypeBuilder('Stream', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional( meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')])); parameter('connection', [ctx.postgreSQLConnectionBuilder]));
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(')')
])
]));
}
var litBuf = new StringBuffer(); var litBuf = new StringBuffer();
_addReturning(litBuf, ctx); _addReturning(litBuf, ctx);
meth.addStatement(buf.invoke('write', [literal(litBuf.toString())]));
var streamController = new TypeBuilder('StreamController', var streamController = new TypeBuilder('StreamController',
genericTypes: [new TypeBuilder(ctx.modelClassName)]); genericTypes: [ctx.modelClassBuilder]);
meth.addStatement(varField('ctrl', meth.addStatement(varField('ctrl',
type: streamController, value: streamController.newInstance([]))); type: streamController, value: streamController.newInstance([])));
var future = var future = reference('connection').invoke('query', [
reference('connection').invoke('query', [buf.invoke('toString', [])]); reference('toSql').call([literal('DELETE FROM "${ctx.tableName}"')]) +
literal(litBuf.toString())
]);
_invokeStreamClosure(future, meth); _invokeStreamClosure(future, meth);
return meth; return meth;
@ -469,11 +522,11 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildDeleteOneMethod(PostgresBuildContext ctx) { MethodBuilder buildDeleteOneMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('deleteOne', var meth = new MethodBuilder('deleteOne',
modifier: MethodModifier.asAsync, modifier: MethodModifier.asAsync,
returnType: new TypeBuilder('Future', returnType:
genericTypes: [new TypeBuilder(ctx.modelClassName)])) new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]))
..addPositional(parameter('id', [lib$core.int])) ..addPositional(parameter('id', [lib$core.int]))
..addPositional( ..addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')])); parameter('connection', [ctx.postgreSQLConnectionBuilder]));
var id = reference('id'); var id = reference('id');
var connection = reference('connection'); var connection = reference('connection');
@ -498,10 +551,10 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildInsertMethod(PostgresBuildContext ctx) { MethodBuilder buildInsertMethod(PostgresBuildContext ctx) {
var meth = new MethodBuilder('insert', var meth = new MethodBuilder('insert',
modifier: MethodModifier.asAsync, modifier: MethodModifier.asAsync,
returnType: new TypeBuilder('Future', returnType:
genericTypes: [new TypeBuilder(ctx.modelClassName)])); new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional( meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')])); parameter('connection', [ctx.postgreSQLConnectionBuilder]));
// Add all named params // Add all named params
_addAllNamed(meth, ctx); _addAllNamed(meth, ctx);
@ -554,22 +607,21 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildInsertModelMethod(PostgresBuildContext ctx) { MethodBuilder buildInsertModelMethod(PostgresBuildContext ctx) {
var rc = new ReCase(ctx.modelClassName); var rc = new ReCase(ctx.modelClassName);
var meth = new MethodBuilder('insert${rc.pascalCase}', var meth = new MethodBuilder('insert${rc.pascalCase}',
returnType: new TypeBuilder('Future', returnType:
genericTypes: [new TypeBuilder(ctx.modelClassName)])); new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional( meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')])); parameter('connection', [ctx.postgreSQLConnectionBuilder]));
meth.addPositional( meth.addPositional(parameter(rc.camelCase, [ctx.modelClassBuilder]));
parameter(rc.snakeCase, [new TypeBuilder(ctx.modelClassName)]));
Map<String, ExpressionBuilder> args = {}; Map<String, ExpressionBuilder> args = {};
var ref = reference(rc.snakeCase); var ref = reference(rc.camelCase);
ctx.fields.forEach((f) { ctx.fields.forEach((f) {
if (f.name != 'id') args[f.name] = ref.property(f.name); 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) .invoke('insert', [reference('connection')], namedArguments: args)
.asReturn()); .asReturn());
@ -579,19 +631,18 @@ class PostgresORMGenerator extends GeneratorForAnnotation<ORM> {
MethodBuilder buildUpdateModelMethod(PostgresBuildContext ctx) { MethodBuilder buildUpdateModelMethod(PostgresBuildContext ctx) {
var rc = new ReCase(ctx.modelClassName); var rc = new ReCase(ctx.modelClassName);
var meth = new MethodBuilder('update${rc.pascalCase}', var meth = new MethodBuilder('update${rc.pascalCase}',
returnType: new TypeBuilder('Future', returnType:
genericTypes: [new TypeBuilder(ctx.modelClassName)])); new TypeBuilder('Future', genericTypes: [ctx.modelClassBuilder]));
meth.addPositional( meth.addPositional(
parameter('connection', [new TypeBuilder('PostgreSQLConnection')])); parameter('connection', [ctx.postgreSQLConnectionBuilder]));
meth.addPositional( meth.addPositional(parameter(rc.camelCase, [ctx.modelClassBuilder]));
parameter(rc.snakeCase, [new TypeBuilder(ctx.modelClassName)]));
// var query = new XQuery(); // var query = new XQuery();
var ref = reference(rc.snakeCase); var ref = reference(rc.camelCase);
var query = reference('query'); var query = reference('query');
meth.addStatement(varField('query', meth.addStatement(
value: new TypeBuilder(ctx.queryClassName).newInstance([]))); varField('query', value: ctx.queryClassBuilder.newInstance([])));
// query.where.id.equals(x.id); // query.where.id.equals(x.id);
meth.addStatement(query.property('where').property('id').invoke('equals', [ 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_orm/angel_orm.dart';
import 'package:angel_serialize_generator/context.dart'; import 'package:angel_serialize_generator/context.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart';
class PostgresBuildContext extends BuildContext { class PostgresBuildContext extends BuildContext {
DartType _dateTimeTypeCache; DartType _dateTimeTypeCache;
LibraryElement _libraryCache; LibraryElement _libraryCache;
TypeProvider _typeProviderCache; TypeProvider _typeProviderCache;
TypeBuilder _modelClassBuilder,
_queryClassBuilder,
_whereClassBuilder,
_postgresqlConnectionBuilder;
final Map<String, Column> columnInfo = {}; final Map<String, Column> columnInfo = {};
final Map<String, IndexType> indices = {}; final Map<String, IndexType> indices = {};
final Map<String, Relationship> relationships = {}; final Map<String, Relationship> relationships = {};
@ -28,6 +33,18 @@ class PostgresBuildContext extends BuildContext {
final List<FieldElement> fields = [], relationshipFields = []; 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, String> get aliases => raw.aliases;
Map<String, bool> get shimmed => raw.shimmed; Map<String, bool> get shimmed => raw.shimmed;

View file

@ -1,5 +1,5 @@
name: angel_orm_generator name: angel_orm_generator
version: 1.0.0-alpha version: 1.0.0-alpha+1
description: Code generators for Angel's ORM. description: Code generators for Angel's ORM.
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,4 +1,3 @@
import 'dart:io';
import 'package:angel_orm/angel_orm.dart'; import 'package:angel_orm/angel_orm.dart';
import 'package:postgres/postgres.dart'; import 'package:postgres/postgres.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@ -84,10 +83,32 @@ main() {
expect(car.recalledAt, isNull); 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() var query = new CarQuery()
..where.make.like('Fer%') ..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()); print(query.toSql());
var cars = await query.get(connection).toList(); var cars = await query.get(connection).toList();
expect(cars, isEmpty); expect(cars, isEmpty);
@ -108,6 +129,8 @@ main() {
test('delete stream', () async { test('delete stream', () async {
var query = new CarQuery()..where.make.equals('Ferrari'); 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(); var cars = await query.delete(connection).toList();
expect(cars, hasLength(1)); expect(cars, hasLength(1));
expect(cars.first.toJson(), ferrari.toJson()); expect(cars.first.toJson(), ferrari.toJson());

View file

@ -11,51 +11,79 @@ import 'package:postgres/postgres.dart';
import 'author.dart'; import 'author.dart';
class AuthorQuery { 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(); final AuthorQueryWhere where = new AuthorQueryWhere();
void and(AuthorQuery other) { void union(AuthorQuery query) {
var compiled = other.where.toWhereClause(keyword: false); _unions[query] = false;
if (compiled != null) {
_and.add(compiled);
}
} }
void or(AuthorQuery other) { void unionAll(AuthorQuery query) {
var compiled = other.where.toWhereClause(keyword: false); _unions[query] = true;
if (compiled != null) {
_or.add(compiled);
}
} }
void not(AuthorQuery other) { void sortDescending(String key) {
var compiled = other.where.toWhereClause(keyword: false); _sortMode = 'Descending';
if (compiled != null) { _sortKey = key;
_not.add(compiled);
}
} }
String toSql() { void sortAscending(String key) {
var buf = new StringBuffer('SELECT * FROM "authors"'); _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(); var whereClause = where.toWhereClause();
if (whereClause != null) { if (whereClause != null) {
buf.write(' ' + whereClause); buf.write(' ' + whereClause);
} }
if (_and.isNotEmpty) { _or.forEach((x) {
buf.write(' AND (' + _and.join(',') + ')'); var whereClause = x.toWhereClause(keyword: false);
if (whereClause != null) {
buf.write(' OR (' + whereClause + ')');
}
});
if (prefix == null) {
if (limit != null) {
buf.write(' LIMIT ' + limit.toString());
}
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(';');
} }
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(',') + ')');
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(',') + ')');
}
buf.write(';');
return buf.toString(); return buf.toString();
} }
@ -108,23 +136,11 @@ class AuthorQuery {
} }
Stream<Author> delete(PostgreSQLConnection connection) { 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>(); 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); rows.map(parseRow).forEach(ctrl.add);
ctrl.close(); ctrl.close();
}).catchError(ctrl.addError); }).catchError(ctrl.addError);

View file

@ -12,51 +12,79 @@ import 'book.dart';
import 'author.orm.g.dart'; import 'author.orm.g.dart';
class BookQuery { 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(); final BookQueryWhere where = new BookQueryWhere();
void and(BookQuery other) { void union(BookQuery query) {
var compiled = other.where.toWhereClause(keyword: false); _unions[query] = false;
if (compiled != null) {
_and.add(compiled);
}
} }
void or(BookQuery other) { void unionAll(BookQuery query) {
var compiled = other.where.toWhereClause(keyword: false); _unions[query] = true;
if (compiled != null) {
_or.add(compiled);
}
} }
void not(BookQuery other) { void sortDescending(String key) {
var compiled = other.where.toWhereClause(keyword: false); _sortMode = 'Descending';
if (compiled != null) { _sortKey = key;
_not.add(compiled);
}
} }
String toSql() { void sortAscending(String key) {
var buf = new StringBuffer('SELECT * FROM "books"'); _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(); var whereClause = where.toWhereClause();
if (whereClause != null) { if (whereClause != null) {
buf.write(' ' + whereClause); buf.write(' ' + whereClause);
} }
if (_and.isNotEmpty) { _or.forEach((x) {
buf.write(' AND (' + _and.join(',') + ')'); var whereClause = x.toWhereClause(keyword: false);
if (whereClause != null) {
buf.write(' OR (' + whereClause + ')');
}
});
if (prefix == null) {
if (limit != null) {
buf.write(' LIMIT ' + limit.toString());
}
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(';');
} }
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(',') + ')');
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(',') + ')');
}
buf.write(';');
return buf.toString(); return buf.toString();
} }
@ -110,23 +138,11 @@ class BookQuery {
} }
Stream<Book> delete(PostgreSQLConnection connection) { 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>(); 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); rows.map(parseRow).forEach(ctrl.add);
ctrl.close(); ctrl.close();
}).catchError(ctrl.addError); }).catchError(ctrl.addError);

View file

@ -11,51 +11,79 @@ import 'package:postgres/postgres.dart';
import 'car.dart'; import 'car.dart';
class CarQuery { 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(); final CarQueryWhere where = new CarQueryWhere();
void and(CarQuery other) { void union(CarQuery query) {
var compiled = other.where.toWhereClause(keyword: false); _unions[query] = false;
if (compiled != null) {
_and.add(compiled);
}
} }
void or(CarQuery other) { void unionAll(CarQuery query) {
var compiled = other.where.toWhereClause(keyword: false); _unions[query] = true;
if (compiled != null) {
_or.add(compiled);
}
} }
void not(CarQuery other) { void sortDescending(String key) {
var compiled = other.where.toWhereClause(keyword: false); _sortMode = 'Descending';
if (compiled != null) { _sortKey = key;
_not.add(compiled);
}
} }
String toSql() { void sortAscending(String key) {
var buf = new StringBuffer('SELECT * FROM "cars"'); _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(); var whereClause = where.toWhereClause();
if (whereClause != null) { if (whereClause != null) {
buf.write(' ' + whereClause); buf.write(' ' + whereClause);
} }
if (_and.isNotEmpty) { _or.forEach((x) {
buf.write(' AND (' + _and.join(',') + ')'); var whereClause = x.toWhereClause(keyword: false);
if (whereClause != null) {
buf.write(' OR (' + whereClause + ')');
}
});
if (prefix == null) {
if (limit != null) {
buf.write(' LIMIT ' + limit.toString());
}
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(';');
} }
if (_or.isNotEmpty) {
buf.write(' OR (' + _or.join(',') + ')');
}
if (_not.isNotEmpty) {
buf.write(' NOT (' + _not.join(',') + ')');
}
buf.write(';');
return buf.toString(); return buf.toString();
} }
@ -120,24 +148,11 @@ class CarQuery {
} }
Stream<Car> delete(PostgreSQLConnection connection) { 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>(); 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); rows.map(parseRow).forEach(ctrl.add);
ctrl.close(); ctrl.close();
}).catchError(ctrl.addError); }).catchError(ctrl.addError);