Skip to content

Commit

Permalink
feat: pass parameters natively via http interface along the query
Browse files Browse the repository at this point in the history
  • Loading branch information
simPod committed Jan 17, 2024
1 parent 37523eb commit f78ed5b
Show file tree
Hide file tree
Showing 22 changed files with 693 additions and 51 deletions.
9 changes: 6 additions & 3 deletions src/Client/ClickHouseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
use Psr\Http\Client\ClientExceptionInterface;
use SimPod\ClickHouseClient\Exception\CannotInsert;
use SimPod\ClickHouseClient\Exception\ServerError;
use SimPod\ClickHouseClient\Exception\UnsupportedValue;
use SimPod\ClickHouseClient\Exception\UnsupportedParamType;
use SimPod\ClickHouseClient\Exception\UnsupportedParamValue;
use SimPod\ClickHouseClient\Format\Format;
use SimPod\ClickHouseClient\Output\Output;

Expand All @@ -27,7 +28,8 @@ public function executeQuery(string $query, array $settings = []): void;
*
* @throws ClientExceptionInterface
* @throws ServerError
* @throws UnsupportedValue
* @throws UnsupportedParamType
* @throws UnsupportedParamValue
*/
public function executeQueryWithParams(string $query, array $params, array $settings = []): void;

Expand All @@ -53,7 +55,8 @@ public function select(string $query, Format $outputFormat, array $settings = []
*
* @throws ClientExceptionInterface
* @throws ServerError
* @throws UnsupportedValue
* @throws UnsupportedParamType
* @throws UnsupportedParamValue
*
* @template O of Output
*/
Expand Down
31 changes: 31 additions & 0 deletions src/Client/Http/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use SimPod\ClickHouseClient\Exception\UnsupportedParamType;
use SimPod\ClickHouseClient\Param\ParamValueConverterRegistry;
use SimPod\ClickHouseClient\Sql\Type;

use function array_keys;
use function array_reduce;
use function http_build_query;
use function is_string;
use function preg_match_all;
use function SimPod\ClickHouseClient\absurd;

use const PHP_QUERY_RFC3986;
Expand All @@ -23,6 +29,7 @@ final class RequestFactory

/** @throws InvalidArgumentException */
public function __construct(
private ParamValueConverterRegistry $paramValueConverterRegistry,
private RequestFactoryInterface $requestFactory,
UriFactoryInterface|null $uriFactory = null,
UriInterface|string $uri = '',
Expand All @@ -40,6 +47,7 @@ public function __construct(
$this->uri = $uri;
}

/** @throws UnsupportedParamType */
public function prepareRequest(RequestOptions $requestOptions): RequestInterface
{
$query = http_build_query(
Expand All @@ -62,7 +70,30 @@ public function prepareRequest(RequestOptions $requestOptions): RequestInterface

$request = $this->requestFactory->createRequest('POST', $uri);

preg_match_all('~\{([a-zA-Z\d]+):([a-zA-Z\d ]+(\(.+\))?)}~', $requestOptions->sql, $matches);

$typeToParam = array_reduce(
array_keys($matches[1]),
static function (array $acc, string|int $k) use ($matches) {
$acc[$matches[1][$k]] = Type::fromString($matches[2][$k]);

return $acc;
},
[],
);

$streamElements = [['name' => 'query', 'contents' => $requestOptions->sql]];
foreach ($requestOptions->params as $name => $value) {
$type = $typeToParam[$name] ?? null;
if ($type === null) {
continue;
}

$streamElements[] = [
'name' => 'param_' . $name,
'contents' => $this->paramValueConverterRegistry->get($type)($value, $type, false),
];
}

try {
$body = new MultipartStream($streamElements);
Expand Down
9 changes: 7 additions & 2 deletions src/Client/Http/RequestOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ final class RequestOptions
public array $settings;

/**
* @param array<string, mixed> $params
* @param array<string, float|int|string> $defaultSettings
* @param array<string, float|int|string> $querySettings
*/
public function __construct(public string $sql, array $defaultSettings, array $querySettings)
{
public function __construct(
public string $sql,
public array $params,
array $defaultSettings,
array $querySettings,
) {
$this->settings = $querySettings + $defaultSettings;
}
}
10 changes: 8 additions & 2 deletions src/Client/PsrClickHouseAsyncClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,31 @@ public function selectWithParams(
$sql
$formatClause
CLICKHOUSE,
$settings,
static fn (ResponseInterface $response): Output => $outputFormat::output($response->getBody()->__toString())
params: $params,
settings: $settings,
processResponse: static fn (ResponseInterface $response): Output => $outputFormat::output(
$response->getBody()->__toString(),
)
);
}

/**
* @param array<string, mixed> $params
* @param array<string, float|int|string> $settings
* @param (callable(ResponseInterface):mixed)|null $processResponse
*
* @throws Exception
*/
private function executeRequest(
string $sql,
array $params,
array $settings = [],
callable|null $processResponse = null,
): PromiseInterface {
$request = $this->requestFactory->prepareRequest(
new RequestOptions(
$sql,
$params,
$this->defaultSettings,
$settings,
),
Expand Down
56 changes: 38 additions & 18 deletions src/Client/PsrClickHouseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
use SimPod\ClickHouseClient\Client\Http\RequestOptions;
use SimPod\ClickHouseClient\Exception\CannotInsert;
use SimPod\ClickHouseClient\Exception\ServerError;
use SimPod\ClickHouseClient\Exception\UnsupportedValue;
use SimPod\ClickHouseClient\Exception\UnsupportedParamType;
use SimPod\ClickHouseClient\Exception\UnsupportedParamValue;
use SimPod\ClickHouseClient\Format\Format;
use SimPod\ClickHouseClient\Output\Output;
use SimPod\ClickHouseClient\Sql\Escaper;
Expand Down Expand Up @@ -46,13 +47,18 @@ public function __construct(

public function executeQuery(string $query, array $settings = []): void
{
$this->executeRequest($query, settings: $settings);
try {
$this->executeRequest($query, params: [], settings: $settings);
} catch (UnsupportedParamType) {
absurd();
}
}

public function executeQueryWithParams(string $query, array $params, array $settings = []): void
{
$this->executeRequest(
$this->sqlFactory->createWithParameters($query, $params),
params: $params,
settings: $settings,
);
}
Expand All @@ -61,7 +67,7 @@ public function select(string $query, Format $outputFormat, array $settings = []
{
try {
return $this->selectWithParams($query, params: [], outputFormat: $outputFormat, settings: $settings);
} catch (UnsupportedValue) {
} catch (UnsupportedParamValue | UnsupportedParamType) {
absurd();
}
}
Expand All @@ -77,6 +83,7 @@ public function selectWithParams(string $query, array $params, Format $outputFor
$sql
$formatClause
CLICKHOUSE,
params: $params,
settings: $settings,
);

Expand Down Expand Up @@ -112,14 +119,19 @@ public function insert(string $table, array $values, array|null $columns = null,

$table = Escaper::quoteIdentifier($table);

$this->executeRequest(
<<<CLICKHOUSE
INSERT INTO $table
$columnsSql
VALUES $valuesSql
CLICKHOUSE,
settings: $settings,
);
try {
$this->executeRequest(
<<<CLICKHOUSE
INSERT INTO $table
$columnsSql
VALUES $valuesSql
CLICKHOUSE,
params: [],
settings: $settings,
);
} catch (UnsupportedParamType) {
absurd();
}
}

public function insertWithFormat(string $table, Format $inputFormat, string $data, array $settings = []): void
Expand All @@ -128,25 +140,33 @@ public function insertWithFormat(string $table, Format $inputFormat, string $dat

$table = Escaper::quoteIdentifier($table);

$this->executeRequest(
<<<CLICKHOUSE
INSERT INTO $table $formatSql $data
CLICKHOUSE,
settings: $settings,
);
try {
$this->executeRequest(
<<<CLICKHOUSE
INSERT INTO $table $formatSql $data
CLICKHOUSE,
params: [],
settings: $settings,
);
} catch (UnsupportedParamType) {
absurd();
}
}

/**
* @param array<string, mixed> $params
* @param array<string, float|int|string> $settings
*
* @throws ServerError
* @throws ClientExceptionInterface
* @throws UnsupportedParamType
*/
private function executeRequest(string $sql, array $settings): ResponseInterface
private function executeRequest(string $sql, array $params, array $settings): ResponseInterface
{
$request = $this->requestFactory->prepareRequest(
new RequestOptions(
$sql,
$params,
$this->defaultSettings,
$settings,
),
Expand Down
21 changes: 21 additions & 0 deletions src/Exception/UnsupportedParamType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace SimPod\ClickHouseClient\Exception;

use InvalidArgumentException;
use SimPod\ClickHouseClient\Sql\Type;

final class UnsupportedParamType extends InvalidArgumentException implements ClickHouseClientException
{
public static function fromType(Type $type): self
{
return new self($type->name);
}

public static function fromString(string $type): self
{
return new self($type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use function sprintf;
use function var_export;

final class UnsupportedValue extends InvalidArgumentException implements ClickHouseClientException
final class UnsupportedParamValue extends InvalidArgumentException implements ClickHouseClientException
{
public static function type(mixed $value): self
{
Expand Down
Loading

0 comments on commit f78ed5b

Please sign in to comment.