Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 249 – Python Database API Spezifikation v2.0

Autor:
Marc-André Lemburg <mal at lemburg.com>
Discussions-To:
Db-SIG Liste
Status:
Final
Typ:
Informational
Erstellt:
12-Apr-1999
Post-History:

Ersetzt:
248

Inhaltsverzeichnis

Einleitung

Diese API wurde definiert, um die Ähnlichkeit zwischen den für den Datenbankzugriff verwendeten Python-Modulen zu fördern. Auf diese Weise hoffen wir, eine Konsistenz zu erreichen, die zu leichter verständlichen Modulen, generell portablerem Code über verschiedene Datenbanken hinweg und einer breiteren Reichweite der Datenbankkonnektivität aus Python heraus führt.

Kommentare und Fragen zu dieser Spezifikation können an die SIG für Datenbankanbindung mit Python gerichtet werden.

Weitere Informationen zur Datenbankanbindung mit Python und verfügbaren Paketen finden Sie im Datenbank-Themenführer.

Dieses Dokument beschreibt die Python Database API Spezifikation 2.0 und eine Reihe gemeinsamer optionaler Erweiterungen. Die vorherige Version 1.0 ist noch als Referenz in PEP 248 verfügbar. Paketautoren werden ermutigt, diese Version der Spezifikation als Grundlage für neue Schnittstellen zu verwenden.

Modulschnittstelle

Konstruktoren

Der Zugriff auf die Datenbank wird über Connection-Objekte ermöglicht. Das Modul muss den folgenden Konstruktor dafür bereitstellen

connect( parameter… )
Konstruktor zum Erstellen einer Verbindung zur Datenbank.

Gibt ein Connection-Objekt zurück. Er nimmt eine Reihe von Parametern entgegen, die datenbankabhängig sind. [1]

Globale Variablen

Diese globalen Modulvariablen müssen definiert sein

apilevel
Zeichenkettenkonstante, die das unterstützte DB API-Level angibt.

Derzeit sind nur die Zeichenketten „1.0“ und „2.0“ zulässig. Wenn nicht angegeben, sollte eine DB-API 1.0-Schnittstelle angenommen werden.

threadsafety
Ganzzahlkonstante, die das Level der Thread-Sicherheit angibt, das die Schnittstelle unterstützt. Mögliche Werte sind
threadsafety Bedeutung
0 Threads dürfen das Modul nicht gemeinsam nutzen.
1 Threads dürfen das Modul, aber keine Verbindungen gemeinsam nutzen.
2 Threads dürfen das Modul und Verbindungen gemeinsam nutzen.
3 Threads dürfen das Modul, Verbindungen und Cursor gemeinsam nutzen.

Gemeinsame Nutzung im obigen Kontext bedeutet, dass zwei Threads eine Ressource nutzen können, ohne sie mit einem Mutex-Semaphore zur Implementierung von Ressourcensperren zu umschließen. Beachten Sie, dass externe Ressourcen nicht immer durch die Verwaltung des Zugriffs mit einem Mutex-Mechanismus threadsicher gemacht werden können: Die Ressource kann von globalen Variablen oder anderen externen Quellen abhängen, die außerhalb Ihrer Kontrolle liegen.

paramstyle
Zeichenkettenkonstante, die die Art der Parameterkennzeichnungsformatierung angibt, die von der Schnittstelle erwartet wird. Mögliche Werte sind [2]
paramstyle Bedeutung
qmark Fragezeichen-Stil, z.B. ...WHERE name=?
numeric Numerischer, positioneller Stil, z.B. ...WHERE name=:1
named Benannter Stil, z.B. ...WHERE name=:name
format ANSI C printf-Formatcodes, z.B. ...WHERE name=%s
pyformat Python-erweiterte Formatcodes, z.B. ...WHERE name=%(name)s

Ausnahmen

Das Modul sollte alle Fehlerinformationen über diese Ausnahmen oder davon abgeleitete Klassen zugänglich machen

Warnung
Ausnahme, die für wichtige Warnungen ausgelöst wird, wie z.B. Datenabschneidungen beim Einfügen usw. Sie muss eine Unterklasse der Python Exception-Klasse sein [10] [11].
Error
Ausnahme, die die Basisklasse aller anderen Fehlerausnahmen ist. Sie können diese verwenden, um alle Fehler mit einer einzigen except-Anweisung abzufangen. Warnungen gelten nicht als Fehler und sollten daher diese Klasse nicht als Basis verwenden. Sie muss eine Unterklasse der Python Exception-Klasse sein [10].
InterfaceError
Ausnahme, die für Fehler ausgelöst wird, die sich auf die Datenbank-Schnittstelle und nicht auf die Datenbank selbst beziehen. Sie muss eine Unterklasse von Error sein.
DatabaseError
Ausnahme, die für Fehler ausgelöst wird, die sich auf die Datenbank beziehen. Sie muss eine Unterklasse von Error sein.
DataError
Ausnahme, die für Fehler ausgelöst wird, die auf Probleme mit den verarbeiteten Daten zurückzuführen sind, wie z.B. Division durch Null, numerischer Wert außerhalb des Bereichs usw. Sie muss eine Unterklasse von DatabaseError sein.
OperationalError
Ausnahme, die für Fehler ausgelöst wird, die sich auf den Betrieb der Datenbank beziehen und nicht unbedingt unter der Kontrolle des Programmierers stehen, z.B. eine unerwartete Trennung, die Datenquellen-Name nicht gefunden, eine Transaktion konnte nicht verarbeitet werden, ein Speicherzuordnungsfehler trat während der Verarbeitung auf usw. Sie muss eine Unterklasse von DatabaseError sein.
IntegrityError
Ausnahme, die ausgelöst wird, wenn die relationale Integrität der Datenbank beeinträchtigt wird, z.B. ein Fremdschlüssel-Check schlägt fehl. Sie muss eine Unterklasse von DatabaseError sein.
InternalError
Ausnahme, die ausgelöst wird, wenn die Datenbank einen internen Fehler feststellt, z.B. der Cursor ist nicht mehr gültig, die Transaktion ist außer sync usw. Sie muss eine Unterklasse von DatabaseError sein.
ProgrammingError
Ausnahme, die für Programmierfehler ausgelöst wird, z.B. Tabelle nicht gefunden oder bereits vorhanden, Syntaxfehler in der SQL-Anweisung, falsche Anzahl von Parametern angegeben usw. Sie muss eine Unterklasse von DatabaseError sein.
NotSupportedError
Ausnahme, die ausgelöst wird, wenn eine Methode oder DB API verwendet wurde, die von der Datenbank nicht unterstützt wird, z.B. Anforderung eines .rollback() auf einer Verbindung, die keine Transaktionen unterstützt oder Transaktionen ausgeschaltet hat. Sie muss eine Unterklasse von DatabaseError sein.

