Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ composer.lock
vendor/
var/
phpunit
temp/
temp/
/.phpunit.result.cache
/tests/docker-clickhouse-21/
/tests/docker-clickhouse-latest/
54 changes: 53 additions & 1 deletion doc/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ $db->insert('table', [

## Date & Time Types

### Date and DateTime

```php
use ClickHouseDB\Type\Date;
use ClickHouseDB\Type\DateTime;

Date::fromString('2024-02-29');
Date::fromDateTime(new DateTimeImmutable('2024-02-29 12:00:00'));
DateTime::fromString('2024-02-29 12:00:00');
DateTime::fromDateTime(new DateTimeImmutable('2024-02-29 12:00:00+00:00'), 'Europe/Amsterdam');
```

The optional timezone converts PHP date/time objects before formatting, without
mutating them. Use the timezone of the destination column (or native parameter).
Without it, the object's timezone is preserved. String factories preserve the
input exactly; ClickHouse validates date ranges and parses strings in the column's timezone.

### DateTime64

Sub-second precision timestamps (milliseconds, microseconds, nanoseconds).
Expand All @@ -64,8 +81,10 @@ $db->insert('table', [
[DateTime64::fromDateTime($dt, 3)], // → '2024-06-15 12:00:00.456'
], ['created_at']);

// Precision options: 1-9 (1=tenths, 3=ms, 6=μs, 9=ns)
// Precision options: 0-9 (1=tenths, 3=ms, 6=μs, 9=ns)
DateTime64::fromDateTime($dt, 6); // → '2024-06-15 12:00:00.456789'
// PHP date/time objects provide at most 6 fractional digits.
// Use fromString() to preserve an existing nanosecond timestamp.
```

### Date32
Expand Down Expand Up @@ -112,6 +131,39 @@ $db->insert('table', [

## String Types

### StringType and FixedString

PHP reserves the name `String`, so the wrapper is named `StringType`.

```php
use ClickHouseDB\Type\StringType;
use ClickHouseDB\Type\FixedString;

StringType::fromString("it's a string");
FixedString::fromString('a', 1); // For a FixedString(1) column.
FixedString::fromString('ab', 2); // For a FixedString(2) column.
```

`FixedString` requires a positive length in bytes and a value with exactly that
byte length. Both shorter and longer values are rejected.
These wrappers retain raw values in `getValue()`, `__toString()`, and `$value`;
use bindings or `insert()` to escape them safely, rather than SQL interpolation.

### Enum8 and Enum16

```php
use ClickHouseDB\Type\Enum8;
use ClickHouseDB\Type\Enum16;

Enum8::fromString('active');
Enum16::fromString('pending');
```

These wrappers represent enum labels. Define the label-to-number mapping in the
column or native parameter type, e.g. `Enum8('active' = 1, 'inactive' = 2)`.
ClickHouse validates membership and the numeric range of that mapping.


### UUID

```php
Expand Down
7 changes: 7 additions & 0 deletions src/Quote/ValueFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@

use ClickHouseDB\Exception\UnsupportedValueType;
use ClickHouseDB\Query\Expression\Expression;
use ClickHouseDB\Type\StringableType;
use ClickHouseDB\Type\Type;
use DateTimeInterface;

use function addslashes;
use function is_bool;
use function is_callable;
use function is_float;
use function is_int;
use function is_object;
use function is_string;
use function property_exists;
use function sprintf;

class ValueFormatter
Expand All @@ -29,6 +32,10 @@ public static function formatValue(mixed $value, bool $addQuotes = true): mixed
return $value;
}

if ($value instanceof StringableType) {
return self::formatValue($value->getValue(), $addQuotes);
}

if ($value instanceof Type) {
return $value->getValue();
}
Expand Down
3 changes: 3 additions & 0 deletions src/Transport/Http.php
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,9 @@ private function convertParamValue(mixed $value): string
if ($value instanceof \ClickHouseDB\Type\IPv4 || $value instanceof \ClickHouseDB\Type\IPv6) {
return $value->value;
}
if ($value instanceof \ClickHouseDB\Type\StringableType) {
return $this->convertParamValue($value->getValue());
}
if ($value instanceof \ClickHouseDB\Type\MapType) {
return json_encode($value->value);
}
Expand Down
38 changes: 38 additions & 0 deletions src/Type/Date.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

use DateTimeInterface;
use Stringable;

final class Date implements DateType, Stringable
{
public string $value;

private function __construct(string $value)
{
$this->value = $value;
}

public static function fromString(string $value): self
{
return new self($value);
}

public static function fromDateTime(DateTimeInterface $dateTime): self
{
return new self($dateTime->format('Y-m-d'));
}

public function getValue(): string
{
return $this->value;
}

public function __toString(): string
{
return $this->value;
}
}
44 changes: 44 additions & 0 deletions src/Type/DateTime.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Stringable;

final class DateTime implements DateType, Stringable
{
public string $value;

private function __construct(string $value)
{
$this->value = $value;
}

public static function fromString(string $value): self
{
return new self($value);
}

public static function fromDateTime(DateTimeInterface $dateTime, ?string $timezone = null): self
{
if ($timezone !== null) {
$dateTime = DateTimeImmutable::createFromInterface($dateTime)->setTimezone(new DateTimeZone($timezone));
}

return new self($dateTime->format('Y-m-d H:i:s'));
}

public function getValue(): string
{
return $this->value;
}

public function __toString(): string
{
return $this->value;
}
}
9 changes: 9 additions & 0 deletions src/Type/DateType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

interface DateType extends StringableType
{
}
32 changes: 32 additions & 0 deletions src/Type/Enum16.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

use Stringable;

final class Enum16 implements EnumType, Stringable
{
public string $value;

private function __construct(string $value)
{
$this->value = $value;
}

public static function fromString(string $value): self
{
return new self($value);
}

public function getValue(): string
{
return $this->value;
}

public function __toString(): string
{
return $this->value;
}
}
32 changes: 32 additions & 0 deletions src/Type/Enum8.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

use Stringable;

final class Enum8 implements EnumType, Stringable
{
public string $value;

private function __construct(string $value)
{
$this->value = $value;
}

public static function fromString(string $value): self
{
return new self($value);
}

public function getValue(): string
{
return $this->value;
}

public function __toString(): string
{
return $this->value;
}
}
9 changes: 9 additions & 0 deletions src/Type/EnumType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

interface EnumType extends StringableType
{
}
39 changes: 39 additions & 0 deletions src/Type/FixedString.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

use InvalidArgumentException;
use Stringable;

use function strlen;

final class FixedString implements StringableType, Stringable
{
public string $value;

private function __construct(string $value)
{
$this->value = $value;
}

public static function fromString(string $value, int $length): self
{
if ($length < 1 || strlen($value) !== $length) {
throw new InvalidArgumentException('FixedString requires a positive byte length equal to the value length.');
}

return new self($value);
}

public function getValue(): string
{
return $this->value;
}

public function __toString(): string
{
return $this->value;
}
}
32 changes: 32 additions & 0 deletions src/Type/StringType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

use Stringable;

final class StringType implements StringableType, Stringable
{
public string $value;

private function __construct(string $value)
{
$this->value = $value;
}

public static function fromString(string $value): self
{
return new self($value);
}

public function getValue(): string
{
return $this->value;
}

public function __toString(): string
{
return $this->value;
}
}
11 changes: 11 additions & 0 deletions src/Type/StringableType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace ClickHouseDB\Type;

/** A raw string value that must be escaped when used as an SQL literal. */
interface StringableType extends Type
{
public function getValue(): string;
}
Loading
Loading