SQLite
 sql >> Database >  >> RDS >> SQLite

Avvertenze su Python e SQLite

SQLite è un popolare database relazionale che puoi incorporare nella tua applicazione. Python viene fornito con collegamenti ufficiali a SQLite. Questo articolo esamina le avvertenze sull'utilizzo di SQLite in Python. Dimostra i problemi che possono causare diverse versioni di librerie SQLite collegate, come datetime gli oggetti non sono archiviati correttamente e come è necessario prestare la massima attenzione quando si fa affidamento su with connection di Python gestore del contesto per salvare i tuoi dati.

Introduzione

SQLite è un popolare sistema di database relazionali (DB) . A differenza dei suoi fratelli maggiori basati su client-server, come MySQL, SQLite può essere incorporato nella tua applicazione come libreria . Python supporta ufficialmente SQLite tramite collegamenti (documenti ufficiali). Tuttavia, lavorare con queste associazioni non è sempre semplice. A parte gli avvertimenti generici su SQLite di cui ho discusso in precedenza, ci sono diversi problemi specifici di Python che esamineremo in questo articolo .

Incompatibilità della versione con la destinazione di distribuzione

È abbastanza comune che gli sviluppatori creino e testino codice su una macchina (molto) diversa da quella su cui è distribuito il codice, in termini di sistema operativo (SO) e hardware. Ciò causa tre tipi di problemi:

  • L'applicazione si comporta in modo diverso a causa delle differenze del sistema operativo o dell'hardware . Ad esempio, potresti riscontrare problemi di prestazioni quando la macchina di destinazione ha meno memoria della tua macchina. Oppure SQLite potrebbe eseguire alcune operazioni più lentamente su un sistema operativo rispetto ad altri, perché le API del sistema operativo di basso livello sottostanti che utilizza sono diverse.
  • La versione di SQLite sulla destinazione di distribuzione è diverso dalla versione della macchina dello sviluppatore . Ciò può causare problemi in entrambe le direzioni, perché nel tempo vengono aggiunte nuove funzionalità (e cambia il comportamento), vedere il log delle modifiche ufficiale. Ad esempio, una versione SQLite distribuita obsoleta potrebbe non avere funzionalità che hanno funzionato bene durante lo sviluppo. Inoltre, una versione SQLite più recente in distribuzione potrebbe comportarsi in modo diverso rispetto a una versione precedente utilizzata sulla macchina di sviluppo, ad es. quando il team SQLite cambia alcuni valori predefiniti.
  • O i collegamenti Python di SQLite o la libreria C potrebbero mancare del tutto nella destinazione di distribuzione . Questo è un Linux -problema specifico della distribuzione . Le distribuzioni ufficiali di Windows e macOS conterranno un raggruppato versione della libreria SQLite C. Su Linux, la libreria SQLite è un pacchetto separato. Se compili tu stesso Python, ad es. perché usi un Debian/Raspbian/etc. distribuzione fornita con versioni di funzionalità antiche, il Python make build script creerà solo i collegamenti SQLite di Python se una libreria SQLite C installata è stata rilevata durante il processo di compilazione di Python . Se esegui tu stesso una tale ricompilazione di Python, dovresti assicurarti che la libreria SQLite C installata sia recente . Questo, ancora una volta, non è il caso di Debian ecc. quando si installa SQLite tramite apt , quindi potresti anche dover compilare e installare SQLite da solo, prima alla creazione di Python.

Per scoprire quale versione della libreria SQLite C è utilizzata dal tuo interprete Python, esegui questo comando:

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

Sostituzione di sqlite3.sqlite_version con sqlite3.version ti fornirà la versione di SQLite binding di Python .

Aggiornamento della libreria C SQLite sottostante

Se vuoi trarre profitto dalle funzionalità o dalle correzioni di bug della versione SQLite più recente, sei fortunato. La libreria SQLite C è in genere collegata in fase di esecuzione e quindi può essere sostituita senza alcuna modifica all'interprete Python installato. I passaggi concreti dipendono dal tuo sistema operativo (testato per Python 3.6+):

1) Finestre: Scarica i binari precompilati x86 o x64 dalla pagina di download di SQLite e sostituisci sqlite3.dll file trovato nelle DLLs cartella della tua installazione di Python con quella appena scaricata.

2) Linux: dalla pagina di download di SQLite ottieni autoconf sorgenti, estrai l'archivio ed esegui ./configure && make && make install che installerà la libreria in /usr/local/lib per impostazione predefinita.
Quindi aggiungi la riga export LD_LIBRARY_PATH=/usr/local/lib all'inizio dello script di shell che avvia il tuo script Python, che costringe il tuo interprete Python a utilizzare la libreria autocostruita.

