Skip to content

Commit c464ebc

Browse files
committed
feat: add Client::insertPayload()
1 parent 6f69f20 commit c464ebc

12 files changed

+230
-60
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"require-dev": {
4040
"cdn77/coding-standard": "^7.0",
4141
"infection/infection": "^0.29.0",
42+
"kafkiansky/phpclick": "dev-master",
4243
"nyholm/psr7": "^1.2",
4344
"php-http/message-factory": "^1.1",
4445
"phpstan/extension-installer": "^1.1",

src/Client/ClickHouseClient.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace SimPod\ClickHouseClient\Client;
66

77
use Psr\Http\Client\ClientExceptionInterface;
8+
use Psr\Http\Message\StreamInterface;
89
use SimPod\ClickHouseClient\Exception\CannotInsert;
910
use SimPod\ClickHouseClient\Exception\ServerError;
1011
use SimPod\ClickHouseClient\Exception\UnsupportedParamType;
@@ -85,4 +86,20 @@ public function insert(string $table, array $values, array|null $columns = null,
8586
* @template O of Output
8687
*/
8788
public function insertWithFormat(string $table, Format $inputFormat, string $data, array $settings = []): void;
89+
90+
/**
91+
* @param array<string, float|int|string> $settings
92+
* @param list<string> $columns
93+
* @param Format<Output<mixed>> $inputFormat
94+
*
95+
* @throws ClientExceptionInterface
96+
* @throws ServerError
97+
*/
98+
public function insertPayload(
99+
string $table,
100+
Format $inputFormat,
101+
StreamInterface $payload,
102+
array $columns = [],
103+
array $settings = [],
104+
): void;
88105
}

src/Client/Http/RequestFactory.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@ public function __construct(
4949
$this->uri = $uri;
5050
}
5151

52-
/** @throws UnsupportedParamType */
53-
public function prepareRequest(RequestOptions $requestOptions): RequestInterface
54-
{
52+
/** @param array<string, mixed> $additionalOptions */
53+
public function initRequest(
54+
RequestSettings $requestSettings,
55+
array $additionalOptions = [],
56+
): RequestInterface {
5557
$query = http_build_query(
56-
$requestOptions->settings,
58+
$requestSettings->settings + $additionalOptions,
5759
'',
5860
'&',
5961
PHP_QUERY_RFC3986,
@@ -70,11 +72,20 @@ public function prepareRequest(RequestOptions $requestOptions): RequestInterface
7072
}
7173
}
7274

73-
$request = $this->requestFactory->createRequest('POST', $uri);
75+
return $this->requestFactory->createRequest('POST', $uri);
76+
}
7477

75-
preg_match_all('~\{([a-zA-Z\d]+):([a-zA-Z\d ]+(\(.+\))?)}~', $requestOptions->sql, $matches);
78+
/** @throws UnsupportedParamType */
79+
public function prepareSqlRequest(
80+
string $sql,
81+
RequestSettings $requestSettings,
82+
RequestOptions $requestOptions,
83+
): RequestInterface {
84+
$request = $this->initRequest($requestSettings);
85+
86+
preg_match_all('~\{([a-zA-Z\d]+):([a-zA-Z\d ]+(\(.+\))?)}~', $sql, $matches);
7687
if ($matches[0] === []) {
77-
$body = $this->streamFactory->createStream($requestOptions->sql);
88+
$body = $this->streamFactory->createStream($sql);
7889
try {
7990
return $request->withBody($body);
8091
} catch (InvalidArgumentException) {
@@ -93,7 +104,7 @@ static function (array $acc, string|int $k) use ($matches) {
93104
[],
94105
);
95106

96-
$streamElements = [['name' => 'query', 'contents' => $requestOptions->sql]];
107+
$streamElements = [['name' => 'query', 'contents' => $sql]];
97108
foreach ($requestOptions->params as $name => $value) {
98109
$type = $paramToType[$name] ?? null;
99110
if ($type === null) {

src/Client/Http/RequestOptions.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,9 @@
66

77
final class RequestOptions
88
{
9-
/** @var array<string, float|int|string> */
10-
public array $settings;
11-
12-
/**
13-
* @param array<string, mixed> $params
14-
* @param array<string, float|int|string> $defaultSettings
15-
* @param array<string, float|int|string> $querySettings
16-
*/
9+
/** @param array<string, mixed> $params */
1710
public function __construct(
18-
public string $sql,
1911
public array $params,
20-
array $defaultSettings,
21-
array $querySettings,
2212
) {
23-
$this->settings = $querySettings + $defaultSettings;
2413
}
2514
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimPod\ClickHouseClient\Client\Http;
6+
7+
final class RequestSettings
8+
{
9+
/** @var array<string, float|int|string> */
10+
public array $settings;
11+
12+
/**
13+
* @param array<string, float|int|string> $defaultSettings
14+
* @param array<string, float|int|string> $querySettings
15+
*/
16+
public function __construct(
17+
array $defaultSettings,
18+
array $querySettings,
19+
) {
20+
$this->settings = $querySettings + $defaultSettings;
21+
}
22+
}

src/Client/PsrClickHouseAsyncClient.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Psr\Http\Message\ResponseInterface;
1313
use SimPod\ClickHouseClient\Client\Http\RequestFactory;
1414
use SimPod\ClickHouseClient\Client\Http\RequestOptions;
15+
use SimPod\ClickHouseClient\Client\Http\RequestSettings;
1516
use SimPod\ClickHouseClient\Exception\ServerError;
1617
use SimPod\ClickHouseClient\Format\Format;
1718
use SimPod\ClickHouseClient\Output\Output;
@@ -83,13 +84,15 @@ private function executeRequest(
8384
array $settings = [],
8485
callable|null $processResponse = null,
8586
): PromiseInterface {
86-
$request = $this->requestFactory->prepareRequest(
87-
new RequestOptions(
88-
$sql,
89-
$params,
87+
$request = $this->requestFactory->prepareSqlRequest(
88+
$sql,
89+
new RequestSettings(
9090
$this->defaultSettings,
9191
$settings,
9292
),
93+
new RequestOptions(
94+
$params,
95+
),
9396
);
9497

9598
return Create::promiseFor(

src/Client/PsrClickHouseClient.php

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
namespace SimPod\ClickHouseClient\Client;
66

77
use DateTimeZone;
8+
use InvalidArgumentException;
89
use Psr\Http\Client\ClientExceptionInterface;
910
use Psr\Http\Client\ClientInterface;
1011
use Psr\Http\Message\ResponseInterface;
12+
use Psr\Http\Message\StreamInterface;
1113
use SimPod\ClickHouseClient\Client\Http\RequestFactory;
1214
use SimPod\ClickHouseClient\Client\Http\RequestOptions;
15+
use SimPod\ClickHouseClient\Client\Http\RequestSettings;
1316
use SimPod\ClickHouseClient\Exception\CannotInsert;
1417
use SimPod\ClickHouseClient\Exception\ServerError;
1518
use SimPod\ClickHouseClient\Exception\UnsupportedParamType;
@@ -198,6 +201,46 @@ public function insertWithFormat(string $table, Format $inputFormat, string $dat
198201
}
199202
}
200203

204+
public function insertPayload(
205+
string $table,
206+
Format $inputFormat,
207+
StreamInterface $payload,
208+
array $columns = [],
209+
array $settings = [],
210+
): void {
211+
$formatSql = $inputFormat::toSql();
212+
213+
$table = Escaper::quoteIdentifier($table);
214+
215+
$columnsSql = $columns === [] ? '' : sprintf('(%s)', implode(',', $columns));
216+
217+
$sql = <<<CLICKHOUSE
218+
INSERT INTO $table $columnsSql $formatSql
219+
CLICKHOUSE;
220+
221+
$request = $this->requestFactory->initRequest(
222+
new RequestSettings(
223+
$this->defaultSettings,
224+
$settings,
225+
),
226+
['query' => $sql],
227+
);
228+
229+
try {
230+
$request = $request->withBody($payload);
231+
} catch (InvalidArgumentException) {
232+
absurd();
233+
}
234+
235+
$response = $this->client->sendRequest($request);
236+
237+
if ($response->getStatusCode() !== 200) {
238+
throw ServerError::fromResponse($response);
239+
}
240+
241+
return;
242+
}
243+
201244
/**
202245
* @param array<string, mixed> $params
203246
* @param array<string, float|int|string> $settings
@@ -208,13 +251,15 @@ public function insertWithFormat(string $table, Format $inputFormat, string $dat
208251
*/
209252
private function executeRequest(string $sql, array $params, array $settings): ResponseInterface
210253
{
211-
$request = $this->requestFactory->prepareRequest(
212-
new RequestOptions(
213-
$sql,
214-
$params,
254+
$request = $this->requestFactory->prepareSqlRequest(
255+
$sql,
256+
new RequestSettings(
215257
$this->defaultSettings,
216258
$settings,
217259
),
260+
new RequestOptions(
261+
$params,
262+
),
218263
);
219264

220265
$response = $this->client->sendRequest($request);

src/Format/RowBinary.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimPod\ClickHouseClient\Format;
6+
7+
use SimPod\ClickHouseClient\Output\Basic;
8+
use SimPod\ClickHouseClient\Output\Output;
9+
10+
/**
11+
* @template T
12+
* @implements Format<Basic<T>>
13+
*/
14+
final class RowBinary implements Format
15+
{
16+
public static function output(string $contents): Output
17+
{
18+
/** @var Basic<T> $output */
19+
$output = new Basic($contents);
20+
21+
return $output;
22+
}
23+
24+
public static function toSql(): string
25+
{
26+
return 'FORMAT RowBinary';
27+
}
28+
}

tests/Client/Http/RequestFactoryTest.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPUnit\Framework\Attributes\DataProvider;
1111
use SimPod\ClickHouseClient\Client\Http\RequestFactory;
1212
use SimPod\ClickHouseClient\Client\Http\RequestOptions;
13+
use SimPod\ClickHouseClient\Client\Http\RequestSettings;
1314
use SimPod\ClickHouseClient\Param\ParamValueConverterRegistry;
1415
use SimPod\ClickHouseClient\Tests\TestCaseBase;
1516

@@ -28,12 +29,16 @@ public function testPrepareRequest(string $uri, string $expectedUri): void
2829
$uri,
2930
);
3031

31-
$request = $requestFactory->prepareRequest(new RequestOptions(
32+
$request = $requestFactory->prepareSqlRequest(
3233
'SELECT 1',
33-
[],
34-
['max_block_size' => 1],
35-
['database' => 'database'],
36-
));
34+
new RequestSettings(
35+
['max_block_size' => 1],
36+
['database' => 'database'],
37+
),
38+
new RequestOptions(
39+
[],
40+
),
41+
);
3742

3843
self::assertSame('POST', $request->getMethod());
3944
self::assertSame(

tests/Client/Http/RequestOptionsTest.php

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)