fix standalone delete

This commit is contained in:
Tobe O 2018-12-07 21:03:03 -05:00
parent ff12ed6bc0
commit e647663fad
7 changed files with 118 additions and 34 deletions

View file

@ -1,5 +1,8 @@
import 'dart:async';
import 'package:angel_model/angel_model.dart';
import 'package:angel_orm/angel_orm.dart';
import 'package:angel_orm/src/query.dart';
import 'package:angel_serialize/angel_serialize.dart';
part 'main.g.dart';
part 'main.serializer.g.dart';
@ -26,6 +29,11 @@ class _FakeExecutor extends QueryExecutor {
[1, 'Rich', 'Person', 100000.0, now, now]
];
}
@override
Future<T> transaction<T>(FutureOr<T> Function() f) {
throw new UnsupportedError('Transactions are not supported.');
}
}
@orm

View file

@ -1,3 +1,4 @@
import 'package:charcode/ascii.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:string_scanner/string_scanner.dart';
import 'query.dart';
@ -21,11 +22,11 @@ String sanitizeExpression(String unsafe) {
continue;
// Otherwise, add the next char, unless it's a null byte.
else if ((ch = scanner.readChar()) != 0 && ch != null)
else if ((ch = scanner.readChar()) != $nul && ch != null)
buf.writeCharCode(ch);
}
return buf.toString();
return toSql(buf.toString(), withQuotes: false);
}
abstract class SqlExpressionBuilder<T> {

View file

@ -47,28 +47,33 @@ class OrderBy {
String compile() => descending ? '$key DESC' : '$key ASC';
}
String toSql(Object obj) {
String toSql(Object obj, {bool withQuotes: true}) {
if (obj is DateTime) {
return "'${dateYmdHms.format(obj)}'";
return withQuotes ? "'${dateYmdHms.format(obj)}'" : dateYmdHms.format(obj);
} else if (obj is bool) {
return obj ? 'TRUE' : 'FALSE';
} else if (obj == null) {
return 'NULL';
} else if (obj is String) {
var s = obj.replaceAll("'", "\\'");
return "'$s'";
var b = new StringBuffer();
var escaped = false;
var it = obj.runes.iterator;
while (it.moveNext()) {
if (it.current == $nul)
continue; // Skip null byte
else if (isAscii(it.current)) {
else if (it.current == $single_quote) {
escaped = true;
b.write('\\x');
b.write(it.current.toRadixString(16).padLeft(2, '0'));
} else if (isAscii(it.current)) {
b.writeCharCode(it.current);
} else if (it.currentSize == 1) {
escaped = true;
b.write('\\u');
b.write(it.current.toRadixString(16).padLeft(4, '0'));
} else if (it.currentSize == 2) {
escaped = true;
b.write('\\U');
b.write(it.current.toRadixString(16).padLeft(8, '0'));
} else {
@ -77,7 +82,12 @@ String toSql(Object obj) {
}
}
return "'$b'";
if (!withQuotes)
return b.toString();
else if (escaped)
return "E'$b'";
else
return "'$b'";
} else {
return obj.toString();
}
@ -103,6 +113,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
/// This is usually a generated class.
QueryValues get values;
String adornWithTableName(String s) => '$tableName.$s';
/// Makes a new [Where] clause.
Where newWhereClause() {
throw new UnsupportedError(
@ -218,6 +230,8 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
}
if (withFields) b.write(f.join(', '));
b.write(' FROM $tableName');
if (_crossJoin != null) b.write(' CROSS JOIN $_crossJoin');
for (var join in _joins) b.write(' ${join.compile()}');
var whereClause =
where.compile(tableName: includeTableName ? tableName : null);
if (whereClause.isNotEmpty) b.write(' WHERE $whereClause');
@ -225,8 +239,6 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
if (_offset != null) b.write(' OFFSET $_offset');
if (_groupBy != null) b.write(' GROUP BY $_groupBy');
for (var item in _orderBy) b.write(' ${item.compile()}');
if (_crossJoin != null) b.write(' CROSS JOIN $_crossJoin');
for (var join in _joins) b.write(' ${join.compile()}');
return b.toString();
}
@ -236,11 +248,13 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
return super.getOne(executor);
}
Future<List<T>> delete(QueryExecutor executor) async {
var sql = compile(preamble: 'DELETE', withFields: false);
return executor
.query(sql, fields)
.then((it) => it.map(deserialize).toList());
Future<List<T>> delete(QueryExecutor executor) {
return executor.transaction(() async {
var existing = await get(executor);
//var sql = compile(preamble: 'SELECT $tableName.id', withFields: false);
var sql = compile(preamble: 'DELETE', withFields: false);
return executor.query(sql).then((_) => existing);
});
}
Future<T> deleteOne(QueryExecutor executor) {
@ -254,7 +268,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
throw new StateError('No values have been specified for update.');
} else {
return executor
.query(sql, fields)
.query(sql, fields.map(adornWithTableName).toList())
.then((it) => it.isEmpty ? null : deserialize(it.first));
}
}
@ -271,7 +285,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
if (whereClause.isNotEmpty) sql.write(' WHERE $whereClause');
if (_limit != null) sql.write(' LIMIT $_limit');
return executor
.query(sql.toString(), fields)
.query(sql.toString(), fields.map(adornWithTableName).toList())
.then((it) => it.map(deserialize).toList());
}
}
@ -470,4 +484,6 @@ abstract class QueryExecutor {
const QueryExecutor();
Future<List<List>> query(String query, [List<String> returningFields]);
Future<T> transaction<T>(FutureOr<T> f());
}

View file

@ -195,11 +195,8 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
// If there are any relations, we need some overrides.
if (ctx.relations.isNotEmpty) {
clazz.methods.add(new Method((b) {
clazz.constructors.add(new Constructor((b) {
b
..name = 'get'
..annotations.add(refer('override'))
..requiredParameters.add(new Parameter((b) => b..name = 'executor'))
..body = new Block((b) {
ctx.relations.forEach((fieldName, relation) {
//var name = ctx.buildContext.resolveFieldName(fieldName);
@ -220,10 +217,42 @@ class OrmGenerator extends GeneratorForAnnotation<Orm> {
}));
}
});
});
}));
b.addExpression(refer('super')
.property('get')
.call([refer('executor')]).returned);
clazz.methods.add(new Method((b) {
b
..name = 'insert'
..annotations.add(refer('override'))
..requiredParameters.add(new Parameter((b) => b..name = 'executor'))
..body = new Block((b) {
var inTransaction = new Method((b) {
b
..modifier = MethodModifier.async
..body = new Block((b) {
b.addExpression(refer('super')
.property('insert')
.call([refer('executor')])
.awaited
.assignVar('result'));
// Just call get() again
b.addExpression(
refer('where').property('id').property('equals').call([
(refer('int')
.property('parse')
.call([refer('result').property('id')]))
]));
b.addExpression(refer('result').assign(
refer('getOne').call([refer('executor')]).awaited));
b.addExpression(refer('result').returned);
});
});
b.addExpression(refer('executor')
.property('transaction')
.call([inTransaction.closure]).returned);
});
}));
}

View file

@ -16,11 +16,11 @@ Future<PostgresExecutor> connectToPostgres(Iterable<String> schemas) async {
}
class PostgresExecutor extends QueryExecutor {
final PostgreSQLConnection connection;
PostgreSQLExecutionContext connection;
PostgresExecutor(this.connection);
Future close() => connection.close();
Future close() => (connection as PostgreSQLConnection).close();
@override
Future<List<List>> query(String query, [List<String> returningFields]) {
@ -33,4 +33,19 @@ class PostgresExecutor extends QueryExecutor {
print('Running: $query');
return connection.query(query);
}
@override
Future<T> transaction<T>(FutureOr<T> Function() f) async {
var old = connection;
T result;
try {
await (connection as PostgreSQLConnection).transaction((ctx) async {
connection = ctx;
result = await f();
});
} finally {
connection = old;
return result;
}
}
}

View file

@ -7,6 +7,13 @@ part of angel_orm.generator.models.book;
// **************************************************************************
class BookQuery extends Query<Book, BookQueryWhere> {
BookQuery() {
leftJoin('authors', 'author_id', 'id',
additionalFields: const ['name', 'created_at', 'updated_at']);
leftJoin('authors', 'partner_author_id', 'id',
additionalFields: const ['name', 'created_at', 'updated_at']);
}
@override
final BookQueryValues values = new BookQueryValues();
@ -58,12 +65,13 @@ class BookQuery extends Query<Book, BookQueryWhere> {
}
@override
get(executor) {
leftJoin('authors', 'author_id', 'id',
additionalFields: const ['name', 'created_at', 'updated_at']);
leftJoin('authors', 'partner_author_id', 'id',
additionalFields: const ['name', 'created_at', 'updated_at']);
return super.get(executor);
insert(executor) {
return executor.transaction(() async {
var result = await super.insert(executor);
where.id.equals(int.parse(result.id));
result = await getOne(executor);
return result;
});
}
}

View file

@ -7,6 +7,8 @@ part of angel_orm_generator.test.models.leg;
// **************************************************************************
class LegQuery extends Query<Leg, LegQueryWhere> {
LegQuery() {}
@override
final LegQueryValues values = new LegQueryValues();
@ -46,8 +48,13 @@ class LegQuery extends Query<Leg, LegQueryWhere> {
}
@override
get(executor) {
return super.get(executor);
insert(executor) {
return executor.transaction(() async {
var result = await super.insert(executor);
where.id.equals(int.parse(result.id));
result = await getOne(executor);
return result;
});
}
}