Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
52ced95
fix: pass DSN to clickhouse-client if configured
paddycarey Dec 10, 2024
574ed50
feat: #108 Queryset.iterator use clickhouse_driver.Client.execute_iter
jayvynl Dec 30, 2024
511f97c
chore: test for python3.13
jayvynl Dec 30, 2024
858c408
chore: run isort -c in code lint
jayvynl Dec 30, 2024
fcc9c41
docs: 1.3.2 changelog
jayvynl Dec 30, 2024
30291fa
Update tox.ini
jayvynl Dec 31, 2024
9500d1b
ci: fix Error: The version '3.7' with architecture 'x64' was not foun…
jayvynl Jan 10, 2025
c61bb19
refactor base merge tree engine to check Iterable value
RuslanSibgatulin Dec 13, 2024
e8f8718
refactor model fields to check Iterable value
RuslanSibgatulin Dec 30, 2024
493caa4
Code linting. Changelog updated
RuslanSibgatulin Jan 9, 2025
1facffe
Update base-test.yml
jayvynl Jan 10, 2025
6cc97e9
test(IPv6Field): fix test for IPv6Field.
jayvynl Jan 10, 2025
660bd77
Update base-test.yml
jayvynl Jan 10, 2025
c00b4c8
Update tests.py
jayvynl Jan 10, 2025
3eae948
Update tox.ini
jayvynl Jan 10, 2025
b9494c3
Update version to 1.3.2
jayvynl Jan 10, 2025
af15236
test: Setting columnar and use_numpy for given query
paxcodes Apr 2, 2025
405bdcf
feat: Allow setting of columnar and use_numpy for given_query
paxcodes Apr 2, 2025
b49e3b4
chore: Format code
paxcodes Apr 2, 2025
ec4cffe
test: Use numpy but not columnar format to improve test coverage
paxcodes Apr 2, 2025
2e43978
chore: Install numpy before running tests
paxcodes Apr 3, 2025
0272d44
chore: Rename method to set_query_execution_args
paxcodes Apr 3, 2025
cb955c4
chore: Install pandas
paxcodes Apr 3, 2025
8f0bee7
docs: Update changelog and add documentation on set_query_execution_args
paxcodes Apr 3, 2025
f9816df
Add additional clickhouse database datetime functions
AndrewSpeed May 29, 2025
4c2d9dd
Add changelog entry
AndrewSpeed May 29, 2025
d576a4a
Update CHANGELOG
AndrewSpeed Jun 2, 2025
493b422
Set explicit password for default user in attempt to fix intermittent…
AndrewSpeed Jun 2, 2025
42e23ca
feat: Django 5.2 Support #122
jayvynl Jun 4, 2025
faee0aa
Update release.yml
jayvynl Jun 5, 2025
2f90a00
Update release.yml
jayvynl Jun 5, 2025
4966dce
fix clickhouse_fields.tests.IPv6FieldTests
jayvynl Aug 5, 2025
df2908a
Added toYearWeek functionality
caitriona-cloudsmith Jul 28, 2025
c9ff3fd
Updating changelog
caitriona-cloudsmith Jul 28, 2025
59c1fe5
Formatting and linting
caitriona-cloudsmith Jul 28, 2025
0abe336
Support for distributed migrations (#130)
rcampos87 Sep 18, 2025
03b9adb
Fix for simultaneous queries issue when iterating (#133)
caitriona-cloudsmith Sep 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .github/workflows/base-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,29 @@ on:

jobs:
test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.12"]
django-version: ["3.2", "4.0", "4.1", "4.2", "5.0", "5.1"]
python-version: ["3.10", "3.13"]
django-version: ["3.2", "4.0", "4.1", "4.2", "5.0", "5.1", "5.2"]
clickhouse-version: ["23.8", "latest"]
include:
- python-version: "3.7"
django-version: "3.2"
- python-version: "3.8"
django-version: "3.2"
- python-version: "3.8"
django-version: "4.0"
- python-version: "3.8"
django-version: "4.1"
- python-version: "3.8"
django-version: "4.2"
exclude:
- python-version: "3.13"
django-version: "3.2"
- python-version: "3.13"
django-version: "4.0"

name: ClickHouse${{ matrix.clickhouse-version }} Python${{ matrix.python-version }} Django${{ matrix.django-version }}
steps:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
Expand All @@ -43,7 +43,7 @@ jobs:

steps:
- name: Download all the dists
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
Expand All @@ -64,12 +64,12 @@ jobs:

steps:
- name: Download all the dists
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v2.1.1
uses: sigstore/gh-action-sigstore-python@v3.0.0
with:
inputs: >-
./dist/*.tar.gz
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
### 1.5.0
- feat: #133: Fix simultaneous queries error when iteration is interrupted
- feat: #130: Add `distributed_migrations` database setting to support distributed migration queries.
- feat: #129: Add `toYearWeek` datetime functionality

### 1.4.0

- feat: #119 Allow query results returned in columns and deserialized to `numpy` objects
- feat: #125 Add database functions `toStartOfMinute`, `toStartOfFiveMinutes`, `toStartOfTenMinutes`, `toStartOfFifteenMinutes` and `toStartofHour`
Copy link
Preview

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: 'toStartofHour' should be 'toStartOfHour' (capital 'O').

Suggested change
- feat: #125 Add database functions `toStartOfMinute`, `toStartOfFiveMinutes`, `toStartOfTenMinutes`, `toStartOfFifteenMinutes` and `toStartofHour`
- feat: #125 Add database functions `toStartOfMinute`, `toStartOfFiveMinutes`, `toStartOfTenMinutes`, `toStartOfFifteenMinutes` and `toStartOfHour`

Copilot uses AI. Check for mistakes.

- feat: #122 Django 5.2 Support

### 1.3.2

- feat(aggragation-function): add anyLast function.
Copy link
Preview

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: 'aggragation' should be 'aggregation'.

Suggested change
- feat(aggragation-function): add anyLast function.
- feat(aggregation-function): add anyLast function.

Copilot uses AI. Check for mistakes.

- fix: pass DSN to clickhouse-client if configured.
- feat: #108 Queryset.iterator use clickhouse_driver.Client.execute_iter.
- chore: test for python3.13.
- refactor: Using collections.abc.Iterable instead of deprecated django.utils.itercompat.is_iterable

### 1.3.1

- fix: #99 update value containing "where" cause exception.
Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,7 @@ run test for all supported Python version and Django version:
```shell
tox
```

### Other

- Don't forget writing [Changelog](CHANGELOG.md)
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Read [Documentation](https://github.com/jayvynl/django-clickhouse-backend/blob/m
- Support most clickhouse data types.
- Support [SETTINGS in SELECT Query](https://clickhouse.com/docs/en/sql-reference/statements/select/#settings-in-select-query).
- Support [PREWHERE clause](https://clickhouse.com/docs/en/sql-reference/statements/select/prewhere).
- Support query results returned in columns and [deserialized to `numpy` objects](https://clickhouse-driver.readthedocs.io/en/latest/features.html#numpy-pandas-support).

**Notes:**

Expand Down Expand Up @@ -381,6 +382,60 @@ and [distributed table engine](https://clickhouse.com/docs/en/engines/table-engi
The following example assumes that a cluster defined by [docker compose in this repository](https://github.com/jayvynl/django-clickhouse-backend/blob/main/compose.yaml) is used.
This cluster name is `cluster`, it has 2 shards, every shard has 2 replica.

Query results returned as columns and/or deserialized into `numpy` objects
---

`clickhouse-driver` allows results to be returned as columns and/or deserialized into
`numpy` objects. This backend supports both options by using the context manager,
`Cursor.set_query_execution_args()`.

```python
import numpy as np
from django.db import connection

sql = """
SELECT toDateTime32('2022-01-01 01:00:05', 'UTC'), number, number*2.5
FROM system.numbers
LIMIT 3
"""
with connection.cursor() as cursorWrapper:
with cursorWrapper.cursor.set_query_execution_args(
columnar=True, use_numpy=True
) as cursor:
cursor.execute(sql)
np.testing.assert_equal(
cursor.fetchall(),
[
np.array(
[
np.datetime64("2022-01-01T01:00:05"),
np.datetime64("2022-01-01T01:00:05"),
np.datetime64("2022-01-01T01:00:05"),
],
dtype="datetime64[s]",
),
np.array([0, 1, 2], dtype=np.uint64),
np.array([0, 2.5, 5.0], dtype=np.float64),
],
)

cursor.execute(sql)
np.testing.assert_equal(
cursor.fetchmany(2),
[
np.array(
[
np.datetime64("2022-01-01T01:00:05"),
np.datetime64("2022-01-01T01:00:05"),
np.datetime64("2022-01-01T01:00:05"),
],
dtype="datetime64[s]",
),
np.array([0, 1, 2], dtype=np.uint64),
],
)
```

### Configuration

```python
Expand Down Expand Up @@ -470,6 +525,34 @@ Extra settings explanation:
Do not hardcode database name when you define replicated table or distributed table.
Because test database name is different from deployed database name.

#### Clickhouse cluster behind a load balancer

If your clickhouse cluster is running behind a load balancer, you can optionally set `distributed_migrations` to `True` under database OPTIONS.
Then a distributed migration table will be created on all nodes of the cluster, and all migration operations will be performed on this
distributed migrations table instead of a local migrations table. Otherwise, sequentially running migrations will have no effect on other nodes.

Configuration example:

```python
DATABASES = {
"default": {
"HOST": "clickhouse-load-balancer",
"PORT": 9000,
"ENGINE": "clickhouse_backend.backend",
"OPTIONS": {
"migration_cluster": "cluster",
"distributed_migrations": True,
"settings": {
"mutations_sync": 2,
"insert_distributed_sync": 1,
"insert_quorum": 2,
"alter_sync": 2,
},
},
}
}
```

### Model

`cluster` in `Meta` class will make models being created on cluster.
Expand Down
6 changes: 5 additions & 1 deletion clickhouse-config/node1/remote-servers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@
<replica>
<host>node1</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
<replica>
<host>node2</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>node3</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
<replica>
<host>node4</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
</shard>
</cluster>
</remote_servers>
</clickhouse>
</clickhouse>
6 changes: 5 additions & 1 deletion clickhouse-config/node2/remote-servers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@
<replica>
<host>node1</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
<replica>
<host>node2</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>node3</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
<replica>
<host>node4</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
</shard>
</cluster>
</remote_servers>
</clickhouse>
</clickhouse>
6 changes: 5 additions & 1 deletion clickhouse-config/node3/remote-servers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@
<replica>
<host>node1</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
<replica>
<host>node2</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>node3</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
<replica>
<host>node4</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
</shard>
</cluster>
</remote_servers>
</clickhouse>
</clickhouse>
6 changes: 5 additions & 1 deletion clickhouse-config/node4/remote-servers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@
<replica>
<host>node1</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
<replica>
<host>node2</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>node3</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
<replica>
<host>node4</host>
<port>9000</port>
<password>clickhouse_password</password>
</replica>
</shard>
</cluster>
</remote_servers>
</clickhouse>
</clickhouse>
2 changes: 1 addition & 1 deletion clickhouse_backend/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from clickhouse_backend.utils.version import get_version

VERSION = (1, 3, 1, "final", 0)
VERSION = (1, 4, 0, "final", 0)
Copy link
Preview

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version tuple indicates 1.4.0 while CHANGELOG documents 1.5.0 at the top. Align the package version with the latest documented release (or adjust the changelog) to avoid release confusion.

Suggested change
VERSION = (1, 4, 0, "final", 0)
VERSION = (1, 5, 0, "final", 0)

Copilot uses AI. Check for mistakes.


__version__ = get_version(VERSION)
11 changes: 11 additions & 0 deletions clickhouse_backend/backend/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS):
self.migration_cluster = self.settings_dict["OPTIONS"].pop(
"migration_cluster", None
)
self.distributed_migrations = self.settings_dict["OPTIONS"].pop(
"distributed_migrations", None
)
# https://clickhouse-driver.readthedocs.io/en/latest/quickstart.html#streaming-results
self.max_block_size = self.settings_dict["OPTIONS"].pop("max_block_size", 65409)
if not self.settings_dict["NAME"]:
self.settings_dict["NAME"] = "default"

Expand Down Expand Up @@ -224,6 +229,12 @@ def init_connection_state(self):
def create_cursor(self, name=None):
return self.connection.cursor()

@async_unsafe
def chunked_cursor(self):
cursor = self._cursor()
cursor.cursor.set_stream_results(True, self.max_block_size)
return cursor

def _savepoint(self, sid):
pass

Expand Down
28 changes: 16 additions & 12 deletions clickhouse_backend/backend/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,23 @@ def settings_to_cmd_args_env(cls, settings_dict, parameters):
user = settings_dict.get("USER")
passwd = settings_dict.get("PASSWORD")
secure = options.get("secure")
dsn = options.get("dsn")

if host:
args += ["-h", host]
if port:
args += ["--port", str(port)]
if user:
args += ["-u", user]
if passwd:
args += ["--password", passwd]
if dbname:
args += ["-d", dbname]
if secure:
args += ["--secure"]
if dsn:
args += [dsn]
else:
if host:
args += ["-h", host]
if port:
args += ["--port", str(port)]
if user:
args += ["-u", user]
if passwd:
args += ["--password", passwd]
if dbname:
args += ["-d", dbname]
if secure:
args += ["--secure"]
args.extend(parameters)
return args, None

Expand Down
Loading
Loading