Dies ist das Vererbungsschema der Ausnahmen [10] [11]

Exception
|__Warning
|__Error
   |__InterfaceError
   |__DatabaseError
      |__DataError
      |__OperationalError
      |__IntegrityError
      |__InternalError
      |__ProgrammingError
      |__NotSupportedError

Hinweis

Die Werte dieser Ausnahmen sind nicht definiert. Sie sollten dem Benutzer jedoch eine ziemlich gute Vorstellung davon geben, was schief gelaufen ist.

Connection-Objekte

Connection-Objekte sollten auf die folgenden Methoden reagieren.

Connection-Methoden

.close()
Schließt die Verbindung jetzt (anstatt wann immer .__del__() aufgerufen wird).

Die Verbindung ist ab diesem Zeitpunkt unbrauchbar; eine Error (oder Unterklasse)-Ausnahme wird ausgelöst, wenn eine Operation mit der Verbindung versucht wird. Dasselbe gilt für alle Cursor-Objekte, die versuchen, die Verbindung zu nutzen. Beachten Sie, dass das Schließen einer Verbindung, ohne die Änderungen vorher zu committen, einen impliziten Rollback zur Folge hat.

.commit()
Committet alle ausstehenden Transaktionen zur Datenbank.

Beachten Sie, dass, wenn die Datenbank eine Auto-Commit-Funktion unterstützt, diese zunächst ausgeschaltet sein muss. Eine Schnittstellenmethode kann bereitgestellt werden, um sie wieder einzuschalten.

Datenbankmodule, die keine Transaktionen unterstützen, sollten diese Methode mit leerer Funktionalität implementieren.

.rollback()
Diese Methode ist optional, da nicht alle Datenbanken Transaktionsunterstützung bieten. [3]

Falls eine Datenbank Transaktionen anbietet, bewirkt diese Methode, dass die Datenbank zu Beginn jeder ausstehenden Transaktion zurückgerollt wird. Das Schließen einer Verbindung, ohne die Änderungen vorher zu committen, führt zu einem impliziten Rollback.

.cursor()
Gibt ein neues Cursor-Objekt zurück, das die Verbindung verwendet.

Wenn die Datenbank kein direktes Cursor-Konzept anbietet, muss das Modul Cursor auf andere Weise emulieren, soweit dies für diese Spezifikation erforderlich ist. [4]

Cursor-Objekte

Diese Objekte stellen einen Datenbank-Cursor dar, der zur Verwaltung des Kontexts einer Abfrageoperation verwendet wird. Cursor, die von derselben Verbindung erstellt wurden, sind nicht isoliert, d.h. alle Änderungen, die von einem Cursor an der Datenbank vorgenommen werden, sind für andere Cursor sofort sichtbar. Cursor, die von verschiedenen Verbindungen erstellt wurden, können isoliert sein oder auch nicht, je nachdem, wie die Transaktionsunterstützung implementiert ist (siehe auch die Methoden .rollback() und .commit() der Verbindung).

Cursor-Objekte sollten auf die folgenden Methoden und Attribute reagieren.

Cursor-Attribute

.description
Dieses schreibgeschützte Attribut ist eine Sequenz von 7-elementigen Sequenzen.

Jede dieser Sequenzen enthält Informationen, die eine Ergebnissspalte beschreiben

  • name
  • type_code
  • display_size
  • internal_size
  • precision
  • scale
  • null_ok

Die ersten beiden Elemente (name und type_code) sind obligatorisch, die anderen fünf sind optional und werden auf None gesetzt, wenn keine aussagekräftigen Werte bereitgestellt werden können.

Dieses Attribut ist None für Operationen, die keine Zeilen zurückgeben, oder wenn für den Cursor noch keine Operation über die Methode .execute*() aufgerufen wurde.

Der type_code kann durch Vergleich mit den Typ-Objekten interpretiert werden, die im folgenden Abschnitt angegeben sind.

.rowcount
Dieses schreibgeschützte Attribut gibt die Anzahl der Zeilen an, die die letzte .execute*()-Operation erzeugt hat (für DQL-Anweisungen wie SELECT) oder beeinflusst hat (für DML-Anweisungen wie UPDATE oder INSERT). [9]

Das Attribut ist -1, wenn noch keine .execute*()-Operation auf dem Cursor ausgeführt wurde oder wenn die Zeilenanzahl der letzten Operation von der Schnittstelle nicht ermittelt werden kann. [7]

Hinweis

Zukünftige Versionen der DB API Spezifikation könnten den letzteren Fall so umdefinieren, dass das Objekt stattdessen None zurückgibt.

Cursor-Methoden

.callproc( procname [, parameter ] )
(Diese Methode ist optional, da nicht alle Datenbanken gespeicherte Prozeduren anbieten. [3])

