Merge pull request #2308 from davidmartos96/pg_improvements

Remove hackyvariables from Postgres tests
This commit is contained in:
Simon Binder 2023-02-09 11:44:56 +01:00 committed by GitHub
commit a3ca208ded
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 52 additions and 33 deletions

View File

@ -74,15 +74,23 @@ class Variable<T extends Object> extends Expression<T> {
var explicitStart = context.explicitVariableIndex; var explicitStart = context.explicitVariableIndex;
var mark = '?'; var mark = '?';
var suffix = '';
if (context.dialect == SqlDialect.postgres) { if (context.dialect == SqlDialect.postgres) {
explicitStart = 1; explicitStart = 1;
mark = '@'; mark = '@';
if (value is List<int>) {
// We need to explicitly bind the variable as byte array. Otherwise
// a Uint8List like [1,2,3] would bind to "{1,2,3}" as bytes
suffix += ":bytea";
}
} }
if (explicitStart != null) { if (explicitStart != null) {
context.buffer context.buffer
..write(mark) ..write(mark)
..write(explicitStart + context.amountOfVariables); ..write(explicitStart + context.amountOfVariables)
..write(suffix);
context.introduceVariable( context.introduceVariable(
this, this,
mapToSimpleValue(context), mapToSimpleValue(context),

View File

@ -25,7 +25,7 @@ class _PgDelegate extends DatabaseDelegate {
final bool closeUnderlyingWhenClosed; final bool closeUnderlyingWhenClosed;
@override @override
TransactionDelegate get transactionDelegate => _PgTransactionDelegate(_db); TransactionDelegate get transactionDelegate => const NoTransactionDelegate();
@override @override
late DbVersionDelegate versionDelegate; late DbVersionDelegate versionDelegate;
@ -58,9 +58,7 @@ class _PgDelegate extends DatabaseDelegate {
final stmt = statements.statements[row.statementIndex]; final stmt = statements.statements[row.statementIndex];
final args = row.arguments; final args = row.arguments;
await _ec.execute(stmt, await _ec.execute(stmt, substitutionValues: _convertArgs(args));
substitutionValues: args.asMap().map((key, value) =>
MapEntry((key + 1).toString(), _convertValue(value))));
} }
return Future.value(); return Future.value();
@ -72,9 +70,7 @@ class _PgDelegate extends DatabaseDelegate {
if (args.isEmpty) { if (args.isEmpty) {
return _ec.execute(statement); return _ec.execute(statement);
} else { } else {
return _ec.execute(statement, return _ec.execute(statement, substitutionValues: _convertArgs(args));
substitutionValues: args.asMap().map((key, value) =>
MapEntry((key + 1).toString(), _convertValue(value))));
} }
} }
@ -90,9 +86,10 @@ class _PgDelegate extends DatabaseDelegate {
if (args.isEmpty) { if (args.isEmpty) {
result = await _ec.query(statement); result = await _ec.query(statement);
} else { } else {
result = await _ec.query(statement, result = await _ec.query(
substitutionValues: args.asMap().map((key, value) => statement,
MapEntry((key + 1).toString(), _convertValue(value)))); substitutionValues: _convertArgs(args),
);
} }
return result.firstOrNull?[0] as int? ?? 0; return result.firstOrNull?[0] as int? ?? 0;
} }
@ -105,9 +102,10 @@ class _PgDelegate extends DatabaseDelegate {
@override @override
Future<QueryResult> runSelect(String statement, List<Object?> args) async { Future<QueryResult> runSelect(String statement, List<Object?> args) async {
await _ensureOpen(); await _ensureOpen();
final result = await _ec.query(statement, final result = await _ec.query(
substitutionValues: args.asMap().map((key, value) => statement,
MapEntry((key + 1).toString(), _convertValue(value)))); substitutionValues: _convertArgs(args),
);
return Future.value(QueryResult.fromRows( return Future.value(QueryResult.fromRows(
result.map((e) => e.toColumnMap()).toList(growable: false))); result.map((e) => e.toColumnMap()).toList(growable: false)));
@ -126,6 +124,15 @@ class _PgDelegate extends DatabaseDelegate {
} }
return value; return value;
} }
Map<String, dynamic> _convertArgs(List<Object?> args) {
return args.asMap().map(
(key, value) => MapEntry(
(key + 1).toString(),
_convertValue(value),
),
);
}
} }
class _PgVersionDelegate extends DynamicVersionDelegate { class _PgVersionDelegate extends DynamicVersionDelegate {
@ -154,17 +161,3 @@ class _PgVersionDelegate extends DynamicVersionDelegate {
substitutionValues: {'1': version}); substitutionValues: {'1': version});
} }
} }
class _PgTransactionDelegate extends SupportedTransactionDelegate {
final PostgreSQLConnection _db;
const _PgTransactionDelegate(this._db);
@override
bool get managesLockInternally => false;
@override
Future startTransaction(Future Function(QueryDelegate) run) async {
await _db.transaction((connection) => run(_PgDelegate(_db, connection)));
}
}

