Ich habe versucht, die pyodbc-Quelle zu lesen, aber es ist alles C ++ - Code (ich bin nicht kompetent in C ++). Ich muss das Verhalten einer Aussage kennen wie:

 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

Dies ist in demselben Zusammenhang, in dem wir uns noch nicht verpflichtet haben - ich bin gespannt, ob die Auswahl aus Tabelle A die neuen Werte ergibt, die in den ersten Cursor eingefügt wurden - oder ob der Fehler in query_2 dazu führt, dass die Arbeit des ersten Cursors gerollt wird zurück zu Tisch A.

2
Andrew 21 Aug. 2015 im 00:14

3 Antworten

Beste Antwort

Betrachten Sie die Quelle und die ODBC-Dokumentation Das Verhalten hängt teilweise davon ab, ob {{ X0}} ist aktiviert oder deaktiviert.

  • Der erste Aufruf von cursor.execute() öffnet implizit eine neue Transaktion, wenn autocommit False ist. (Siehe Hinweis 1) Jeder nachfolgende Aufruf von execute() für diesen Cursor verwendet dieselbe Transaktion, es sei denn, commit() oder rollback() werden aufgerufen.
  • Wenn Python den Block with verlässt und __exit__ aufruft:
    • Wenn autocommit False ist und keine Fehler aufgetreten sind, schreibt der Cursor die Transaktion automatisch fest. Siehe Anmerkung 2 für die Quelle dieser Operation.
    • Wenn autocommit True ist oder wenn ein Fehler auftritt, wird der Cursor beendet, ohne die Transaktion zu beenden.
  • Wenn der Cursor geschlossen wird, entweder mit cursor.close() oder wenn __del__ explizit oder implizit aufgerufen wird, werden ausstehende Anweisungsergebnisse auch automatisch gelöscht, wenn das interne Handle des Cursors freigegeben wird. (Notiz 3)

Wenn cursor.execute() fehlschlägt, ist die Transaktion noch offen, aber nicht festgeschrieben. Es liegt an Ihnen, in diesem Fall ein Commit oder Rollback durchzuführen.

Trotzdem sollten Sie das Verhalten in Ihrer Zielumgebung testen. Verschiedene ODBC-Datenquellen bieten unterschiedliche Ebenen der Transaktionsunterstützung.


Hinweis 1: ( source )

Wenn sich die Datenquelle im manuellen Festschreibungsmodus befindet (explizite Transaktionsinitiierung erforderlich) und eine Transaktion noch nicht initiiert wurde, initiiert der Treiber eine Transaktion, bevor er die SQL-Anweisung sendet.

Anmerkung 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);

Hinweis 3: ( source

Wenn eine Anwendung SQLFreeHandle aufruft, um eine Anweisung mit ausstehenden Ergebnissen freizugeben, werden die ausstehenden Ergebnisse gelöscht.

5
theB 9 Sept. 2015 im 03:39

Pyodbc wickelt Transaktionen nicht automatisch für Sie ab.

Dies bedeutet, dass select_query_1 Datensätze sieht, die von query_1 eingefügt werden, auch wenn query_2 fehlgeschlagen ist. (Ich gehe davon aus, dass der erste Codeblock versucht / abgefangen wird, damit der zweite ausgeführt wird.) Einige RDBMS, nämlich PostgreSQL, erlauben jedoch nicht, dass eine andere Anweisung (außer Rollback) ausgeführt wird, wenn eine der vorherigen Anweisungen in derselben Transaktion fehlschlägt. Im Fall von PostrgreSQL RDBMS (zum Beispiel) und ohne Autocommit schlägt select_query_1 nur fehl, wenn query_2 fehlschlägt.

0
Konstantin Svintsov 9 Sept. 2015 im 15:56

Nach meiner Erfahrung mit einer Pyodbc-Verbindung (mit einem Microsoft Access-Treiber) ist dies:

Angenommen, Autocommit ist False (dies scheint die Standardeinstellung zu sein).

Verpflichtet nicht:

with pyodbc.connect(connectionString) as con:

    with con.cursor() as cursor:

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

Commit:

with pyodbc.connect(connectionString) as con:

    with con.cursor() as cursor:

        cursor.execute(query)

    raise Exception('failed')

Verpflichtet nicht:

with pyodbc.connect(connectionString) as con:

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

    raise Exception('failed')

Commit:

with pyodbc.connect(connectionString) as con:

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

raise Exception('failed')
0
johnDanger 30 Okt. 2019 im 17:01