23

我在 Python 中使用 Psycopg2 来访问 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()

另外我应该提一下,我不知道 Psycopg2 的连接类cursor()方法是否会引发错误(文档没有说)所以安全总比抱歉好,不是吗?

我应该使用哪种发出查询和管理事务的方法?

4

1 回答 1

9

您指向 Psycopg2 文档的链接本身就解释了它,不是吗?

...请注意,关闭连接而不首先提交更改将 导致任何挂起的更改被丢弃,就好像执行了 ROLLBACK 一样(除非选择了不同的隔离级别:请参阅 set_isolation_level())。

在 2.2 版更改: 以前 Psycopg 在 close() 上发出显式回滚。该命令可能在不适当的时间发送到后端,因此Psycopg 当前依赖后端隐式丢弃未提交的更改。尽管在事务期间关闭连接(当状态为 STATUS_IN_TRANSACTION 时),一些中间件已知行为不正确,例如 PgBouncer 报告不干净的服务器并丢弃连接。为避免此问题,您可以确保在关闭之前使用 commit()/rollback() 终止事务。

因此,除非您使用不同的隔离级别或使用 PgBouncer,否则您的第一个示例应该可以正常工作。但是,如果您希望对事务期间发生的事情进行更细粒度的控制,那么 try/except 方法可能是最好的,因为它与数据库事务状态本身平行。

于 2012-09-26T19:04:04.230 回答