Color scheme: Light
You are here: start » de » Dokumentation » Datenintegration und Datenzugriff » WKL OData-ODBC-Treiber » Konzepte des OData-ODBC-Treibers » Wie der Treiber Anweisungen übersetzt

Wie der Treiber Anweisungen übersetzt

graph TD
    A["SQL-Anweisung der Anwendung (Access, ADO.NET, VBA)"] --> B{"Lässt sie sich als OData-Anfrage ausdrücken?"}
    B -->|ja| C["OData-Anfrage an den Dienst: $select, $filter, $orderby, $top, $skip, $count, $apply, POST/PATCH/DELETE"]
    B -->|nein| D{"Nennt die Verbindung oder der Dienst eine Adresse für eigene Anweisungen?"}
    D -->|ja| E["Unverändert an die Datenbank (Durchreiche, mit gebundenen Werten)"]
    D -->|nein| F["Abgewiesen mit SQLSTATE 42000 und einer Meldung, die den Grund nennt"]

Der Treiber rechnet nichts selbst. Jede Anweisung wird entweder in eine OData-Anfrage übersetzt, an die Datenbank durchgereicht oder abgewiesen – nie lädt er eine ganze Tabelle, um lokal zu filtern, zu gruppieren oder zu verbinden. Das erklärt, warum eine Abfrage in Access gegen den einen Dienst läuft und gegen einen anderen mit „ODBC-Aufruf fehlgeschlagen“ endet.

Wichtige Funktionen

  • Lesen, Filtern, Sortieren und Zählen auf dem Dienst. Bedingungen, Sortierung, Spaltenauswahl und COUNT(*) gehen als Abfrageoptionen an den Dienst; nur die gefundenen Zeilen reisen.
  • Bearbeiten wie an einer Tabelle von SQL Server. INSERT, UPDATE und DELETE aus Datenblatt und Formular werden zu POST, PATCH und DELETE, mit Schreibkonflikt-Erkennung über If-Match.
  • Summen und Gruppen ohne Durchreiche, wenn der Dienst Gruppieren ankündigt.
  • Blättern mit OFFSET … FETCH wie in SQL Server.
  • Lange Bedingungen im Rumpf statt in der Adresse.
  • Alles andere unverändert an die Datenbank, wenn die Verbindung das anbietet – sonst eine klare Ablehnung statt eines falschen Ergebnisses.

Grundkonzepte

Übersetzt

Die Anwendung schicktDer Treiber fragt den Dienst
SELECT a, b FROM t WHERE x = ? ORDER BY aGET t?$select=a,b&$filter=x eq …&$orderby=a
… WHERE x LIKE '%Keller%'$filter=contains(x,'Keller') (ebenso x% → startswith, %x → endswith)
… WHERE Nr IN (?, ?, …)Oder-Kette Nr eq … or Nr eq …
SELECT COUNT(*) FROM t WHERE …GET t/$count?$filter=… – ohne Zeilen zu laden
… ORDER BY a OFFSET 500 ROWS FETCH NEXT 200 ROWS ONLY$orderby=a&$skip=500&$top=200
SELECT g, SUM(x) FROM t GROUP BY g HAVING …$apply=groupby((g),aggregate(x with sum as …))&$filter=…, nur wenn der Dienst $apply ankündigt
SELECT … WHERE Schlüssel = ?$filter=Schlüssel eq … – gelesen wird immer über die Menge, nie über die Schlüsseladresse
INSERT INTO t …POST t
UPDATE t SET … WHERE Schlüssel = ? AND Zeilenversion = ?PATCH t(…) mit If-Match
DELETE FROM t WHERE Schlüssel = ? AND Zeilenversion = ?DELETE t(…) mit If-Match
UPDATE/DELETE einer Tabelle ohne Schlüssel über alle alten Wertegebundene Aktionen UpdateByOriginal/DeleteByOriginal, wenn der Dienst sie anbietet

Eine Adresse, die länger als MAXURLLENGTH würde, geht als POST …/$query mit den Optionen im Rumpf. Die vollständigen Regeln stehen unter Unterstütztes SQL: Gruppieren und Zusammenfassen und Unterstütztes SQL: Seitenweise lesen mit OFFSET/FETCH.

Durchgereicht

Was sich nicht übersetzen lässt – ein Verbund zweier Tabellen, eine Unterabfrage, eine Änderung vieler Zeilen, eine Schemaanweisung, Rechnen zwischen Aggregaten –, schickt der Treiber unverändert an die Adresse für eigene Anweisungen. Gebundene Werte bleiben Werte: Aus den Platzhaltern ? werden benannte Parameter. Die Spalten des Ergebnisses beschreibt der Dienst, der Treiber rät keine Typen.

Die Adresse nennt entweder die Verbindung (PASSTHROUGH) oder der Dienst in seinem Schemadokument. Ob die Anweisung ausgeführt werden darf, entscheidet der Dienst, nicht der Treiber; der Treiber prüft sie nicht und stuft sie nicht ein. Warum, steht unter Sicherheitsmodell und Vertrauensgrenzen; die Einrichtung unter Pass-Through-Abfragen einrichten.

