Passed
Push — master ( 37e483...d2fc64 )
by Wilmer
11:53 queued 09:06
created

CommonCommandTest   F

Complexity

Total Complexity 93

Size/Duplication

Total Lines 1617
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 896
dl 0
loc 1617
rs 1.704
c 1
b 0
f 0
wmc 93

56 Methods

Rating   Name   Duplication   Size   Complexity  
A testBatchInsertWithYield() 0 13 1
A testBatchInsertDataTypesLocale() 0 61 4
A testQueryColumn() 0 31 1
A testNoTablenameReplacement() 0 40 2
A testQueryAll() 0 37 3
A testCreateTable() 0 23 2
A testIntegrityViolation() 0 13 1
A testQuery() 0 35 4
A testBatchInsertFailsOld() 0 21 1
B testQueryCache() 0 96 1
A testQueryScalar() 0 25 1
A testBatchInsertWithManyData() 0 19 2
A testExecute() 0 32 1
A testUpdate() 0 13 1
A testTransaction() 0 25 1
A testDataReaderRewindException() 0 15 1
A testQueryOne() 0 27 1
A testCreateView() 0 30 3
A testDropView() 0 14 1
A testUpsert() 0 27 1
A testDropDefaultValue() 0 25 2
A testDropCheck() 0 25 2
A testInsertEx() 0 14 1
A testAddPrimaryKey() 0 22 3
A testRenameColumn() 0 11 1
A testDropUnique() 0 22 2
A testInsert() 0 27 1
A performAndCompareUpsertResult() 0 16 1
A testSetRetryHandler() 0 56 2
A testBatchInsert() 0 20 1
A testTruncateTable() 0 21 1
A testAddCommentOnTable() 0 9 1
A testDropCommentFromTable() 0 14 1
A testDropCommentFromColumn() 0 23 1
A testExecuteWithTransaction() 0 46 1
A testAddCheck() 0 20 2
A testAddUnique() 0 22 3
A testDropForeignKey() 0 22 2
A testsInsertQueryAsColumnValue() 0 44 2
A testInsertSelectAlias() 0 41 1
A testDelete() 0 17 1
A testInsertSelectFailed() 0 12 1
A testExecuteWithoutSql() 0 8 1
A testAddCommentOnColumn() 0 16 1
A testInsertSelect() 0 41 1
A testAddForeignKey() 0 45 4
A testInsertToBlob() 0 25 2
A testInsertExpression() 0 34 1
A testDropPrimaryKey() 0 22 2
A testRenameTable() 0 18 2
A testAddColumn() 0 9 1
A testDropColumn() 0 19 2
A testCreateIndex() 0 32 4
A testAddDefaultValue() 0 20 2
A testDropTable() 0 18 2
A testDropIndex() 0 23 2

How to fix   Complexity   

Complex Class

Complex classes like CommonCommandTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CommonCommandTest, and based on these observations, apply Extract Interface, too.

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\InvalidArgumentException;
12
use Yiisoft\Db\Exception\InvalidCallException;
13
use Yiisoft\Db\Expression\Expression;
14
use Yiisoft\Db\Expression\ExpressionInterface;
15
use Yiisoft\Db\Query\Data\DataReaderInterface;
16
use Yiisoft\Db\Query\Query;
17
use Yiisoft\Db\Schema\Schema;
18
use Yiisoft\Db\Tests\AbstractCommandTest;
19
use Yiisoft\Db\Tests\Support\Assert;
20
use Yiisoft\Db\Tests\Support\DbHelper;
21
use Yiisoft\Db\Transaction\TransactionInterface;
22
23
use function call_user_func_array;
24
use function is_string;
25
use function setlocale;
26
27
abstract class CommonCommandTest extends AbstractCommandTest
28
{
29
    public function testAddCheck(): void
30
    {
31
        $db = $this->getConnection();
32
33
        $command = $db->createCommand();
34
        $schema = $db->getSchema();
35
36
        if ($schema->getTableSchema('{{test_ck}}') !== null) {
37
            $command->dropTable('{{test_ck}}')->execute();
38
        }
39
40
        $command->createTable('{{test_ck}}', ['int1' => 'integer'])->execute();
41
42
        $this->assertEmpty($schema->getTableChecks('{{test_ck}}', true));
43
44
        $command->addCheck('{{test_ck_constraint}}', '{{test_ck}}', '{{int1}} > 1')->execute();
45
46
        $this->assertMatchesRegularExpression(
47
            '/^.*int1.*>.*1.*$/',
48
            $schema->getTableChecks('{{test_ck}}', true)[0]->getExpression()
49
        );
50
    }
51
52
    public function testAddColumn(): void
53
    {
54
        $db = $this->getConnection(true);
55
56
        $command = $db->createCommand();
57
        $command->addColumn('{{customer}}', '{{city}}', Schema::TYPE_STRING)->execute();
58
59
        $this->assertTrue($db->getTableSchema('{{customer}}')->getColumn('city') !== null);
60
        $this->assertSame(Schema::TYPE_STRING, $db->getTableSchema('{{customer}}')->getColumn('city')->getType());
61
    }
62
63
    public function testAddCommentOnColumn(): void
64
    {
65
        $db = $this->getConnection(true);
66
67
        $command = $db->createCommand();
68
        $schema = $db->getSchema();
69
        $command->addCommentOnColumn('{{customer}}', 'id', 'Primary key.')->execute();
70
71
        $commentOnColumn = match ($db->getName()) {
72
            'mysql', 'pgsql', 'oci' => [
73
                'comment' => $schema->getTableSchema('{{customer}}')->getColumn('id')->getComment(),
74
            ],
75
            'sqlsrv' => DbHelper::getCommmentsFromColumn('customer', 'id', $db),
76
        };
77
78
        $this->assertSame(['comment' => 'Primary key.'], $commentOnColumn);
79
    }
80
81
    public function testAddCommentOnTable(): void
82
    {
83
        $db = $this->getConnection(true);
84
85
        $command = $db->createCommand();
86
        $command->addCommentOnTable('{{customer}}', 'Customer table.')->execute();
87
        $commentOnTable = DbHelper::getCommmentsFromTable('customer', $db);
88
89
        $this->assertSame(['comment' => 'Customer table.'], $commentOnTable);
90
    }
91
92
    public function testAddDefaultValue()
93
    {
94
        $db = $this->getConnection();
95
96
        $command = $db->createCommand();
97
        $schema = $db->getSchema();
98
99
        if ($schema->getTableSchema('{{test_def}}') !== null) {
100
            $command->dropTable('{{test_def}}')->execute();
101
        }
102
103
        $command->createTable('{{test_def}}', ['int1' => Schema::TYPE_INTEGER])->execute();
104
105
        $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}', true));
106
107
        $command->addDefaultValue('{{test_def_constraint}}', '{{test_def}}', 'int1', 41)->execute();
108
109
        $this->assertMatchesRegularExpression(
110
            '/^.*41.*$/',
111
            $schema->getTableDefaultValues('{{test_def}}', true)[0]->getValue(),
112
        );
113
    }
114
115
    /**
116
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addForeignKey()
117
     */
