SQLite
 sql >> Base de données >  >> RDS >> SQLite

Mises en garde Python et SQLite

SQLite est une base de données relationnelle populaire que vous intégrez dans votre application. Python est livré avec des liaisons officielles à SQLite. Cet article examine les mises en garde concernant l'utilisation de SQLite dans Python. Il démontre les problèmes que différentes versions de bibliothèques SQLite liées peuvent causer, comment datetime les objets ne sont pas correctement stockés et comment vous devez être très prudent lorsque vous vous fiez au with connection de Python gestionnaire de contexte pour valider vos données.

Présentation

SQLite est un système de base de données relationnelle (DB) populaire . Contrairement à ses grands frères basés sur client-serveur, tels que MySQL, SQLite peut être intégré dans votre application en tant que bibliothèque . Python prend officiellement en charge SQLite via des liaisons (documents officiels). Cependant, travailler avec ces liaisons n'est pas toujours simple. Outre les mises en garde génériques de SQLite dont j'ai parlé précédemment, il existe plusieurs problèmes spécifiques à Python que nous examinerons dans cet article .

Incompatibilités de version avec la cible de déploiement

Il est assez courant que les développeurs créent et testent du code sur une machine (très) différente de celle où le code est déployé, en termes de système d'exploitation (OS) et de matériel. Cela entraîne trois types de problèmes :

  • L'application se comporte différemment en raison de différences de système d'exploitation ou de matériel . Par exemple, vous pouvez rencontrer des problèmes de performances lorsque la machine cible dispose de moins de mémoire que votre machine. Ou SQLite peut exécuter certaines opérations plus lentement sur un système d'exploitation que sur d'autres, car les API de système d'exploitation de bas niveau sous-jacentes qu'il utilise sont différentes.
  • La version SQLite sur la cible de déploiement diffère de la version de la machine du développeur . Cela peut causer des problèmes dans les deux sens, car de nouvelles fonctionnalités sont ajoutées (et des changements de comportement) au fil du temps, voir le changelog officiel. Par exemple, une version obsolète de SQLite déployée peut manquer de fonctionnalités qui ont bien fonctionné lors du développement. De plus, une version plus récente de SQLite en déploiement peut se comporter différemment d'une version plus ancienne que vous utilisez sur votre machine de développement, par ex. lorsque l'équipe SQLite modifie certaines valeurs par défaut.
  • Soit les liaisons Python de SQLite, soit la bibliothèque C, peuvent être totalement absentes sur la cible de déploiement . Ceci est un Linux -problème spécifique à la distribution . Les distributions officielles Windows et macOS contiendront un groupé version de la bibliothèque SQLite C. Sous Linux, la bibliothèque SQLite est un package séparé. Si vous compilez Python vous-même, par ex. parce que vous utilisez un Debian/Raspbian/etc. distribution livrée avec d'anciennes versions de fonctionnalités, le Python make le script de construction ne construira que les liaisons SQLite de Python si une bibliothèque SQLite C installée a été détectée lors du processus de compilation de Python . Si vous effectuez vous-même une telle recompilation de Python, vous devez vous assurer que la bibliothèque SQLite C installée est récente . Ceci, encore une fois, n'est pas le cas pour Debian etc. lors de l'installation de SQLite via apt , vous devrez donc peut-être également créer et installer SQLite vous-même, avant pour construire Python.

Pour savoir quelle version de la bibliothèque SQLite C est utilisée par votre interpréteur Python, exécutez cette commande :

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

Remplacement de sqlite3.sqlite_version avec sqlite3.version vous donnera la version des liaisons SQLite de Python .

Mise à jour de la bibliothèque SQLite C sous-jacente

Si vous souhaitez profiter des fonctionnalités ou des corrections de bogues de la version la plus récente de SQLite, vous avez de la chance. La bibliothèque SQLite C est généralement liée au moment de l'exécution et peut donc être remplacée sans aucune modification de votre interpréteur Python installé. Les étapes concrètes dépendent de votre système d'exploitation (testé pour Python 3.6+) :

1) Windows : Téléchargez les binaires précompilés x86 ou x64 depuis la page de téléchargement de SQLite et remplacez le sqlite3.dll fichier présent dans les DLLs dossier de votre installation Python avec celui que vous venez de télécharger.

2) Linux : depuis la page de téléchargement de SQLite, obtenez l'autoconf sources, extrayez l'archive et exécutez ./configure && make && make install qui installera la bibliothèque dans /usr/local/lib par défaut.
Ajoutez ensuite la ligne export LD_LIBRARY_PATH=/usr/local/lib au début du script shell qui démarre votre script Python, ce qui oblige votre interpréteur Python à utiliser la bibliothèque auto-construite.