Eine Ausnahme: Anweisungen über Tabellen mit dem Präfix MSys – die Systemtabellen der Anwendung, etwa die Abfrage von Access nach MSysConf bei jeder Verbindung – weist der Treiber selbst ab. Sie gibt es im Dienst nicht, und Access erwartet genau diese Antwort.

Abgewiesen

Ohne Adresse für eigene Anweisungen weist der Treiber alles Nicht-Übersetzbare mit SQLSTATE 42000 ab. Access zeigt das als Fehler 3146 „ODBC-Aufruf fehlgeschlagen“ und rechnet die Abfrage dann nicht selbst – auch nicht für Summen und Verbünde, die es über eine lokale Tabelle selbst könnte. Siehe „ODBC-Aufruf fehlgeschlagen“ bei Summen oder Verbünden.

Ebenso abgewiesen statt angenähert – und mit einer Adresse ebenfalls durchgereicht: LIKE mit _ oder einer Zeichenklasse ([a-c]), Funktionen außerhalb von SUM, MIN, MAX, AVG, COUNT in einer Gruppierung, Ändern oder Löschen einer Tabelle ohne Zeilenversion über die alten Werte aller Spalten, wenn der Dienst weder eine Nebenläufigkeitseigenschaft noch die Aktionen für Tabellen ohne Schlüssel für sie deklariert. Der Grund ist immer derselbe: Eine stillschweigend vereinfachte Anweisung lieferte ein anderes Ergebnis, das wie die Antwort aussähe.

Transaktionen

Jede Anweisung ist eine eigene Anfrage an den Dienst. Mehrere davon klammert der Treiber nur, wenn der Dienst Transaktionen anbietet: Er nennt dann in $metadata die Adresse Store.Transactions (der Datendienst Wkl.Data.Gateway tut das).

DienstSQL_TXN_CAPABLEAutocommit aus (BeginTrans, BeginTransaction)
mit Store.TransactionsSQL_TC_ALL, Isolation READ COMMITTEDMit der ersten Anfrage öffnet der Treiber eine Transaktion auf dem Dienst; jede weitere Anfrage der Verbindung – Lesen, Schreiben, eigene Anweisungen – läuft in ihr und sieht ihre eigenen, noch nicht gespeicherten Änderungen. SQLEndTran speichert oder verwirft sie; Trennen verwirft sie
ohneSQL_TC_NONEHYC00, statt einer scheinbar geöffneten Transaktion

Access und DAO entscheiden allein nach SQL_TXN_CAPABLE, ob sie BeginTrans an den Treiber weitergeben. Ohne Transaktionen des Dienstes behalten sie die Transaktion für sich: Jede Änderung ist sofort auf dem Dienst gespeichert, und ein Rollback nimmt dort nichts zurück – ohne Fehler. Mit ihnen verhält sich VBA-Code mit BeginTrans/Rollback wie über den Treiber von SQL Server: Ändern, Anlegen und Aktualisierungsabfragen nimmt Rollback zurück, CommitTrans speichert sie, und der Fehler einer Anweisung lässt die Transaktion bestehen.

Unterschiede zum Treiber von SQL Server: Die Transaktion hält auf dem Dienst eine eigene Verbindung; nutzt die Anwendung sie eine Zeit lang nicht, nimmt der Dienst sie zurück, und CommitTrans meldet dann 40001 – nichts davon ist gespeichert. Wie lange der Dienst wartet, legt er selbst fest; beim Datendienst Wkl.Data.Gateway sind es in der Vorgabe zwei Minuten.

Vor dem Einsatz prüfen, ob Transaktionen wirken

Verlässt sich eine Anwendung auf Rollback – etwa ein Vorgang, der Kopf und Positionen zusammen anlegt –, prüfen Sie mit dem Dienst, an dem sie laufen soll:

  1. Fragen Sie die Fähigkeit ab: in DAO CurrentDb.OpenRecordset("<verknüpfte Tabelle>").Transactions (muss True sein), in ODBC SQLGetInfo(SQL_TXN_CAPABLE) (muss SQL_TC_ALL sein).
  2. Probieren Sie es an einer Testzeile aus: BeginTrans, die Zeile ändern, Rollback, die Zeile neu lesen. Sie muss den alten Wert tragen.

Steht in Schritt 1 False, nimmt Rollback auf dem Dienst nichts zurück, ohne Fehler. Ein Vorgang, der mehrere Sätze nur gemeinsam speichern darf, gehört dann in eine Prozedur der Datenbank, die er über die Durchreiche aufruft, oder er braucht einen Dienst mit Transaktionen.

Was bei Fehlern geschieht

