代码之家  ›  专栏  ›  技术社区  ›  Andrew

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

  •  4
  • Andrew  · 技术社区  · 9 年前

    我尝试阅读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 回复  |  直到 9 年前
        1
  •  6
  •   theB    9 年前

    查看 source ,以及 ODBC Documentation 行为部分取决于 autocommit 启用或禁用。

    • 第一次呼叫 cursor.execute() 如果 自动提交 False 。(见注释1)每次后续调用 execute() 对于此游标使用相同的事务,除非 commit() rollback() 被调用。
    • 当Python离开 with 阻止和调用 __exit__ :
      • 如果 自动提交 错误 ,并且没有错误,游标将自动提交事务。有关此操作的来源,请参见注释2。
      • 如果 自动提交 True ,或者如果出现错误,光标将退出而不结束事务。
    • 当光标关闭时,使用 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以释放具有挂起结果的语句时,挂起结果将被删除。

        2
  •  3
  •   johnDanger Aman    5 年前

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

    假设自动提交为 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')
    
        3
  •  0
  •   Konstantin Svintsov    9 年前

    pyodbc不会自动为您处理事务。

    这意味着select_query_1将看到由query_1插入的记录,即使query_2失败。(我假设在第一个代码块周围尝试/捕获,所以将执行第二个代码块)。 然而,一些RDBMS,即PostgreSQL,不允许在同一事务中的前一个语句失败时执行任何其他语句(回滚除外)。对于PostrgreSQL RDBMS(例如)并且没有自动提交的情况,如果query_2失败,select_query_1将失败。