Использовать try/except с psycopg2 или "с закрытием"?

Я использую Psycopg2 в Python для доступа к базе данных PostgreSQL. Мне любопытно, можно ли использовать шаблон with closing() для создания и использования курсора, или если я должен использовать явный try/except, обернутый вокруг запроса. Мой вопрос касается вставки или обновления и транзакций.

Как я понимаю, все запросы Psycopg2 встречаются внутри транзакции, и это до вызова кода для совершения или откат транзакции. Если в блоке with closing(... возникает ошибка, выдается ли откат? В более старых версиях Psycopg2 откат был явно выпущен на close(), но это уже не так (см. http://initd.org/psycopg/docs/connection.html#connection.close).

Мой вопрос может иметь больше смысла с примером. Здесь пример с использованием with closing(...

with closing(db.cursor()) as cursor:
     cursor.execute("""UPDATE users                    
             SET password = %s, salt = %s
             WHERE user_id = %s""",
             (pw_tuple[0], pw_tuple[1], user_id))
     module.rase_unexpected_error()
     cursor.commit()

Что происходит, когда module.raise_unexpected_error() вызывает ошибку? Откат транзакции? Поскольку я понимаю транзакции, мне либо нужно их совершить, либо отбросить назад. Итак, что происходит в этом случае?

В качестве альтернативы я могу написать свой запрос следующим образом:

cursor = None
try:
    cursor = db.cursor()
    cursor.execute("""UPDATE users                    
            SET password = %s, salt = %s
            WHERE user_id = %s""",
            (pw_tuple[0], pw_tuple[1], user_id))
    module.rase_unexpected_error()
    cursor.commit()
except BaseException:
    if cursor is not None:
        cursor.rollback()
finally:
    if cursor is not None:
        cursor.close()

Также я должен упомянуть, что я понятия не имею, может ли метод соединения класса cursor() класса Psycopg2 вызвать ошибку или нет (документация не говорит), так что лучше безопасно, чем извините, нет?

Какой метод выдачи запроса и управления транзакцией следует использовать?

Ответ 1

Ваша ссылка на документы Psycopg2 объясняет сам, нет?

... Обратите внимание, что сначала закрытие соединения без внесения изменений будет вызывают отклонение ожидающих изменений, как если бы ROLLBACK был выполнено (если не выбран другой уровень изоляции: см. set_isolation_level()).

Изменено в версии 2.2: ранее явный ROLLBACK был выпущен Psycopg on close(). Команда могла быть отправлена ​​на бэкэнд в неподходящее время, поэтому Psycopg в настоящее время полагается на неявно отбрасывать незафиксированные изменения. Известно, что некоторые неправильно, если соединение закрыто во время транзакции (когда статус STATUS_IN_TRANSACTION), например. PgBouncer сообщает нечистый сервер и отбрасывает соединение. Чтобы избежать этого проблема, которую вы можете обеспечить для прекращения транзакции с помощью commit()/rollback() перед закрытием.

Итак, если вы не используете другой уровень изоляции или используете PgBouncer, ваш первый пример должен работать нормально. Однако, если вы хотите получить более тонкий контроль над тем, что происходит во время транзакции, тогда метод try/except может быть лучшим, поскольку он параллелен самому состоянию транзакции базы данных.