3) macOS : d'après mon analyse, il semble que la bibliothèque SQLite C soit compilée dans les liaisons Python binaire (_sqlite3.cpython-36m-darwin.so ). Si vous souhaitez le remplacer, vous devrez probablement obtenir le code source Python correspondant à votre installation Python installée (par exemple, 3.7.6 ou quelle que soit la version que vous utilisez). Compilez Python à partir de la source, à l'aide du script de construction macOS. Ce script inclut le téléchargement et la construction de la bibliothèque C de SQLite, alors assurez-vous de modifier le script pour référencer la version la plus récente de SQLite. Enfin, utilisez le fichier de liaisons compilé (par exemple _sqlite3.cpython-37m-darwin.so ), pour remplacer l'ancien.

Travailler avec les datetime sensibles au fuseau horaire objets

La plupart des développeurs Python utilisent généralement datetime objets lorsque vous travaillez avec des horodatages. Il y a des naïfs datetime objets qui ne connaissent pas leur fuseau horaire, et non-naïfs ceux qui sont conscients du fuseau horaire . Il est bien connu que le datetime de Python le module est excentrique, ce qui rend même difficile la création de datetime.datetime prenant en compte le fuseau horaire objets. Par exemple, l'appel datetime.datetime.utcnow() crée un naïf objet, ce qui est contre-intuitif pour les développeurs qui découvrent le datetime API, s'attendant à ce que Python utilise le fuseau horaire UTC ! Les bibliothèques tierces, telles que python-dateutil, facilitent cette tâche. Pour créer un objet sensible au fuseau horaire, vous pouvez utiliser un code tel que :

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

Malheureusement, la documentation Python officielle de sqlite3 module est trompeur en ce qui concerne la gestion des horodatages. Comme décrit ici, datetime les objets sont automatiquement convertis lors de l'utilisation de PARSE_DECLTYPES (et en déclarant un TIMESTAMP colonne). Bien que cela soit techniquement correct, la conversion sera perdue le fuseau horaire informations ! Par conséquent, si vous utilisez réellement le fuseau horaire conscient datetime.datetime objets, vous devez enregistrer vos propres convertisseurs , qui conservent les informations de fuseau horaire, comme suit :

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)

Comme vous pouvez le voir, l'horodatage est simplement stocké en tant que TEXT à la fin. Il n'y a pas de vrai type de données "date" ou "datetime" dans SQLite.

Transactions et auto-commit

Python sqlite3 le module ne valide pas automatiquement les données modifiées par vos requêtes . Lorsque vous effectuez des requêtes qui modifient d'une manière ou d'une autre la base de données, vous devez soit émettre un COMMIT explicite déclaration, ou vous utilisez la connexion en tant que gestionnaire de contexte objet, comme illustré dans l'exemple suivant :

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

Une fois le bloc ci-dessus quitté, sqlite3 appelle implicitement connection.commit() , mais ne le fait que si une transaction est en cours . Les instructions DML (Data Modification Language) démarrent automatiquement une transaction, mais les requêtes impliquant DROP ou CREATE TABLE / INDEX les instructions ne le font pas, car elles ne comptent pas comme DML selon la documentation. C'est contre-intuitif, car ces déclarations modifient clairement les données.

Ainsi, si vous exécutez un DROP ou CREATE TABLE / INDEX instructions à l'intérieur du gestionnaire de contexte, il est recommandé d'exécuter explicitement un BEGIN TRANSACTION déclaration d'abord , de sorte que le gestionnaire de contexte appellera réellement connection.commit() pour vous.

Gestion des entiers 64 bits

Dans un article précédent, j'ai déjà expliqué que SQLite avait des problèmes avec les grands entiers inférieurs à -2^63 , ou supérieur ou égal à 2^63 . Si vous essayez de les utiliser dans les paramètres de requête (avec le ? symbole), sqlite3 de Python module lèvera une OverflowError: Python int too large to convert to SQLite INTEGER , vous protégeant contre la perte accidentelle de données.

Pour gérer correctement les très grands nombres entiers, vous devez :

  1. Utilisez le TEXT type pour la colonne de tableau correspondante, et
  2. Convertir le nombre en str déjà en Python , avant de l'utiliser comme paramètre.
  3. Reconvertir les chaînes en int en Python, lorsque SELECT données

Conclusion

sqlite3 officiel de Python module est une excellente liaison à SQLite. Cependant, les développeurs qui découvrent SQLite doivent comprendre qu'il existe une différence entre les liaisons Python et la bibliothèque SQLite C sous-jacente. Il y a un danger caché dans l'ombre, à cause des différences de version de SQLite. Cela peut se produire même si vous exécutez le même Version Python sur deux machines différentes, car la bibliothèque SQLite C utilise peut-être encore une version différente. J'ai également discuté d'autres problèmes tels que la gestion des objets datetime et la modification persistante des données à l'aide de transactions. Je n'en avais pas conscience moi-même, ce qui provoquait une perte de données pour les utilisateurs de mes applications, j'espère donc que vous pourrez éviter les mêmes erreurs que j'ai commises.