Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not rely on message text when transforming exception. #382

Merged
merged 1 commit into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
- `EncoderFn` value converter for generic Dart object -> Postgres-encoded bytes
(for values where type is not specified).
- `DatabaseInfo` tracks information about relations and oids (currently limited to `RelationMessage` caching).
- **Behaviour / soft-breaking changes**:
- **Behaviour changes**, may be breaking in some cases:
- Preparing/executing a stamement on the main connection while in a `runTx` callback will throw an exception.
- Setting `timeout` will try to actively cancel the current statement using a new connection.
- `ServerException` may be transformed into `_PgQueryCancelledException` which is both `PgException` and `TimeoutException` (but no longer `ServerException`).
- **API deprecations**:
- Deprecated `TupleDataColumn.data`, use `.value` instead (for binary protocol messages).
- Deprecated some logical replication message parsing method.
- Removed `@internal`-annotated methods from the public API of `ServerException` and `Severity`.
- `ServerException` may be transformed into `_PgTimeoutException` which is both `PgException` and `TimeoutException` (but no longer `ServerException`).

## 3.3.0

Expand Down
24 changes: 10 additions & 14 deletions lib/src/exceptions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -190,27 +190,23 @@ ServerException buildExceptionFromErrorFields(List<ErrorField> errorFields) {
);
}

PgException transformServerException(
ServerException ex, {
bool timeoutTriggered = false,
}) {
if (ex.code == '57014' &&
ex.message == 'canceling statement due to statement timeout') {
return _PgTimeoutException(
['${ex.code}:', ex.message, ex.trace].whereType<String>().join(' '),
);
}
if (ex.code == '57014' && timeoutTriggered) {
return _PgTimeoutException(
PgException transformServerException(ServerException ex) {
if (ex.code == '57014') {
return _PgQueryCancelledException(
['${ex.code}:', ex.message, ex.trace].whereType<String>().join(' '),
severity: ex.severity,
);
}
return ex;
}

class _PgTimeoutException extends PgException implements TimeoutException {
class _PgQueryCancelledException extends PgException
implements TimeoutException {
@override
late final duration = null;

_PgTimeoutException(super.message) : super(severity: Severity.error);
_PgQueryCancelledException(
super.message, {
required super.severity,
});
}
7 changes: 0 additions & 7 deletions lib/src/v3/connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -892,11 +892,9 @@ class _PgResultStreamSubscription
required List<ResultRow> items,
required Duration? timeout,
}) async {
bool timeoutTriggered = false;
final cancelTimer = timeout == null
? null
: Timer(timeout, () async {
timeoutTriggered = true;
await connection.cancelPendingStatement();
});
try {
Expand All @@ -906,11 +904,6 @@ class _PgResultStreamSubscription
affectedRows: await affectedRows,
schema: await schema,
);
} on ServerException catch (e) {
if (timeoutTriggered) {
throw transformServerException(e, timeoutTriggered: timeoutTriggered);
}
rethrow;
} finally {
cancelTimer?.cancel();
}
Expand Down
10 changes: 9 additions & 1 deletion test/timeout_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ void main() {
test('Cancel current statement through a new connection', () async {
final f = conn.execute('SELECT pg_sleep(2);');
await (conn as PgConnectionImplementation).cancelPendingStatement();
await expectLater(f, throwsA(isA<ServerException>()));
await expectLater(
f,
throwsA(
allOf(
isA<PgException>(),
isA<TimeoutException>(),
),
),
);
// connection is still usable
final rs = await conn.execute('SELECT 1;');
expect(rs[0][0], 1);
Expand Down
Loading