-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added configuration for using specific SQLite versions. #50
base: main
Are you sure you want to change the base?
Changes from all commits
3bd0e30
d584e91
975b9d3
9b59095
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,11 @@ | ||
PYTHON_IMPLEMENTATION=python | ||
PYTHON_VERSION=3.10 | ||
PYTHON_VERSION=3.12 | ||
MARIADB_VERSION=10.5 | ||
MYSQL_VERSION=8.0 | ||
ORACLE_VERSION=23.5.0.0 | ||
POSTGRESQL_VERSION=14 | ||
POSTGIS_VERSION=3.1 | ||
SQLITE_VERSION= | ||
SQLITE_CFLAGS="-DSQLITE_ENABLE_DESERIALIZE \ | ||
-DSQLITE_ENABLE_JSON1 \ | ||
-DSQLITE_MAX_VARIABLE_NUMBER=32766" | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,7 +36,7 @@ Tooling and test execution support for [Django][0] :unicorn: | |
3. Build the image: | ||
|
||
```console | ||
$ docker compose build sqlite | ||
$ docker compose build base | ||
``` | ||
|
||
4. Run the tests: | ||
|
@@ -193,7 +193,7 @@ environment variables: | |
| ----------------------- | ------------- | ---------------------------------------------------- | | ||
| `DJANGO_PATH` | `../django` | Path to the Django repostory on your local machine | | ||
| `PYTHON_IMPLEMENTATION` | `python` | Implementation of Python to use — `python` or `pypy` | | ||
| `PYTHON_VERSION` | `3.10` | Version of Python container image to use | | ||
| `PYTHON_VERSION` | `3.12` | Version of Python container image to use | | ||
|
||
The versions of various backend services can be switched by setting these environment variables: | ||
|
||
|
@@ -204,15 +204,21 @@ The versions of various backend services can be switched by setting these enviro | |
| `ORACLE_VERSION` | `23.5.0.0` | Version of Oracle container image to use | | ||
| `POSTGRESQL_VERSION` | `14` | Version of PostgreSQL container image to use | | ||
| `POSTGIS_VERSION` | `3.1` | Version of PostGIS extension to use | | ||
| `SQLITE_VERSION` | | Version of SQLite to compile and use | | ||
|
||
> [!NOTE] | ||
> | ||
> If left unspecified, the SQLite version provided by Debian will be used. | ||
> Using a specific SQLite version requires compiling it from source. For more | ||
> details, see [SQLite Versions](#SQLite-Versions). | ||
|
||
### Python Versions | ||
|
||
The `PYTHON_VERSION` environment variable controls which version of Python you | ||
are running the tests against, e.g. | ||
|
||
```console | ||
$ PYTHON_VERSION=3.10 docker compose run --rm sqlite | ||
$ PYTHON_VERSION=3.12 docker compose run --rm sqlite | ||
``` | ||
|
||
In addition, it's possible to select a different implementation of Python, i.e. | ||
|
@@ -229,7 +235,8 @@ restrictions with respect to the range of versions available. | |
### Database Versions | ||
|
||
Most database container images are pulled from [Docker Hub][2]. Oracle database | ||
is pulled from the [Oracle Container Registry][3]. | ||
is pulled from the [Oracle Container Registry][3]. Specific versions of SQLite | ||
are compiled directly from the tags in the [official Git mirror][11]. | ||
|
||
You can switch the version of the database you test against by changing the | ||
appropriate environment variable. Available options and their defaults can be | ||
|
@@ -273,6 +280,46 @@ To determine what database versions can be used you can check the release notes | |
for the branch of Django that you have checked out, or alternatively there is | ||
the [supported database versions][4] page on Django's Trac Wiki. | ||
|
||
#### SQLite Versions | ||
|
||
SQLite is normally bundled in the Python installation using the version | ||
available on the system where Python is compiled. We use the Python Docker image | ||
based on Debian `bookworm`, which has SQLite 3.40.1. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated https://code.djangoproject.com/wiki/SupportedDatabaseVersions to better reflect our policy for SQLite. |
||
|
||
To use a different version, we compile SQLite from source and load the library | ||
dynamically using `LD_PRELOAD`. There are a few caveats as a result: | ||
|
||
- Some SQLite features are only available if certain flags are set during | ||
compilation. SQLite is known to change these flags in newer releases, such as | ||
to enable features by default that were previously opt-in. When Python is | ||
compiled, it inspects the system's SQLite to determine features that are | ||
included in the `sqlite` module. A mismatch in the module and the dynamically | ||
loaded library may result in Python failing to load, which may happen if we | ||
use an SQLite version that is older than the system version. | ||
- Debian and Ubuntu use a custom `CFLAGS` variable to compile their distributed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
SQLite. Historically, Django's CI has only been configured with SQLite | ||
versions that come with the operating system. If SQLite is compiled with | ||
different flags, some tests may fail. | ||
|
||
We currently work around the above caveats by setting the simplest `CFLAGS` | ||
value that allows all the tests to pass. To customize the `CFLAGS` used for the | ||
compilation, you can set the `SQLITE_CFLAGS` environment variable. See the | ||
[`.env`][10] file for its default value. | ||
|
||
``` | ||
SQLITE_VERSION=3.48.0 SQLITE_CFLAGS="-DSQLITE_OMIT_JSON -DSQLITE_MAX_VARIABLE_NUMBER=999" docker compose run --build --rm sqlite | ||
``` | ||
|
||
> [!NOTE] | ||
> | ||
> The `--build` argument is necessary if you've changed `SQLITE_CFLAGS` since | ||
> the last run, as it's not part of the image tag. You can also rebuild the | ||
> image separately by running `docker compose build sqlite`, optionally with | ||
> `--no-cache` to ignore the cached build. | ||
|
||
In the future, the Django codebase may be more robust when tested against | ||
different SQLite configurations and the `CFLAGS` workaround may no longer be | ||
necessary. | ||
|
||
### Other Versions | ||
|
||
|
@@ -309,3 +356,5 @@ with no promises that they'll be delivered: | |
[7]: https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/unit-tests/#running-the-unit-tests | ||
[8]: https://docs.djangoproject.com/en/stable/internals/contributing/writing-code/unit-tests/#running-the-selenium-tests | ||
[9]: https://docs.djangoproject.com/en/stable/ref/contrib/gis/testing/#geodjango-tests | ||
[10]: .env | ||
[11]: https://github.com/sqlite/sqlite |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,7 @@ x-base: &base | |
args: | ||
- PYTHON_IMPLEMENTATION=${PYTHON_IMPLEMENTATION} | ||
- PYTHON_VERSION=${PYTHON_VERSION} | ||
additional_contexts: | ||
additional_contexts: &additional-contexts | ||
src: ${DJANGO_PATH:-../django} | ||
volumes: | ||
- ${DJANGO_PATH:-../django}:/django/source:rw | ||
|
@@ -139,6 +139,10 @@ volumes: | |
|
||
services: | ||
|
||
# Base service to allow building the image with `docker compose build base`. | ||
base: | ||
<<: *base | ||
|
||
# Services: Databases | ||
|
||
mariadb-db: | ||
|
@@ -285,6 +289,38 @@ services: | |
|
||
sqlite: | ||
<<: *base | ||
image: django-docker-box:${PYTHON_IMPLEMENTATION}-${PYTHON_VERSION}-sqlite${SQLITE_VERSION} | ||
pull_policy: never | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't push the image to Docker Hub. This ensures doing Could also try |
||
build: | ||
context: . | ||
dockerfile_inline: | | ||
FROM django-docker-box:${PYTHON_IMPLEMENTATION}-${PYTHON_VERSION} | ||
SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-o", "xtrace", "-c"] | ||
# Only compile SQLite and set LD_PRELOAD if a version is specified. | ||
RUN <<EOF | ||
if [[ "${SQLITE_VERSION}" ]]; then | ||
export CFLAGS="${SQLITE_CFLAGS}" | ||
git clone --depth 1 --branch version-${SQLITE_VERSION} \ | ||
https://github.com/sqlite/sqlite.git /tmp/sqlite | ||
cd /tmp/sqlite | ||
./configure | ||
make | ||
if [ -f libsqlite3.so ]; then | ||
cp libsqlite3.so /tmp/ | ||
else | ||
cp .libs/libsqlite3.so /tmp/ | ||
fi | ||
rm -rf /tmp/sqlite | ||
Comment on lines
+308
to
+313
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "configure" script underwent major refactoring in 3.48.0: https://www.sqlite.org/releaselog/3_48_0.html As a result, the compiled library is placed directly in the source root. In older versions, it's placed inside a The |
||
fi | ||
EOF | ||
SHELL ["/bin/bash", "-c"] | ||
ENV LD_PRELOAD=${SQLITE_VERSION:+/tmp/libsqlite3.so} | ||
args: | ||
- PYTHON_IMPLEMENTATION=${PYTHON_IMPLEMENTATION} | ||
- PYTHON_VERSION=${PYTHON_VERSION} | ||
- SQLITE_VERSION=${SQLITE_VERSION} | ||
- SQLITE_CFLAGS=${SQLITE_CFLAGS} | ||
additional_contexts: *additional-contexts | ||
depends_on: | ||
<<: *depends-on-caches | ||
environment: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,4 +14,5 @@ libpq-dev | |
libproj-dev | ||
libsqlite3-mod-spatialite | ||
pkg-config | ||
tcl-dev | ||
unzip |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I explained this before in more detail in django/django#18899 (comment), but here's a breakdown of each flag:
SQLITE_ENABLE_DESERIALIZE
This flag was not enabled by default until SQLite 3.36. Without this flag, you'll encounter an error like pyenv/pyenv#2625.
serialize()
(< 3.11), or by usingbullseye
(which ships with 3.34.1 and thus Python 3.12 wasn't built to have SQLite deserialize functions). Or, of course, compiling Python from source ourselves.SQLITE_ENABLE_JSON1
This flag was not enabled by default until SQLite 3.38.
We have the following test that fails if you run it with a database backend that does not support
JSONField
, because it's missing the@skipUnlessDBFeature
decorator:https://github.com/django/django/blob/8a6b4175d790424312965ec77e4e9b072fba188b/tests/schema/tests.py#L2425-L2443
You can verify this by setting
SQLITE_VERSION
to a version < 3.38 and removing-DSQLITE_ENABLE_JSON1
fromSQLITE_CFLAGS
, or by using a version >= 3.38 and adding-DSQLITE_OMIT_JSON
.Happy to raise a ticket and PR for that.
Edit: I've raised a ticket https://code.djangoproject.com/ticket/36156#ticket
SQLITE_MAX_VARIABLE_NUMBER
There are a few tests that would fail when it does
ContentType.objects.all().delete()
due to a row count query exceeding the variable limit. This only happens when the whole test suite is run, but not when the failing tests are run in isolation (likely because other tests would create moreContentType
instances).This flag defaulted to 999 in < 3.32 (and 32766 in >= 3.32.0).
This is something that Debian/Ubuntu has customized, even in 3.31.
Related: