sql >> Database >  >> RDS >> SQLite

Python- en SQLite-waarschuwingen

SQLite is een populaire, relationele database die u in uw toepassing insluit. Python wordt geleverd met officiële bindingen voor SQLite. Dit artikel onderzoekt de kanttekeningen bij het gebruik van SQLite in Python. Het demonstreert problemen die verschillende versies van gekoppelde SQLite-bibliotheken kunnen veroorzaken, hoe datetime objecten niet correct worden opgeslagen, en hoe u extra voorzichtig moet zijn wanneer u vertrouwt op with connection van Python contextmanager om uw gegevens vast te leggen.

Inleiding

SQLite is een populair, relationeel databasesysteem (DB) . In tegenstelling tot zijn grotere, client-server-gebaseerde broers, zoals MySQL, kan SQLite als bibliotheek in uw applicatie worden ingesloten . Python ondersteunt officieel SQLite via bindingen (officiële documenten). Het werken met die bindingen is echter niet altijd eenvoudig. Afgezien van de algemene SQLite-waarschuwingen die ik eerder heb besproken, er zijn verschillende Python-specifieke problemen die we in dit artikel zullen onderzoeken .

Versie incompatibiliteit met implementatiedoel

Het is vrij gebruikelijk dat ontwikkelaars code maken en testen op een machine die (heel) anders is dan die waarop de code wordt geïmplementeerd, wat betreft besturingssysteem (OS) en hardware. Dit veroorzaakt drie soorten problemen:

  • De applicatie gedraagt ​​zich anders vanwege verschillen in besturingssysteem of hardware . U kunt bijvoorbeeld prestatieproblemen tegenkomen wanneer de doelcomputer minder geheugen heeft dan uw computer. Of SQLite kan sommige bewerkingen langzamer uitvoeren op het ene besturingssysteem dan op andere, omdat de onderliggende OS-API's op laag niveau die het gebruikt anders zijn.
  • De SQLite versie op het implementatiedoel verschilt van de versie van de ontwikkelmachine . Dit kan in beide richtingen problemen veroorzaken, omdat er in de loop van de tijd nieuwe functies worden toegevoegd (en gedragsveranderingen), zie de officiële changelog. Een verouderde geïmplementeerde SQLite-versie kan bijvoorbeeld functies missen die tijdens de ontwikkeling prima werkten. Ook kan een nieuwere SQLite-versie in implementatie zich anders gedragen dan een oudere versie die u op uw ontwikkelmachine gebruikt, b.v. wanneer het SQLite-team enkele standaardwaarden wijzigt.
  • Ofwel de Python-bindingen van SQLite, of de C-bibliotheek, kunnen volledig ontbreken op het implementatiedoel . Dit is een Linux -distributiespecifiek probleem . Officiële Windows- en macOS-distributies bevatten een gebundelde versie van de SQLite C-bibliotheek. Op Linux is de SQLite-bibliotheek een apart pakket. Als je Python zelf compileert, b.v. omdat u een Debian/Raspbian/etc. distributie die wordt geleverd met oude functieversies, de Python make build-script zal alleen Python's SQLite-bindingen bouwen als er is een geïnstalleerde SQLite C-bibliotheek gedetecteerd tijdens het compilatieproces van Python . Als u zelf zo'n hercompilatie van Python doet, moet u ervoor zorgen dat de geïnstalleerde SQLite C-bibliotheek recent is . Dit is, nogmaals, niet het geval voor Debian enz. bij het installeren van SQLite via apt , dus u moet mogelijk ook zelf SQLite bouwen en installeren, vooraf om Python te bouwen.

Om erachter te komen welke versie van de SQLite C-bibliotheek wordt gebruikt door uw Python-interpreter, voert u deze opdracht uit:

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

sqlite3.sqlite_version vervangen met sqlite3.version geeft je de versie van Python's SQLite bindingen .

De onderliggende SQLite C-bibliotheek bijwerken

Als u wilt profiteren van functies of bugfixes van de meest recente SQLite-versie, heeft u geluk. De SQLite C-bibliotheek is meestal tijdens runtime gekoppeld en kan dus worden vervangen zonder wijzigingen aan uw geïnstalleerde Python-interpreter. De concrete stappen zijn afhankelijk van uw besturingssysteem (getest voor Python 3.6+):

1) Windows: Download de x86 of x64 voorgecompileerde binaire bestanden van de SQLite-downloadpagina en vervang de sqlite3.dll bestand gevonden in de DLLs map van je Python-installatie met degene die je zojuist hebt gedownload.

2) Linux: haal van de SQLite-downloadpagina de autoconf source, pak het archief uit en voer ./configure && make && make install . uit die de bibliotheek zal installeren in /usr/local/lib standaard.
Voeg vervolgens de regel toe export LD_LIBRARY_PATH=/usr/local/lib aan het begin van het shellscript waarmee je Python-script start, waardoor je Python-interpreter de zelfgebouwde bibliotheek moet gebruiken.

3) macOS: uit mijn analyse lijkt het erop dat de SQLite C-bibliotheek is gecompileerd in de Python bindingen binair (_sqlite3.cpython-36m-darwin.so ). Als je het wilt vervangen, heb je waarschijnlijk de Python-broncode nodig die overeenkomt met je geïnstalleerde Python-installatie (bijv. 3.7.6 of welke versie u ook gebruikt). Compileer Python vanaf de bron met behulp van het macOS-buildscript. Dit script omvat het downloaden en bouwen van de C-bibliotheek van SQLite, dus zorg ervoor dat u het script bewerkt om naar de meest recente SQLite-versie te verwijzen. Gebruik ten slotte het gecompileerde bindingsbestand (bijv. _sqlite3.cpython-37m-darwin.so ), om de verouderde te vervangen.

Werken met timezone-aware datetime objecten

De meeste Python-ontwikkelaars gebruiken meestal datetime objecten bij het werken met tijdstempels. Er zijn naïeve datetime objecten die hun tijdzone niet kennen, en niet-naïef die tijdzone-bewust zijn . Het is bekend dat Python's datetime module is eigenzinnig, waardoor het moeilijk is om zelfs tijdzonebewuste datetime.datetime te maken voorwerpen. Bijvoorbeeld de aanroep datetime.datetime.utcnow() creëert een naïeve object, wat contra-intuïtief is voor ontwikkelaars die nieuw zijn in de datetime API's, in de verwachting dat Python de UTC-tijdzone gebruikt! Bibliotheken van derden, zoals python-dateutil, vergemakkelijken deze taak. Om een ​​tijdzonebewust object te maken, kunt u een code als deze gebruiken:

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

Helaas is de officiële Python-documentatie van de sqlite3 module is misleidend als het gaat om het verwerken van tijdstempels. Zoals hier beschreven, datetime objecten worden automatisch geconverteerd bij gebruik van PARSE_DECLTYPES (en het declareren van een TIMESTAMP kolom). Hoewel dit technisch correct is, zal de conversie verliezen de tijdzone informatie ! Bijgevolg, als u daadwerkelijk tijdzone-bewust . gebruikt datetime.datetime objecten, moet u uw eigen converters registreren , die tijdzone-informatie behouden, als volgt:

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)

Zoals je kunt zien, wordt de tijdstempel gewoon opgeslagen als TEXT uiteindelijk. Er is geen echt gegevenstype "date" of "datetime" in SQLite.

Transacties en automatisch vastleggen

Python's sqlite3 module legt niet automatisch gegevens vast die zijn gewijzigd door uw zoekopdrachten . Wanneer u query's uitvoert die op de een of andere manier de database wijzigen, moet u ofwel een expliciete COMMIT . geven statement, of u gebruikt de verbinding als contextmanager object, zoals weergegeven in het volgende voorbeeld:

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

Nadat het bovenstaande blok was verlaten, sqlite3 roept impliciet connection.commit() . aan , maar doet dit alleen als er een transactie aan de gang is . DML-instructies (Data Modification Language) starten automatisch een transactie, maar zoekopdrachten met betrekking tot DROP of CREATE TABLE / INDEX statements niet, omdat ze volgens de documentatie niet meetellen als DML. Dit is contra-intuïtief, omdat deze uitspraken duidelijk gegevens wijzigen.

Dus, als u een DROP . uitvoert of CREATE TABLE / INDEX instructies in de contextmanager, is het een goede gewoonte om expliciet een BEGIN TRANSACTION uit te voeren verklaring eerst , zodat de contextmanager daadwerkelijk connection.commit() . aanroept voor jou.

Verwerking van 64-bits gehele getallen

In een vorig artikel heb ik al besproken dat SQLite problemen heeft met grote gehele getallen die kleiner zijn dan -2^63 , of groter of gelijk aan 2^63 . Als u ze probeert te gebruiken in queryparameters (met de ? symbool), Python's sqlite3 module geeft een OverflowError: Python int too large to convert to SQLite INTEGER te converteren , die u beschermt tegen onbedoeld gegevensverlies.

Om zeer grote gehele getallen goed te kunnen verwerken, moet u:

  1. Gebruik de TEXT type voor de corresponderende tabelkolom, en
  2. Converteer het getal naar str al in Python , voordat u het als parameter gebruikt.
  3. Converteer de strings terug naar int in Python, wanneer SELECT ing gegevens

Conclusie

Python's officiële sqlite3 module is een uitstekende binding met SQLite. Ontwikkelaars die nieuw zijn met SQLite moeten echter begrijpen dat er een verschil is tussen de Python-bindingen en de onderliggende SQLite C-bibliotheek. Er schuilt gevaar in de schaduw, vanwege versieverschillen van SQLite. Deze kunnen zelfs gebeuren als u dezelfde . uitvoert Python-versie op twee verschillende machines, omdat de SQLite C-bibliotheek mogelijk nog steeds een andere versie gebruikt. Ik heb ook andere problemen besproken, zoals het omgaan met datetime-objecten en het voortdurend wijzigen van gegevens met behulp van transacties. Ik was er zelf niet van op de hoogte, waardoor gebruikers van mijn applicaties gegevens verloren gingen, dus ik hoop dat u dezelfde fouten kunt vermijden als ik heb gemaakt.


  1. De beste manier om periodiek Oracle-query's uit te voeren

  2. MayBeSQL komt naar Microsoft Access!

  3. Een lijst met alle talen in SQL Server (T-SQL) krijgen

  4. POSTGRESQL Foreign Key verwijzend naar primaire sleutels van twee verschillende tabellen