View File

@ -7,7 +7,7 @@ class PgExecutor extends TestExecutor {
bool get supportsReturning => true; bool get supportsReturning => true;
@override @override
bool get hackyVariables => true; bool get supportsNestedTransactions => true;
@override @override
DatabaseConnection createConnection() { DatabaseConnection createConnection() {

View File

@ -69,7 +69,7 @@ class PreferenceConverter extends NullAwareTypeConverter<Preferences, String> {
'ORDER BY (SELECT COUNT(*) FROM friendships ' 'ORDER BY (SELECT COUNT(*) FROM friendships '
'WHERE first_user = u.id OR second_user = u.id) DESC LIMIT :amount', 'WHERE first_user = u.id OR second_user = u.id) DESC LIMIT :amount',
'amountOfGoodFriends': 'SELECT COUNT(*) FROM friendships f WHERE ' 'amountOfGoodFriends': 'SELECT COUNT(*) FROM friendships f WHERE '
'f.really_good_friends = 1 AND ' 'f.really_good_friends = TRUE AND '
'(f.first_user = :user OR f.second_user = :user)', '(f.first_user = :user OR f.second_user = :user)',
'friendshipsOf': ''' SELECT 'friendshipsOf': ''' SELECT
f.really_good_friends, "user".** f.really_good_friends, "user".**

View File

@ -555,7 +555,7 @@ abstract class _$Database extends GeneratedDatabase {
Selectable<int> amountOfGoodFriends(int user) { Selectable<int> amountOfGoodFriends(int user) {
return customSelect( return customSelect(
'SELECT COUNT(*) AS _c0 FROM friendships AS f WHERE f.really_good_friends = 1 AND(f.first_user = @1 OR f.second_user = @1)', 'SELECT COUNT(*) AS _c0 FROM friendships AS f WHERE f.really_good_friends = TRUE AND(f.first_user = @1 OR f.second_user = @1)',
variables: [ variables: [
Variable<int>(user) Variable<int>(user)
], ],

View File

@ -119,11 +119,29 @@ void crudTests(TestExecutor executor) {
tearDown(() => executor.clearDatabaseAndClose(database)); tearDown(() => executor.clearDatabaseAndClose(database));
Future<T?> evaluate<T extends Object>(Expression<T> expr) async { Future<T?> evaluate<T extends Object>(Expression<T> expr) async {
late final Expression<T> effectiveExpr;
if (database.executor.dialect == SqlDialect.postgres) {
// 'SELECT'ing values that don't come from a table return as String
// by default, so we need to explicitly cast it to the expected type
// https://www.postgresql.org/docs/current/typeconv-select.html
effectiveExpr = expr.cast<T>();
} else {
effectiveExpr = expr;
}
final query = database.selectOnly(database.users) final query = database.selectOnly(database.users)
..addColumns([expr]) ..addColumns([effectiveExpr])
..limit(1); ..limit(1);
final row = await query.getSingle(); final row = await query.getSingle();
return row.read(expr); final columnValue = row.read(effectiveExpr);
expect(
columnValue,
TypeMatcher<T?>(),
reason:
"Type of the input argument does not match the returned column value",
);
return columnValue;
} }
test('null', () { test('null', () {