Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
2950707
feat(clickhouse): INSERT ... FORMAT JSONEachRow on the builder
lohanidamodar May 17, 2026
a6c3497
feat(clickhouse): emit trailing SETTINGS clause on DELETE
lohanidamodar May 17, 2026
6053768
feat(clickhouse): CREATE/DROP MATERIALIZED VIEW ... TO target
lohanidamodar May 17, 2026
2edfb26
feat(clickhouse): groupByTimeBucket compiles to toStartOf* dialect fu…
lohanidamodar May 17, 2026
7b211bc
feat(clickhouse): named-typed `{name:Type}` placeholder bindings
lohanidamodar May 17, 2026
c77f228
fix(clickhouse): support lightweight DELETE FROM alongside ALTER TABL…
lohanidamodar May 17, 2026
5cbe988
fix(clickhouse): allow underscores in INSERT FORMAT name
lohanidamodar May 18, 2026
fed48b1
fix(clickhouse): accept nested parameterized types in withParamType
lohanidamodar May 18, 2026
8b62154
fix(clickhouse): reset clears namedBindings and paramTypes
lohanidamodar May 18, 2026
d88f8dc
fix(clickhouse): preserve format metadata across FormattedInsertState…
lohanidamodar May 18, 2026
8742417
docs(clickhouse): flag raw-string body of createMaterializedView as c…
lohanidamodar May 18, 2026
600beff
refactor(clickhouse): wire Binding value object through the rewriter
lohanidamodar May 18, 2026
e901374
refactor(clickhouse): factor materialized views into Feature/Trait
lohanidamodar May 18, 2026
47af52f
test(clickhouse): move new builder + schema tests under Feature/Click…
lohanidamodar May 18, 2026
466f0ce
refactor(builder): store binding column hint at bind time, drop paral…
lohanidamodar May 19, 2026
0af2688
refactor(schema): promote MaterializedViews feature to cross-dialect …
lohanidamodar May 19, 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
77 changes: 76 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1472,7 +1472,55 @@ Query::containsString('tags', ['php']); // position(`tags`, ?) > 0

**Regex** — uses `match()` function instead of `REGEXP`.

**UPDATE/DELETE** — compiles to `ALTER TABLE ... UPDATE/DELETE` with mandatory WHERE:
**Time bucketing** — groups rows into fixed-width windows on a timestamp column. Allowed intervals: `1m`, `5m`, `15m`, `1h`, `1d`, `1w`, `1M`. Compiles to `toStartOfMinute / toStartOfFiveMinutes / toStartOfFifteenMinutes / toStartOfHour / toStartOfDay / toStartOfWeek / toStartOfMonth`:

```php
$result = (new Builder())
->from('events')
->selectRaw('toStartOfHour(`time`) AS `bucket`')
->count('*', 'cnt')
->groupByTimeBucket('time', '1h')
->orderByRaw('`bucket` ASC')
->build();

// SELECT COUNT(*) AS `cnt`, toStartOfHour(`time`) AS `bucket`
// FROM `events`
// GROUP BY toStartOfHour(`time`)
// ORDER BY `bucket` ASC
```

Other dialects throw `UnsupportedException` from `compileGroupByTimeBucket`. Re-emit the bucket function via `selectRaw` / `orderByRaw` when you need to reference it in the SELECT list or ORDER BY (same pattern as `groupByRaw`).

**Named-typed bindings** — opt into ClickHouse `{name:Type}` placeholders for safe parameterization over the HTTP interface. Off by default; positional `?` placeholders remain the default and behave identically to every other dialect:

```php
$result = (new Builder())
->useNamedBindings()
->withParamTypes([
'time' => 'DateTime64(3)',
'tenant' => 'String',
'value' => 'Int64',
])
->from('events')
->filter([
Query::greaterThan('time', '2024-01-01 00:00:00'),
Query::equal('tenant', ['acme']),
Query::lessThanEqual('value', 100),
])
->build();

// SELECT * FROM `events`
// WHERE `time` > {param0:DateTime64(3)}
// AND `tenant` IN ({param1:String})
// AND `value` <= {param2:Int64}

$result->namedBindings;
// ['param0' => '2024-01-01 00:00:00', 'param1' => 'acme', 'param2' => 100]
```

Unregistered columns fall through to value-based inference: `int → Int64`, `float → Float64`, `bool → UInt8`, `null → Nullable(String)`, `DateTimeInterface → DateTime64(3)`, everything else → `String`. Register types via `withParamType($column, $type)` or `withParamTypes($map)` whenever the inference rule doesn't match the column's ClickHouse declaration. The positional `$bindings` array is still exposed on the resulting `Statement` for callers that prefer it.

**UPDATE** — compiles to `ALTER TABLE ... UPDATE` with mandatory WHERE:

```php
$result = (new Builder())
Expand All @@ -1484,6 +1532,31 @@ $result = (new Builder())
// ALTER TABLE `events` UPDATE `status` = ? WHERE `created_at` < ?
```

**DELETE** — two forms. `delete()` defaults to the lightweight `DELETE FROM …` form, which marks rows deleted via a mask and is async by default. Opt into the heavier mutation form (`ALTER TABLE … DELETE`) when you need parts rewritten on disk; the two are not interchangeable, so the builder never auto-translates between them.

```php
// Lightweight (default) — pair with `lightweight_deletes_sync = 0` for async
$result = (new Builder())
->from('audit_log')
->settings(['lightweight_deletes_sync' => '0'])
->filter([Query::lessThan('time', '2024-01-01 00:00:00')])
->delete();

// DELETE FROM `audit_log` WHERE `time` < ? SETTINGS lightweight_deletes_sync=0

// Mutation — opt in. Pair with `mutations_sync = 0` for async
$result = (new Builder())
->from('audit_log')
->deleteMode(Builder::DELETE_MODE_MUTATION)
->settings(['mutations_sync' => '0'])
->filter([Query::lessThan('time', '2024-01-01 00:00:00')])
->delete();

// ALTER TABLE `audit_log` DELETE WHERE `time` < ? SETTINGS mutations_sync=0
```

The trailing `SETTINGS` clause is whatever the caller registers via `settings()` — the builder does not auto-pair a sync setting to a chosen delete mode.

> **Note:** Full-text search (`Query::search()`) is not supported in ClickHouse and throws `UnsupportedException`. The ClickHouse builder also forces all join filter hook conditions to WHERE placement, since ClickHouse does not support subqueries in JOIN ON.

### MongoDB
Expand Down Expand Up @@ -1726,6 +1799,8 @@ Unsupported features are not on the class — consumers type-hint the interface
| ARRAY JOIN | | | | | | | x | |
| ASOF JOIN (typed operator) | | | | | | | x | |
| WITH FILL | | | | | | | x | |
| `groupByTimeBucket` | | | | | | | x | |
| Named-typed `{name:Type}` bindings | | | | | | | x | |
| Approximate Aggregates (incl. `quantiles`) | | | | | | | x | |
| Upsert (Mongo-style) | | | | | | | | x |
| Full-Text Search (Mongo) | | | | | | | | x |
Expand Down
Loading
Loading