如果我将上下文管理器与pyodbc游标一起使用,是否会在发生错误时回滚?

And*_*rew 2 python odbc transactions 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
Run Code Online (Sandbox Code Playgroud)

这是我们尚未提交的同一连接-我很好奇,是否从表A中进行选择是否将提供插入到第一个游标中的新值-或query_2中的错误是否导致第一​​个游标的工作被滚动返回表A。

joh*_*ger 6

根据我使用 pyodbc 连接(使用 Microsoft Access 驱动程序)的经验:

假设自动提交False(这似乎是默认的)

不承诺:

with pyodbc.connect(connectionString) as con:

    with con.cursor() as cursor:

        cursor.execute(query)
        raise Exception('failed')
Run Code Online (Sandbox Code Playgroud)

是否承诺:

with pyodbc.connect(connectionString) as con:

    with con.cursor() as cursor:

        cursor.execute(query)

    raise Exception('failed')
Run Code Online (Sandbox Code Playgroud)

不承诺:

with pyodbc.connect(connectionString) as con:

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

    raise Exception('failed')
Run Code Online (Sandbox Code Playgroud)

是否承诺:

with pyodbc.connect(connectionString) as con:

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

raise Exception('failed')
Run Code Online (Sandbox Code Playgroud)


the*_*heB 5

查看源代码ODBC文档,其行为在某种程度上取决于autocommit是启用还是禁用。

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

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

综上所述,您仍然应该在目标环境中测试行为。不同的ODBC数据源具有不同级别的事务支持。


注1 :(来源

如果数据源处于手动提交模式(需要显式的事务启动)并且尚未启动事务,则驱动程序会在发送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);
Run Code Online (Sandbox Code Playgroud)

注3 :(来源

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