Ruft eine gespeicherte Datenbankprozedur mit dem angegebenen Namen auf. Die Parametersequenz muss einen Eintrag für jedes Argument enthalten, das die Prozedur erwartet. Das Ergebnis des Aufrufs wird als modifizierte Kopie der Eingabesequenz zurückgegeben. Eingabeparameter bleiben unverändert, Ausgabe- und Ein-/Ausgabeparameter werden durch möglicherweise neue Werte ersetzt.

Die Prozedur kann auch einen Ergebnissatz als Ausgabe liefern. Dieser muss dann über die Standardmethoden .fetch*() zugänglich gemacht werden.

.close()
Schließt den Cursor jetzt (anstatt wann immer __del__ aufgerufen wird).

Der Cursor ist ab diesem Zeitpunkt unbrauchbar; eine Error (oder Unterklasse)-Ausnahme wird ausgelöst, wenn eine Operation mit dem Cursor versucht wird.

.execute(operation [, parameter])
Bereitet eine Datenbankoperation (Abfrage oder Befehl) vor und führt sie aus.

Parameter können als Sequenz oder Mapping bereitgestellt werden und werden an Variablen in der Operation gebunden. Variablen werden in einer datenbankspezifischen Notation angegeben (siehe das Attribut paramstyle des Moduls für Details). [5]

Eine Referenz auf die Operation wird vom Cursor beibehalten. Wenn dasselbe Operations-Objekt erneut übergeben wird, kann der Cursor sein Verhalten optimieren. Dies ist am effektivsten für Algorithmen, bei denen dieselbe Operation verwendet wird, aber unterschiedliche Parameter an sie gebunden werden (viele Male).

Für maximale Effizienz bei der Wiederverwendung einer Operation ist es am besten, die Methode .setinputsizes() zu verwenden, um die Parametertypen und -größen im Voraus festzulegen. Es ist zulässig, dass ein Parameter nicht mit den vordefinierten Informationen übereinstimmt; die Implementierung sollte dies kompensieren, möglicherweise mit einem Effizienzverlust.

Die Parameter können auch als Liste von Tupeln angegeben werden, um z.B. mehrere Zeilen in einer einzigen Operation einzufügen, aber diese Art der Verwendung ist veraltet: stattdessen sollte .executemany() verwendet werden.

Rückgabewerte sind nicht definiert.

.executemany( operation, seq_of_parameters )
Bereitet eine Datenbankoperation (Abfrage oder Befehl) vor und führt sie dann für alle Parametersequenzen oder Mappings aus, die in der Sequenz seq_of_parameters gefunden werden.

Module können diese Methode mit mehreren Aufrufen von .execute() oder durch die Verwendung von Array-Operationen implementieren, um die Datenbank die Sequenz als Ganzes in einem Aufruf verarbeiten zu lassen.

Die Verwendung dieser Methode für eine Operation, die einen oder mehrere Ergebnissätze erzeugt, führt zu undefiniertem Verhalten, und die Implementierung darf (muss aber nicht) eine Ausnahme auslösen, wenn sie feststellt, dass durch einen Aufruf der Operation ein Ergebnissatz erzeugt wurde.

Die gleichen Kommentare wie für .execute() gelten entsprechend für diese Methode.

Rückgabewerte sind nicht definiert.

.fetchone()
Holt die nächste Zeile eines Abfrageergebnisses und gibt eine einzelne Sequenz zurück, oder None, wenn keine weiteren Daten verfügbar sind. [6]

Eine Error (oder Unterklasse)-Ausnahme wird ausgelöst, wenn der vorherige Aufruf von .execute*() kein Ergebnis erzeugt hat oder noch kein Aufruf erfolgt ist.

.fetchmany([size=cursor.arraysize])
Holt den nächsten Satz von Zeilen eines Abfrageergebnisses und gibt eine Sequenz von Sequenzen zurück (z.B. eine Liste von Tupeln). Eine leere Sequenz wird zurückgegeben, wenn keine weiteren Zeilen verfügbar sind.

Die Anzahl der Zeilen, die pro Aufruf abgerufen werden sollen, wird durch den Parameter angegeben. Wenn er nicht angegeben ist, bestimmt die arraysize des Cursors die Anzahl der abzurufenden Zeilen. Die Methode sollte versuchen, so viele Zeilen abzurufen, wie der Parameter angibt. Wenn dies aufgrund der nicht verfügbaren Anzahl von Zeilen nicht möglich ist, können weniger Zeilen zurückgegeben werden.

Eine Error (oder Unterklasse)-Ausnahme wird ausgelöst, wenn der vorherige Aufruf von .execute*() kein Ergebnis erzeugt hat oder noch kein Aufruf erfolgt ist.

Beachten Sie, dass es Performance-Überlegungen gibt, die mit dem size-Parameter verbunden sind. Für optimale Leistung ist es normalerweise am besten, das Attribut .arraysize zu verwenden. Wenn der size-Parameter verwendet wird, ist es am besten, wenn er von einem Aufruf von .fetchmany() zum nächsten denselben Wert beibehält.

.fetchall()
Holt alle (verbleibenden) Zeilen eines Abfrageergebnisses und gibt sie als Sequenz von Sequenzen zurück (z.B. eine Liste von Tupeln). Beachten Sie, dass das arraysize-Attribut des Cursors die Leistung dieser Operation beeinflussen kann.

Eine Error (oder Unterklasse)-Ausnahme wird ausgelöst, wenn der vorherige Aufruf von .execute*() kein Ergebnis erzeugt hat oder noch kein Aufruf erfolgt ist.

.nextset()
(Diese Methode ist optional, da nicht alle Datenbanken mehrere Ergebnissätze unterstützen. [3])

Diese Methode bewirkt, dass der Cursor zum nächsten verfügbaren Satz springt und alle verbleibenden Zeilen aus dem aktuellen Satz verwirft.

