sql >> Base de Datos >  >> RDS >> SQLite

Advertencias de Python y SQLite

SQLite es una base de datos relacional popular que usted integra en su aplicación. Python viene con enlaces oficiales a SQLite. Este artículo examina las advertencias sobre el uso de SQLite en Python. Demuestra los problemas que pueden causar las diferentes versiones de las bibliotecas SQLite vinculadas, cómo datetime los objetos no se almacenan correctamente, y cómo debe tener mucho cuidado al confiar en with connection de Python administrador de contexto para confirmar sus datos.

Introducción

SQLite es un popular sistema de base de datos relacional (DB) . A diferencia de sus hermanos mayores basados ​​en cliente-servidor, como MySQL, SQLite se puede integrar en su aplicación como una biblioteca . Python admite oficialmente SQLite a través de enlaces (documentos oficiales). Sin embargo, trabajar con esos enlaces no siempre es sencillo. Además de las advertencias genéricas de SQLite que mencioné anteriormente, existen varios problemas específicos de Python que examinaremos en este artículo .

Incompatibilidades de versión con el destino de implementación

Es bastante común que los desarrolladores creen y prueben el código en una máquina que es (muy) diferente a aquella en la que se implementa el código, en términos de sistema operativo (SO) y hardware. Esto causa tres tipos de problemas:

  • La aplicación se comporta de manera diferente debido a diferencias de SO o hardware . Por ejemplo, puede tener problemas de rendimiento cuando la máquina de destino tiene menos memoria que su máquina. O SQLite podría ejecutar algunas operaciones más lentamente en un sistema operativo que en otros, porque las API subyacentes del sistema operativo de bajo nivel que utiliza son diferentes.
  • La versión de SQLite en el destino de implementación difiere de la versión de la máquina del desarrollador . Esto puede causar problemas en ambas direcciones, porque se agregan nuevas características (y cambios de comportamiento) con el tiempo, consulte el registro de cambios oficial. Por ejemplo, una versión implementada de SQLite desactualizada puede carecer de funciones que funcionaron bien en el desarrollo. Además, una versión más nueva de SQLite en implementación puede comportarse de manera diferente a una versión anterior que usa en su máquina de desarrollo, p. cuando el equipo de SQLite cambia algunos valores predeterminados.
  • Es posible que los enlaces Python de SQLite o la biblioteca C falten por completo en el destino de implementación . Este es un Linux -problema específico de distribución . Las distribuciones oficiales de Windows y macOS contendrán un empaquetado versión de la biblioteca SQLite C. En Linux, la biblioteca SQLite es un paquete separado. Si compila Python usted mismo, p. porque usas un Debian/Raspbian/etc. distribución que se envía con versiones de características antiguas, la make de Python el script de compilación solo compilará los enlaces SQLite de Python if se detectó una biblioteca SQLite C instalada durante el proceso de compilación de Python . Si realiza una recompilación de Python usted mismo, debe asegurarse de que la biblioteca SQLite C instalada sea reciente . Este, nuevamente, no es el caso para Debian, etc. al instalar SQLite a través de apt , por lo que es posible que también deba compilar e instalar SQLite usted mismo, previamente para construir Python.

Para averiguar qué versión de la biblioteca SQLite C utiliza su intérprete de Python, ejecute este comando:

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

Reemplazando sqlite3.sqlite_version con sqlite3.version le dará la versión de los enlaces de SQLite de Python .

Actualización de la biblioteca SQLite C subyacente

Si desea beneficiarse de las funciones o las correcciones de errores de la versión más reciente de SQLite, tiene suerte. La biblioteca SQLite C generalmente está vinculada en tiempo de ejecución y, por lo tanto, se puede reemplazar sin ningún cambio en su intérprete de Python instalado. Los pasos concretos dependen de su sistema operativo (probado para Python 3.6+):

1) Ventanas: Descargue los binarios precompilados x86 o x64 desde la página de descarga de SQLite y reemplace el sqlite3.dll archivo encontrado en las DLLs carpeta de su instalación de Python con la que acaba de descargar.

2) Linux: desde la página de descarga de SQLite obtenga el autoconf fuentes, extraiga el archivo y ejecute ./configure && make && make install que instalará la biblioteca en /usr/local/lib por defecto.
Luego agregue la línea export LD_LIBRARY_PATH=/usr/local/lib al comienzo de la secuencia de comandos de shell que inicia su secuencia de comandos de Python, lo que obliga a su intérprete de Python a usar la biblioteca autoconstruida.

