/PythonDBAPI

Z Brain-wiki

TI:WTBD/PythonDBAPI

Podjęto próbę pewnej standaryzacji interfejsu programistycznego (API) pozwalającego na korzystanie z relacyjnych (SQL-owych) baz danych z poziomu programów w Pythonie. Oczywistym celem jest ułatwienie tworzenia programów, które minimalnym wysiłkiem programisty dałoby się zaadaptować do współpracy z różnymi systemami bazodanowymi. Nie ma co się łudzić, że da się to osiągnąć w ogóle bez trudu -- standaryzacja API jest tylko jednym z warunków. Trzeba brać pod uwagę choćby różnice w dialektach SQL.

Zadania, jakie spełnia DB API to:

  • nawiązanie komunikacji z bazą
    • w przypadku SQLite to po prostu podanie nazwy pliku do otwarcia, i ewent. opcjonalnych parametrów ,,połączenia"
    • dla systemów klient-serwer jest to nawiązanie połączenia sieciowego i dokonanie autoryzacji (w sposób specyficzny dla danego systemu)
  • umożliwienie przekazywania poleceń SQL do silnika bazy
  • odbiór wyników zapytań ewent. informacji o wykonaniu poleceń DDL i DML
  • różne inne zadania np. sterowanie transakcjami, pobieranie metadanych
    • takie zadania ,,różne" bywają różnie zrealizowane w różnych systemach

Specyfikacja DB API jest przedmiotem dokumentu PEP (Python Enhancement Proposal) nr 249: [1].

Główne klasy definiowane w DB API to:

  • połączenie (Connection)
  • kursor (Cursor)

DB API definiuje również hierarchię typów wyjątków, które służą przekazywaniu informacji o zajściu błędów oraz ostrzeżeń. Najczęstszy to ProgrammingError, powstający w wyniku np. błędnej składni polecenia SQL, przekazania niewłaściwej liczby parametrów polecenia, itp. Jest to podklasa DatabaseError, która z kolei dziedziczy od Error. Ostrzeżenia tworzą osobną klasę Warning i powstają np. w sytuacji niepełnego zapisu danych wynikającego z właściwości danego systemu BD.

Connection

Obiekt klasy Connection otrzymuje się jako wynik wywołania funkcji connect(...), której argumenty (parametry połączenia) zależą od specyfiki danego systemu (ścieżka do pliku dla SQLite; adres sieciowy, ew. nr portu, dane autoryzacyjne -- dla systemów klient-serwer). Metody połączenia to:

.close()
po zamknięciu połączenia nie da się go dalej wykorzystywać ani wznowić, należy w razie potrzeby utworzyć nowe, wywołując konstruktor connect(). Nieważne są również wszystkie kursory związane z zamkniętym połączeniem. Jeżeli w momencie wywołania .close() była otwarta transakcja, nastąpi jej wycofanie (ROLLBACK).
.commit()
zatwierdzenie aktualnej transakcji. Jeśli baza nie wspiera transakcji, metoda ta nie zgłasza błędu, tylko nic nie robi.
.rollback()
wycofanie aktualnej transakcji. Jeśli operacja ta jest niewspierana, powinien zostać rzucony wyjątek. Dla modułów nie wspierających transakcji w ogóle, może to być AttributeError (metoda nie istnieje).
.cursor()
konstruktor obiektu klasy Cursor.

Cursor

Obiekt klasy Cursor jest przede wszystkim ,,pojemnikiem" na wyniki zapytania lub innego polecenia i związane z nimi metadane. Atrybuty i metody:

.description
atrybut (tylko do odczytu) opisujący kolumny zbioru wynikowego, w postaci sekwencji, której elementami są sekwencje 7-elementowe. Najważniejsze z tych 7 elementów to dwa pierwsze: nazwa i kod typu danych. Jeśli kursor nie ,,zawiera" żadnego zbioru wynikowego, atrybut ten ma wartość None.
.rowcount
atrybut (tylko do odczytu) którego wartością jest liczba wierszy zbioru wynikowego, lub wierszy tabeli zmienionych o ile ostatnią operacją było polecenie DML. Jeśli liczba ta nie jest możliwa do ustalenia, wartością jest -1.
.callproc(nazwa, parametry ...)
wywołanie procedury składowanej (metoda opcjonalna)
.close()
zamknięcie kursora (nie połączenia), zwolnienie zajmowanych zasobów
.execute(polecenie[, parametry])
przesłanie polecenia SQL, ewent. sparametryzowanego. Parametry podaje się w postaci sekwencji lub słownika (mapowania), w zależności od dostępnego mechanizmu wiązania parametrów. Jest 'zdecydowanie' rekomendowane wykorzystanie poleceń sparametryzowanych, a nie -- każdorazowe konstruowanie pełnego tekstu polecenia SQL (wraz z wartościami parametrów) za pomocą operacji na napisach. Daje to korzyści zarówno na poziomie optymalizacji, jak i bezpieczeństwa. Ma to zwłaszcza znaczenie przy operacjach wielokrotnie powtarzanych z różnymi zestawami parametrów (np. masowe wstawianie danych do tabeli). Należy wówczas wielokrotnie korzystać z tego samego kursora. Wartość zwracana przez tę metodę jest nieokreślona.
.executemany(polecenie, sekwencja_z_parametrami)
wielokrotnie to samo polecenie SQL z różnymi zestawami parametrów. Jeśli poleceniem jest zapytanie zwracające zbiór wynikowy, wynik jest nieokreślony -- może nim być rzucenie wyjątku.
.fetchone()
pobrać kolejny wiersz aktualnego zbioru wynikowego, zwraca None jeśli nie ma już nic do pobrania (zbiór wynikowy został wyczerpany). Rzuci wyjątkiem Error, jeśli poprzednio wywołane polecenie nie zwróciło zbioru wynikowego (a w szczególności, gdy aktualnym kursorem nie wywołano jeszcze żadnego polecenia).
.fetchmany([size=cursor.arraysize])
pobrać max. size kolejnych wierszy aktualnego zbioru wynikowego, jako sekwencję. Poza tym uwagi jw.
.fetchall()
pobrać wszystkie (pozostałe) wiersze aktualnego zbioru wynikowego, jako sekwencję.
.nextset()
niektóre systemy BD implementują kursory będące uchwytami do wielu zbiorów wynikowych równocześnie, ta metoda (opcjonalna) powoduje przejście do kolejnego zbioru wynikowego, zwracając None jeśli zbiory wynikowe zostały wyczerpane.
.arraysize
atrybut modyfikowalny, ustalający domyślną liczbę wierszy pobieranych przez .fetchmany().
.setinputsizes(sizes), .setoutputsize(size[, nr_kolumny])
(często puste) metody związane z ,,tuningowaniem" zarządzania pamięcią przez moduł, rzadko wykorzystywane.
.next(), .__iter__()
metody pozwalające traktować kursor jako iterator (po wierszach zbioru wynikowego). W specyfikacji wymienione jako opcjonalne rozszerzenia, ale w zasadzie już standardowe.

Atrybuty modułu

connect(...)
konstruktor połączenia
apilevel
wersja specyfikacji DP API, możliwe wartości: "1.0" (przestarzała), "2.0"
threadsafety
poziom wielowątkowości, wartości z range(4)
paramstyle
sposób wiązania parametrów w polecenia sparametryzowanych

Sposoby wiązania parametrów

"qmark"
każdy "?" w poleceniu jest zastępowany przez kolejny element sekwencji parametrów
"numeric"
każde ":n" gdzie n jest liczbą naturalną zastępowany jest przez element o indeksie n w sekwencji parametrów
"named"
każde :nazwa zastępowane jest przez element odpowiadający kluczowi nazwa w słowniku parametrów
"format"
kody jak z printf czy pythonowej operacji formatowania "%" zastępowane są kolejnymi elementami sekwencji parametrów
"pyformat"
,,rozszerzone" kody formatowania jak w pythonowej operacji "%", tzn postaci "%(nazwa)s"

Moduł sqlite3 ma paramstyle == "qmark", ale takk naprawdę wspiera również styl "named".