Wenn keine weiteren Sätze vorhanden sind, gibt die Methode None zurück. Andernfalls gibt sie einen wahren Wert zurück und nachfolgende Aufrufe der Methoden .fetch*() geben Zeilen aus dem nächsten Ergebnissatz zurück.

Eine Error (oder Unterklasse)-Ausnahme wird ausgelöst, wenn der vorherige Aufruf von .execute*() kein Ergebnis erzeugt hat oder noch kein Aufruf erfolgt ist.

.arraysize
Dieses Lese-/Schreibattribut gibt die Anzahl der Zeilen an, die auf einmal mit .fetchmany() abgerufen werden. Es ist standardmäßig 1, was bedeutet, dass jeweils eine einzelne Zeile abgerufen wird.

Implementierungen müssen diesen Wert in Bezug auf die Methode .fetchmany() beachten, sind aber frei, zeilenweise mit der Datenbank zu interagieren. Er kann auch bei der Implementierung von .executemany() verwendet werden.

.setinputsizes(größen)
Dies kann vor einem Aufruf von .execute*() verwendet werden, um Speicherbereiche für die Parameter der Operation vorzudefinieren.

Größen werden als Sequenz angegeben – ein Element für jeden Eingabeparameter. Das Element sollte ein Typ-Objekt sein, das der zu verwendenden Eingabe entspricht, oder es sollte eine Ganzzahl sein, die die maximale Länge eines Zeichenkettenparameters angibt. Wenn das Element None ist, wird für diese Spalte kein vordefinierter Speicherbereich reserviert (dies ist nützlich, um vordefinierte Bereiche für große Eingaben zu vermeiden).

Diese Methode würde vor dem Aufruf der Methode .execute*() verwendet werden.

Implementierungen können diese Methode frei haben, nichts zu tun, und Benutzer können sie frei nicht verwenden.

.setoutputsize(größe [, spalte])
Legt eine Puffergröße für das Abrufen großer Spalten (z.B. LONGs, BLOBs usw.) fest. Die Spalte wird als Index in der Ergebnissequenz angegeben. Wenn die Spalte nicht angegeben wird, wird die Standardgröße für alle großen Spalten im Cursor festgelegt.

Diese Methode würde vor dem Aufruf der Methode .execute*() verwendet werden.

Implementierungen können diese Methode frei haben, nichts zu tun, und Benutzer können sie frei nicht verwenden.

Typ-Objekte und Konstruktoren

Viele Datenbanken benötigen die Eingabe in einem bestimmten Format für die Bindung an die Eingabeparameter einer Operation. Wenn beispielsweise eine Eingabe für eine DATE-Spalte bestimmt ist, muss sie in einem bestimmten Zeichenkettenformat an die Datenbank gebunden werden. Ähnliche Probleme gibt es für „Row ID“-Spalten oder große binäre Elemente (z.B. Blobs oder RAW-Spalten). Dies stellt Probleme für Python dar, da die Parameter für die Methode .execute*() untyped sind. Wenn das Datenbankmodul ein Python-Zeichenkettenobjekt sieht, weiß es nicht, ob es als einfache CHAR-Spalte, als rohes BINARY-Element oder als DATE gebunden werden soll.

Um dieses Problem zu lösen, muss ein Modul die unten definierten Konstruktoren bereitstellen, um Objekte zu erstellen, die spezielle Werte enthalten können. Wenn diese an die Cursor-Methoden übergeben werden, kann das Modul den richtigen Typ des Eingabeparameters erkennen und ihn entsprechend binden.

Das Attribut description eines Cursor-Objekts gibt Informationen über jede der Ergebnissspalten einer Abfrage zurück. Der type_code muss mit einem der unten definierten Typ-Objekte übereinstimmen. Typ-Objekte können mit mehr als einem Typ-Code gleich sein (z.B. könnte DATETIME gleich den Typ-Codes für Datums-, Zeit- und Zeitstempelspalten sein; siehe die Implementierungshinweise unten für Details).

Das Modul exportiert die folgenden Konstruktoren und Singletons

Date(jahr, monat, tag)
Diese Funktion konstruiert ein Objekt, das einen Datumswert enthält.
Time(stunde, minute, sekunde)
Diese Funktion konstruiert ein Objekt, das einen Zeitwert enthält.
Timestamp(jahr, monat, tag, stunde, minute, sekunde)
Diese Funktion konstruiert ein Objekt, das einen Zeitstempelwert enthält.
DateFromTicks(ticks)
Diese Funktion konstruiert ein Objekt, das einen Datumswert aus dem angegebenen Ticks-Wert enthält (Anzahl der Sekunden seit der Epoche; siehe Dokumentation des standardmäßigen Python-Zeitmoduls für Details).
TimeFromTicks(ticks)
Diese Funktion konstruiert ein Objekt, das einen Zeitwert aus dem angegebenen Ticks-Wert enthält (Anzahl der Sekunden seit der Epoche; siehe Dokumentation des Standard-Python-Zeitmoduls für Details).
TimestampFromTicks(ticks)
Diese Funktion konstruiert ein Objekt, das einen Zeitstempelwert aus dem angegebenen Ticks-Wert enthält (Anzahl der Sekunden seit der Epoche; siehe Dokumentation des Standard-Python-Zeitmoduls für Details).
Binary(zeichenkette)
Diese Funktion konstruiert ein Objekt, das einen binären (langen) Zeichenkettenwert aufnehmen kann.
STRING-Typ
Dieses Typ-Objekt wird verwendet, um Spalten in einer Datenbank zu beschreiben, die zeichenkettenbasiert sind (z.B. CHAR).
BINARY-Typ
Dieses Typ-Objekt wird verwendet, um (lange) binäre Spalten in einer Datenbank zu beschreiben (z.B. LONG, RAW, BLOBs).
NUMBER-Typ
Dieses Typ-Objekt wird verwendet, um numerische Spalten in einer Datenbank zu beschreiben.
DATETIME-Typ
Dieses Typ-Objekt wird verwendet, um Datums-/Zeitspalten in einer Datenbank zu beschreiben.
ROWID-Typ
Dieses Typ-Objekt wird verwendet, um die „Row ID“-Spalte in einer Datenbank zu beschreiben.