118
    public function testAddForeignKey(
119
        string $name,
120
        string $tableName,
121
        array|string $column1,
122
        array|string $column2,
123
        string $expectedName,
124
    ): void {
125
        $db = $this->getConnection();
126
127
        $command = $db->createCommand();
128
        $schema = $db->getSchema();
129
130
        if ($schema->getTableSchema($tableName) !== null) {
131
            $command->dropTable($tableName)->execute();
132
        }
133
134
        $command->createTable(
135
            $tableName,
136
            [
137
                'int1' => 'integer not null unique',
138
                'int2' => 'integer not null unique',
139
                'int3' => 'integer not null unique',
140
                'int4' => 'integer not null unique',
141
                'unique ([[int1]], [[int2]])',
142
                'unique ([[int3]], [[int4]])',
143
            ],
144
        )->execute();
145
146
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
147
148
        $command->addForeignKey($name, $tableName, $column1, $tableName, $column2)->execute();
149
150
        $this->assertSame($expectedName, $schema->getTableForeignKeys($tableName, true)[0]->getName());
151
152
        if (is_string($column1)) {
0 ignored issues
show
introduced by
The condition is_string($column1) is always false.
Loading history...
153
            $column1 = [$column1];
154
        }
155
156
        $this->assertSame($column1, $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames());
157
158
        if (is_string($column2)) {
0 ignored issues
show
introduced by
The condition is_string($column2) is always false.
Loading history...
159
            $column2 = [$column2];
160
        }
161
162
        $this->assertSame($column2, $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames());
163
    }
164
165
    /**
166
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addPrimaryKey()
167
     */
168
    public function testAddPrimaryKey(string $name, string $tableName, array|string $column): void
169
    {
170
        $db = $this->getConnection();
171
172
        $command = $db->createCommand();
173
        $schema = $db->getSchema();
174
175
        if ($schema->getTableSchema($tableName) !== null) {
176
            $command->dropTable($tableName)->execute();
177
        }
178
179
        $command->createTable($tableName, ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
180
181
        $this->assertNull($schema->getTablePrimaryKey($tableName, true));
182
183
        $db->createCommand()->addPrimaryKey($name, $tableName, $column)->execute();
184
185
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always false.
Loading history...
186
            $column = [$column];
187
        }
188
189
        $this->assertSame($column, $schema->getTablePrimaryKey($tableName, true)->getColumnNames());
190
    }
191
192
    /**
193
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addUnique()
194
     */
195
    public function testAddUnique(string $name, string $tableName, array|string $column): void
196
    {
197
        $db = $this->getConnection();
198
199
        $command = $db->createCommand();
200
        $schema = $db->getSchema();
201
202
        if ($schema->getTableSchema($tableName) !== null) {
203
            $command->dropTable($tableName)->execute();
204
        }
205
206
        $command->createTable($tableName, ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
207
208
        $this->assertEmpty($schema->getTableUniques($tableName, true));
209
210
        $command->addUnique($name, $tableName, $column)->execute();
211
212
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always false.
Loading history...
213
            $column = [$column];
214
        }
215
216
        $this->assertSame($column, $schema->getTableUniques($tableName, true)[0]->getColumnNames());
217
    }
218
219
    /**
220
     * Make sure that `{{something}}` in values will not be encoded.
221
     *
222
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::batchInsert()
223
     *
224
     * {@see https://github.com/yiisoft/yii2/issues/11242}
225
     */
226
    public function testBatchInsert(
227
        string $table,
228
        array $columns,
229
        array $values,
230
        string $expected,
231
        array $expectedParams = [],
232
        int $insertedRow = 1
233
    ): void {
234
        $db = $this->getConnection(true);
235
236
        $command = $db->createCommand();
237
        $command->batchInsert($table, $columns, $values);
238
        $command->prepare(false);
239
240
        $this->assertSame($expected, $command->getSql());
241
        $this->assertSame($expectedParams, $command->getParams());
242
243
        $command->execute();
244
245
        $this->assertEquals($insertedRow, (new Query($db))->from($table)->count());
246
    }
247
248
    /**
249
     * Test batch insert with different data types.
250
     *
251
     * Ensure double is inserted with `.` decimal separator.
252
     *
253
     * @link https://github.com/yiisoft/yii2/issues/6526
254
     */
255
    public function testBatchInsertDataTypesLocale(): void
256
    {
257
        $locale = setlocale(LC_NUMERIC, 0);
258
259
        if ($locale === false) {
260
            $this->markTestSkipped('Your platform does not support locales.');
261
        }
262
263
        $db = $this->getConnection(true);
264
265
        $command = $db->createCommand();
266
267
        try {
268
            /* This one sets decimal mark to comma sign */
269
            setlocale(LC_NUMERIC, 'ru_RU.utf8');
270
271
            $cols = ['int_col', 'char_col', 'float_col', 'bool_col'];
272
            $data = [[1, 'A', 9.735, true], [2, 'B', -2.123, false], [3, 'C', 2.123, false]];
273
274
            /* clear data in "type" table */
275
            $command->delete('{{type}}')->execute();
276
277
            /* change, for point oracle. */
278
            if ($db->getName() === 'oci') {
279
                $command->setSql(
280
                    <<<SQL
281
                    ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'
282
                    SQL
283
                )->execute();
284
            }
285
286
            /* batch insert on "type" table */
287
            $command->batchInsert('{{type}}', $cols, $data)->execute();
288
            $data = $command->setSql(
289
                <<<SQL
290
                SELECT [[int_col]], [[char_col]], [[float_col]], [[bool_col]] FROM {{type}} WHERE [[int_col]] IN (1,2,3) ORDER BY [[int_col]]
291
                SQL
292
            )->queryAll();
293
294
            $this->assertCount(3, $data);
295
            $this->assertEquals(1, $data[0]['int_col']);
296
            $this->assertEquals(2, $data[1]['int_col']);
297
            $this->assertEquals(3, $data[2]['int_col']);
298
299
            /* rtrim because Postgres padds the column with whitespace */
300
            $this->assertSame('A', rtrim($data[0]['char_col']));
301
            $this->assertSame('B', rtrim($data[1]['char_col']));
302
            $this->assertSame('C', rtrim($data[2]['char_col']));
303
            $this->assertEquals(9.735, $data[0]['float_col']);
304
            $this->assertEquals(-2.123, $data[1]['float_col']);
305
            $this->assertEquals(2.123, $data[2]['float_col']);
306
            $this->assertEquals(1, $data[0]['bool_col']);
307
            Assert::isOneOf($data[1]['bool_col'], ['0', false]);
308
            Assert::isOneOf($data[2]['bool_col'], ['0', false]);
309
        } catch (Exception | Throwable $e) {
310
            setlocale(LC_NUMERIC, $locale);
311
312
            throw $e;
313
        }
314
315
        setlocale(LC_NUMERIC, $locale);
316
    }
317
318
    public function testBatchInsertFailsOld(): void
319
    {
320
        $db = $this->getConnection(true);
321
322
        $command = $db->createCommand();
323
        $command->batchInsert(
324
            '{{customer}}',
325
            ['email', 'name', 'address'],
326
            [['[email protected]', 'test_name', 'test_address']],
327
        );
328
329
        $this->assertSame(1, $command->execute());
330
331
        $result = (new Query($db))
332
            ->select(['email', 'name', 'address'])
333
            ->from('{{customer}}')
334
            ->where(['=', '{{email}}', '[email protected]'])
335
            ->one();
336
337
        $this->assertCount(3, $result);
338
        $this->assertSame(['email' => '[email protected]', 'name' => 'test_name', 'address' => 'test_address'], $result);
339
    }
340
341
    public function testBatchInsertWithManyData(): void
342
    {
343
        $db = $this->getConnection(true);
344
345
        $values = [];
346
        $attemptsInsertRows = 200;
347
        $command = $db->createCommand();
348
349
        for ($i = 0; $i < $attemptsInsertRows; $i++) {
350
            $values[$i] = ['t' . $i . '@any.com', 't' . $i, 't' . $i . ' address'];
351
        }
352
353
        $command->batchInsert('{{customer}}', ['email', 'name', 'address'], $values);
354
355
        $this->assertSame($attemptsInsertRows, $command->execute());
356
357
        $insertedRowsCount = (new Query($db))->from('{{customer}}')->count();
358
359
        $this->assertGreaterThanOrEqual($attemptsInsertRows, $insertedRowsCount);
360
    }
361
362
    public function testBatchInsertWithYield(): void
363
    {
364
        $db = $this->getConnection(true);
365
366
        $rows = (
367
            static function () {
368
                yield ['[email protected]', 'test name', 'test address'];
369
            }
370
        )();
371
        $command = $db->createCommand();
372
        $command->batchInsert('{{customer}}', ['email', 'name', 'address'], $rows);
373
374
        $this->assertSame(1, $command->execute());
375
    }
376
377
    /**
378
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::createIndex()
379
     */
380
    public function testCreateIndex(
381
        string $name,
382
        string $tableName,
383
        array|string $column,
384
        string|null $indexType,
385
        string|null $indexMethod,
386
    ): void {
387
        $db = $this->getConnection();
388
389
        $command = $db->createCommand();
390
        $schema = $db->getSchema();
391
392
        if ($schema->getTableSchema($tableName) !== null) {
393
            $command->dropTable($tableName)->execute();
394
        }
395
396
        $command->createTable($tableName, ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
397
398
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
399
400
        $command->createIndex($name, $tableName, $column, $indexType, $indexMethod)->execute();
401
402
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always false.
Loading history...
403
            $column = [$column];
404
        }
405
406
        $this->assertSame($column, $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
407
408
        if ($indexType === 'UNIQUE') {
409
            $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
410
        } else {
411
            $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
412
        }
413
    }
414
415
    public function testCreateTable(): void
416
    {
417
        $db = $this->getConnection();
418
419
        $command = $db->createCommand();
420
        $schema = $db->getSchema();
421
422
        if ($schema->getTableSchema('{{testCreateTable}}', true) !== null) {
423
            $command->dropTable('{{testCreateTable}}')->execute();
424
        }
425
426
        $command->createTable(
427
            '{{testCreateTable}}',
428
            ['[[id]]' => Schema::TYPE_PK, '[[bar]]' => Schema::TYPE_INTEGER],
429
        )->execute();
430
        $command->insert('{{testCreateTable}}', ['[[bar]]' => 1])->execute();
431
        $records = $command->setSql(
432
            <<<SQL
433
            SELECT [[id]], [[bar]] FROM [[testCreateTable]];
434
            SQL
435
        )->queryAll();
436
437
        $this->assertEquals([['id' => 1, 'bar' => 1]], $records);
438
    }
439
440
    public function testCreateView(): void
441
    {
442
        $db = $this->getConnection();
443
444
        $command = $db->createCommand();
445
        $schema = $db->getSchema();
446
        $subQuery = (new Query($db))->select('{{bar}}')->from('{{testCreateViewTable}}')->where(['>', 'bar', '5']);
447
448
        if ($schema->getTableSchema('{{testCreateView}}') !== null) {
449
            $command->dropView('{{testCreateView}}')->execute();
450
        }
451
452
        if ($schema->getTableSchema('{{testCreateViewTable}}')) {
453
            $command->dropTable('{{testCreateViewTable}}')->execute();
454
        }
455
456
        $command->createTable(
457
            '{{testCreateViewTable}}',
458
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER],
459
        )->execute();
460
        $command->insert('{{testCreateViewTable}}', ['bar' => 1])->execute();
461
        $command->insert('{{testCreateViewTable}}', ['bar' => 6])->execute();
462
        $command->createView('{{testCreateView}}', $subQuery)->execute();
463
        $records = $command->setSql(
464
            <<<SQL
465
            SELECT [[bar]] FROM {{testCreateView}};
466
            SQL
467
        )->queryAll();
468
469
        $this->assertEquals([['bar' => 6]], $records);
470
    }
471
472
    public function testDataReaderRewindException(): void
473
    {
474
        $db = $this->getConnection(true);
475
476
        $this->expectException(InvalidCallException::class);
477
        $this->expectExceptionMessage('DataReader cannot rewind. It is a forward-only reader.');
478
479
        $command = $db->createCommand();
480
        $reader = $command->setSql(
481
            <<<SQL
482
            SELECT * FROM {{customer}}
483
            SQL
484
        )->query();
485
        $reader->next();
486
        $reader->rewind();
487
    }
488
489
    public function testDelete(): void
490
    {
491
        $db = $this->getConnection(true);
492
493
        $command = $db->createCommand();
494
        $command->delete('{{customer}}', ['id' => 2])->execute();
495
        $chekSql = <<<SQL
496
        SELECT COUNT([[id]]) FROM [[customer]]
497
        SQL;
498
        $command->setSql($chekSql);
499
500
        $this->assertSame('2', $command->queryScalar());
501
502
        $command->delete('{{customer}}', ['id' => 3])->execute();
503
        $command->setSql($chekSql);
504
505
        $this->assertSame('1', $command->queryScalar());
506
    }
507
508
    public function testDropCheck()
509
    {
510
        $db = $this->getConnection();
511
512
        $command = $db->createCommand();
513
        $schema = $db->getSchema();
514
515
        if ($schema->getTableSchema('{{test_ck}}') !== null) {
516
            $command->dropTable('{{test_ck}}')->execute();
517
        }
518
519
        $command->createTable('{{test_ck}}', ['int1' => 'integer'])->execute();
520
521
        $this->assertEmpty($schema->getTableChecks('{{test_ck}}', true));
522
523
        $command->addCheck('{{test_ck_constraint}}', '{{test_ck}}', '[[int1]] > 1')->execute();
524
525
        $this->assertMatchesRegularExpression(
526
            '/^.*int1.*>.*1.*$/',
527
            $schema->getTableChecks('{{test_ck}}', true)[0]->getExpression(),
528
        );
529
530
        $command->dropCheck('{{test_ck_constraint}}', '{{test_ck}}')->execute();
531
532
        $this->assertEmpty($schema->getTableChecks('{{test_ck}}', true));
533
    }
534
535
    public function testDropColumn(): void
536
    {
537
        $db = $this->getConnection();
538
539
        $command = $db->createCommand();
540
        $schema = $db->getSchema();
541
542
        if ($schema->getTableSchema('{{testDropColumn}}', true) !== null) {
543
            $command->dropTable('{{testDropColumn}}')->execute();
544
        }
545
546
        $command->createTable(
547
            '{{testDropColumn}}',
548
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER, 'baz' => Schema::TYPE_INTEGER],
549
        )->execute();
550
        $command->dropColumn('{{testDropColumn}}', 'bar')->execute();
551
552
        $this->assertArrayNotHasKey('bar', $schema->getTableSchema('{{testDropColumn}}')->getColumns());
553
        $this->assertArrayHasKey('baz', $schema->getTableSchema('{{testDropColumn}}')->getColumns());
554
    }
555
556
    public function testDropCommentFromColumn(): void
