我试过阅读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回滚。
答案 0 :(得分:3)
查看source和ODBC Documentation行为部分取决于autocommit
是启用还是禁用。
cursor.execute()
为autocommit
,则对False
的第一次调用会隐式开启新的交易。 (参见注释1)除非execute()
或commit()
被调用,否则对此游标的rollback()
的每次后续调用都使用相同的事务。with
块并调用__exit__
时:
autocommit
为False
且没有错误,则游标会自动提交事务。有关此操作的来源,请参阅注释2。autocommit
为True
,或者如果出现错误,则光标退出而不会结束交易。cursor.close()
还是显式或隐式调用__del__
,当释放光标的内部句柄时,也会自动删除待处理语句结果。 (注3)如果cursor.execute()
失败,则事务仍处于打开状态,但未提交。在这种情况下,您可以提交或回滚。
所有这一切,您仍应测试目标环境中的行为。不同的ODBC数据源具有不同级别的事务支持。
注1:(source)
如果数据源处于手动提交模式(需要显式事务启动)并且尚未启动事务,则驱动程序在发送SQL语句之前启动事务。
// 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')