SQL NULL-Werte werden bei der Eingabe und Ausgabe durch das Python-Singleton None repräsentiert.

Hinweis

Die Verwendung von Unix-Ticks für die Datenbankanbindung kann Probleme verursachen, da sie einen begrenzten Datumsbereich abdecken.

Implementierungshinweise für Modulautoren

  • Datums-/Zeitobjekte können als Python datetime-Modul-Objekte (verfügbar seit Python 2.3, mit einer C-API seit 2.4) oder unter Verwendung des mxDateTime-Pakets (verfügbar für alle Python-Versionen seit 1.5.2) implementiert werden. Beide bieten alle notwendigen Konstruktoren und Methoden auf Python- und C-Ebene.
  • Hier ist eine Beispielimplementierung der Unix-Ticks-basierten Konstruktoren für Datum/Zeit, die die Arbeit an generische Konstruktoren delegiert
    import time
    
    def DateFromTicks(ticks):
        return Date(*time.localtime(ticks)[:3])
    
    def TimeFromTicks(ticks):
        return Time(*time.localtime(ticks)[3:6])
    
    def TimestampFromTicks(ticks):
        return Timestamp(*time.localtime(ticks)[:6])
    
  • Der bevorzugte Objekttyp für Binär-Objekte sind die Puffer-Typen, die im Standard-Python ab Version 1.5.2 verfügbar sind. Einzelheiten finden Sie in der Python-Dokumentation. Informationen zur C-Schnittstelle finden Sie in Include/bufferobject.h und Objects/bufferobject.c in der Python-Quellcodeverteilung.
  • Diese Python-Klasse ermöglicht die Implementierung der oben genannten Typ-Objekte, auch wenn das Beschreibungsfeld type_code mehrere Werte für ein Typ-Objekt liefert
    class DBAPITypeObject:
        def __init__(self,*values):
            self.values = values
        def __cmp__(self,other):
            if other in self.values:
                return 0
            if other < self.values:
                return 1
            else:
                return -1
    

    Das resultierende Typ-Objekt ist gleich allen an den Konstruktor übergebenen Werten.

  • Hier ist ein Codeausschnitt in Python, der die oben definierte Ausnahmehierarchie implementiert [10]
    class Error(Exception):
        pass
    
    class Warning(Exception):
        pass
    
    class InterfaceError(Error):
        pass
    
    class DatabaseError(Error):
        pass
    
    class InternalError(DatabaseError):
        pass
    
    class OperationalError(DatabaseError):
        pass
    
    class ProgrammingError(DatabaseError):
        pass
    
    class IntegrityError(DatabaseError):
        pass
    
    class DataError(DatabaseError):
        pass
    
    class NotSupportedError(DatabaseError):
        pass
    

    In C können Sie die API PyErr_NewException(fullname, base, NULL) verwenden, um die Ausnahmeobjekte zu erstellen.

Optionale DB API Erweiterungen

Während der Lebensdauer von DB API 2.0 haben Modulautoren ihre Implementierungen oft über das hinaus erweitert, was diese DB API-Spezifikation verlangt. Um die Kompatibilität zu verbessern und einen sauberen Upgrade-Pfad zu zukünftigen Versionen der Spezifikation zu bieten, definiert dieser Abschnitt eine Reihe gemeinsamer Erweiterungen der Kern-DB API 2.0-Spezifikation.

Wie bei allen optionalen DB API-Funktionen steht es den Autoren von Datenbankmodulen frei, diese zusätzlichen Attribute und Methoden nicht zu implementieren (deren Verwendung führt dann zu einem AttributeError) oder eine NotSupportedError auszulösen, falls die Verfügbarkeit erst zur Laufzeit geprüft werden kann.

Es wurde vorgeschlagen, die Nutzung dieser Erweiterungen dem Programmierer optional sichtbar zu machen, indem Python-Warnungen über das Python-Warnungs-Framework ausgegeben werden. Um diese Funktion nutzbar zu machen, müssen die Warnmeldungen standardisiert sein, um sie maskieren zu können. Diese Standardmeldungen werden im Folgenden als Warnmeldung bezeichnet.

Cursor.rownumber
Dieses schreibgeschützte Attribut sollte den aktuellen 0-basierten Index des Cursors im Ergebnissatz liefern oder None, wenn der Index nicht ermittelt werden kann.

Der Index kann als Index des Cursors in einer Sequenz (dem Ergebnissatz) betrachtet werden. Der nächste Abrufoperation ruft die Zeile ab, die in dieser Sequenz durch .rownumber indiziert ist.

Warnmeldung: „DB-API-Erweiterung cursor.rownumber verwendet“

Connection.Error, Connection.ProgrammingError, etc.
Alle von der DB API-Norm definierten Ausnahmeklassen sollten auf den Connection-Objekten als Attribute (zusätzlich zur Verfügbarkeit auf Modul-Ebene) zugänglich gemacht werden.

Diese Attribute vereinfachen die Fehlerbehandlung in Umgebungen mit mehreren Verbindungen.

Warnmeldung: „DB-API-Erweiterung connection.<exception> verwendet“

Cursor.connection
Dieses schreibgeschützte Attribut gibt eine Referenz auf das Connection-Objekt zurück, auf dem der Cursor erstellt wurde.

Das Attribut vereinfacht das Schreiben von polymorphem Code in Umgebungen mit mehreren Verbindungen.

