Skip to content

Commit cb1298d

Browse files
author
kruglov
committed
Added query with 'with' support
1 parent 57c4676 commit cb1298d

File tree

2 files changed

+53
-45
lines changed

2 files changed

+53
-45
lines changed

src/ClickHouseStatement.php

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,6 @@
2323
use Doctrine\DBAL\Platforms\AbstractPlatform;
2424
use FOD\DBALClickHouse\Driver\Exception\Exception;
2525

26-
use function array_map;
27-
use function array_replace;
28-
use function current;
29-
use function implode;
30-
use function is_array;
31-
use function is_bool;
32-
use function is_float;
33-
use function is_int;
34-
use function mb_stripos;
35-
3626
class ClickHouseStatement implements Statement
3727
{
3828
protected Client $client;
@@ -47,9 +37,9 @@ class ClickHouseStatement implements Statement
4737

4838
public function __construct(Client $client, string $statement, AbstractPlatform $platform)
4939
{
50-
$this->client = $client;
40+
$this->client = $client;
5141
$this->statement = $statement;
52-
$this->platform = $platform;
42+
$this->platform = $platform;
5343
}
5444

5545
/**
@@ -58,7 +48,7 @@ public function __construct(Client $client, string $statement, AbstractPlatform
5848
public function bindValue($param, $value, $type = ParameterType::STRING): bool
5949
{
6050
$this->values[$param] = $value;
61-
$this->types[$param] = $type;
51+
$this->types[$param] = $type;
6252

6353
return true;
6454
}
@@ -69,7 +59,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool
6959
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool
7060
{
7161
$this->values[$param] = $variable;
72-
$this->types[$param] = $type;
62+
$this->types[$param] = $type;
7363

7464
return true;
7565
}
@@ -79,16 +69,16 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le
7969
*/
8070
public function execute($params = null): Result
8171
{
82-
if (is_array($params)) {
83-
$this->values = array_replace($this->values, $params);
72+
if (\is_array($params)) {
73+
$this->values = \array_replace($this->values, $params);
8474
}
8575

8676
$statement = $this->statement;
8777

8878
$firstPlaceholder = array_key_first($this->values);
8979

90-
$positionalPlaceholders = is_int($firstPlaceholder);
91-
$positionalPlaceholdersIsList = $firstPlaceholder === 0;
80+
$positionalPlaceholders = \is_int($firstPlaceholder);
81+
$positionalPlaceholdersIsList = 0 === $firstPlaceholder;
9282

9383
if ($positionalPlaceholders) {
9484
$pieces = explode('?', $statement);
@@ -101,14 +91,14 @@ public function execute($params = null): Result
10191
}
10292
}
10393

104-
$statement = implode('', $pieces);
94+
$statement = \implode('', $pieces);
10595
} else {
10696
foreach (array_keys($this->values) as $key) {
107-
$namedPlaceholder = ":$key";
108-
$namedPlaceholderOffset = mb_stripos($statement, $namedPlaceholder);
97+
$namedPlaceholder = ":$key";
98+
$namedPlaceholderOffset = \mb_stripos($statement, $namedPlaceholder);
10999
$namedPlaceholderLength = mb_strlen($namedPlaceholder);
110100

111-
if ($namedPlaceholderOffset !== false) {
101+
if (false !== $namedPlaceholderOffset) {
112102
$statement = substr_replace(
113103
$statement,
114104
$this->resolveType($key),
@@ -122,9 +112,10 @@ public function execute($params = null): Result
122112
try {
123113
return new ClickHouseResult(
124114
new \ArrayIterator(
125-
mb_stripos($statement, 'select') === 0 ||
126-
mb_stripos($statement, 'show') === 0 ||
127-
mb_stripos($statement, 'describe') === 0
115+
0 === \mb_stripos($statement, 'select')
116+
|| 1 === preg_match('/with(.*)\)\s*select/ms', mb_strtolower($statement))
117+
|| 0 === \mb_stripos($statement, 'show')
118+
|| 0 === \mb_stripos($statement, 'describe')
128119
? $this->client->select($statement)->rows()
129120
: $this->client->write($statement)->rows()
130121
)
@@ -141,32 +132,32 @@ protected function resolveType(int|string $key): string
141132
{
142133
$value = $this->values[$key];
143134

144-
if ($value === null) {
135+
if (null === $value) {
145136
return 'NULL';
146137
}
147138

148-
if (is_array($value)) {
149-
if (is_int(current($value)) || is_float(current($value))) {
139+
if (\is_array($value)) {
140+
if (\is_int(\current($value)) || \is_float(\current($value))) {
150141
foreach ($value as $item) {
151-
if (!is_int($item) && !is_float($item)) {
142+
if (!\is_int($item) && !\is_float($item)) {
152143
throw new DBALException('Array values must all be int/float or string, mixes not allowed');
153144
}
154145
}
155146
} else {
156-
$value = array_map(function (?string $item): string {
157-
return $item === null ? 'NULL' : $this->platform->quoteStringLiteral($item);
147+
$value = \array_map(function (?string $item): string {
148+
return null === $item ? 'NULL' : $this->platform->quoteStringLiteral($item);
158149
}, $value);
159150
}
160151

161-
return '[' . implode(', ', $value) . ']';
152+
return '[' . \implode(', ', $value) . ']';
162153
}
163154

164155
$type = $this->types[$key] ?? null;
165156

166-
if ($type === null) {
167-
if (is_int($value) || is_float($value)) {
157+
if (null === $type) {
158+
if (\is_int($value) || \is_float($value)) {
168159
$type = ParameterType::INTEGER;
169-
} elseif (is_bool($value)) {
160+
} elseif (\is_bool($value)) {
170161
$type = ParameterType::BOOLEAN;
171162
}
172163
}

tests/SelectTest.php

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class SelectTest extends TestCase
2828
{
2929
private Connection $connection;
3030

31-
public function setUp(): void
31+
protected function setUp(): void
3232
{
3333
$this->connection = CreateConnectionTest::createConnection();
3434

@@ -50,7 +50,7 @@ public function setUp(): void
5050
$this->connection->executeStatement("INSERT INTO test_select_table(id, payload, hits) VALUES (1, 'v1', 101), (2, 'v2', 202), (3, 'v3', 303), (4, 'v4', 404), (5, 'v4', 505), (6, ' t1 ', 606), (7, 'aat2aaa', 707)");
5151
}
5252

53-
public function tearDown(): void
53+
protected function tearDown(): void
5454
{
5555
$this->connection->executeStatement('DROP TABLE test_select_table');
5656
}
@@ -81,7 +81,7 @@ public function testFetchAssocSelect(): void
8181
$this->assertEquals([['id' => 3, 'hits' => 303], ['id' => 4, 'hits' => 404]], $results);
8282
}
8383

84-
public function testFetchNumSelect():void
84+
public function testFetchNumSelect(): void
8585
{
8686
$result = $this->connection->executeQuery('SELECT MAX(hits) as maxHits FROM test_select_table');
8787

@@ -90,7 +90,7 @@ public function testFetchNumSelect():void
9090

9191
public function testFetchAllBothSelect(): void
9292
{
93-
$result = $this->connection->executeQuery("SELECT * FROM test_select_table WHERE id IN (1, 3)");
93+
$result = $this->connection->executeQuery('SELECT * FROM test_select_table WHERE id IN (1, 3)');
9494

9595
$this->assertEquals([
9696
[
@@ -102,13 +102,13 @@ public function testFetchAllBothSelect(): void
102102
'id' => 3,
103103
'payload' => 'v3',
104104
'hits' => 303,
105-
]
105+
],
106106
], $result->fetchAllAssociative());
107107
}
108108

109109
public function testFetchAllNumSelect(): void
110110
{
111-
$result = $this->connection->executeQuery("SELECT AVG(hits) FROM test_select_table");
111+
$result = $this->connection->executeQuery('SELECT AVG(hits) FROM test_select_table');
112112

113113
$this->assertEquals([[404]], $result->fetchAllNumeric());
114114
}
@@ -117,7 +117,7 @@ public function testFetchColumnValidOffsetSelect(): void
117117
{
118118
$results = [];
119119

120-
$result = $this->connection->executeQuery("SELECT payload, hits FROM test_select_table WHERE id > 1 ORDER BY id LIMIT 3");
120+
$result = $this->connection->executeQuery('SELECT payload, hits FROM test_select_table WHERE id > 1 ORDER BY id LIMIT 3');
121121

122122
while ($row = $result->fetchNumeric()) {
123123
$results[] = $row[1];
@@ -130,7 +130,7 @@ public function testFetchColumnInvalidOffsetSelect(): void
130130
{
131131
$results = [];
132132

133-
$result = $this->connection->executeQuery("SELECT payload, hits FROM test_select_table WHERE id > 1 ORDER BY id");
133+
$result = $this->connection->executeQuery('SELECT payload, hits FROM test_select_table WHERE id > 1 ORDER BY id');
134134

135135
while ($row = $result->fetchOne()) {
136136
$results[] = $row;
@@ -151,7 +151,8 @@ public function testQueryBuilderSelect(): void
151151
->groupBy('payload')
152152
->orderBy('payload')
153153
->setMaxResults(2)
154-
->fetchAllAssociative();
154+
->fetchAllAssociative()
155+
;
155156

156157
$this->assertEquals([
157158
[
@@ -161,7 +162,7 @@ public function testQueryBuilderSelect(): void
161162
[
162163
'payload' => 'aat2aaa',
163164
'uniques' => '1',
164-
]
165+
],
165166
], $result);
166167
}
167168

@@ -243,4 +244,20 @@ public function testTrimChar(): void
243244

244245
$this->assertEquals('t2', $result->fetchOne());
245246
}
247+
248+
public function testWith(): void
249+
{
250+
$result = $this->connection->executeQuery("
251+
WITH subselect as (
252+
SELECT id
253+
FROM test_select_table
254+
WHERE payload = 'v4'
255+
)
256+
SELECT *
257+
FROM test_select_table tbl
258+
JOIN subselect sub ON sub.id = tbl.id
259+
");
260+
261+
$this->assertEquals(2, $result->columnCount());
262+
}
246263
}

0 commit comments

Comments
 (0)