SQLite
 sql >> Baza danych >  >> RDS >> SQLite

Zastrzeżenia dotyczące Pythona i SQLite

SQLite to popularna, relacyjna baza danych, którą osadzasz w swojej aplikacji. Python jest dostarczany z oficjalnymi powiązaniami z SQLite. W tym artykule omówiono zastrzeżenia dotyczące używania SQLite w Pythonie. Pokazuje problemy, jakie mogą powodować różne wersje połączonych bibliotek SQLite, jak datetime obiekty nie są prawidłowo przechowywane i jak należy zachować szczególną ostrożność, gdy polegasz na with connection Pythona menedżer kontekstu, aby zatwierdzić Twoje dane.

Wprowadzenie

SQLite to popularny system relacyjnych baz danych (DB) . W przeciwieństwie do swoich większych braci opartych na kliencie i serwerze, takich jak MySQL, SQLite może być osadzony w Twojej aplikacji jako biblioteka . Python oficjalnie wspiera SQLite poprzez wiązania (oficjalna dokumentacja). Jednak praca z tymi wiązaniami nie zawsze jest prosta. Oprócz ogólnych zastrzeżeń SQLite, które omówiłem wcześniej, istnieje kilka problemów specyficznych dla Pythona, które omówimy w tym artykule .

Niezgodności wersji z celem wdrożenia

Często zdarza się, że programiści tworzą i testują kod na maszynie, która (bardzo) różni się od tej, na której kod jest wdrażany, pod względem systemu operacyjnego (OS) i sprzętu. Powoduje to trzy rodzaje problemów:

  • Aplikacja zachowuje się inaczej ze względu na różnice w systemie operacyjnym lub sprzęcie . Na przykład możesz napotkać problemy z wydajnością, gdy maszyna docelowa ma mniej pamięci niż twoja. Lub SQLite może wykonywać niektóre operacje wolniej na jednym systemie operacyjnym niż na innych, ponieważ bazowe interfejsy API niskiego poziomu, z których korzysta, są różne.
  • Wersja SQLite w miejscu docelowym wdrożenia różni się od wersji na maszynie deweloperskiej . Może to powodować problemy w obu kierunkach, ponieważ z czasem dodawane są nowe funkcje (i zmiany w zachowaniu), zobacz oficjalny dziennik zmian. Na przykład przestarzała wdrożona wersja SQLite może nie mieć funkcji, które działały dobrze podczas programowania. Ponadto nowsza wersja SQLite we wdrożeniu może zachowywać się inaczej niż starsza wersja używana na komputerze deweloperskim, np. gdy zespół SQLite zmieni niektóre wartości domyślne.
  • W miejscu docelowym wdrożenia może brakować całkowicie powiązań Pythona SQLite lub biblioteki C. . To jest Linux -problem związany z dystrybucją . Oficjalne dystrybucje Windows i macOS będą zawierać dołączony wersja biblioteki SQLite C. W systemie Linux biblioteka SQLite jest oddzielnym pakietem. Jeśli sam kompilujesz Pythona, np. ponieważ używasz Debiana/Raspbiana/etc. dystrybucja, która jest dostarczana ze starożytnymi wersjami funkcji, make Pythona Skrypt budowania zbuduje tylko powiązania Pythona SQLite jeśli zainstalowana biblioteka SQLite C została wykryta podczas procesu kompilacji Pythona . Jeśli sam wykonasz taką ponowną kompilację Pythona, powinieneś upewnić się, że zainstalowana biblioteka SQLite C jest najnowsza . To znowu nie dotyczy Debiana itp. podczas instalacji SQLite przez apt , więc może być konieczne samodzielne skompilowanie i zainstalowanie programu SQLite, przed do budowania Pythona.

Aby dowiedzieć się, która wersja biblioteki SQLite C jest używana przez Twój interpreter Pythona, uruchom to polecenie:

python3 -c "import sqlite3; print(sqlite3.sqlite_version)"Code language: Bash (bash)