Warnmeldung: „DB-API-Erweiterung cursor.connection verwendet“

Cursor.scroll(wert [, modus=’relative’ ])
Scrollt den Cursor im Ergebnissatz an eine neue Position gemäß modus.

Wenn modus relative (Standard) ist, wird wert als Offset zur aktuellen Position im Ergebnissatz genommen, wenn auf absolute gesetzt, gibt wert eine absolute Zielposition an.

Eine IndexError sollte ausgelöst werden, wenn ein Scroll-Vorgang den Ergebnissatz verlässt. In diesem Fall bleibt die Cursorposition undefiniert (ideal wäre es, den Cursor gar nicht zu bewegen).

Hinweis

Diese Methode sollte native scrollbare Cursor verwenden, falls verfügbar, oder auf eine Emulation für vorwärts-only scrollbare Cursor zurückgreifen. Die Methode kann NotSupportedError auslösen, um anzuzeigen, dass eine bestimmte Operation von der Datenbank nicht unterstützt wird (z.B. Rückwärts-Scrolling).

Warnmeldung: „DB-API-Erweiterung cursor.scroll() verwendet“

Cursor.messages
Dies ist ein Python-Listenobjekt, dem die Schnittstelle Tupel (Ausnahmeklasse, Ausnahme-Wert) für alle Nachrichten anhängt, die die Schnittstelle von der zugrunde liegenden Datenbank für diesen Cursor empfängt.

Die Liste wird durch alle Standardaufrufe von Cursor-Methoden gelöscht (vor der Ausführung des Aufrufs) mit Ausnahme der .fetch*()-Aufrufe, um übermäßigen Speicherverbrauch zu vermeiden, und kann auch durch Ausführen von del cursor.messages[:] gelöscht werden.

Alle von der Datenbank generierten Fehler- und Warnmeldungen werden in diese Liste eingetragen, sodass die Überprüfung der Liste es dem Benutzer ermöglicht, den korrekten Betrieb der Methodenaufrufe zu verifizieren.

Ziel dieses Attributs ist es, die Notwendigkeit einer Warnungs-Ausnahme zu eliminieren, die oft Probleme verursacht (einige Warnungen haben nur informativen Charakter).

Warnmeldung: „DB-API-Erweiterung cursor.messages verwendet“

Connection.messages
Ähnlich wie bei Cursor.messages, nur dass die Nachrichten in der Liste verbindungsorientiert sind.

Die Liste wird automatisch durch alle Standardaufrufe von Verbindungs-Methoden gelöscht (vor der Ausführung des Aufrufs), um übermäßigen Speicherverbrauch zu vermeiden, und kann auch durch Ausführen von del connection.messages[:] gelöscht werden.

Warnmeldung: „DB-API-Erweiterung connection.messages verwendet“

Cursor.next()
Gibt die nächste Zeile aus der aktuell ausgeführten SQL-Anweisung zurück, mit denselben Semantik wie .fetchone(). Eine StopIteration-Ausnahme wird ausgelöst, wenn das Ergebnissatz für Python-Versionen 2.2 und neuer erschöpft ist. Frühere Versionen haben keine StopIteration-Ausnahme und die Methode sollte stattdessen eine IndexError auslösen.

Warnmeldung: „DB-API-Erweiterung cursor.next() verwendet“

Cursor.__iter__()
Gibt self zurück, um Cursor mit dem Iterationsprotokoll kompatibel zu machen [8].

Warnmeldung: „DB-API-Erweiterung cursor.__iter__() verwendet“

Cursor.lastrowid
Dieses schreibgeschützte Attribut liefert die RowID der zuletzt geänderten Zeile (die meisten Datenbanken geben eine RowID nur bei einer einzelnen INSERT-Operation zurück). Wenn die Operation keine RowID setzt oder die Datenbank keine RowIDs unterstützt, sollte dieses Attribut auf None gesetzt werden.

Die Semantik von .lastrowid ist undefiniert, wenn die zuletzt ausgeführte Anweisung mehr als eine Zeile geändert hat, z.B. bei Verwendung von INSERT mit .executemany().

Warnmeldung: „DB-API-Erweiterung cursor.lastrowid verwendet“

Connection.autocommit
Attribut zum Abfragen und Setzen des Autocommit-Modus der Verbindung.

Gibt True zurück, wenn die Verbindung im Autocommit-Modus (nicht transaktional) arbeitet. Gibt False zurück, wenn die Verbindung im manuellen Commit-Modus (transaktional) arbeitet.

Das Setzen des Attributs auf True oder False passt den Modus der Verbindung entsprechend an.

Das Ändern der Einstellung von True auf False (Deaktivieren von Autocommit) bewirkt, dass die Datenbank den Autocommit-Modus verlässt und eine neue Transaktion startet. Das Ändern von False auf True (Aktivieren von Autocommit) hat datenbankabhängige Semantik in Bezug darauf, wie ausstehende Transaktionen behandelt werden. [12]

Hinweis zur Veralterung: Obwohl mehrere Datenbankmodule sowohl die Lese- als auch die Schreibfunktionalität dieses Attributs implementieren, ist das Setzen des Autocommit-Modus durch Schreiben auf das Attribut veraltet, da dies zu E/A- und verwandten Ausnahmen führen kann, was die Implementierung in einem asynchronen Kontext erschwert. [13]

Warnmeldung: „DB-API-Erweiterung connection.autocommit verwendet“

Optionale Fehlerbehandlungs-Erweiterungen

Die Kern-DB API-Spezifikation führt nur eine Reihe von Ausnahmen ein, die ausgelöst werden können, um dem Benutzer Fehler zu melden. In einigen Fällen können Ausnahmen den Programmfluss zu stark stören oder die Ausführung sogar unmöglich machen.

