From 7298b4638be84a48e25b8d2e196ceb88d364fc74 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Fri, 14 Jul 2017 19:03:39 -0400 Subject: [PATCH] Fixed #13 - #18 --- angel_orm/lib/src/query.dart | 1 - angel_orm_generator/CHANGELOG.md | 7 + .../lib/src/builder/postgres/postgres.dart | 261 +++++++++++------- .../postgres/postgres_build_context.dart | 17 ++ angel_orm_generator/pubspec.yaml | 2 +- angel_orm_generator/test/car_test.dart | 29 +- .../test/models/author.orm.g.dart | 106 ++++--- .../test/models/book.orm.g.dart | 106 ++++--- .../test/models/car.orm.g.dart | 107 ++++--- 9 files changed, 390 insertions(+), 246 deletions(-) create mode 100644 angel_orm_generator/CHANGELOG.md diff --git a/angel_orm/lib/src/query.dart b/angel_orm/lib/src/query.dart index 603d8385..479e396e 100644 --- a/angel_orm/lib/src/query.dart +++ b/angel_orm/lib/src/query.dart @@ -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'; diff --git a/angel_orm_generator/CHANGELOG.md b/angel_orm_generator/CHANGELOG.md new file mode 100644 index 00000000..ae80dcb7 --- /dev/null +++ b/angel_orm_generator/CHANGELOG.md @@ -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`. \ No newline at end of file diff --git a/angel_orm_generator/lib/src/builder/postgres/postgres.dart b/angel_orm_generator/lib/src/builder/postgres/postgres.dart index 56b035a2..c490ab05 100644 --- a/angel_orm_generator/lib/src/builder/postgres/postgres.dart +++ b/angel_orm_generator/lib/src/builder/postgres/postgres.dart @@ -12,7 +12,12 @@ import 'package:source_gen/source_gen.dart'; import 'build_context.dart'; import 'postgres_build_context.dart'; -const List RELATIONS = const ['and', 'or', 'not']; +const List RELATIONS = const ['or']; +const List RESTRICTORS = const ['limit', 'offset']; +const Map SORT_MODES = const { + 'Descending': 'DESC', + 'Ascending': 'ASC' +}; // TODO: HasOne, HasMany, BelongsTo class PostgresORMGenerator extends GeneratorForAnnotation { @@ -95,26 +100,51 @@ class PostgresORMGenerator extends GeneratorForAnnotation { 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 = {'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 { 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 { ])); 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 data = {}; @@ -217,18 +302,9 @@ class PostgresORMGenerator extends GeneratorForAnnotation { 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 { }); // 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 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 { 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', [ diff --git a/angel_orm_generator/lib/src/builder/postgres/postgres_build_context.dart b/angel_orm_generator/lib/src/builder/postgres/postgres_build_context.dart index 9a52805a..289e05e8 100644 --- a/angel_orm_generator/lib/src/builder/postgres/postgres_build_context.dart +++ b/angel_orm_generator/lib/src/builder/postgres/postgres_build_context.dart @@ -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 columnInfo = {}; final Map indices = {}; final Map relationships = {}; @@ -28,6 +33,18 @@ class PostgresBuildContext extends BuildContext { final List 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 get aliases => raw.aliases; Map get shimmed => raw.shimmed; diff --git a/angel_orm_generator/pubspec.yaml b/angel_orm_generator/pubspec.yaml index eb3b7f5e..0bb44716 100644 --- a/angel_orm_generator/pubspec.yaml +++ b/angel_orm_generator/pubspec.yaml @@ -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 homepage: https://github.com/angel-dart/orm diff --git a/angel_orm_generator/test/car_test.dart b/angel_orm_generator/test/car_test.dart index 626202d4..7f3d0a98 100644 --- a/angel_orm_generator/test/car_test.dart +++ b/angel_orm_generator/test/car_test.dart @@ -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()); diff --git a/angel_orm_generator/test/models/author.orm.g.dart b/angel_orm_generator/test/models/author.orm.g.dart index a690bdbd..02f38433 100644 --- a/angel_orm_generator/test/models/author.orm.g.dart +++ b/angel_orm_generator/test/models/author.orm.g.dart @@ -11,51 +11,79 @@ import 'package:postgres/postgres.dart'; import 'author.dart'; class AuthorQuery { - final List _and = []; + final Map _unions = {}; - final List _or = []; + String _sortKey; - final List _not = []; + String _sortMode; + + int limit; + + int offset; + + final List _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 (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(); } @@ -108,23 +136,11 @@ class AuthorQuery { } Stream 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 ctrl = new StreamController(); - 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); diff --git a/angel_orm_generator/test/models/book.orm.g.dart b/angel_orm_generator/test/models/book.orm.g.dart index 20e2e17b..a3dde4ba 100644 --- a/angel_orm_generator/test/models/book.orm.g.dart +++ b/angel_orm_generator/test/models/book.orm.g.dart @@ -12,51 +12,79 @@ import 'book.dart'; import 'author.orm.g.dart'; class BookQuery { - final List _and = []; + final Map _unions = {}; - final List _or = []; + String _sortKey; - final List _not = []; + String _sortMode; + + int limit; + + int offset; + + final List _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 (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(); } @@ -110,23 +138,11 @@ class BookQuery { } Stream 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 ctrl = new StreamController(); - 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); diff --git a/angel_orm_generator/test/models/car.orm.g.dart b/angel_orm_generator/test/models/car.orm.g.dart index a4783e56..ab2b5422 100644 --- a/angel_orm_generator/test/models/car.orm.g.dart +++ b/angel_orm_generator/test/models/car.orm.g.dart @@ -11,51 +11,79 @@ import 'package:postgres/postgres.dart'; import 'car.dart'; class CarQuery { - final List _and = []; + final Map _unions = {}; - final List _or = []; + String _sortKey; - final List _not = []; + String _sortMode; + + int limit; + + int offset; + + final List _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 (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(); } @@ -120,24 +148,11 @@ class CarQuery { } Stream 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 ctrl = new StreamController(); - 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);