Skip to content

Commit

Permalink
feat: insert with parameters (#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
simPod authored Jan 18, 2024
1 parent 425b8d2 commit 2c644c5
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 3 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ use SimPod\ClickHouseClient\Client\ClickHouseClient;
$client->insert('table', $data, $columnNames);
```

If `$columnNames` is provided and is key->value array column names are generated based on it and values are passed as parameters:

`$client->insert( 'table', [[1,2]], ['a' => 'Int8, 'b' => 'String'] );` generates `INSERT INTO table (a,b) VALUES ({p1:Int8},{p2:String})` and values are passed along the query.

If `$columnNames` is provided column names are generated based on it:

`$client->insert( 'table', [[1,2]], ['a', 'b'] );` generates `INSERT INTO table (a,b) VALUES (1,2)`.
Expand Down
2 changes: 1 addition & 1 deletion src/Client/ClickHouseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function selectWithParams(string $query, array $params, Format $outputFor

/**
* @param array<array<mixed>> $values
* @param list<string>|null $columns
* @param list<string>|array<string, string>|null $columns
* @param array<string, float|int|string> $settings
*
* @throws CannotInsert
Expand Down
53 changes: 51 additions & 2 deletions src/Client/PsrClickHouseClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
use SimPod\ClickHouseClient\Sql\SqlFactory;
use SimPod\ClickHouseClient\Sql\ValueFormatter;

use function array_is_list;
use function array_key_first;
use function array_keys;
use function array_map;
use function array_values;
use function implode;
use function is_array;
use function is_int;
use function SimPod\ClickHouseClient\absurd;
use function sprintf;
Expand Down Expand Up @@ -96,6 +99,54 @@ public function insert(string $table, array $values, array|null $columns = null,
throw CannotInsert::noValues();
}

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

if (is_array($columns) && ! array_is_list($columns)) {
$columnsSql = sprintf('(%s)', implode(',', array_keys($columns)));

$types = array_values($columns);

$params = [];
$pN = 1;
foreach ($values as $row) {
foreach ($row as $value) {
$params['p' . $pN++] = $value;
}
}

$pN = 1;
$valuesSql = implode(
',',
array_map(
static function (array $row) use (&$pN, $types): string {
return sprintf(
'(%s)',
implode(',', array_map(static function ($i) use (&$pN, $types) {
return sprintf('{p%d:%s}', $pN++, $types[$i]);
}, array_keys($row))),
);
},
$values,
),
);

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

return;
}

if ($columns === null) {
$firstRow = $values[array_key_first($values)];
$columns = array_keys($firstRow);
Expand All @@ -117,8 +168,6 @@ public function insert(string $table, array $values, array|null $columns = null,
),
);

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

try {
$this->executeRequest(
<<<CLICKHOUSE
Expand Down
29 changes: 29 additions & 0 deletions tests/Client/InsertTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,35 @@ public function testInsertUseColumns(string $tableSql): void
self::assertSame($expectedData, $output->data);
}

#[DataProvider('providerInsert')]
public function testInsertUseColumnsWithTypes(string $tableSql): void
{
$expectedData = [
['PageViews' => 5, 'UserID' => '4324182021466249494', 'Duration' => 146, 'Sign' => -1],
['PageViews' => 6, 'UserID' => '4324182021466249494', 'Duration' => 185, 'Sign' => 1],
];

self::$client->executeQuery($tableSql);

self::$client->insert(
'UserActivity',
[
[5, 4324182021466249494, 146, -1],
[6, 4324182021466249494, 185, 1],
],
['PageViews' => 'UInt32', 'UserID' => 'UInt64', 'Duration' => 'UInt32', 'Sign' => 'Int8'],
);

$output = self::$client->select(
<<<'CLICKHOUSE'
SELECT * FROM UserActivity
CLICKHOUSE,
new JsonEachRow(),
);

self::assertSame($expectedData, $output->data);
}

public function testInsertEscaping(): void
{
self::$client->executeQuery(
Expand Down

0 comments on commit 2c644c5

Please sign in to comment.