Skip to content

Commit 851a1f9

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

15 files changed

+253
-61
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ jobs:
4444

4545
- name: "Install dependencies with Composer"
4646
uses: "ramsey/composer-install@v2"
47+
env:
48+
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_AUTH }}"}}'
4749
with:
4850
composer-options: "${{ matrix.composer-options }}"
4951
dependency-versions: "${{ matrix.dependency-versions }}"
@@ -100,6 +102,8 @@ jobs:
100102

101103
- name: "Install dependencies with Composer"
102104
uses: "ramsey/composer-install@v2"
105+
env:
106+
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_AUTH }}"}}'
103107
with:
104108
dependency-versions: "${{ matrix.dependency-versions }}"
105109

.github/workflows/coding-standards.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ jobs:
3030

3131
- name: "Install dependencies with Composer"
3232
uses: "ramsey/composer-install@v2"
33+
env:
34+
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_AUTH }}"}}'
3335

3436
- name: "Run PHP_CodeSniffer"
3537
run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"

.github/workflows/static-analysis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ jobs:
3030

3131
- name: "Install dependencies with Composer"
3232
uses: "ramsey/composer-install@v2"
33+
env:
34+
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_AUTH }}"}}'
35+
3336

3437
- name: "Run a static analysis with phpstan/phpstan"
3538
run: "vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"

composer.json

Lines changed: 6 additions & 1 deletion
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-bump",
4243
"nyholm/psr7": "^1.2",
4344
"php-http/message-factory": "^1.1",
4445
"phpstan/extension-installer": "^1.1",
@@ -58,5 +59,9 @@
5859
"psr-4": {
5960
"SimPod\\ClickHouseClient\\Tests\\": "tests/"
6061
}
61-
}
62+
},
63+
"repositories": [{
64+
"type": "vcs",
65+
"url": "https://github.com/simPod/PHPClick"
66+
}]
6267
}

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);

0 commit comments

Comments
 (0)