Für diese Fälle und um die Fehlerbehandlung bei der Arbeit mit Datenbanken zu vereinfachen, können Modulautoren von Datenbanken optionale Fehlerbehandlungsmechanismen implementieren. Dieser Abschnitt beschreibt eine standardisierte Methode zur Definition dieser Fehlerbehandler.

Connection.errorhandler, Cursor.errorhandler
Lese-/Schreibattribut, das auf einen Fehlerbehandler verweist, der im Falle eines Fehlerzustands aufgerufen werden soll.

Der Handler muss ein Python-aufrufbares Objekt sein, das die folgenden Argumente entgegennimmt

errorhandler(connection, cursor, errorclass, errorvalue)

wobei connection eine Referenz auf die Verbindung ist, auf der der Cursor operiert, cursor eine Referenz auf den Cursor (oder None im Falle, dass der Fehler nicht auf einen Cursor zutrifft), errorclass eine Fehlerklasse ist, die mit errorvalue als Konstruktorargument instanziiert werden soll.

Der Standard-Fehlerbehandler sollte die Fehlerinformationen zum entsprechenden .messages-Attribut (Connection.messages oder Cursor.messages) hinzufügen und die durch die gegebenen Parameter errorclass und errorvalue definierte Ausnahme auslösen.

Wenn kein .errorhandler gesetzt ist (das Attribut ist None), sollte das oben beschriebene Standard-Fehlerbehandlungsverfahren angewendet werden.

Warnmeldung: „DB-API-Erweiterung .errorhandler verwendet“

Cursor sollten die Einstellung .errorhandler von ihren Verbindungsobjekten zum Zeitpunkt der Cursor-Erstellung erben.

Optionale Two-Phase Commit Erweiterungen

Viele Datenbanken unterstützen Two-Phase Commit (TPC), was die Verwaltung von Transaktionen über mehrere Datenbankverbindungen und andere Ressourcen hinweg ermöglicht.

Wenn ein Datenbank-Backend TPC unterstützt und der Autor des Datenbankmoduls diese Unterstützung offenlegen möchte, sollte die folgende API implementiert werden. NotSupportedError sollte ausgelöst werden, wenn die Unterstützung des Datenbank-Backends für TPC erst zur Laufzeit geprüft werden kann.

TPC Transaktions-IDs

Da viele Datenbanken der XA-Spezifikation folgen, bestehen Transaktions-IDs aus drei Komponenten

  • eine Format-ID
  • eine globale Transaktions-ID
  • ein Branch-Qualifier

Für eine bestimmte globale Transaktion sollten die ersten beiden Komponenten für alle Ressourcen gleich sein. Jede Ressource in der globalen Transaktion sollte einen anderen Branch-Qualifier erhalten.

Die verschiedenen Komponenten müssen folgende Kriterien erfüllen

  • Format-ID: eine nicht-negative 32-Bit-Ganzzahl.
  • Globale Transaktions-ID und Branch-Qualifier: Byte-Strings nicht länger als 64 Zeichen.

Transaktions-IDs werden mit der Connection-Methode .xid() erstellt.

.xid(format_id, global_transaction_id, branch_qualifier)
Gibt ein Transaktions-ID-Objekt zurück, das für die Übergabe an die .tpc_*()-Methoden dieser Verbindung geeignet ist.

Wenn die Datenbankverbindung TPC nicht unterstützt, wird eine NotSupportedError ausgelöst.

Der Typ des von .xid() zurückgegebenen Objekts ist nicht definiert, aber es muss Sequenzverhalten aufweisen und den Zugriff auf die drei Komponenten ermöglichen. Ein konformes Datenbankmodul könnte Transaktions-IDs anstelle eines benutzerdefinierten Objekts mit Tupeln darstellen.

TPC Connection-Methoden

.tpc_begin(xid)
Beginnt eine TPC-Transaktion mit der gegebenen Transaktions-ID xid.

Diese Methode sollte außerhalb einer Transaktion aufgerufen werden (d.h. seit dem letzten .commit() oder .rollback() darf nichts ausgeführt worden sein).

Darüber hinaus ist es ein Fehler, .commit() oder .rollback() innerhalb der TPC-Transaktion aufzurufen. Eine ProgrammingError wird ausgelöst, wenn die Anwendung .commit() oder .rollback() während einer aktiven TPC-Transaktion aufruft.

Wenn die Datenbankverbindung TPC nicht unterstützt, wird eine NotSupportedError ausgelöst.

.tpc_prepare()
Führt die erste Phase einer mit .tpc_begin() gestarteten Transaktion aus. Eine ProgrammingError sollte ausgelöst werden, wenn diese Methode außerhalb einer TPC-Transaktion aufgerufen wird.

Nach dem Aufruf von .tpc_prepare() können keine Anweisungen mehr ausgeführt werden, bis .tpc_commit() oder .tpc_rollback() aufgerufen wurden.

.tpc_commit([ xid ])
Wenn ohne Argumente aufgerufen, committet .tpc_commit() eine zuvor mit .tpc_prepare() vorbereitete TPC-Transaktion.

Wenn .tpc_commit() vor .tpc_prepare() aufgerufen wird, wird ein Single-Phase-Commit durchgeführt. Ein Transaktionsmanager kann dies tun, wenn nur eine einzige Ressource an der globalen Transaktion beteiligt ist.

Wenn mit einer Transaktions-ID xid aufgerufen, committet die Datenbank die gegebene Transaktion. Wenn eine ungültige Transaktions-ID angegeben wird, wird eine ProgrammingError ausgelöst. Diese Form sollte außerhalb einer Transaktion aufgerufen werden und ist für die Wiederherstellung gedacht.

Nach der Rückkehr ist die TPC-Transaktion beendet.

.tpc_rollback([ xid ])
Wenn ohne Argumente aufgerufen, rollt .tpc_rollback() eine TPC-Transaktion zurück. Es kann vor oder nach .tpc_prepare() aufgerufen werden.