Zastępowanie sqlite3.sqlite_version z sqlite3.version poda wersję powiązań Pythona z SQLite .

Aktualizacja podstawowej biblioteki SQLite C

Jeśli chcesz skorzystać z funkcji lub poprawek błędów najnowszej wersji SQLite, masz szczęście. Biblioteka SQLite C jest zazwyczaj łączona w czasie wykonywania, dzięki czemu można ją zastąpić bez żadnych zmian w zainstalowanym interpreterze Pythona. Konkretne kroki zależą od systemu operacyjnego (testowane dla Pythona 3.6+):

1) Okna: Pobierz prekompilowane pliki binarne x86 lub x64 ze strony pobierania SQLite i zastąp plik sqlite3.dll plik znaleziony w DLLs folder instalacji Pythona z tym, który właśnie pobrałeś.

2) Linux: ze strony pobierania SQLite pobierz autoconf źródła, rozpakuj archiwum i uruchom ./configure && make && make install który zainstaluje bibliotekę w /usr/local/lib domyślnie.
Następnie dodaj linię export LD_LIBRARY_PATH=/usr/local/lib na początku skryptu powłoki, który uruchamia skrypt Pythona, co zmusza interpreter Pythona do korzystania z własnej biblioteki.

3) macOS: z mojej analizy wynika, że ​​biblioteka SQLite C jest skompilowana do powiązań Pythona binarny (_sqlite3.cpython-36m-darwin.so ). Jeśli chcesz go zastąpić, prawdopodobnie będziesz musiał uzyskać kod źródłowy Pythona pasujący do zainstalowanej instalacji Pythona (np. 3.7.6 lub jakąkolwiek wersję używasz). Skompiluj Pythona ze źródła, używając skryptu kompilacji macOS. Ten skrypt obejmuje pobieranie i budowanie biblioteki C SQLite, więc upewnij się, że edytujesz skrypt, aby odwoływał się do najnowszej wersji SQLite. Na koniec użyj skompilowanego pliku powiązań (np. _sqlite3.cpython-37m-darwin.so ), aby zastąpić przestarzały.

Praca z datetime uwzględniającym strefy czasowe obiekty

Większość programistów Pythona zazwyczaj używa datetime obiekty podczas pracy ze znacznikami czasu. Są naiwni datetime obiekty, które nie znają swojej strefy czasowej i nienaiwne te, które są świadome strefy czasowej . Powszechnie wiadomo, że datetime w Pythonie moduł jest dziwaczny, co utrudnia nawet tworzenie uwzględniających strefę czasową datetime.datetime przedmioty. Na przykład wywołanie datetime.datetime.utcnow() tworzy naiwny obiekt, co jest sprzeczne z intuicją dla programistów, którzy są nowicjuszami w datetime API, spodziewając się, że Python użyje strefy czasowej UTC! Biblioteki innych firm, takie jak python-dateutil, ułatwiają to zadanie. Aby utworzyć obiekt uwzględniający strefę czasową, możesz użyć kodu takiego:

from dateutil.tz import tzutc
import datetime
timezone_aware_dt = datetime.datetime.now(tzutc())Code language: Python (python)

Niestety, oficjalna dokumentacja Pythona sqlite3 moduł wprowadza w błąd, jeśli chodzi o obsługę znaczników czasu. Jak opisano tutaj, datetime obiekty są automatycznie konwertowane podczas używania PARSE_DECLTYPES (i deklarowanie TIMESTAMP kolumna). Chociaż jest to technicznie poprawne, konwersja straci strefa czasowa informacje ! W związku z tym, jeśli faktycznie korzystasz ze strefy czasowej — świadomy datetime.datetime obiekty, musisz zarejestrować własne konwertery , które przechowują informacje o strefie czasowej w następujący sposób:

def convert_timestamp_to_tzaware(timestamp: bytes) -> datetime.datetime:
    # sqlite3 provides the timestamp as byte-string
    return dateutil.parser.parse(timestamp.decode("utf-8"))
 
