Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
775a152
docs: add ClickHouse migration driver design spec
kuaukutsu May 28, 2026
247b6f9
docs: add ClickHouse driver implementation plan
kuaukutsu May 28, 2026
5d39329
test: add ClickHouse test harness (docker compose, CI service, smoke …
kuaukutsu May 28, 2026
3413f32
test: address review (compose CLICKHOUSE_DB, guarded setup read and s…
kuaukutsu May 28, 2026
919ff5e
feat: add Mode enum (Mutation, AppendOnly)
kuaukutsu May 28, 2026
ac23fc3
feat: add Connection and no-op Transaction over ClickHouseDB\Client
kuaukutsu May 28, 2026
3293955
feat: add Mutation mode (ReplacingMergeTree command + setup.sql)
kuaukutsu May 28, 2026
8d77a36
test: cover down() dry-run; declare abstract command methods explicitly
kuaukutsu May 28, 2026
c9ca2b0
feat: add AppendOnly mode (journal command + setup.sql)
kuaukutsu May 28, 2026
e2d13b2
feat: add ClickHouse Driver wiring Mode to Command and setup path
kuaukutsu May 28, 2026
26e3830
refactor: document getSetupPath return and extract CONNECTION_TTL con…
kuaukutsu May 28, 2026
ff0535c
test: add end-to-end Migrator workflow tests for both modes
kuaukutsu May 28, 2026
03e0beb
test: drop unused Options import in MigratorMutationTest
kuaukutsu May 28, 2026
34ffa16
docs: add runnable ClickHouse migration example
kuaukutsu May 28, 2026
425b2df
docs: document ClickHouse driver usage and modes
kuaukutsu May 28, 2026
70a8b0a
chore: satisfy phpcs/psalm/phpstan across src, tests, example
kuaukutsu May 28, 2026
63db68d
test: kill Infection survivors; ignore equivalent config/cast mutants
kuaukutsu May 28, 2026
7e44a2d
test: kill JournalCommand ORDER BY mutant via full ordered fetchAppli…
kuaukutsu May 28, 2026
c133255
test: harden atime-ordering tests; document makeConnection ignore rat…
kuaukutsu May 28, 2026
d7d4d31
style: wrap long SQL literals in command tests under the 120-char limit
kuaukutsu May 28, 2026
7644f05
fix style
kuaukutsu May 28, 2026
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
3 changes: 3 additions & 0 deletions .docker/ClickHouse/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM clickhouse/clickhouse-server:26.3-alpine

COPY init.sh /docker-entrypoint-initdb.d/
3 changes: 3 additions & 0 deletions .docker/ClickHouse/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

clickhouse-client --user "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD" --query "CREATE DATABASE IF NOT EXISTS main;"
23 changes: 22 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ jobs:
name: unit tests
runs-on: ubuntu-latest

services:
clickhouse:
image: clickhouse/clickhouse-server:26.3-alpine
ports:
- 8123:8123
env:
CLICKHOUSE_DB: main
CLICKHOUSE_USER: default
CLICKHOUSE_PASSWORD: ""
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: "1"
options: >-
--health-cmd "wget --no-verbose --tries=1 --spider http://127.0.0.1:8123/ping || exit 1"
--health-interval 5s
--health-timeout 5s
--health-retries 20

strategy:
fail-fast: false
matrix:
Expand All @@ -23,7 +39,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: pdo xdebug
extensions: curl xdebug
coverage: xdebug
env:
fail-fast: true
Expand All @@ -39,3 +55,8 @@ jobs:
run: vendor/bin/testo
env:
XDEBUG_MODE: coverage
CLICKHOUSE_HOST: 127.0.0.1
CLICKHOUSE_PORT: "8123"
CLICKHOUSE_DB: main
CLICKHOUSE_USER: default
CLICKHOUSE_PASSWORD: ""
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

## [Unreleased]

### Added
- ClickHouse migration driver with two version-table strategies selectable via
`Mode` (`Mutation`, `AppendOnly`).
- Native `Driver(host, port, database, mode, username?, password?, options?)`
configuration over `smi2/phpclickhouse`.
- End-to-end Migrator workflow tests against a real ClickHouse (local docker
compose + CI service).
53 changes: 17 additions & 36 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ VERSION ?= $$(git rev-parse --verify HEAD)
USER = $$(id -u)
ARGS = $(filter-out $@,$(MAKECMDGOALS))
DOCKER_RUN = docker run --init -it --rm -u ${USER} -v "$$(pwd):/app" -w /app
DOCKER_COMPOSE = UID=$(USER) PHP_VERSION=$(PHP_VERSION) docker compose

# https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
.PHONY: help tests fix check
Expand Down Expand Up @@ -70,45 +71,25 @@ check: ## run analysis tools
make psalm
make phpstan

## Tests

infection:
docker build \
--build-arg PHP_VERSION=$(PHP_VERSION) \
--build-arg USER=$(USER) \
--build-arg WORKDIR=/app \
--target tests \
-t app_cli .docker/php/cli
- docker run --init -it --rm \
-u $(USER) \
-v "$$(pwd):/app" \
-w /app \
app_cli ./vendor/bin/infection \
--coverage=/app/runtime/coverage \
--threads=max \
--skip-initial-tests
docker image rm -f app_cli
$(DOCKER_COMPOSE) up -d --build --wait clickhouse
- $(DOCKER_COMPOSE) run --rm cli ./vendor/bin/infection \
--coverage=/app/runtime/coverage \
--threads=max \
--skip-initial-tests
$(DOCKER_COMPOSE) down -v

tests:
docker build \
--build-arg PHP_VERSION=$(PHP_VERSION) \
--build-arg USER=$(USER) \
--build-arg WORKDIR=/app \
--target tests \
-t app_cli .docker/php/cli
- docker run --init -it --rm \
-u $(USER) \
-v "$$(pwd):/app" \
-w /app \
app_cli ./vendor/bin/testo \
--coverage --log-junit=/app/runtime/coverage/junit.xml
- docker run --init -it --rm \
-u $(USER) \
-v "$$(pwd):/app" \
-w /app \
app_cli ./vendor/bin/infection \
--coverage=/app/runtime/coverage \
--threads=max \
--skip-initial-tests
docker image rm -f app_cli
$(DOCKER_COMPOSE) up -d --build --wait clickhouse
- $(DOCKER_COMPOSE) run --rm cli ./vendor/bin/testo \
--coverage --log-junit=/app/runtime/coverage/junit.xml
- $(DOCKER_COMPOSE) run --rm cli ./vendor/bin/infection \
--coverage=/app/runtime/coverage \
--threads=max \
--skip-initial-tests
$(DOCKER_COMPOSE) down -v

## Application

Expand Down
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
# Database Migrator: ClickHouse

A [dbschemix](https://dbschemix.github.io/) migration driver for ClickHouse,
built on [smi2/phpclickhouse](https://github.com/smi2/phpClickHouse).

## Usage

```php
use dbschemix\clickhouse\Driver;
use dbschemix\clickhouse\Mode;
use dbschemix\core\Config;
use dbschemix\core\Migration;
use dbschemix\core\Migrator;

$driver = new Driver(
host: '127.0.0.1',
port: 8123,
database: 'main',
mode: Mode::Mutation, // or Mode::AppendOnly
username: 'default',
password: '',
// options: ['settings' => [...], 'https' => true, ...] // phpClickHouse passthrough
);

$migrator = new Migrator([
new Migration(path: __DIR__ . '/migration/main', driver: $driver, config: new Config()),
]);

$migrator->init(); // creates the version table
$migrator->up(); // applies pending migrations
```

### Modes

- `Mode::Mutation` — version table is a `ReplacingMergeTree`; reads use `FINAL`,
rollback removes the row via a synchronous `ALTER TABLE ... DELETE`.
- `Mode::AppendOnly` — append-only journal; every `up`/`down` appends a row and
the current state is derived with `argMax`. Nothing is physically deleted.

> ClickHouse has no transactions: `up`/`down` run the migration and the version
> bookkeeping as separate statements, so a failure between them cannot be rolled
> back. The target database must already exist.

### Static analysis

To run static analysis:
Expand All @@ -21,7 +62,7 @@ make fix
### Testing

The package is tested with
- [PHPUnit](https://phpunit.de/)
- [testo](https://php-testo.github.io/)
- [Infection](https://github.com/infection/infection)

To run tests:
Expand Down
53 changes: 53 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
x-default-logging: &default-logging
driver: local
options:
max-size: "5m"
max-file: "3"

name: dbschemix
services:
clickhouse:
container_name: dbschemix_clickhouse
build: .docker/ClickHouse
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.2'
memory: 256M
environment:
CLICKHOUSE_DB: main
CLICKHOUSE_USER: default
CLICKHOUSE_PASSWORD: ""
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: "1"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8123/ping"]
interval: 2s
timeout: 5s
retries: 30
logging: *default-logging

cli:
container_name: dbschemix_clickhouse_cli
build:
context: .docker/php/cli
target: tests
args:
PHP_VERSION: "${PHP_VERSION:-8.3}"
UID: "${UID:-10001}"
WORKDIR: /app
volumes:
- ".:/app"
working_dir: /app
environment:
CLICKHOUSE_HOST: clickhouse
CLICKHOUSE_PORT: "8123"
CLICKHOUSE_DB: main
CLICKHOUSE_USER: default
CLICKHOUSE_PASSWORD: ""
depends_on:
clickhouse:
condition: service_healthy
logging: *default-logging
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dbschemix/clickhouse",
"description": "dbschemix: database migration clickhouse",
"description": "dbschemix: clickhouse database migration driver",
"type": "library",
"license": "MIT",
"authors": [
Expand Down
Loading
Loading