3) mac OS: de mi análisis, parece que la biblioteca SQLite C está compilada en los enlaces de Python binario (_sqlite3.cpython-36m-darwin.so ). Si desea reemplazarlo, es probable que necesite obtener el código fuente de Python que coincida con su instalación de Python instalada (por ejemplo, 3.7.6 o la versión que uses). Compile Python desde la fuente, utilizando el script de compilación de macOS. Este script incluye la descarga y la creación de la biblioteca C de SQLite, así que asegúrese de editar el script para hacer referencia a la versión más reciente de SQLite. Finalmente, use el archivo de enlaces compilado (por ejemplo, _sqlite3.cpython-37m-darwin.so ), para reemplazar el obsoleto.

Trabajar con timezoneaware datetime objetos

La mayoría de los desarrolladores de Python suelen utilizar datetime objetos cuando se trabaja con marcas de tiempo. Hay ingenuos datetime objetos que no conocen su zona horaria y no ingenuos que tienen en cuenta la zona horaria consciente . Es bien sabido que el datetime de Python El módulo es peculiar, lo que dificulta incluso la creación de datetime.datetime consciente de la zona horaria objetos. Por ejemplo, la llamada datetime.datetime.utcnow() crea un ingenuo objeto, que es contrario a la intuición para los desarrolladores que son nuevos en datetime API, ¡esperando que Python use la zona horaria UTC! Las bibliotecas de terceros, como python-dateutil, facilitan esta tarea. Para crear un objeto que reconozca la zona horaria, puede usar un código como este:

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

Desafortunadamente, la documentación oficial de Python del sqlite3 El módulo es engañoso cuando se trata de manejar las marcas de tiempo. Como se describe aquí, datetime los objetos se convierten automáticamente cuando se usa PARSE_DECLTYPES (y declarando un TIMESTAMP columna). Si bien esto es técnicamente correcto, la conversión perderá la zona horaria información ! En consecuencia, si en realidad está utilizando timezone-aware datetime.datetime objetos, debe registrar sus propios convertidores , que conservan la información de la zona horaria, de la siguiente manera:

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)

Como puede ver, la marca de tiempo se almacena como TEXT en el final. No existe un tipo de datos real de "fecha" o "fecha y hora" en SQLite.

Transacciones y compromiso automático

sqlite3 de Python el módulo no confirma automáticamente los datos modificados por sus consultas . Cuando realiza consultas que de alguna manera cambian la base de datos, debe emitir un COMMIT explícito declaración, o usa la conexión como administrador de contexto objeto, como se muestra en el siguiente ejemplo:

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

Una vez que se salió del bloque anterior, sqlite3 implícitamente llama a connection.commit() , pero solo lo hace si hay una transacción en curso . Las declaraciones DML (lenguaje de modificación de datos) inician automáticamente una transacción, pero las consultas que involucran DROP o CREATE TABLE / INDEX las declaraciones no lo hacen, porque no cuentan como DML según la documentación. Esto es contrario a la intuición, porque estas declaraciones claramente modifican los datos.

Por lo tanto, si ejecuta cualquier DROP o CREATE TABLE / INDEX declaraciones dentro del administrador de contexto, es una buena práctica ejecutar explícitamente un BEGIN TRANSACTION declaración primero , para que el administrador de contexto llame a connection.commit() para ti.

Manejo de enteros de 64 bits

En un artículo anterior, ya discutí que SQLite tiene problemas con números enteros grandes que son más pequeños que -2^63 , o mayor o igual que 2^63 . Si intenta usarlos en los parámetros de consulta (con el ? símbolo), sqlite3 de Python generará un OverflowError: Python int too large to convert to SQLite INTEGER , protegiéndolo de la pérdida accidental de datos.

Para manejar correctamente números enteros muy grandes, debe:

  1. Utilice el TEXT escriba para la columna de la tabla correspondiente, y
  2. Convertir el número a str ya en Python , antes de usarlo como parámetro.
  3. Convertir las cadenas de nuevo a int en Python, cuando SELECT ing datos

Conclusión

sqlite3 oficial de Python El módulo es un enlace excelente para SQLite. Sin embargo, los desarrolladores nuevos en SQLite deben comprender que existe una diferencia entre los enlaces de Python y la biblioteca SQLite C subyacente. Existe un peligro al acecho en las sombras, debido a las diferencias de versión de SQLite. Esto puede suceder incluso si ejecuta el mismo Versión de Python en dos máquinas diferentes, porque la biblioteca SQLite C aún podría estar usando una versión diferente. También discutí otros problemas, como el manejo de objetos de fecha y hora y el cambio persistente de datos mediante transacciones. Yo mismo no estaba al tanto de ellos, lo que causó la pérdida de datos para los usuarios de mis aplicaciones, así que espero que pueda evitar los mismos errores que cometí.