如果我使用带有pyodbc游标的上下文管理器,它会回滚错误吗?

时间:2015-08-20 21:14:39

标签: python transactions odbc rollback pyodbc

我试过阅读pyodbc源代码,但它是所有C ++代码(我不能胜任C ++)。我需要知道如下语句的行为:

 with connection.cursor() as cursor:
     cursor.execute(query_1) #inserts some stuff into table A
     cursor.execute(query_2) #inserts some stuff into table B, but throws an error

 with connection.cursor() as cursor2:
     cursor.execute(select_query_1) #selects from table A
     cursor.execute(select_query_2) #selects from table B

这是我们尚未提交的相同连接 - 我很好奇是从表A中选择是否会在第一个游标中插入新值 - 或者query_2中的错误是否导致第一​​个游标的工作为表A回滚。

3 个答案:

答案 0 :(得分:3)

查看sourceODBC Documentation行为部分取决于autocommit是启用还是禁用。

  • 如果cursor.execute()autocommit,则对False的第一次调用会隐式开启新的交易。 (参见注释1)除非execute()commit()被调用,否则对此游标的rollback()的每次后续调用都使用相同的事务。
  • 当Python离开with块并调用__exit__时:
    • 如果autocommitFalse且没有错误,则游标会自动提交事务。有关此操作的来源,请参阅注释2。
    • 如果autocommitTrue,或者如果出现错误,则光标退出而不会结束交易。
  • 当光标关闭时,无论是cursor.close()还是显式或隐式调用__del__,当释放光标的内部句柄时,也会自动删除待处理语句结果。 (注3)

如果cursor.execute()失败,则事务仍处于打开状态,但未提交。在这种情况下,您可以提交或回滚。

所有这一切,您仍应测试目标环境中的行为。不同的ODBC数据源具有不同级别的事务支持。

注1:(source

  

如果数据源处于手动提交模式(需要显式事务启动)并且尚未启动事务,则驱动程序在发送SQL语句之前启动事务。

注2:(pyodbc/cursor.cpp @ 2151

// If an error has occurred, `args` will be a tuple of 3 values.  
// Otherwise it will be a tuple of 3 `None`s.
I(PyTuple_Check(args));
if (cursor->cnxn->nAutoCommit == SQL_AUTOCOMMIT_OFF && PyTuple_GetItem(args, 0) == Py_None)
    ...
    ret = SQLEndTran(SQL_HANDLE_DBC, cursor->cnxn->hdbc, SQL_COMMIT);

注3:(source

  

当应用程序调用SQLFreeHandle释放具有挂起结果的语句时,将删除挂起的结果。

答案 1 :(得分:0)

pyodbc不会自动处理您的交易。

这意味着select_query_1将查看query_1插入的记录,即使query_2失败也是如此。 (我假设尝试/捕获第一个代码块,因此第二个代码将被执行)。 但是,如果同一事务中的某个先前语句失败,则某些RDBMS(即PostgreSQL)不允许执行任何其他语句(回滚除外)。如果是PostrgreSQL RDBMS(例如)并且没有自动提交,则select_query_1将在query_2失败时失败。

答案 2 :(得分:0)

根据我使用pyodbc连接(通过Microsoft Access Driver)的经验是:

假定自动提交为False (这似乎是默认设置)

不提交:

with pyodbc.connect(connectionString) as con:

    with con.cursor() as cursor:

        cursor.execute(query)
        raise Exception('failed')

是否提交:

with pyodbc.connect(connectionString) as con:

    with con.cursor() as cursor:

        cursor.execute(query)

    raise Exception('failed')

不提交:

with pyodbc.connect(connectionString) as con:

    cursor = con.cursor()
    cursor.execute(query)
    cursor.close()

    raise Exception('failed')

是否提交:

with pyodbc.connect(connectionString) as con:

    cursor = con.cursor()
    cursor.execute(query)
    cursor.close()

raise Exception('failed')