Wenn mit einer Transaktions-ID xid aufgerufen, rollt es die gegebene Transaktion zurück. Wenn eine ungültige Transaktions-ID angegeben wird, wird eine ProgrammingError ausgelöst. Diese Form sollte außerhalb einer Transaktion aufgerufen werden und ist für die Wiederherstellung gedacht.

Nach der Rückkehr ist die TPC-Transaktion beendet.

.tpc_recover()
Gibt eine Liste ausstehender Transaktions-IDs zurück, die mit .tpc_commit(xid) oder .tpc_rollback(xid) verwendet werden können.

Wenn die Datenbank keine Transaktionswiederherstellung unterstützt, kann sie eine leere Liste zurückgeben oder NotSupportedError auslösen.

Häufig gestellte Fragen

Die Datenbank-SIG sieht häufig wiederkehrende Fragen zur DB-API-Spezifikation. Dieser Abschnitt behandelt einige der Probleme, die Leute manchmal mit der Spezifikation haben.

Frage

Wie kann ich aus den von .fetch*() zurückgegebenen Tupeln ein Dictionary erstellen?

Antwort

Es gibt mehrere existierende Tools, die Helfer für diese Aufgabe bereitstellen. Die meisten davon verwenden den Ansatz, die Spaltennamen aus dem Cursor-Attribut .description als Grundlage für die Schlüssel im Zeilen-Dictionary zu verwenden.

Beachten Sie, dass der Grund für die Nicht-Erweiterung der DB-API-Spezifikation um die Unterstützung von Dictionary-Rückgabewerten für die Methoden .fetch*() darin liegt, dass dieser Ansatz mehrere Nachteile hat

  • Einige Datenbanken unterstützen keine case-sensitiven Spaltennamen oder wandeln sie automatisch in Klein- oder Großbuchstaben um.
  • Spalten im Ergebnisdatensatz, die durch die Abfrage generiert werden (z. B. durch die Verwendung von SQL-Funktionen), entsprechen keinen Tabellenspaltennamen, und Datenbanken generieren für diese Spalten normalerweise auf sehr datenbankspezifische Weise Namen.

Dadurch variiert der Zugriff auf die Spalten über Dictionary-Schlüssel zwischen Datenbanken und macht das Schreiben portablen Codes unmöglich.

Wesentliche Änderungen von Version 1.0 auf Version 2.0

Die Python Database API 2.0 führt im Vergleich zur Version 1.0 einige wichtige Änderungen ein. Da einige dieser Änderungen bestehende DB API 1.0-basierte Skripte brechen werden, wurde die Hauptversionsnummer angepasst, um diese Änderung widerzuspiegeln.

Dies sind die wichtigsten Änderungen von 1.0 auf 2.0

  • Die Notwendigkeit eines separaten dbi-Moduls wurde gestrichen und die Funktionalität in die Modulschnittstelle selbst integriert.
  • Neue Konstruktoren und Type Objects wurden für Datums-/Zeitwerte hinzugefügt, das RAW Type Object wurde in BINARY umbenannt. Der resultierende Satz sollte alle grundlegenden Datentypen abdecken, die üblicherweise in modernen SQL-Datenbanken vorkommen.
  • Neue Konstanten (apilevel, threadsafety, paramstyle) und Methoden (.executemany(), .nextset()) wurden hinzugefügt, um bessere Datenbankbindungen zu ermöglichen.
  • Die Semantik von .callproc() zum Aufruf gespeicherter Prozeduren ist nun klar definiert.
  • Die Definition des Rückgabewerts von .execute() wurde geändert. Zuvor basierte der Rückgabewert auf dem SQL-Anweisungstyp (was schwer richtig zu implementieren war) – er ist nun undefiniert; verwenden Sie stattdessen das flexiblere Attribut .rowcount. Module können die alten Rückgabewerte zurückgeben, aber diese sind nicht mehr zwingend durch die Spezifikation vorgeschrieben und sollten als datenbankschnittstellenabhängig betrachtet werden.
  • Klassenbasierte Ausnahmen wurden in die Spezifikation aufgenommen. Modulimplementierer können das in dieser Spezifikation definierte Ausnahme-Layout erweitern, indem sie die definierten Ausnahmeklassen ableiten.

Ergänzungen zur DB API 2.0-Spezifikation nach der Veröffentlichung

  • Zusätzliche optionale DB-API-Erweiterungen zum Satz der Kernfunktionalität wurden spezifiziert.

Offene Fragen

Obwohl die Spezifikation Version 2.0 viele Fragen klärt, die in Version 1.0 offen geblieben waren, gibt es noch einige offene Punkte, die in zukünftigen Versionen angegangen werden sollten.

  • Definieren Sie einen nützlichen Rückgabewert für .nextset() für den Fall, dass ein neuer Ergebnissatz verfügbar ist.
  • Integrieren Sie das Decimal-Objekt aus dem decimal-Modul zur Verwendung als verlustfreie monetäre und dezimale Austauschformat.

Fußnoten

Danksagungen

Vielen Dank an Andrew Kuchling, der die Python Database API Specification 2.0 im Jahr 2001 aus dem ursprünglichen HTML-Format in das PEP-Format konvertierte.

Vielen Dank an James Henstridge für die Leitung der Diskussion, die 2008 zur Standardisierung der Two-Phase Commit API-Erweiterungen führte.

Vielen Dank an Daniele Varrazzo für die Konvertierung der Spezifikation vom Text-PEP-Format in das ReST-PEP-Format, das 2012 Links zu verschiedenen Teilen ermöglicht.


Quelle: https://github.com/python/peps/blob/main/peps/pep-0249.rst

Zuletzt geändert: 2025-02-01 08:55:40 GMT