3) macOS: dalla mia analisi, sembra che la libreria SQLite C sia compilata nei bindings di Python binario (_sqlite3.cpython-36m-darwin.so ). Se vuoi sostituirlo, probabilmente dovrai ottenere il codice sorgente Python che corrisponda all'installazione Python installata (ad es. 3.7.6 o qualunque versione tu usi). Compila Python dal sorgente, usando lo script di build di macOS. Questo script include il download e la creazione della libreria C di SQLite, quindi assicurati di modificare lo script per fare riferimento alla versione SQLite più recente. Infine, usa il file di binding compilato (ad es. _sqlite3.cpython-37m-darwin.so ), per sostituire quello obsoleto.

Utilizzo di datetime in grado di riconoscere il fuso orario oggetti

La maggior parte degli sviluppatori Python in genere utilizza datetime oggetti quando si lavora con timestamp. Ci sono ingenui datetime oggetti che non conoscono il loro fuso orario e non ingenui quelli, che sono a conoscenza del fuso orario . È noto che datetime di Python modulo è bizzarro, rendendo difficile persino creare datetime.datetime in grado di riconoscere il fuso orario oggetti. Ad esempio, la chiamata datetime.datetime.utcnow() crea un ingenuo oggetto, che è controintuitivo per gli sviluppatori che non conoscono datetime API, in attesa che Python utilizzi il fuso orario UTC! Le librerie di terze parti, come python-dateutil, facilitano questa attività. Per creare un oggetto sensibile al fuso orario, puoi utilizzare un codice come questo:

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

Sfortunatamente, la documentazione Python ufficiale di sqlite3 modulo è fuorviante quando si tratta di gestire i timestamp. Come descritto qui, datetime gli oggetti vengono convertiti automaticamente quando si utilizza PARSE_DECLTYPES (e dichiarando un TIMESTAMP colonna). Sebbene ciò sia tecnicamente corretto, la conversione perderà il fuso orario informazioni ! Di conseguenza, se stai effettivamente utilizzando il fuso orario, consapevole datetime.datetime oggetti, devi registrare i tuoi convertitori , che conservano le informazioni sul fuso orario, come segue:

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)

Come puoi vedere, il timestamp è semplicemente memorizzato come TEXT alla fine. Non esiste un vero tipo di dati "data" o "data ora" in SQLite.

Transazioni e commit automatico

sqlite3 di Python module non esegue automaticamente il commit dei dati che vengono modificati dalle tue query . Quando esegui query che in qualche modo modificano il database, devi emettere un esplicito COMMIT istruzione oppure utilizzi la connessione come gestore di contesto oggetto, come mostrato nell'esempio seguente:

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

Una volta terminato il blocco precedente, sqlite3 chiama implicitamente connection.commit() , ma ​​lo fa solo se è in corso una transazione . Le istruzioni DML (Data Modification Language) avviano automaticamente una transazione, ma le query che coinvolgono DROP o CREATE TABLE / INDEX le istruzioni non lo fanno, perché non contano come DML secondo la documentazione. Questo è contro-intuitivo, perché queste affermazioni modificano chiaramente i dati.

Pertanto, se esegui qualsiasi DROP o CREATE TABLE / INDEX istruzioni all'interno del gestore del contesto, è buona norma eseguire esplicitamente una BEGIN TRANSACTION prima l'affermazione , in modo che il gestore del contesto chiami effettivamente connection.commit() per te.

Gestione di interi a 64 bit

In un articolo precedente ho già discusso del fatto che SQLite ha problemi con numeri interi grandi inferiori a -2^63 , o maggiore o uguale a 2^63 . Se provi a usarli nei parametri di query (con il ? simbolo), sqlite3 di Python il modulo solleverà un OverflowError: Python int too large to convert to SQLite INTEGER , proteggendoti dalla perdita accidentale dei dati.

Per gestire correttamente numeri interi molto grandi, devi:

  1. Usa il TEXT digitare per la colonna della tabella corrispondente, e
  2. Converti il ​​numero in str già in Python , prima di utilizzarlo come parametro.
  3. Riconvertire le stringhe in int in Python, quando SELECT zione dei dati

Conclusione

sqlite3 ufficiale di Python module è un eccellente legame con SQLite. Tuttavia, gli sviluppatori che non conoscono SQLite devono comprendere che esiste una differenza tra i collegamenti Python e la libreria C SQLite sottostante. C'è un pericolo in agguato nell'ombra, a causa delle differenze di versione di SQLite. Questi possono accadere anche se esegui lo uguale Versione Python su due macchine diverse, perché la libreria SQLite C potrebbe ancora utilizzare una versione diversa. Ho anche discusso di altri problemi come la gestione di oggetti datetime e la modifica persistente dei dati utilizzando le transazioni. Non ne ero a conoscenza, il che ha causato la perdita di dati agli utenti delle mie applicazioni, quindi spero che tu possa evitare gli stessi errori che ho commesso.