withConnection<T> method

  1. @override
Future<T> withConnection<T>(
  1. Future<T> action(
    1. Connection connection
    )
)
override

Pins a pooled connection until action and its issued statements finish.

The callback connection expires immediately when action settles. Callback errors preserve their cause and stack. After failure, reuse requires all issued SQL to be inside transactions that only rolled back. SQL/cleanup failures, commits and work outside a transaction discard it. The first SQL failure stays primary if automatic rollback also fails. Normal callbacks retain PostgreSQL session state. Use SET LOCAL inside a transaction for scoped settings; manage session advisory locks explicitly. Unfinished direct BEGIN/START transactions roll back before reuse. Nested acquisition is rejected so a callback cannot wait on its own pool slot.

Implementation

@override
Future<T> withConnection<T>(
  Future<T> Function(Connection connection) action,
) async {
  if (_closing) {
    throw StateError('The PostgreSQL driver is closing or closed.');
  }
  if (Zone.current[_callbackZone] != null) {
    throw StateError('Nested PostgreSQL acquisition is not supported.');
  }
  _active++;
  try {
    final outcome = await _pool.withConnection((native) async {
      final connection = _PostgresConnection(native);
      T? value;
      (Object, StackTrace)? failure;
      try {
        value = await runZoned(
          () => action(connection),
          zoneValues: {_callbackZone: true},
        );
      } catch (error, stack) {
        failure = (error, stack);
      }
      try {
        await connection._finish();
      } catch (error, stack) {
        final cause = failure ?? (error, stack);
        await connection._discard();
        Error.throwWithStackTrace(cause.$1, cause.$2);
      }
      if (failure != null && !connection._canReuseAfterFailure) {
        await connection._discard();
        Error.throwWithStackTrace(failure.$1, failure.$2);
      }
      return (value: value, failure: failure);
    });
    if (outcome.failure case final failure?) {
      Error.throwWithStackTrace(failure.$1, failure.$2);
    }
    return outcome.value as T;
  } finally {
    _active--;
    if (_active == 0) {
      _idle?.complete();
      _idle = null;
    }
  }
}