fix standalone delete
This commit is contained in:
parent
ff12ed6bc0
commit
e647663fad
7 changed files with 118 additions and 34 deletions
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue