|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
declare(strict_types=1); |
|
4
|
|
|
|
|
5
|
|
|
namespace Yiisoft\Db\Tests\Common; |
|
6
|
|
|
|
|
7
|
|
|
use Throwable; |
|
8
|
|
|
use Yiisoft\Db\Driver\PDO\ConnectionPDOInterface; |
|
9
|
|
|
use Yiisoft\Db\Exception\Exception; |
|
10
|
|
|
use Yiisoft\Db\Exception\IntegrityException; |
|
11
|
|
|
use Yiisoft\Db\Exception\InvalidCallException; |
|
12
|
|
|
use Yiisoft\Db\Query\Data\DataReaderInterface; |
|
13
|
|
|
use Yiisoft\Db\Query\Query; |
|
14
|
|
|
use Yiisoft\Db\Schema\Schema; |
|
15
|
|
|
use Yiisoft\Db\Tests\AbstractCommandTest; |
|
16
|
|
|
use Yiisoft\Db\Tests\Support\Assert; |
|
17
|
|
|
|
|
18
|
|
|
use function setlocale; |
|
19
|
|
|
|
|
20
|
|
|
abstract class CommonCommandTest extends AbstractCommandTest |
|
21
|
|
|
{ |
|
22
|
|
|
public function testAlterTable(): void |
|
23
|
|
|
{ |
|
24
|
|
|
$db = $this->getConnection(); |
|
25
|
|
|
|
|
26
|
|
|
$command = $db->createCommand(); |
|
27
|
|
|
$schema = $db->getSchema(); |
|
28
|
|
|
|
|
29
|
|
|
if ($schema->getTableSchema('testAlterTable', true) !== null) { |
|
30
|
|
|
$command->dropTable('testAlterTable')->execute(); |
|
31
|
|
|
} |
|
32
|
|
|
|
|
33
|
|
|
$command->createTable('testAlterTable', ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER])->execute(); |
|
34
|
|
|
$command->insert('testAlterTable', ['bar' => 1])->execute(); |
|
35
|
|
|
$command->alterColumn('testAlterTable', 'bar', Schema::TYPE_STRING)->execute(); |
|
36
|
|
|
$command->insert('testAlterTable', ['bar' => 'hello'])->execute(); |
|
37
|
|
|
$records = $command->setSql( |
|
38
|
|
|
<<<SQL |
|
39
|
|
|
SELECT [[id]], [[bar]] FROM {{testAlterTable}} |
|
40
|
|
|
SQL |
|
41
|
|
|
)->queryAll(); |
|
42
|
|
|
|
|
43
|
|
|
$this->assertSame([['id' => 1, 'bar' => 1], ['id' => 2, 'bar' => 'hello']], $records); |
|
44
|
|
|
} |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* Make sure that `{{something}}` in values will not be encoded. |
|
48
|
|
|
* |
|
49
|
|
|
* {@see https://github.com/yiisoft/yii2/issues/11242} |
|
50
|
|
|
*/ |
|
51
|
|
|
public function testBatchInsertSQL( |
|
52
|
|
|
string $table, |
|
53
|
|
|
array $columns, |
|
54
|
|
|
array $values, |
|
55
|
|
|
string $expected, |
|
56
|
|
|
array $expectedParams = [], |
|
57
|
|
|
int $insertedRow = 1, |
|
58
|
|
|
string $fixture = 'type' |
|
59
|
|
|
): void { |
|
60
|
|
|
$db = $this->getConnection($fixture); |
|
61
|
|
|
|
|
62
|
|
|
$command = $db->createCommand(); |
|
63
|
|
|
$command->batchInsert($table, $columns, $values); |
|
64
|
|
|
$command->prepare(false); |
|
65
|
|
|
|
|
66
|
|
|
$this->assertSame($expected, $command->getSql()); |
|
67
|
|
|
$this->assertSame($expectedParams, $command->getParams()); |
|
68
|
|
|
|
|
69
|
|
|
$command->execute(); |
|
70
|
|
|
|
|
71
|
|
|
$this->assertEquals($insertedRow, (new Query($db))->from($table)->count()); |
|
72
|
|
|
} |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* Test batch insert with different data types. |
|
76
|
|
|
* |
|
77
|
|
|
* Ensure double is inserted with `.` decimal separator. |
|
78
|
|
|
* |
|
79
|
|
|
* @link https://github.com/yiisoft/yii2/issues/6526 |
|
80
|
|
|
*/ |
|
81
|
|
|
public function testBatchInsertDataTypesLocale(): void |
|
82
|
|
|
{ |
|
83
|
|
|
$locale = setlocale(LC_NUMERIC, 0); |
|
84
|
|
|
|
|
85
|
|
|
if ($locale === false) { |
|
86
|
|
|
$this->markTestSkipped('Your platform does not support locales.'); |
|
87
|
|
|
} |
|
88
|
|
|
|
|
89
|
|
|
$db = $this->getConnection('type'); |
|
90
|
|
|
|
|
91
|
|
|
$command = $db->createCommand(); |
|
92
|
|
|
|
|
93
|
|
|
try { |
|
94
|
|
|
/* This one sets decimal mark to comma sign */ |
|
95
|
|
|
setlocale(LC_NUMERIC, 'ru_RU.utf8'); |
|
96
|
|
|
|
|
97
|
|
|
$cols = ['int_col', 'char_col', 'float_col', 'bool_col']; |
|
98
|
|
|
$data = [[1, 'A', 9.735, true], [2, 'B', -2.123, false], [3, 'C', 2.123, false]]; |
|
99
|
|
|
|
|
100
|
|
|
/* clear data in "type" table */ |
|
101
|
|
|
$command->delete('type')->execute(); |
|
102
|
|
|
|
|
103
|
|
|
/* change, for point oracle. */ |
|
104
|
|
|
if ($db->getName() === 'oci') { |
|
105
|
|
|
$command->setSql( |
|
106
|
|
|
<<<SQL |
|
107
|
|
|
ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,' |
|
108
|
|
|
SQL |
|
109
|
|
|
)->execute(); |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
/* batch insert on "type" table */ |
|
113
|
|
|
$command->batchInsert('type', $cols, $data)->execute(); |
|
114
|
|
|
$data = $command->setSql( |
|
115
|
|
|
<<<SQL |
|
116
|
|
|
SELECT [[int_col]], [[char_col]], [[float_col]], [[bool_col]] FROM {{type}} WHERE [[int_col]] IN (1,2,3) ORDER BY [[int_col]] |
|
117
|
|
|
SQL |
|
118
|
|
|
)->queryAll(); |
|
119
|
|
|
|
|
120
|
|
|
$this->assertCount(3, $data); |
|
121
|
|
|
$this->assertEquals(1, $data[0]['int_col']); |
|
122
|
|
|
$this->assertEquals(2, $data[1]['int_col']); |
|
123
|
|
|
$this->assertEquals(3, $data[2]['int_col']); |
|
124
|
|
|
|
|
125
|
|
|
/* rtrim because Postgres padds the column with whitespace */ |
|
126
|
|
|
$this->assertSame('A', rtrim($data[0]['char_col'])); |
|
127
|
|
|
$this->assertSame('B', rtrim($data[1]['char_col'])); |
|
128
|
|
|
$this->assertSame('C', rtrim($data[2]['char_col'])); |
|
129
|
|
|
$this->assertEquals(9.735, $data[0]['float_col']); |
|
130
|
|
|
$this->assertEquals(-2.123, $data[1]['float_col']); |
|
131
|
|
|
$this->assertEquals(2.123, $data[2]['float_col']); |
|
132
|
|
|
$this->assertEquals(1, $data[0]['bool_col']); |
|
133
|
|
|
Assert::isOneOf($data[1]['bool_col'], ['0', false]); |
|
134
|
|
|
Assert::isOneOf($data[2]['bool_col'], ['0', false]); |
|
135
|
|
|
} catch (Exception | Throwable $e) { |
|
136
|
|
|
setlocale(LC_NUMERIC, $locale); |
|
137
|
|
|
|
|
138
|
|
|
throw $e; |
|
139
|
|
|
} |
|
140
|
|
|
|
|
141
|
|
|
setlocale(LC_NUMERIC, $locale); |
|
142
|
|
|
} |
|
143
|
|
|
|
|
144
|
|
|
public function testBatchInsertFailsOld(): void |
|
145
|
|
|
{ |
|
146
|
|
|
$db = $this->getConnection('customer'); |
|
147
|
|
|
|
|
148
|
|
|
$command = $db->createCommand(); |
|
149
|
|
|
$command->batchInsert( |
|
150
|
|
|
'{{customer}}', |
|
151
|
|
|
['email', 'name', 'address'], |
|
152
|
|
|
[['[email protected]', 'test_name', 'test_address']], |
|
153
|
|
|
); |
|
154
|
|
|
|
|
155
|
|
|
$this->assertSame(1, $command->execute()); |
|
156
|
|
|
|
|
157
|
|
|
$result = (new Query($db)) |
|
158
|
|
|
->select(['email', 'name', 'address']) |
|
159
|
|
|
->from('{{customer}}') |
|
160
|
|
|
->where(['=', '[[email]]', '[email protected]']) |
|
161
|
|
|
->one(); |
|
162
|
|
|
|
|
163
|
|
|
$this->assertCount(3, $result); |
|
164
|
|
|
$this->assertSame(['email' => '[email protected]', 'name' => 'test_name', 'address' => 'test_address'], $result); |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
public function testBatchInsertWithManyData(): void |
|
168
|
|
|
{ |
|
169
|
|
|
$db = $this->getConnection('customer'); |
|
170
|
|
|
|
|
171
|
|
|
$values = []; |
|
172
|
|
|
$attemptsInsertRows = 200; |
|
173
|
|
|
$command = $db->createCommand(); |
|
174
|
|
|
|
|
175
|
|
|
for ($i = 0; $i < $attemptsInsertRows; $i++) { |
|
176
|
|
|
$values[$i] = ['t' . $i . '@any.com', 't' . $i, 't' . $i . ' address']; |
|
177
|
|
|
} |
|
178
|
|
|
|
|
179
|
|
|
$command->batchInsert('{{customer}}', ['email', 'name', 'address'], $values); |
|
180
|
|
|
|
|
181
|
|
|
$this->assertSame($attemptsInsertRows, $command->execute()); |
|
182
|
|
|
|
|
183
|
|
|
$insertedRowsCount = (new Query($db))->from('{{customer}}')->count(); |
|
184
|
|
|
|
|
185
|
|
|
$this->assertGreaterThanOrEqual($attemptsInsertRows, $insertedRowsCount); |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
public function testBatchInsertWithYield(): void |
|
189
|
|
|
{ |
|
190
|
|
|
$db = $this->getConnection('customer'); |
|
191
|
|
|
|
|
192
|
|
|
$rows = ( |
|
193
|
|
|
static function () { |
|
194
|
|
|
yield ['[email protected]', 'test name', 'test address']; |
|
195
|
|
|
} |
|
196
|
|
|
)(); |
|
197
|
|
|
$command = $db->createCommand(); |
|
198
|
|
|
$command->batchInsert('{{customer}}', ['email', 'name', 'address'], $rows); |
|
199
|
|
|
|
|
200
|
|
|
$this->assertSame(1, $command->execute()); |
|
201
|
|
|
} |
|
202
|
|
|
|
|
203
|
|
|
public function testCreateTable(): void |
|
204
|
|
|
{ |
|
205
|
|
|
$db = $this->getConnection(); |
|
206
|
|
|
|
|
207
|
|
|
$command = $db->createCommand(); |
|
208
|
|
|
$schema = $db->getSchema(); |
|
209
|
|
|
|
|
210
|
|
|
if ($schema->getTableSchema('testCreateTable', true) !== null) { |
|
211
|
|
|
$command->dropTable('testCreateTable')->execute(); |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
$command->createTable('testCreateTable', ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER])->execute(); |
|
215
|
|
|
$command->insert('testCreateTable', ['bar' => 1])->execute(); |
|
216
|
|
|
$records = $command->setSql( |
|
217
|
|
|
<<<SQL |
|
218
|
|
|
SELECT [[id]], [[bar]] FROM {{testCreateTable}}; |
|
219
|
|
|
SQL |
|
220
|
|
|
)->queryAll(); |
|
221
|
|
|
|
|
222
|
|
|
$this->assertEquals([['id' => 1, 'bar' => 1]], $records); |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
|
|
public function testCreateView(): void |
|
226
|
|
|
{ |
|
227
|
|
|
$db = $this->getConnection(); |
|
228
|
|
|
|
|
229
|
|
|
$command = $db->createCommand(); |
|
230
|
|
|
$schema = $db->getSchema(); |
|
231
|
|
|
$subQuery = (new Query($db))->select('bar')->from('testCreateViewTable')->where(['>', 'bar', '5']); |
|
232
|
|
|
|
|
233
|
|
|
if ($schema->getTableSchema('testCreateView') !== null) { |
|
234
|
|
|
$command->dropView('testCreateView')->execute(); |
|
235
|
|
|
} |
|
236
|
|
|
|
|
237
|
|
|
if ($schema->getTableSchema('testCreateViewTable')) { |
|
238
|
|
|
$command->dropTable('testCreateViewTable')->execute(); |
|
239
|
|
|
} |
|
240
|
|
|
|
|
241
|
|
|
$command->createTable( |
|
242
|
|
|
'testCreateViewTable', |
|
243
|
|
|
['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER], |
|
244
|
|
|
)->execute(); |
|
245
|
|
|
$command->insert('testCreateViewTable', ['bar' => 1])->execute(); |
|
246
|
|
|
$command->insert('testCreateViewTable', ['bar' => 6])->execute(); |
|
247
|
|
|
$command->createView('testCreateView', $subQuery)->execute(); |
|
248
|
|
|
$records = $command->setSql( |
|
249
|
|
|
<<<SQL |
|
250
|
|
|
SELECT [[bar]] FROM {{testCreateView}}; |
|
251
|
|
|
SQL |
|
252
|
|
|
)->queryAll(); |
|
253
|
|
|
|
|
254
|
|
|
$this->assertEquals([['bar' => 6]], $records); |
|
255
|
|
|
} |
|
256
|
|
|
|
|
257
|
|
|
public function testDataReaderRewindException(): void |
|
258
|
|
|
{ |
|
259
|
|
|
$db = $this->getConnection('customer'); |
|
260
|
|
|
|
|
261
|
|
|
$this->expectException(InvalidCallException::class); |
|
262
|
|
|
$this->expectExceptionMessage('DataReader cannot rewind. It is a forward-only reader.'); |
|
263
|
|
|
|
|
264
|
|
|
$command = $db->createCommand(); |
|
265
|
|
|
$reader = $command->setSql( |
|
266
|
|
|
<<<SQL |
|
267
|
|
|
SELECT * FROM {{customer}} |
|
268
|
|
|
SQL |
|
269
|
|
|
)->query(); |
|
270
|
|
|
$reader->next(); |
|
271
|
|
|
$reader->rewind(); |
|
272
|
|
|
} |
|
273
|
|
|
|
|
274
|
|
|
public function testDropView(): void |
|
275
|
|
|
{ |
|
276
|
|
|
$db = $this->getConnection('animal'); |
|
277
|
|
|
|
|
278
|
|
|
/* since it already exists in the fixtures */ |
|
279
|
|
|
$viewName = 'animal_view'; |
|
280
|
|
|
|
|
281
|
|
|
$schema = $db->getSchema(); |
|
282
|
|
|
|
|
283
|
|
|
$this->assertNotNull($schema->getTableSchema($viewName)); |
|
284
|
|
|
|
|
285
|
|
|
$db->createCommand()->dropView($viewName)->execute(); |
|
286
|
|
|
|
|
287
|
|
|
$this->assertNull($schema->getTableSchema($viewName)); |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
public function testExecute(): void |
|
291
|
|
|
{ |
|
292
|
|
|
$db = $this->getConnection('customer'); |
|
293
|
|
|
|
|
294
|
|
|
$command = $db->createCommand(); |
|
295
|
|
|
$command->setSql( |
|
296
|
|
|
<<<SQL |
|
297
|
|
|
INSERT INTO {{customer}}([[email]], [[name]], [[address]]) VALUES ('[email protected]', 'user4', 'address4') |
|
298
|
|
|
SQL |
|
299
|
|
|
); |
|
300
|
|
|
|
|
301
|
|
|
$this->assertSame(1, $command->execute()); |
|
302
|
|
|
|
|
303
|
|
|
$command = $command->setSql( |
|
304
|
|
|
<<<SQL |
|
305
|
|
|
SELECT COUNT(*) FROM {{customer}} WHERE [[name]] = 'user4' |
|
306
|
|
|
SQL |
|
307
|
|
|
); |
|
308
|
|
|
|
|
309
|
|
|
$this->assertEquals(1, $command->queryScalar()); |
|
310
|
|
|
|
|
311
|
|
|
$command->setSql('bad SQL'); |
|
312
|
|
|
$message = match ($db->getName()) { |
|
313
|
|
|
'sqlite' => 'SQLSTATE[HY000]: General error: 1 near "bad": syntax error', |
|
314
|
|
|
'sqlsrv' => 'SQLSTATE[42000]: [Microsoft]', |
|
315
|
|
|
}; |
|
316
|
|
|
|
|
317
|
|
|
$this->expectException(Exception::class); |
|
318
|
|
|
$this->expectExceptionMessage($message); |
|
319
|
|
|
|
|
320
|
|
|
$command->execute(); |
|
321
|
|
|
} |
|
322
|
|
|
|
|
323
|
|
|
public function testIntegrityViolation(): void |
|
324
|
|
|
{ |
|
325
|
|
|
$db = $this->getConnection('profile'); |
|
326
|
|
|
|
|
327
|
|
|
$this->expectException(IntegrityException::class); |
|
328
|
|
|
|
|
329
|
|
|
$command = $db->createCommand( |
|
330
|
|
|
<<<SQL |
|
331
|
|
|
INSERT INTO {{profile}}([[id]], [[description]]) VALUES (123, 'duplicate') |
|
332
|
|
|
SQL |
|
333
|
|
|
); |
|
334
|
|
|
$command->execute(); |
|
335
|
|
|
$command->execute(); |
|
336
|
|
|
} |
|
337
|
|
|
|
|
338
|
|
|
public function testLastInsertId(): void |
|
339
|
|
|
{ |
|
340
|
|
|
$db = $this->getConnection('profile'); |
|
341
|
|
|
|
|
342
|
|
|
$command = $db->createCommand(); |
|
343
|
|
|
|
|
344
|
|
|
$sql = <<<SQL |
|
345
|
|
|
INSERT INTO {{profile}}([[description]]) VALUES ('non duplicate') |
|
346
|
|
|
SQL; |
|
347
|
|
|
$command->setSql($sql)->execute(); |
|
348
|
|
|
|
|
349
|
|
|
$this->assertSame('3', $db->getLastInsertID()); |
|
350
|
|
|
} |
|
351
|
|
|
|
|
352
|
|
|
public function testNoTablenameReplacement(): void |
|
353
|
|
|
{ |
|
354
|
|
|
$db = $this->getConnection('customer'); |
|
355
|
|
|
|
|
356
|
|
|
$command = $db->createCommand(); |
|
357
|
|
|
$command->insert( |
|
358
|
|
|
'{{customer}}', |
|
359
|
|
|
['name' => 'Some {{weird}} name', 'email' => '[email protected]', 'address' => 'Some {{%weird}} address'] |
|
360
|
|
|
)->execute(); |
|
361
|
|
|
|
|
362
|
|
|
if ($db->getName() === 'pgsql') { |
|
363
|
|
|
$customerId = $db->getLastInsertID('public.customer_id_seq'); |
|
364
|
|
|
} else { |
|
365
|
|
|
$customerId = $db->getLastInsertID(); |
|
366
|
|
|
} |
|
367
|
|
|
|
|
368
|
|
|
$customer = $command->setSql( |
|
369
|
|
|
<<<SQL |
|
370
|
|
|
SELECT [[name]], [[email]], [[address]] FROM {{customer}} WHERE [[id]]=:id |
|
371
|
|
|
SQL, |
|
372
|
|
|
)->bindValues([':id' => $customerId])->queryOne(); |
|
373
|
|
|
|
|
374
|
|
|
$this->assertIsArray($customer); |
|
375
|
|
|
$this->assertSame('Some {{weird}} name', $customer['name']); |
|
376
|
|
|
$this->assertSame('Some {{%weird}} address', $customer['address']); |
|
377
|
|
|
|
|
378
|
|
|
$command->update( |
|
379
|
|
|
'{{customer}}', |
|
380
|
|
|
['name' => 'Some {{updated}} name', 'address' => 'Some {{%updated}} address'], |
|
381
|
|
|
['id' => $customerId] |
|
382
|
|
|
)->execute(); |
|
383
|
|
|
$customer = $command->setSql( |
|
384
|
|
|
<<<SQL |
|
385
|
|
|
SELECT [[name]], [[email]], [[address]] FROM {{customer}} WHERE [[id]] = :id |
|
386
|
|
|
SQL |
|
387
|
|
|
)->bindValues([':id' => $customerId])->queryOne(); |
|
388
|
|
|
|
|
389
|
|
|
$this->assertIsArray($customer); |
|
390
|
|
|
$this->assertSame('Some {{updated}} name', $customer['name']); |
|
391
|
|
|
$this->assertSame('Some {{%updated}} address', $customer['address']); |
|
392
|
|
|
} |
|
393
|
|
|
|
|
394
|
|
|
public function testQuery(): void |
|
395
|
|
|
{ |
|
396
|
|
|
$db = $this->getConnection('customer'); |
|
397
|
|
|
|
|
398
|
|
|
$command = $db->createCommand(); |
|
399
|
|
|
|
|
400
|
|
|
$command->setSql( |
|
401
|
|
|
<<<SQL |
|
402
|
|
|
SELECT * FROM {{customer}} |
|
403
|
|
|
SQL |
|
404
|
|
|
); |
|
405
|
|
|
|
|
406
|
|
|
$this->assertNull($command->getPdoStatement()); |
|
|
|
|
|
|
407
|
|
|
|
|
408
|
|
|
$reader = $command->query(); |
|
409
|
|
|
|
|
410
|
|
|
// check tests that the reader is a valid iterator |
|
411
|
|
|
if ($db->getName() !== 'sqlite' && $db->getName() !== 'pgsql' && $db->getName() !== 'sqlsrv') { |
|
412
|
|
|
$this->assertEquals(3, $reader->count()); |
|
413
|
|
|
} |
|
414
|
|
|
|
|
415
|
|
|
$this->assertNotNull($command->getPdoStatement()); |
|
416
|
|
|
$this->assertInstanceOf(DataReaderInterface::class, $reader); |
|
417
|
|
|
$this->assertIsInt($reader->count()); |
|
418
|
|
|
|
|
419
|
|
|
foreach ($reader as $row) { |
|
420
|
|
|
$this->assertIsArray($row); |
|
421
|
|
|
$this->assertCount(6, $row); |
|
422
|
|
|
} |
|
423
|
|
|
|
|
424
|
|
|
$command = $db->createCommand('bad SQL'); |
|
425
|
|
|
|
|
426
|
|
|
$this->expectException(Exception::class); |
|
427
|
|
|
|
|
428
|
|
|
$command->query(); |
|
429
|
|
|
} |
|
430
|
|
|
|
|
431
|
|
|
public function testQueryAll(): void |
|
432
|
|
|
{ |
|
433
|
|
|
$db = $this->getConnection('customer'); |
|
434
|
|
|
|
|
435
|
|
|
$command = $db->createCommand(); |
|
436
|
|
|
|
|
437
|
|
|
$command->setSql( |
|
438
|
|
|
<<<SQL |
|
439
|
|
|
SELECT * FROM {{customer}} |
|
440
|
|
|
SQL |
|
441
|
|
|
); |
|
442
|
|
|
$rows = $command->queryAll(); |
|
443
|
|
|
|
|
444
|
|
|
$this->assertIsArray($rows); |
|
445
|
|
|
$this->assertCount(3, $rows); |
|
446
|
|
|
$this->assertIsArray($rows[0]); |
|
447
|
|
|
$this->assertCount(6, $rows[0]); |
|
448
|
|
|
|
|
449
|
|
|
$command->setSql('bad SQL'); |
|
450
|
|
|
|
|
451
|
|
|
$this->expectException(Exception::class); |
|
452
|
|
|
|
|
453
|
|
|
$command->queryAll(); |
|
454
|
|
|
$command->setSql( |
|
455
|
|
|
<<<SQL |
|
456
|
|
|
SELECT * FROM {{customer}} where id = 100 |
|
457
|
|
|
SQL |
|
458
|
|
|
); |
|
459
|
|
|
$rows = $command->queryAll(); |
|
460
|
|
|
|
|
461
|
|
|
$this->assertIsArray($rows); |
|
462
|
|
|
$this->assertCount(0, $rows); |
|
463
|
|
|
$this->assertSame([], $rows); |
|
464
|
|
|
} |
|
465
|
|
|
|
|
466
|
|
|
public function testQueryOne(): void |
|
467
|
|
|
{ |
|
468
|
|
|
$db = $this->getConnection('customer'); |
|
469
|
|
|
|
|
470
|
|
|
$command = $db->createCommand(); |
|
471
|
|
|
$sql = <<<SQL |
|
472
|
|
|
SELECT * FROM {{customer}} ORDER BY [[id]] |
|
473
|
|
|
SQL; |
|
474
|
|
|
$row = $command->setSql($sql)->queryOne(); |
|
475
|
|
|
|
|
476
|
|
|
$this->assertIsArray($row); |
|
477
|
|
|
$this->assertEquals(1, $row['id']); |
|
478
|
|
|
$this->assertEquals('user1', $row['name']); |
|
479
|
|
|
|
|
480
|
|
|
$command->setSql($sql)->prepare(); |
|
481
|
|
|
$row = $command->queryOne(); |
|
482
|
|
|
|
|
483
|
|
|
$this->assertIsArray($row); |
|
484
|
|
|
$this->assertEquals(1, $row['id']); |
|
485
|
|
|
$this->assertEquals('user1', $row['name']); |
|
486
|
|
|
|
|
487
|
|
|
$sql = <<<SQL |
|
488
|
|
|
SELECT * FROM {{customer}} WHERE [[id]] = 10 |
|
489
|
|
|
SQL; |
|
490
|
|
|
$command = $command->setSql($sql); |
|
491
|
|
|
|
|
492
|
|
|
$this->assertNull($command->queryOne()); |
|
493
|
|
|
} |
|
494
|
|
|
|
|
495
|
|
|
public function testQueryCache(): void |
|
496
|
|
|
{ |
|
497
|
|
|
$db = $this->getConnection('customer'); |
|
498
|
|
|
|
|
499
|
|
|
$query = (new Query($db))->select(['name'])->from('customer'); |
|
500
|
|
|
$command = $db->createCommand(); |
|
501
|
|
|
$update = $command->setSql( |
|
502
|
|
|
<<<SQL |
|
503
|
|
|
UPDATE {{customer}} SET [[name]] = :name WHERE [[id]] = :id |
|
504
|
|
|
SQL |
|
505
|
|
|
); |
|
506
|
|
|
|
|
507
|
|
|
$this->assertSame('user1', $query->where(['id' => 1])->scalar(), 'Asserting initial value'); |
|
508
|
|
|
|
|
509
|
|
|
/* No cache */ |
|
510
|
|
|
$update->bindValues([':id' => 1, ':name' => 'user11'])->execute(); |
|
511
|
|
|
|
|
512
|
|
|
$this->assertSame( |
|
513
|
|
|
'user11', |
|
514
|
|
|
$query->where(['id' => 1])->scalar(), |
|
515
|
|
|
'Query reflects DB changes when caching is disabled', |
|
516
|
|
|
); |
|
517
|
|
|
|
|
518
|
|
|
/* Connection cache */ |
|
519
|
|
|
$db->cache( |
|
520
|
|
|
static function (ConnectionPDOInterface $db) use ($query, $update) { |
|
521
|
|
|
self::assertSame('user2', $query->where(['id' => 2])->scalar(), 'Asserting initial value for user #2'); |
|
522
|
|
|
|
|
523
|
|
|
$update->bindValues([':id' => 2, ':name' => 'user22'])->execute(); |
|
524
|
|
|
|
|
525
|
|
|
self::assertSame( |
|
526
|
|
|
'user2', |
|
527
|
|
|
$query->where(['id' => 2])->scalar(), |
|
528
|
|
|
'Query does NOT reflect DB changes when wrapped in connection caching', |
|
529
|
|
|
); |
|
530
|
|
|
|
|
531
|
|
|
$db->noCache( |
|
532
|
|
|
static function () use ($query) { |
|
533
|
|
|
self::assertSame( |
|
534
|
|
|
'user22', |
|
535
|
|
|
$query->where(['id' => 2])->scalar(), |
|
536
|
|
|
'Query reflects DB changes when wrapped in connection caching and noCache simultaneously', |
|
537
|
|
|
); |
|
538
|
|
|
} |
|
539
|
|
|
); |
|
540
|
|
|
|
|
541
|
|
|
self::assertSame( |
|
542
|
|
|
'user2', |
|
543
|
|
|
$query->where(['id' => 2])->scalar(), |
|
544
|
|
|
'Cache does not get changes after getting newer data from DB in noCache block.', |
|
545
|
|
|
); |
|
546
|
|
|
}, |
|
547
|
|
|
10, |
|
548
|
|
|
); |
|
549
|
|
|
|
|
550
|
|
|
$db->queryCacheEnable(false); |
|
551
|
|
|
|
|
552
|
|
|
$db->cache( |
|
553
|
|
|
static function () use ($query, $update) { |
|
554
|
|
|
self::assertSame( |
|
555
|
|
|
'user22', |
|
556
|
|
|
$query->where(['id' => 2])->scalar(), |
|
557
|
|
|
'When cache is disabled for the whole connection, Query inside cache block does not get cached', |
|
558
|
|
|
); |
|
559
|
|
|
|
|
560
|
|
|
$update->bindValues([':id' => 2, ':name' => 'user2'])->execute(); |
|
561
|
|
|
|
|
562
|
|
|
self::assertSame('user2', $query->where(['id' => 2])->scalar()); |
|
563
|
|
|
}, |
|
564
|
|
|
10, |
|
565
|
|
|
); |
|
566
|
|
|
|
|
567
|
|
|
$db->queryCacheEnable(true); |
|
568
|
|
|
$query->cache(); |
|
569
|
|
|
|
|
570
|
|
|
$this->assertSame('user11', $query->where(['id' => 1])->scalar()); |
|
571
|
|
|
|
|
572
|
|
|
$update->bindValues([':id' => 1, ':name' => 'user1'])->execute(); |
|
573
|
|
|
|
|
574
|
|
|
$this->assertSame( |
|
575
|
|
|
'user11', |
|
576
|
|
|
$query->where(['id' => 1])->scalar(), |
|
577
|
|
|
'When both Connection and Query have cache enabled, we get cached value', |
|
578
|
|
|
); |
|
579
|
|
|
$this->assertSame( |
|
580
|
|
|
'user1', |
|
581
|
|
|
$query->noCache()->where(['id' => 1])->scalar(), |
|
582
|
|
|
'When Query has disabled cache, we get actual data', |
|
583
|
|
|
); |
|
584
|
|
|
|
|
585
|
|
|
$db->cache( |
|
586
|
|
|
static function () use ($query) { |
|
587
|
|
|
self::assertSame('user1', $query->noCache()->where(['id' => 1])->scalar()); |
|
588
|
|
|
self::assertSame('user11', $query->cache()->where(['id' => 1])->scalar()); |
|
589
|
|
|
}, |
|
590
|
|
|
10, |
|
591
|
|
|
); |
|
592
|
|
|
} |
|
593
|
|
|
|
|
594
|
|
|
public function testQueryColumn(): void |
|
595
|
|
|
{ |
|
596
|
|
|
$db = $this->getConnection('customer'); |
|
597
|
|
|
|
|
598
|
|
|
$command = $db->createCommand(); |
|
599
|
|
|
$command->setSql( |
|
600
|
|
|
<<<SQL |
|
601
|
|
|
SELECT * FROM {{customer}} |
|
602
|
|
|
SQL |
|
603
|
|
|
); |
|
604
|
|
|
$rows = $command->queryColumn(); |
|
605
|
|
|
|
|
606
|
|
|
$this->assertIsArray($rows); |
|
607
|
|
|
$this->assertCount(3, $rows); |
|
608
|
|
|
$this->assertEquals('1', $rows[0]); |
|
609
|
|
|
|
|
610
|
|
|
$command->setSql('bad SQL'); |
|
611
|
|
|
|
|
612
|
|
|
$this->expectException(Exception::class); |
|
613
|
|
|
|
|
614
|
|
|
$command->queryColumn(); |
|
615
|
|
|
$command->setSql( |
|
616
|
|
|
<<<SQL |
|
617
|
|
|
SELECT * FROM {{customer}} where id = 100 |
|
618
|
|
|
SQL |
|
619
|
|
|
); |
|
620
|
|
|
$rows = $command->queryColumn(); |
|
621
|
|
|
|
|
622
|
|
|
$this->assertIsArray($rows); |
|
623
|
|
|
$this->assertCount(0, $rows); |
|
624
|
|
|
$this->assertSame([], $rows); |
|
625
|
|
|
} |
|
626
|
|
|
|
|
627
|
|
|
public function testQueryScalar(): void |
|
628
|
|
|
{ |
|
629
|
|
|
$db = $this->getConnection('customer'); |
|
630
|
|
|
|
|
631
|
|
|
$command = $db->createCommand(); |
|
632
|
|
|
$sql = <<<SQL |
|
633
|
|
|
SELECT * FROM {{customer}} ORDER BY [[id]] |
|
634
|
|
|
SQL; |
|
635
|
|
|
|
|
636
|
|
|
$this->assertEquals(1, $command->setSql($sql)->queryScalar()); |
|
637
|
|
|
|
|
638
|
|
|
$sql = <<<SQL |
|
639
|
|
|
SELECT [[id]] FROM {{customer}} ORDER BY [[id]] |
|
640
|
|
|
SQL; |
|
641
|
|
|
$command->setSql($sql)->prepare(); |
|
642
|
|
|
|
|
643
|
|
|
$this->assertEquals(1, $command->queryScalar()); |
|
644
|
|
|
|
|
645
|
|
|
$command = $command->setSql( |
|
646
|
|
|
<<<SQL |
|
647
|
|
|
SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10 |
|
648
|
|
|
SQL |
|
649
|
|
|
); |
|
650
|
|
|
|
|
651
|
|
|
$this->assertFalse($command->queryScalar()); |
|
652
|
|
|
} |
|
653
|
|
|
|
|
654
|
|
|
public function testRetryHandler(): void |
|
655
|
|
|
{ |
|
656
|
|
|
$db = $this->getConnection('profile'); |
|
657
|
|
|
|
|
658
|
|
|
$command = $db->createCommand(); |
|
659
|
|
|
|
|
660
|
|
|
$this->assertNull($db->getTransaction()); |
|
661
|
|
|
|
|
662
|
|
|
$command->setSql( |
|
663
|
|
|
<<<SQL |
|
664
|
|
|
INSERT INTO {{profile}}([[description]]) VALUES('command retry') |
|
665
|
|
|
SQL |
|
666
|
|
|
)->execute(); |
|
667
|
|
|
|
|
668
|
|
|
$this->assertNull($db->getTransaction()); |
|
669
|
|
|
$this->assertEquals( |
|
670
|
|
|
1, |
|
671
|
|
|
$command->setSql( |
|
672
|
|
|
<<<SQL |
|
673
|
|
|
SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command retry' |
|
674
|
|
|
SQL |
|
675
|
|
|
)->queryScalar() |
|
676
|
|
|
); |
|
677
|
|
|
|
|
678
|
|
|
$attempts = null; |
|
679
|
|
|
$hitHandler = false; |
|
680
|
|
|
$hitCatch = false; |
|
681
|
|
|
$command->setSql( |
|
682
|
|
|
<<<SQL |
|
683
|
|
|
INSERT INTO {{profile}}([[id]], [[description]]) VALUES(1, 'command retry') |
|
684
|
|
|
SQL |
|
685
|
|
|
); |
|
686
|
|
|
|
|
687
|
|
|
Assert::invokeMethod( |
|
688
|
|
|
$command, |
|
689
|
|
|
'setRetryHandler', |
|
690
|
|
|
[static function ($exception, $attempt) use (&$attempts, &$hitHandler) { |
|
691
|
|
|
$attempts = $attempt; |
|
692
|
|
|
$hitHandler = true; |
|
693
|
|
|
|
|
694
|
|
|
return $attempt <= 2; |
|
695
|
|
|
}] |
|
696
|
|
|
); |
|
697
|
|
|
|
|
698
|
|
|
try { |
|
699
|
|
|
$command->execute(); |
|
700
|
|
|
} catch (Exception $e) { |
|
701
|
|
|
$hitCatch = true; |
|
702
|
|
|
|
|
703
|
|
|
$this->assertInstanceOf(IntegrityException::class, $e); |
|
704
|
|
|
} |
|
705
|
|
|
|
|
706
|
|
|
$this->assertNull($db->getTransaction()); |
|
707
|
|
|
$this->assertSame(3, $attempts); |
|
708
|
|
|
$this->assertTrue($hitHandler); |
|
709
|
|
|
$this->assertTrue($hitCatch); |
|
710
|
|
|
} |
|
711
|
|
|
|
|
712
|
|
|
public function testTransaction(): void |
|
713
|
|
|
{ |
|
714
|
|
|
$db = $this->getConnection('profile'); |
|
715
|
|
|
|
|
716
|
|
|
$this->assertNull($db->getTransaction()); |
|
717
|
|
|
|
|
718
|
|
|
$command = $db->createCommand(); |
|
719
|
|
|
$command = $command->setSql( |
|
720
|
|
|
<<<SQL |
|
721
|
|
|
INSERT INTO {{profile}}([[description]]) VALUES('command transaction') |
|
722
|
|
|
SQL |
|
723
|
|
|
); |
|
724
|
|
|
|
|
725
|
|
|
Assert::invokeMethod($command, 'requireTransaction'); |
|
726
|
|
|
|
|
727
|
|
|
$command->execute(); |
|
728
|
|
|
|
|
729
|
|
|
$this->assertNull($db->getTransaction()); |
|
730
|
|
|
$this->assertEquals( |
|
731
|
|
|
1, |
|
732
|
|
|
$command->setSql( |
|
733
|
|
|
<<<SQL |
|
734
|
|
|
SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction' |
|
735
|
|
|
SQL |
|
736
|
|
|
)->queryScalar(), |
|
737
|
|
|
); |
|
738
|
|
|
} |
|
739
|
|
|
|
|
740
|
|
|
public function testUpdate( |
|
741
|
|
|
string $table, |
|
742
|
|
|
array $columns, |
|
743
|
|
|
array|string $conditions, |
|
744
|
|
|
array $params, |
|
745
|
|
|
string $expected |
|
746
|
|
|
): void { |
|
747
|
|
|
$db = $this->getConnection(); |
|
748
|
|
|
|
|
749
|
|
|
$command = $db->createCommand(); |
|
750
|
|
|
$sql = $command->update($table, $columns, $conditions, $params)->getSql(); |
|
751
|
|
|
|
|
752
|
|
|
$this->assertSame($expected, $sql); |
|
753
|
|
|
} |
|
754
|
|
|
|
|
755
|
|
|
public function testUpsert(array $firstData, array $secondData): void |
|
756
|
|
|
{ |
|
757
|
|
|
$db = $this->getConnection('customer', 't_upsert'); |
|
758
|
|
|
|
|
759
|
|
|
if (version_compare($db->getServerVersion(), '3.8.3', '<')) { |
|
760
|
|
|
$this->markTestSkipped('SQLite < 3.8.3 does not support "WITH" keyword.'); |
|
761
|
|
|
} |
|
762
|
|
|
|
|
763
|
|
|
$this->assertEquals(0, $db->createCommand('SELECT COUNT(*) FROM {{T_upsert}}')->queryScalar()); |
|
764
|
|
|
|
|
765
|
|
|
$this->performAndCompareUpsertResult($db, $firstData); |
|
766
|
|
|
|
|
767
|
|
|
$this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{T_upsert}}')->queryScalar()); |
|
768
|
|
|
|
|
769
|
|
|
$this->performAndCompareUpsertResult($db, $secondData); |
|
770
|
|
|
} |
|
771
|
|
|
} |
|
772
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.