Issue: rollback only if connection is not closed
Author: velom
State: closed
Created: 2018-11-07 22:43:55

this fix prevent next error
```
  File "contrib/python/peewee/peewee.py", line 5952, in __iter__
    self.execute()
  File "contrib/python/peewee/peewee.py", line 1604, in inner
    return method(self, database, *args, **kwargs)
  File "contrib/python/peewee/peewee.py", line 1675, in execute
    return self._execute(database)
  File "contrib/python/peewee/peewee.py", line 1826, in _execute
    cursor = database.execute(self)
  File "contrib/python/peewee/peewee.py", line 2696, in execute
    return self.execute_sql(sql, params, commit=commit)
  File "contrib/python/peewee/peewee.py", line 2690, in execute_sql
    self.commit()
  File "contrib/python/peewee/peewee.py", line 2481, in __exit__
    reraise(new_type, new_type(*exc_args), traceback)
  File "contrib/python/peewee/peewee.py", line 2686, in execute_sql
    self.rollback()
  File "contrib/python/peewee/peewee.py", line 2805, in rollback
    return self._state.conn.rollback()
InterfaceError: connection already closed
```

I'm unclear on when this would occur? Take a look at the code to `execute_sql()`:

```python
    def execute_sql(self, sql, params=None, commit=SENTINEL):
        logger.debug((sql, params))
        if commit is SENTINEL:
            if self.in_transaction():
                commit = False
            elif self.commit_select:
                commit = True
            else:
                commit = not sql[:6].lower().startswith('select')

        with __exception_wrapper__:
            cursor = self.cursor(commit)
            try:
                cursor.execute(sql, params or ())
            except Exception:
                if self.autorollback and not self.in_transaction():
                    self.rollback()
                raise
            else:
                if commit and not self.in_transaction():
                    self.commit()
        return cursor
```

You can see that we call `cursor = self.cursor(...)`, so what happens in the `cursor` method?

```python
    def cursor(self, commit=None):
        if self.is_closed():
            self.connect()
        return self._state.conn.cursor()
```

Peewee is already checking if the conn is closed, and if so, it will connect and obtain a DB-API cursor from the driver.

So what is going on in your example? Is the connection being closed by the server midway through the query execution or something?

In that case, the underlying connection wouldn't appear closed *anyways* since Peewee's determination about whether the conn is closed or not refers only to the internal state within Peewee -- not the actual *liveness* of the connection.

It seems like trading one exception for another, and I'm OK with the current behavior.

Let's see, what happens in `execute_sql` using `PostgresqlDatabase` with `psycopg2 == 2.7.3`
Section
```python
            try:
                cursor.execute(sql, params or ())
            except Exception:
                if self.autorollback and not self.in_transaction():
                    self.rollback()
                raise
            else:
                if commit and not self.in_transaction():
                    self.commit()
```
During `cursor.execute` there may be an exception like
```
OperationalError: server closed the connection unexpectedly
	This probably means the server terminated abnormally
	before or while processing the request.
```
or
```
OperationalError: could not connect to server: Connection timed out
	Is the server running on host "XXX" (1.1.1.1) and accepting
	TCP/IP connections on port 6432?
could not make a writable connection to server "YYY:6432"
could not make a writable connection to server "ZZZ:6432"
```
Than, psycopg2 connection object became something like this
```
>> self._state.conn.closed
2
```
Afterwards, when we catch this `OperationalError` and call `self.rollback()` psycopg2 driver raises exception `InterfaceError: connection already closed`.
In this case we will see in logs only `InterfaceError`, original `OperationalError` will be lost. I spent too many debug hours to find this problem.

You might consider not using autorollback?

Sure, i can reproduce this code in each database request, but autorollback functionality is still broken.