LageErgebnisWas die Anwendung tun muss
Eine Anweisung in der Transaktion scheitert (etwa ein verletzter Schlüssel)Die Anweisung meldet den Fehler; die Transaktion bleibt offen, die übrigen Änderungen darin bleiben bestehen – wie über den Treiber von SQL Serverselbst entscheiden: Rollback oder die übrigen mit CommitTrans speichern
Der Dienst hat die Transaktion zurückgenommen (Leerlauf, unbrauchbar gewordene Transaktion)CommitTrans meldet 40001; nichts ist gespeichertden Vorgang neu beginnen
Die Verbindung bricht während CommitTrans ab08S01. Der Treiber wiederholt einen Abschluss nicht: ob gespeichert wurde, ist unbekanntdie betroffenen Sätze neu lesen; kam der Abschluss beim Dienst nicht an, nimmt der Dienst die Transaktion nach seiner Leerlaufzeit zurück
Die Verbindung bricht während Rollback ab08S01; nichts wird gespeichert, weil der Dienst die offene Transaktion spätestens nach seiner Leerlaufzeit zurücknimmtnichts; die Sätze sind nach Ablauf der Leerlaufzeit wieder frei
Der Dienst bietet keine Transaktionen anADO.NET: HYC00 bei BeginTransaction; DAO/Access: jede Änderung ist sofort gespeichert, Rollback bleibt wirkungslossiehe Prüfung oben

Meldungen der Datenbank

Lehnt die Datenbank eine Anweisung ab und gibt der Dienst ihre Meldungen mit (error.details mit Meldungsnummer als code, so der Datendienst Wkl.Data.Gateway), meldet der Treiber sie wie der Treiber von SQL Server: einen Diagnosesatz je Meldung, die Meldungsnummer als native Fehlernummer und den SQLSTATE, den der Dienst dazu nennt (23000 für einen verletzten Schlüssel, Bezug oder Pflichtwert, 22001 für einen zu langen Wert, 01000 für einen begleitenden Hinweis).

Anwendungsieht
Access, Meldungsfensterden Text der ersten Meldung, dahinter ihre Nummer: … (#547)
VBA/DAODBEngine.Errors(0).Number = Meldungsnummer, .Description = Text; der letzte Eintrag ist wie immer der Fehler von Access (3146)
ADO.NETOdbcException.Errors[i].NativeError und .SQLState

Code, der an einer direkten Verbindung auf die Nummer verzweigt (If DBEngine.Errors(0).Number = 2627 Then), läuft damit unverändert. Den Text gibt der Dienst nur weiter, wenn er nichts nennt, was der Aufrufer nicht sehen darf; sonst steht dort ein Text des Dienstes, Nummer und SQLSTATE stimmen trotzdem. Ohne details meldet der Treiber einen einzigen Satz mit dem HTTP-Status als Nummer.

Warum so: erwogene Alternativen

Die Aufteilung in übersetzen, durchreichen und abweisen ist eine Entwurfsentscheidung. Drei Alternativen hätten Richtigkeit, Kosten oder Sicherheit wesentlich anders verteilt:

AlternativeWas sie gebracht hätteWarum sie verworfen ist
Lokal rechnen: Der Treiber lädt die Zeilen und filtert, gruppiert oder verbindet selbstJede Abfrage liefe, auch gegen einen Dienst ohne Gruppieren und ohne DurchreicheDie Kosten wachsen mit der Tabelle, nicht mit dem Ergebnis, und bleiben unsichtbar, bis die Tabelle groß ist. Außerdem rechnete der Treiber mit seinen eigenen Regeln für Sortierfolge, NULL und Rundung, nicht mit denen der Datenbank – das Ergebnis wiche ohne Hinweis ab
Nur durchreichen: Jede Anweisung geht unverändert an die DatenbankVolle SQL-Sprache der Datenbank, keine ÜbersetzungsregelnDer Treiber wäre an einen Dienst gebunden, der eine solche Adresse anbietet, und an dessen SQL-Dialekt; jeder Aufrufer bräuchte das Recht, eigene Anweisungen auszuführen. Eine durchgereichte Anweisung wird zudem ganz gelesen, ohne Folgeseiten
Nur übersetzen, alles andere abweisenKein Weg an den Regeln des Dienstes vorbeiAccess rechnet nach einer Abweisung nicht selbst, auch wenn SQLGetInfo die Fähigkeit verneint. Verbünde und Summen über verknüpfte Tabellen scheiterten dann immer

Die gewählte Form verbindet die beiden letzten: Was sich als OData-Anfrage ausdrücken lässt, läuft gegen jeden OData-v4-Dienst und unter dessen Regeln je Tabelle; nur der Rest braucht die Adresse für eigene Anweisungen. Ihr Preis: Eine Abfrage, die keiner der beiden Wege trägt, scheitert sichtbar, statt langsam oder ungenau zu laufen. Ebenso verworfen ist das Annähern einzelner Bestandteile, etwa LIKE 'a_c' als contains; der Grund steht unter Abgewiesen.

Verwandte Themen

de/wiki/data-conectivity/odata-odbc/concepts/how-the-driver-translates-statements.txt · Last modified: by Wolfgang van der Stille