Skip to content

Commit 2996c83

Browse files
committed
feat: add Client::insertPayload()
1 parent 7c553f6 commit 2996c83

15 files changed

+256
-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@v3"
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 }}"
@@ -93,6 +95,8 @@ jobs:
9395

9496
- name: "Install dependencies with Composer"
9597
uses: "ramsey/composer-install@v3"
98+
env:
99+
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_AUTH }}"}}'
96100
with:
97101
dependency-versions: "${{ matrix.dependency-versions }}"
98102

.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@v3"
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: 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@v3"
33+
env:
34+
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ secrets.COMPOSER_AUTH }}"}}'
3335

3436
- name: "Run a static analysis with phpstan/phpstan"
3537
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: 53 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,50 @@ 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+
if ($payload->getSize() === 0) {
212+
throw CannotInsert::noValues();
213+
}
214+
215+
$formatSql = $inputFormat::toSql();
216+
217+
$table = Escaper::quoteIdentifier($table);
218+
219+
$columnsSql = $columns === [] ? '' : sprintf('(%s)', implode(',', $columns));
220+
221+
$sql = <<<CLICKHOUSE
222+
INSERT INTO $table $columnsSql $formatSql
223+
CLICKHOUSE;
224+
225+
$request = $this->requestFactory->initRequest(
226+
new RequestSettings(
227+
$this->defaultSettings,
228+
$settings,
229+
),
230+
['query' => $sql],
231+
);
232+
233+
try {
234+
$request = $request->withBody($payload);
235+
} catch (InvalidArgumentException) {
236+
absurd();
237+
}
238+
239+
$response = $this->client->sendRequest($request);
240+
241+
if ($response->getStatusCode() !== 200) {
242+
throw ServerError::fromResponse($response);
243+
}
244+
245+
return;
246+
}
247+
201248
/**
202249
* @param array<string, mixed> $params
203250
* @param array<string, float|int|string> $settings
@@ -208,13 +255,15 @@ public function insertWithFormat(string $table, Format $inputFormat, string $dat
208255
*/
209256
private function executeRequest(string $sql, array $params, array $settings): ResponseInterface
210257
{
211-
$request = $this->requestFactory->prepareRequest(
212-
new RequestOptions(
213-
$sql,
214-
$params,
258+
$request = $this->requestFactory->prepareSqlRequest(
259+
$sql,
260+
new RequestSettings(
215261
$this->defaultSettings,
216262
$settings,
217263
),
264+
new RequestOptions(
265+
$params,
266+
),
218267
);
219268

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

0 commit comments

Comments
 (0)