def convert_timestamp_to_sqlite(dt: datetime.datetime) -> str:
    return dt.isoformat()  # includes the timezone information at the end of the string
 
sqlite3.register_converter("timestamp", convert_timestamp_to_tzaware)
sqlite3.register_adapter(datetime.datetime, convert_timestamp_to_sqlite)Code language: Python (python)

Jak widać, znacznik czasu jest po prostu przechowywany jako TEXT na końcu. W SQLite nie ma prawdziwych typów danych „data” ani „data-godzina”.

Transakcje i automatyczne zatwierdzanie

Python sqlite3 moduł nie zatwierdza automatycznie danych, które są modyfikowane przez twoje zapytania . Kiedy wykonujesz zapytania, które w jakiś sposób zmieniają bazę danych, musisz albo wydać jawne polecenie COMMIT oświadczenie lub używasz połączenia jako menedżer kontekstu obiekt, jak pokazano w poniższym przykładzie:

with connection:  # this uses the connection as context manager
    # do something with it, e.g.
    connection.execute("SOME QUERY")Code language: Python (python)

Po wyjściu z powyższego bloku sqlite3 niejawnie wywołuje connection.commit() , ale robi to tylko wtedy, gdy trwa transakcja . Instrukcje DML (Data Modification Language) automatycznie rozpoczynają transakcję, ale zapytania dotyczące DROP lub CREATE TABLE / INDEX oświadczenia tego nie robią, ponieważ zgodnie z dokumentacją nie są liczone jako DML. Jest to sprzeczne z intuicją, ponieważ te stwierdzenia wyraźnie modyfikują dane.

Tak więc, jeśli uruchomisz jakikolwiek DROP lub CREATE TABLE / INDEX instrukcji wewnątrz menedżera kontekstu, dobrą praktyką jest jawne wykonanie BEGIN TRANSACTION najpierw oświadczenie , aby menedżer kontekstu faktycznie wywołał connection.commit() dla Ciebie.

Obsługa 64-bitowych liczb całkowitych

W poprzednim artykule omówiłem już, że SQLite ma problemy z dużymi liczbami całkowitymi, które są mniejsze niż -2^63 lub większe lub równe 2^63 . Jeśli spróbujesz ich użyć w parametrach zapytania (z ? symbol), sqlite3 Pythona moduł zgłosi OverflowError: Python int too large to convert to SQLite INTEGER , chroniąc Cię przed przypadkową utratą danych.

Aby prawidłowo obsługiwać bardzo duże liczby całkowite, musisz:

  1. Użyj TEXT wpisz odpowiednią kolumnę tabeli, i
  2. Zmień liczbę na str już w Pythonie , przed użyciem go jako parametru.
  3. Konwertuj ciągi z powrotem na int w Pythonie, gdy SELECT danych

Wniosek

Oficjalny sqlite3 Pythona moduł jest doskonałym powiązaniem z SQLite. Jednak programiści nowi w SQLite muszą zrozumieć, że istnieje różnica między powiązaniami Pythona a podstawową biblioteką SQLite C. W cieniu czai się niebezpieczeństwo z powodu różnic wersji SQLite. Może się to zdarzyć, nawet jeśli uruchomisz to samo Wersja Pythona na dwóch różnych maszynach, ponieważ biblioteka SQLite C może nadal używać innej wersji. Omówiłem również inne problemy, takie jak obsługa obiektów datetime i uporczywe zmienianie danych za pomocą transakcji. Sam nie zdawałem sobie z tego sprawy, co spowodowało utratę danych dla użytkowników moich aplikacji, więc mam nadzieję, że unikniesz tych samych błędów, które popełniłem.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jaka jest różnica między REFERENCJAMI z KLUCZEM OBCYM lub bez niego?

  2. Jak przechowywać i pobierać tablicę bajtów (dane obrazu) do iz bazy danych SQLite?

  3. Zamów SQLite według daty1530019888000

  4. Dołącz do SQLite

  5. Czy możliwa jest baza danych Android SQLite z bazą danych w określonej lokalizacji?