557
    {
558
        $db = $this->getConnection(true);
559
560
        $command = $db->createCommand();
561
        $schema = $db->getSchema();
562
        $command->addCommentOnColumn('{{customer}}', 'id', 'Primary key.')->execute();
563
        $commentOnColumn = match ($db->getName()) {
564
            'mysql', 'pgsql', 'oci' => [
565
                'comment' => $schema->getTableSchema('{{customer}}')->getColumn('id')->getComment(),
566
            ],
567
            'sqlsrv' => DbHelper::getCommmentsFromColumn('customer', 'id', $db),
568
        };
569
570
        $this->assertSame(['comment' => 'Primary key.'], $commentOnColumn);
571
572
        $command->dropCommentFromColumn('{{customer}}', 'id')->execute();
573
        $commentOnColumn = match ($db->getName()) {
574
            'mysql', 'pgsql', 'oci' => $schema->getTableSchema('{{customer}}')->getColumn('id')->getComment(),
575
            'sqlsrv' => DbHelper::getCommmentsFromColumn('customer', 'id', $db),
576
        };
577
578
        $this->assertEmpty($commentOnColumn);
579
    }
580
581
    public function testDropCommentFromTable(): void
582
    {
583
        $db = $this->getConnection(true);
584
585
        $command = $db->createCommand();
586
        $command->addCommentOnTable('{{customer}}', 'Customer table.')->execute();
587
        $commentOnTable = DbHelper::getCommmentsFromTable('customer', $db);
588
589
        $this->assertSame(['comment' => 'Customer table.'], $commentOnTable);
590
591
        $command->dropCommentFromTable('{{customer}}')->execute();
592
        $commentOnTable = DbHelper::getCommmentsFromTable('customer', $db);
593
594
        $this->assertNull($commentOnTable);
595
    }
596
597
    public function testDropDefaultValue(): void
598
    {
599
        $db = $this->getConnection();
600
601
        $command = $db->createCommand();
602
        $schema = $db->getSchema();
603
604
        if ($schema->getTableSchema('{{test_def}}') !== null) {
605
            $command->dropTable('{{test_def}}')->execute();
606
        }
607
608
        $command->createTable('{{test_def}}', ['int1' => 'integer'])->execute();
609
610
        $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}', true));
611
612
        $command->addDefaultValue('{{test_def_constraint}}', '{{test_def}}', 'int1', 41)->execute();
613
614
        $this->assertMatchesRegularExpression(
615
            '/^.*41.*$/',
616
            $schema->getTableDefaultValues('{{test_def}}', true)[0]->getValue(),
617
        );
618
619
        $command->dropDefaultValue('{{test_def_constraint}}', '{{test_def}}')->execute();
620
621
        $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}', true));
622
    }
623
624
    public function testDropForeignKey(): void
625
    {
626
        $db = $this->getConnection();
627
628
        $command = $db->createCommand();
629
        $schema = $db->getSchema();
630
631
        if ($schema->getTableSchema('{{test_fk}}') !== null) {
632
            $command->dropTable('{{test_fk}}')->execute();
633
        }
634
635
        $command->createTable('{{test_fk}}', ['id' => Schema::TYPE_PK, 'int1' => 'integer'])->execute();
636
637
        $this->assertEmpty($schema->getTableForeignKeys('{{test_fk}}', true));
638
639
        $command->addForeignKey('{{test_fk_constraint}}', '{{test_fk}}', 'int1', '{{test_fk}}', 'id')->execute();
640
641
        $this->assertNotEmpty($schema->getTableForeignKeys('{{test_fk}}', true));
642
643
        $command->dropForeignKey('{{test_fk_constraint}}', '{{test_fk}}')->execute();
644
645
        $this->assertEmpty($schema->getTableForeignKeys('{{test_fk}}', true));
646
    }
647
648
    public function testDropIndex(): void
649
    {
650
        $db = $this->getConnection();
651
652
        $command = $db->createCommand();
653
        $schema = $db->getSchema();
654
655
        if ($schema->getTableSchema('{{test_idx}}') !== null) {
656
            $command->dropTable('{{test_idx}}')->execute();
657
        }
658
659
        $command->createTable('{{test_idx}}', ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
660
661
        $this->assertEmpty($schema->getTableIndexes('{[test_idx}}', true));
662
663
        $command->createIndex('{{test_idx_constraint}}', '{{test_idx}}', ['int1', 'int2'], 'UNIQUE')->execute();
664
665
        $this->assertSame(['int1', 'int2'], $schema->getTableIndexes('{{test_idx}}', true)[0]->getColumnNames());
666
        $this->assertTrue($schema->getTableIndexes('{{test_idx}}', true)[0]->isUnique());
667
668
        $command->dropIndex('{{test_idx_constraint}}', '{{test_idx}}')->execute();
669
670
        $this->assertEmpty($schema->getTableIndexes('{{test_idx}}', true));
671
    }
672
673
    public function testDropPrimaryKey(): void
674
    {
675
        $db = $this->getConnection();
676
677
        $command = $db->createCommand();
678
        $schema = $db->getSchema();
679
680
        if ($schema->getTableSchema('{{test_pk}}') !== null) {
681
            $command->dropTable('{{test_pk}}')->execute();
682
        }
683
684
        $command->createTable('{{test_pk}}', ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
685
686
        $this->assertEmpty($schema->getTableSchema('{{test_pk}}', true)->getPrimaryKey());
687
688
        $command->addPrimaryKey('{{test_pk_constraint}}', '{{test_pk}}', ['int1', 'int2'])->execute();
689
690
        $this->assertSame(['int1', 'int2'], $schema->getTableSchema('{{test_pk}}', true)->getColumnNames());
691
692
        $command->dropPrimaryKey('{{test_pk_constraint}}', '{{test_pk}}')->execute();
693
694
        $this->assertEmpty($schema->getTableSchema('{{test_pk}}', true)->getPrimaryKey());
695
    }
696
697
    public function testDropTable(): void
698
    {
699
        $db = $this->getConnection();
700
701
        $command = $db->createCommand();
702
        $schema = $db->getSchema();
703
704
        if ($schema->getTableSchema('{{testDropTable}}') !== null) {
705
            $command->dropTable('{{testDropTable}}')->execute();
706
        }
707
708
        $command->createTable('{{testDropTable}}', ['id' => Schema::TYPE_PK, 'foo' => 'integer'])->execute();
709
710
        $this->assertNotNull($schema->getTableSchema('{{testDropTable}}', true));
711
712
        $command->dropTable('{{testDropTable}}')->execute();
713
714
        $this->assertNull($schema->getTableSchema('{{testDropTable}}', true));
715
    }
716
717
    public function testDropUnique(): void
718
    {
719
        $db = $this->getConnection();
720
721
        $command = $db->createCommand();
722
        $schema = $db->getSchema();
723
724
        if ($schema->getTableSchema('{{test_uq}}') !== null) {
725
            $command->dropTable('{{test_uq}}')->execute();
726
        }
727
728
        $command->createTable('{{test_uq}}', ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
729
730
        $this->assertEmpty($schema->getTableUniques('{{test_uq}}', true));
731
732
        $command->addUnique('{{test_uq_constraint}}', '{{test_uq}}', ['int1'])->execute();
733
734
        $this->assertSame(['int1'], $schema->getTableUniques('{{test_uq}}', true)[0]->getColumnNames());
735
736
        $command->dropUnique('{{test_uq_constraint}}', '{{test_uq}}')->execute();
737
738
        $this->assertEmpty($schema->getTableUniques('{{test_uq}}', true));
739
    }
740
741
    public function testDropView(): void
742
    {
743
        $db = $this->getConnection(true);
744
745
        /* since it already exists in the fixtures */
746
        $viewName = '{{animal_view}}';
747
748
        $schema = $db->getSchema();
749
750
        $this->assertNotNull($schema->getTableSchema($viewName));
751
752
        $db->createCommand()->dropView($viewName)->execute();
753
754
        $this->assertNull($schema->getTableSchema($viewName));
755
    }
756
757
    public function testExecute(): void
758
    {
759
        $db = $this->getConnection(true);
760
761
        $command = $db->createCommand();
762
        $command->setSql(
763
            <<<SQL
764
            INSERT INTO [[customer]] ([[email]], [[name]], [[address]]) VALUES ('[email protected]', 'user4', 'address4')
765
            SQL
766
        );
767
768
        $this->assertSame(1, $command->execute());
769
770
        $command = $command->setSql(
771
            <<<SQL
772
            SELECT COUNT(*) FROM [[customer]] WHERE [[name]] = 'user4'
773
            SQL
774
        );
775
776
        $this->assertEquals(1, $command->queryScalar());
777
778
        $command->setSql('bad SQL');
779
        $message = match ($db->getName()) {
780
            'pgsql' => 'SQLSTATE[42601]',
781
            'sqlite', 'oci' => 'SQLSTATE[HY000]',
782
            default => 'SQLSTATE[42000]',
783
        };
784
785
        $this->expectException(Exception::class);
786
        $this->expectExceptionMessage($message);
787
788
        $command->execute();
789
    }
790
791
    public function testExecuteWithoutSql(): void
792
    {
793
        $db = $this->getConnection();
794
795
        $command = $db->createCommand();
796
        $result = $command->setSql('')->execute();
797
798
        $this->assertSame(0, $result);
799
    }
800
801
    public function testExecuteWithTransaction(): void
802
    {
803
        $db = $this->getConnection(true);
804
805
        $this->assertNull($db->getTransaction());
806
807
        $command = $db->createCommand(
808
            <<<SQL
809
            INSERT INTO {{profile}} ([[description]]) VALUES('command transaction 1')
810
            SQL,
811
        );
812
813
        Assert::invokeMethod($command, 'requireTransaction');
814
815
        $command->execute();
816
817
        $this->assertNull($db->getTransaction());
818
819
        $this->assertEquals(
820
            1,
821
            $db->createCommand(
822
                <<<SQL
823
                SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction 1'
824
                SQL,
825
            )->queryScalar(),
826
        );
827
828
        $command = $db->createCommand(
829
            <<<SQL
830
            INSERT INTO {{profile}} ([[description]]) VALUES('command transaction 2')
831
            SQL,
832
        );
833
834
        Assert::invokeMethod($command, 'requireTransaction', [TransactionInterface::READ_UNCOMMITTED]);
835
836
        $command->execute();
837
838
        $this->assertNull($db->getTransaction());
839
840
        $this->assertEquals(
841
            1,
842
            $db->createCommand(
843
                <<<SQL
844
                SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction 2'
845
                SQL,
846
            )->queryScalar(),
847
        );
848
    }
849
850
    public function testInsert(): void
851
    {
852
        $db = $this->getConnection(true);
853
854
        $command = $db->createCommand();
855
        $command->delete('{{customer}}')->execute();
856
        $command->insert(
857
            '{{customer}}',
858
            ['[[email]]' => '[email protected]', '[[name]]' => 'test', '[[address]]' => 'test address']
859
        )->execute();
860
861
        $this->assertEquals(
862
            1,
863
            $command->setSql(
864
                <<<SQL
865
                SELECT COUNT(*) FROM {{customer}}
866
                SQL
867
            )->queryScalar(),
868
        );
869
870
        $record = $command->setSql(
871
            <<<SQL
872
            SELECT [[email]], [[name]], [[address]] FROM {{customer}}
873
            SQL
874
        )->queryOne();
875
876
        $this->assertSame(['email' => '[email protected]', 'name' => 'test', 'address' => 'test address'], $record);
877
    }
878
879
    public function testInsertEx(): void
880
    {
881
        $db = $this->getConnection(true);
882
883
        $command = $db->createCommand();
884
885
        $expected = match ($db->getName()) {
886
            'pgsql' => ['id' => 4],
887
            default => ['id' => '4'],
888
        };
889
890
        $this->assertSame(
891
            $expected,
892
            $command->insertEx('{{customer}}', ['name' => 'test_1', 'email' => '[email protected]']),
893
        );
894
    }
895
896
    public function testInsertExpression(): void
897
    {
898
        $db = $this->getConnection(true);
899
900
        $command = $db->createCommand();
901
        $command->delete('{{order_with_null_fk}}')->execute();
902
        $expression = match ($db->getName()) {
903
            'mysql' => 'YEAR(NOW())',
904
            'oci' => "TO_CHAR(SYSDATE, 'YYYY')",
905
            'pgsql' => "EXTRACT(YEAR FROM TIMESTAMP 'now')",
906
            'sqlite' => "strftime('%Y')",
907
            'sqlsrv' => 'YEAR(GETDATE())',
908
        };
909
        $command->insert(
910
            '{{order_with_null_fk}}',
911
            ['created_at' => new Expression($expression), 'total' => 1],
912
        )->execute();
913
914
        $this->assertEquals(
915
            1,
916
            $command->setSql(
917
                <<<SQL
918
                SELECT COUNT(*) FROM {{order_with_null_fk}}
919
                SQL
920
            )->queryScalar(),
921
        );
922
923
        $record = $command->setSql(
924
            <<<SQL
925
            SELECT [[created_at]] FROM {{order_with_null_fk}}
926
            SQL
927
        )->queryOne();
928
929
        $this->assertEquals(['created_at' => date('Y')], $record);
930
    }
931
932
    public function testsInsertQueryAsColumnValue(): void
933
    {
934
        $db = $this->getConnection(true);
935
936
        $command = $db->createCommand();
937
        $time = time();
938
        $command->setSql(
939
            <<<SQL
940
            DELETE FROM [[order_with_null_fk]]
941
            SQL
942
        )->execute();
943
        $command->insert('{{order}}', ['customer_id' => 1, 'created_at' => $time, 'total' => 42])->execute();
944
945
        if ($db->getName() === 'pgsql') {
946
            $orderId = $db->getLastInsertID('public.order_id_seq');
947
        } else {
948
            $orderId = $db->getLastInsertID();
949
        }
950
951
        $columnValueQuery = (new Query($db))->select('{{created_at}}')->from('{{order}}')->where(['id' => $orderId]);
952
        $command->insert(
953
            '{{order_with_null_fk}}',
954
            ['customer_id' => $orderId, 'created_at' => $columnValueQuery, 'total' => 42],
955
        )->execute();
956
957
        $this->assertEquals(
958
            $time,
959
            $command->setSql(
960
                <<<SQL
961
                SELECT [[created_at]] FROM [[order_with_null_fk]] WHERE [[customer_id]] = :id
962
                SQL
963
            )->bindValues([':id' => $orderId])->queryScalar(),
964
        );
965
966
        $command->setSql(
967
            <<<SQL
968
            DELETE FROM [[order_with_null_fk]]
969
            SQL
970
        )->execute();
971
        $command->setSql(
972
            <<<SQL
973
            DELETE FROM [[order]]
974
            SQL
975
        )->execute();
976
    }
977
978
    public function testInsertSelect(): void
979
    {
980
        $db = $this->getConnection(true);
981
982
        $command = $db->createCommand();
983
        $command->setSql(
984
            <<<SQL
985
            DELETE FROM {{customer}}
986
            SQL
987
        )->execute();
988
        $command->insert(
989
            '{{customer}}',
990
            ['email' => '[email protected]', 'name' => 'test', 'address' => 'test address']
991
        )->execute();
992
        $query = (new Query($db))
993
            ->select(['{{customer}}.{{email}} as name', '{{name}} as email', '{{address}}'])
994
            ->from('{{customer}}')
995
            ->where(['and', ['<>', 'name', 'foo'], ['status' => [0, 1, 2, 3]]]);
996
        $command->insert('{{customer}}', $query)->execute();
997
998
        $this->assertEquals(
999
            2,
1000
            $command->setSql(
1001
                <<<SQL
1002
                SELECT COUNT(*) FROM {{customer}}
1003
                SQL
1004
            )->queryScalar(),
1005
        );
1006
1007
        $record = $command->setSql(
1008
            <<<SQL
1009
            SELECT [[email]], [[name]], [[address]] FROM {{customer}}
1010
            SQL
1011
        )->queryAll();
1012
1013
        $this->assertSame(
1014
            [
1015
                ['email' => '[email protected]', 'name' => 'test', 'address' => 'test address'],
1016
                ['email' => 'test', 'name' => '[email protected]', 'address' => 'test address'],
1017
            ],
1018
            $record,
1019
        );
1020
    }
1021
1022
    public function testInsertSelectAlias(): void
1023
    {
1024
        $db = $this->getConnection(true);
1025
1026
        $command = $db->createCommand();
1027
        $command->delete('{{customer}}')->execute();
1028
        $command->insert(
1029
            '{{customer}}',
1030
            [
1031
                'email' => '[email protected]',
1032
                'name' => 'test',
1033
                'address' => 'test address',
1034
            ]
1035
        )->execute();
1036
        $query = (new Query($db))
1037
            ->select(['email' => '{{customer}}.{{email}}', 'address' => 'name', 'name' => 'address'])
1038
            ->from('{{customer}}')
1039
            ->where(['and', ['<>', 'name', 'foo'], ['status' => [0, 1, 2, 3]]]);
1040
        $command->insert('{{customer}}', $query)->execute();
1041
1042
        $this->assertEquals(
1043
            2,
1044
            $command->setSql(
1045
                <<<SQL
1046
                SELECT COUNT(*) FROM [[customer]]
1047
                SQL
1048
            )->queryScalar(),
1049
        );
1050
1051
        $record = $command->setSql(
1052
            <<<SQL
1053
            SELECT [[email]], [[name]], [[address]] FROM [[customer]]
1054
            SQL
1055
        )->queryAll();
1056
1057
        $this->assertSame(
1058
            [
1059
                ['email' => '[email protected]', 'name' => 'test', 'address' => 'test address'],
1060
                ['email' => '[email protected]', 'name' => 'test address', 'address' => 'test'],
1061
            ],
1062
            $record,
1063
        );
1064
    }
1065
1066
    /**
1067
     * Test INSERT INTO ... SELECT SQL statement with wrong query object.
1068
     *
1069
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::invalidSelectColumns()
1070
     *
1071
     * @throws Exception
1072
     * @throws Throwable
1073
     */
1074
    public function testInsertSelectFailed(array|ExpressionInterface|string $invalidSelectColumns): void
1075
    {
1076
        $db = $this->getConnection();
1077
1078
        $query = new Query($db);
1079
        $query->select($invalidSelectColumns)->from('{{customer}}');
1080
        $command = $db->createCommand();
1081
1082
        $this->expectException(InvalidArgumentException::class);
1083
        $this->expectExceptionMessage('Expected select query object with enumerated (named) parameters');
1084
1085
        $command->insert('{{customer}}', $query)->execute();
1086
    }
1087
1088
    public function testInsertToBlob(): void
1089
    {
1090
        $db = $this->getConnection(true);
1091
1092
        $command = $db->createCommand();
1093
        $command->delete('{{type}}')->execute();
1094
        $columns = [
1095
            'int_col' => 1,
1096
            'char_col' => 'test',
1097
            'float_col' => 3.14,
1098
            'bool_col' => true,
1099
            'blob_col' => serialize(['test' => 'data', 'num' => 222]),
1100
        ];
1101
        $command->insert('{{type}}', $columns)->execute();
1102
        $result = $command->setSql(
1103
            <<<SQL
1104
            SELECT [[blob_col]] FROM {{type}}
1105
            SQL
1106
        )->queryOne();
1107
1108
        $this->assertIsArray($result);
1109
1110
        $resultBlob = is_resource($result['blob_col']) ? stream_get_contents($result['blob_col']) : $result['blob_col'];
1111
1112
        $this->assertSame($columns['blob_col'], $resultBlob);
1113
    }
1114
1115
    public function testIntegrityViolation(): void
1116
    {
1117
        $db = $this->getConnection(true);
1118
1119
        $this->expectException(IntegrityException::class);
1120
1121
        $command = $db->createCommand(
1122
            <<<SQL
1123
            INSERT INTO [[profile]] ([[id]], [[description]]) VALUES (123, 'duplicate')
1124
            SQL
1125
        );
1126
        $command->execute();
1127
        $command->execute();
1128
    }
1129
1130
    public function testNoTablenameReplacement(): void
1131
    {
1132
        $db = $this->getConnection(true);
1133
1134
        $command = $db->createCommand();
1135
        $command->insert(
1136
            '{{customer}}',
1137
            ['name' => 'Some {{weird}} name', 'email' => '[email protected]', 'address' => 'Some {{%weird}} address']
1138
        )->execute();
1139
1140
        if ($db->getName() === 'pgsql') {
1141
            $customerId = $db->getLastInsertID('public.customer_id_seq');
1142
        } else {
1143
            $customerId = $db->getLastInsertID();
1144
        }
1145
1146
        $customer = $command->setSql(
1147
            <<<SQL
1148
            SELECT [[name]], [[email]], [[address]] FROM {{customer}} WHERE [[id]]=:id
1149
            SQL,
1150
        )->bindValues([':id' => $customerId])->queryOne();
1151
1152
        $this->assertIsArray($customer);
1153
        $this->assertSame('Some {{weird}} name', $customer['name']);
1154
        $this->assertSame('Some {{%weird}} address', $customer['address']);
1155
1156
        $command->update(
1157
            '{{customer}}',
1158
            ['name' => 'Some {{updated}} name', 'address' => 'Some {{%updated}} address'],
1159
            ['id' => $customerId]
1160
        )->execute();
1161
        $customer = $command->setSql(
1162
            <<<SQL
1163
            SELECT [[name]], [[email]], [[address]] FROM {{customer}} WHERE [[id]] = :id
1164
            SQL
1165
        )->bindValues([':id' => $customerId])->queryOne();
1166
1167
        $this->assertIsArray($customer);
1168
        $this->assertSame('Some {{updated}} name', $customer['name']);
1169
        $this->assertSame('Some {{%updated}} address', $customer['address']);
1170
    }
1171
1172
    public function testQuery(): void
1173
    {
1174
        $db = $this->getConnection(true);
1175
1176
        $command = $db->createCommand();
1177
        $command->setSql(
1178
            <<<SQL
1179
            SELECT * FROM [[customer]]
1180
            SQL
1181
        );
1182
1183
        $this->assertNull($command->getPdoStatement());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $command->getPdoStatement() targeting Yiisoft\Db\Driver\PDO\Co...dPDO::getPdoStatement() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

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.

Loading history...
1184
1185
        $reader = $command->query();
1186
1187
        $this->assertNotNull($command->getPdoStatement());
1188
        $this->assertInstanceOf(DataReaderInterface::class, $reader);
1189
        $this->assertIsInt($reader->count());
1190
1191
        $expectedRow = 6;
1192
1193
        if ($db->getName() === 'oci' || $db->getName() === 'pgsql') {
1194
            $expectedRow = 7;
1195
        }
1196
1197
        foreach ($reader as $row) {
1198
            $this->assertIsArray($row);
1199
            $this->assertCount($expectedRow, $row);
1200
        }
1201
1202
        $command = $db->createCommand('bad SQL');
1203
1204
        $this->expectException(Exception::class);
1205
1206
        $command->query();
1207
    }
1208
1209
    public function testQueryAll(): void
1210
    {
1211
        $db = $this->getConnection(true);
1212
1213
        $command = $db->createCommand();
1214
        $command->setSql(
1215
            <<<SQL
1216
            SELECT * FROM {{customer}}
1217
            SQL
1218
        );
1219
        $rows = $command->queryAll();
1220
        $expectedRow = 6;
1221
1222
        if ($db->getName() === 'oci' || $db->getName() === 'pgsql') {
1223
            $expectedRow = 7;
1224
        }
1225
1226
        $this->assertIsArray($rows);
1227
        $this->assertCount(3, $rows);
1228
        $this->assertIsArray($rows[0]);
1229
        $this->assertCount($expectedRow, $rows[0]);
1230
1231
        $command->setSql('bad SQL');
1232
1233
        $this->expectException(Exception::class);
1234
1235
        $command->queryAll();
1236
        $command->setSql(
1237
            <<<SQL
1238
            SELECT * FROM {{customer}} where id = 100
1239
            SQL
1240
        );
1241
        $rows = $command->queryAll();
1242
1243
        $this->assertIsArray($rows);
1244
        $this->assertCount(0, $rows);
1245
        $this->assertSame([], $rows);
1246
    }
1247
1248
    public function testQueryCache(): void
1249
    {
1250
        $db = $this->getConnection(true);
1251
1252
        $query = (new Query($db))->select(['{{name}}'])->from('{{customer}}');
1253
        $command = $db->createCommand();
1254
        $update = $command->setSql(
1255
            <<<SQL
1256
            UPDATE [[customer]] SET [[name]] = :name WHERE [[id]] = :id
1257
            SQL
1258
        );
1259
1260
        $this->assertSame('user1', $query->where(['id' => 1])->scalar(), 'Asserting initial value');
1261
1262
        /* No cache */
1263
        $update->bindValues([':id' => 1, ':name' => 'user11'])->execute();
1264
1265
        $this->assertSame(
1266
            'user11',
1267
            $query->where(['id' => 1])->scalar(),
1268
            'Query reflects DB changes when caching is disabled',
1269
        );
1270
1271
        /* Connection cache */
1272
        $db->cache(
1273
            static function (ConnectionPDOInterface $db) use ($query, $update) {
1274
                self::assertSame('user2', $query->where(['id' => 2])->scalar(), 'Asserting initial value for user #2');
1275
1276
                $update->bindValues([':id' => 2, ':name' => 'user22'])->execute();
1277
1278
                self::assertSame(
1279
                    'user2',
1280
                    $query->where(['id' => 2])->scalar(),
1281
                    'Query does NOT reflect DB changes when wrapped in connection caching',
1282
                );
1283
1284
                $db->noCache(
1285
                    static function () use ($query) {
1286
                        self::assertSame(
1287
                            'user22',
1288
                            $query->where(['id' => 2])->scalar(),
1289
                            'Query reflects DB changes when wrapped in connection caching and noCache simultaneously',
1290
                        );
1291
                    }
1292
                );
1293
1294
                self::assertSame(
1295
                    'user2',
1296
                    $query->where(['id' => 2])->scalar(),
1297
                    'Cache does not get changes after getting newer data from DB in noCache block.',
1298
                );
1299
            },
1300
            10,
1301
        );
1302
1303
        $db->queryCacheEnable(false);
1304
1305
        $db->cache(
1306
            static function () use ($query, $update) {
1307
                self::assertSame(
1308
                    'user22',
1309
                    $query->where(['id' => 2])->scalar(),
1310
                    'When cache is disabled for the whole connection, Query inside cache block does not get cached',
1311
                );
1312
1313
                $update->bindValues([':id' => 2, ':name' => 'user2'])->execute();
1314
1315
                self::assertSame('user2', $query->where(['id' => 2])->scalar());
1316
            },
1317
            10,
1318
        );
1319
1320
        $db->queryCacheEnable(true);
1321
        $query->cache();
1322
1323
        $this->assertSame('user11', $query->where(['id' => 1])->scalar());
1324
1325
        $update->bindValues([':id' => 1, ':name' => 'user1'])->execute();
1326
1327
        $this->assertSame(
1328
            'user11',
1329
            $query->where(['id' => 1])->scalar(),
1330
            'When both Connection and Query have cache enabled, we get cached value',
1331
        );
1332
        $this->assertSame(
1333
            'user1',
1334
            $query->noCache()->where(['id' => 1])->scalar(),
1335
            'When Query has disabled cache, we get actual data',
1336
        );
1337
1338
        $db->cache(
1339
            static function () use ($query) {
1340
                self::assertSame('user1', $query->noCache()->where(['id' => 1])->scalar());
1341
                self::assertSame('user11', $query->cache()->where(['id' => 1])->scalar());
1342
            },
1343
            10,
1344
        );
1345
    }
1346
1347
    public function testQueryColumn(): void
1348
    {
1349
        $db = $this->getConnection(true);
1350
1351
        $command = $db->createCommand();
1352
        $command->setSql(
1353
            <<<SQL
1354
            SELECT * FROM [[customer]]
1355
            SQL
1356
        );
1357
        $rows = $command->queryColumn();
1358
1359
        $this->assertIsArray($rows);
1360
        $this->assertCount(3, $rows);
1361
        $this->assertEquals('1', $rows[0]);
1362
1363
        $command->setSql('bad SQL');
1364
1365
        $this->expectException(Exception::class);
1366
1367
        $command->queryColumn();
1368
        $command->setSql(
1369
            <<<SQL
1370
            SELECT * FROM [[customer]] where id = 100
1371
            SQL
1372
        );
1373
        $rows = $command->queryColumn();
1374
1375
        $this->assertIsArray($rows);
1376
        $this->assertCount(0, $rows);
1377
        $this->assertSame([], $rows);
1378
    }
1379
1380
    public function testQueryOne(): void
1381
    {
1382
        $db = $this->getConnection(true);
1383
1384
        $command = $db->createCommand();
1385
        $sql = <<<SQL
1386
        SELECT * FROM [[customer]] ORDER BY [[id]]
1387
        SQL;
1388
        $row = $command->setSql($sql)->queryOne();
1389
1390
        $this->assertIsArray($row);
1391
        $this->assertEquals(1, $row['id']);
1392
        $this->assertEquals('user1', $row['name']);
1393
1394
        $command->setSql($sql)->prepare();
1395
        $row = $command->queryOne();
1396
1397
        $this->assertIsArray($row);
1398
        $this->assertEquals(1, $row['id']);
1399
        $this->assertEquals('user1', $row['name']);
1400
1401
        $sql = <<<SQL
1402
        SELECT * FROM [[customer]] WHERE [[id]] = 10
1403
        SQL;
1404
        $command = $command->setSql($sql);
1405
1406
        $this->assertNull($command->queryOne());
1407
    }
1408
1409
    public function testQueryScalar(): void
1410
    {
1411
        $db = $this->getConnection(true);
1412
1413
        $command = $db->createCommand();
1414
        $sql = <<<SQL
1415
        SELECT * FROM [[customer]] ORDER BY [[id]]
1416
        SQL;
1417
1418
        $this->assertEquals(1, $command->setSql($sql)->queryScalar());
1419
1420
        $sql = <<<SQL
1421
        SELECT [[id]] FROM [[customer]] ORDER BY [[id]]
1422
        SQL;
1423
        $command->setSql($sql)->prepare();
1424
1425
        $this->assertEquals(1, $command->queryScalar());
1426
1427
        $command = $command->setSql(
1428
            <<<SQL
1429
            SELECT [[id]] FROM [[customer]] WHERE [[id]] = 10
1430
            SQL
1431
        );
1432
1433
        $this->assertFalse($command->queryScalar());
1434
    }
1435
1436
    public function testRenameColumn(): void
1437
    {
1438
        $db = $this->getConnection(true);
1439
1440
        $command = $db->createCommand();
1441
        $schema = $db->getSchema();
1442
1443
        $command->renameColumn('{{customer}}', 'address', 'address_city')->execute();
1444
1445
        $this->assertContains('address_city', $schema->getTableSchema('{{customer}}')->getColumnNames());
1446
        $this->assertNotContains('address', $schema->getTableSchema('{{customer}}')->getColumnNames());
1447
    }
1448
1449
    public function testRenameTable(): void
1450
    {
1451
        $db = $this->getConnection(true);
1452
1453
        $command = $db->createCommand();
1454
        $schema = $db->getSchema();
1455
1456
        if ($schema->getTableSchema('{{new_type}}') !== null) {
1457
            $command->dropTable('{{new_type}}')->execute();
1458
        }
1459
1460
        $this->assertNotNull($schema->getTableSchema('{{type}}'));
1461
        $this->assertNull($schema->getTableSchema('{{new_type}}'));
1462
1463
        $command->renameTable('{{type}}', '{{new_type}}')->execute();
1464
1465
        $this->assertNull($schema->getTableSchema('{{type}}', true));
1466
        $this->assertNotNull($schema->getTableSchema('{{new_type}}', true));
1467
    }
1468
1469
    public function testSetRetryHandler(): void
1470
    {
1471
        $db = $this->getConnection(true);
1472
1473
        $command = $db->createCommand();
1474
1475
        $this->assertNull($db->getTransaction());
1476
1477
        $command->setSql(
1478
            <<<SQL
1479
            INSERT INTO [[profile]] ([[description]]) VALUES('command retry')
1480
            SQL
1481
        )->execute();
1482
1483
        $this->assertNull($db->getTransaction());
1484
        $this->assertEquals(
1485
            1,
1486
            $command->setSql(
1487
                <<<SQL
1488
                SELECT COUNT(*) FROM [[profile]] WHERE [[description]] = 'command retry'
1489
                SQL
1490
            )->queryScalar()
1491
        );
1492
1493
        $attempts = null;
1494
        $hitHandler = false;
1495
        $hitCatch = false;
1496
        $command->setSql(
1497
            <<<SQL
1498
            INSERT INTO [[profile]] ([[id]], [[description]]) VALUES(1, 'command retry')
1499
            SQL
1500
        );
1501
1502
        Assert::invokeMethod(
1503
            $command,
1504
            'setRetryHandler',
1505
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
1506
                $attempts = $attempt;
1507
                $hitHandler = true;
1508
1509
                return $attempt <= 2;
1510
            }]
1511
        );
1512
1513
        try {
1514
            $command->execute();
1515
        } catch (Exception $e) {
1516
            $hitCatch = true;
1517
1518
            $this->assertInstanceOf(IntegrityException::class, $e);
1519
        }
1520
1521
        $this->assertNull($db->getTransaction());
1522
        $this->assertSame(3, $attempts);
1523
        $this->assertTrue($hitHandler);
1524
        $this->assertTrue($hitCatch);
1525
    }
1526
1527
    public function testTransaction(): void
1528
    {
1529
        $db = $this->getConnection(true);
1530
1531
        $this->assertNull($db->getTransaction());
1532
1533
        $command = $db->createCommand();
1534
        $command = $command->setSql(
1535
            <<<SQL
1536
            INSERT INTO [[profile]] ([[description]]) VALUES('command transaction')
1537
            SQL
1538
        );
1539
1540
        Assert::invokeMethod($command, 'requireTransaction');
1541
1542
        $command->execute();
1543
1544
        $this->assertNull($db->getTransaction());
1545
        $this->assertEquals(
1546
            1,
1547
            $command->setSql(
1548
                <<<SQL
1549
                SELECT COUNT(*) FROM [[profile]] WHERE [[description]] = 'command transaction'
1550
                SQL
1551
            )->queryScalar(),
1552
        );
1553
    }
1554
1555
    public function testTruncateTable(): void
1556
    {
1557
        $db = $this->getConnection(true);
1558
1559
        $command = $db->createCommand();
1560
        $rows = $command->setSql(
1561
            <<<SQL
1562
            SELECT * FROM [[animal]]
1563
            SQL
1564
        )->queryAll();
1565
1566
        $this->assertCount(2, $rows);
1567
1568
        $command->truncateTable('{{animal}}')->execute();
1569
        $rows = $command->setSql(
1570
            <<<SQL
1571
            SELECT * FROM {{animal}}
1572
            SQL
1573
        )->queryAll();
1574
1575
        $this->assertCount(0, $rows);
1576
    }
1577
1578
    /**
1579
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::update()
1580
     */
1581
    public function testUpdate(
1582
        string $table,
1583
        array $columns,
1584
        array|string $conditions,
1585
        array $params,
1586
        string $expected
1587
    ): void {
1588
        $db = $this->getConnection();
1589
1590
        $command = $db->createCommand();
1591
        $sql = $command->update($table, $columns, $conditions, $params)->getSql();
1592
1593
        $this->assertSame($expected, $sql);
1594
    }
1595
1596
    /**
1597
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::upsert()
1598
     */
1599
    public function testUpsert(array $firstData, array $secondData): void
1600
    {
1601
        $db = $this->getConnection(true);
1602
1603
        $command = $db->createCommand();
1604
1605
        $this->assertEquals(
1606
            0,
1607
            $command->setSql(
1608
                <<<SQL
1609
                SELECT COUNT(*) FROM [[T_upsert]]
1610
                SQL,
1611
            )->queryScalar()
1612
        );
1613
1614
        $this->performAndCompareUpsertResult($db, $firstData);
1615
1616
        $this->assertEquals(
1617
            1,
1618
            $command->setSql(
1619
                <<<SQL
1620
                SELECT COUNT(*) FROM [[T_upsert]]
1621
                SQL,
1622
            )->queryScalar()
1623
        );
1624
1625
        $this->performAndCompareUpsertResult($db, $secondData);
1626
    }
1627
1628
    protected function performAndCompareUpsertResult(ConnectionPDOInterface $db, array $data): void
1629
    {
1630
        $params = $data['params'];
1631
        $expected = $data['expected'] ?? $params[1];
1632
1633
        $command = $db->createCommand();
1634
1635
        call_user_func_array([$command, 'upsert'], $params);
1636
1637
        $command->execute();
1638
1639
        $actual = (new Query($db))
1640
            ->select(['email', 'address' => new Expression($this->upsertTestCharCast), 'status'])
1641
            ->from('{{T_upsert}}')
1642
            ->one();
1643
        $this->assertEquals($expected, $actual, $this->upsertTestCharCast);
1644
    }
1645
}
1646