Passed
Pull Request — master (#397)
by Wilmer
02:50
created

CommonCommandTest   F

Complexity

Total Complexity 93

Size/Duplication

Total Lines 1612
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 898
c 1
b 0
f 0
dl 0
loc 1612
rs 1.702
wmc 93

55 Methods

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

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' => ['comment' => $schema->getTableSchema('{{customer}}')->getColumn('id')->getComment()],
73
            'sqlsrv' => DbHelper::getCommmentsFromColumn('customer', 'id', $db),
74
        };
75
76
        $this->assertSame(['comment' => 'Primary key.'], $commentOnColumn);
77
    }
78
79
    public function testAddCommentOnTable(): void
80
    {
81
        $db = $this->getConnection(true);
82
83
        $command = $db->createCommand();
84
        $command->addCommentOnTable('{{customer}}', 'Customer table.')->execute();
85
        $commentOnTable = DbHelper::getCommmentsFromTable('customer', $db);
86
87
        $this->assertSame(['comment' => 'Customer table.'], $commentOnTable);
88
    }
89
90
    public function testAddDefaultValue()
91
    {
92
        $db = $this->getConnection();
93
94
        $command = $db->createCommand();
95
        $schema = $db->getSchema();
96
97
        if ($schema->getTableSchema('{{test_def}}') !== null) {
98
            $command->dropTable('{{test_def}}')->execute();
99
        }
100
101
        $command->createTable('{{test_def}}', ['int1' => Schema::TYPE_INTEGER])->execute();
102
103
        $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}', true));
104
105
        $command->addDefaultValue('{{test_def_constraint}}', '{{test_def}}', 'int1', 41)->execute();
106
107
        $this->assertMatchesRegularExpression(
108
            '/^.*41.*$/',
109
            $schema->getTableDefaultValues('{{test_def}}', true)[0]->getValue(),
110
        );
111
    }
112
113
    /**
114
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addForeignKey()
115
     */
116
    public function testAddForeignKey(
117
        string $name,
118
        string $tableName,
119
        array|string $column1,
120
        array|string $column2,
121
        string $expectedName,
122
    ): void {
123
        $db = $this->getConnection();
124
125
        $command = $db->createCommand();
126
        $schema = $db->getSchema();
127
128
        if ($schema->getTableSchema($tableName) !== null) {
129
            $command->dropTable($tableName)->execute();
130
        }
131
132
        $command->createTable(
133
            $tableName,
134
            [
135
                'int1' => 'integer not null unique',
136
                'int2' => 'integer not null unique',
137
                'int3' => 'integer not null unique',
138
                'int4' => 'integer not null unique',
139
                'unique ([[int1]], [[int2]])',
140
                'unique ([[int3]], [[int4]])',
141
            ],
142
        )->execute();
143
144
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
145
146
        $command->addForeignKey($name, $tableName, $column1, $tableName, $column2)->execute();
147
148
        $this->assertSame($expectedName, $schema->getTableForeignKeys($tableName, true)[0]->getName());
149
150
        if (is_string($column1)) {
0 ignored issues
show
introduced by
The condition is_string($column1) is always false.
Loading history...
151
            $column1 = [$column1];
152
        }
153
154
        $this->assertSame($column1, $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames());
155
156
        if (is_string($column2)) {
0 ignored issues
show
introduced by
The condition is_string($column2) is always false.
Loading history...
157
            $column2 = [$column2];
158
        }
159
160
        $this->assertSame($column2, $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames());
161
    }
162
163
    /**
164
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addPrimaryKey()
165
     */
166
    public function testAddPrimaryKey(string $name, string $tableName, array|string $column): void
167
    {
168
        $db = $this->getConnection();
169
170
        $command = $db->createCommand();
171
        $schema = $db->getSchema();
172
173
        if ($schema->getTableSchema($tableName) !== null) {
174
            $command->dropTable($tableName)->execute();
175
        }
176
177
        $command->createTable($tableName, ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
178
179
        $this->assertNull($schema->getTablePrimaryKey($tableName, true));
180
181
        $db->createCommand()->addPrimaryKey($name, $tableName, $column)->execute();
182
183
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always false.
Loading history...
184
            $column = [$column];
185
        }
186
187
        $this->assertSame($column, $schema->getTablePrimaryKey($tableName, true)->getColumnNames());
188
    }
189
190
    /**
191
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::addUnique()
192
     */
193
    public function testAddUnique(string $name, string $tableName, array|string $column): void
194
    {
195
        $db = $this->getConnection();
196
197
        $command = $db->createCommand();
198
        $schema = $db->getSchema();
199
200
        if ($schema->getTableSchema($tableName) !== null) {
201
            $command->dropTable($tableName)->execute();
202
        }
203
204
        $command->createTable($tableName, ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
205
206
        $this->assertEmpty($schema->getTableUniques($tableName, true));
207
208
        $command->addUnique($name, $tableName, $column)->execute();
209
210
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always false.
Loading history...
211
            $column = [$column];
212
        }
213
214
        $this->assertSame($column, $schema->getTableUniques($tableName, true)[0]->getColumnNames());
215
    }
216
217
    /**
218
     * Make sure that `{{something}}` in values will not be encoded.
219
     *
220
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::batchInsert()
221
     *
222
     * {@see https://github.com/yiisoft/yii2/issues/11242}
223
     */
224
    public function testBatchInsert(
225
        string $table,
226
        array $columns,
227
        array $values,
228
        string $expected,
229
        array $expectedParams = [],
230
        int $insertedRow = 1
231
    ): void {
232
        $db = $this->getConnection(true);
233
234
        $command = $db->createCommand();
235
        $command->batchInsert($table, $columns, $values);
236
        $command->prepare(false);
237
238
        $this->assertSame($expected, $command->getSql());
239
        $this->assertSame($expectedParams, $command->getParams());
240
241
        $command->execute();
242
243
        $this->assertEquals($insertedRow, (new Query($db))->from($table)->count());
244
    }
245
246
    /**
247
     * Test batch insert with different data types.
248
     *
249
     * Ensure double is inserted with `.` decimal separator.
250
     *
251
     * @link https://github.com/yiisoft/yii2/issues/6526
252
     */
253
    public function testBatchInsertDataTypesLocale(): void
254
    {
255
        $locale = setlocale(LC_NUMERIC, 0);
256
257
        if ($locale === false) {
258
            $this->markTestSkipped('Your platform does not support locales.');
259
        }
260
261
        $db = $this->getConnection(true);
262
263
        $command = $db->createCommand();
264
265
        try {
266
            /* This one sets decimal mark to comma sign */
267
            setlocale(LC_NUMERIC, 'ru_RU.utf8');
268
269
            $cols = ['int_col', 'char_col', 'float_col', 'bool_col'];
270
            $data = [[1, 'A', 9.735, true], [2, 'B', -2.123, false], [3, 'C', 2.123, false]];
271
272
            /* clear data in "type" table */
273
            $command->delete('{{type}}')->execute();
274
275
            /* change, for point oracle. */
276
            if ($db->getName() === 'oci') {
277
                $command->setSql(
278
                    <<<SQL
279
                    ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'
280
                    SQL
281
                )->execute();
282
            }
283
284
            /* batch insert on "type" table */
285
            $command->batchInsert('{{type}}', $cols, $data)->execute();
286
            $data = $command->setSql(
287
                <<<SQL
288
                SELECT [[int_col]], [[char_col]], [[float_col]], [[bool_col]] FROM {{type}} WHERE [[int_col]] IN (1,2,3) ORDER BY [[int_col]]
289
                SQL
290
            )->queryAll();
291
292
            $this->assertCount(3, $data);
293
            $this->assertEquals(1, $data[0]['int_col']);
294
            $this->assertEquals(2, $data[1]['int_col']);
295
            $this->assertEquals(3, $data[2]['int_col']);
296
297
            /* rtrim because Postgres padds the column with whitespace */
298
            $this->assertSame('A', rtrim($data[0]['char_col']));
299
            $this->assertSame('B', rtrim($data[1]['char_col']));
300
            $this->assertSame('C', rtrim($data[2]['char_col']));
301
            $this->assertEquals(9.735, $data[0]['float_col']);
302
            $this->assertEquals(-2.123, $data[1]['float_col']);
303
            $this->assertEquals(2.123, $data[2]['float_col']);
304
            $this->assertEquals(1, $data[0]['bool_col']);
305
            Assert::isOneOf($data[1]['bool_col'], ['0', false]);
306
            Assert::isOneOf($data[2]['bool_col'], ['0', false]);
307
        } catch (Exception | Throwable $e) {
308
            setlocale(LC_NUMERIC, $locale);
309
310
            throw $e;
311
        }
312
313
        setlocale(LC_NUMERIC, $locale);
314
    }
315
316
    public function testBatchInsertFailsOld(): void
317
    {
318
        $db = $this->getConnection(true);
319
320
        $command = $db->createCommand();
321
        $command->batchInsert(
322
            '{{customer}}',
323
            ['email', 'name', 'address'],
324
            [['[email protected]', 'test_name', 'test_address']],
325
        );
326
327
        $this->assertSame(1, $command->execute());
328
329
        $result = (new Query($db))
330
            ->select(['email', 'name', 'address'])
331
            ->from('{{customer}}')
332
            ->where(['=', '{{email}}', '[email protected]'])
333
            ->one();
334
335
        $this->assertCount(3, $result);
336
        $this->assertSame(['email' => '[email protected]', 'name' => 'test_name', 'address' => 'test_address'], $result);
337
    }
338
339
    public function testBatchInsertWithManyData(): void
340
    {
341
        $db = $this->getConnection(true);
342
343
        $values = [];
344
        $attemptsInsertRows = 200;
345
        $command = $db->createCommand();
346
347
        for ($i = 0; $i < $attemptsInsertRows; $i++) {
348
            $values[$i] = ['t' . $i . '@any.com', 't' . $i, 't' . $i . ' address'];
349
        }
350
351
        $command->batchInsert('{{customer}}', ['email', 'name', 'address'], $values);
352
353
        $this->assertSame($attemptsInsertRows, $command->execute());
354
355
        $insertedRowsCount = (new Query($db))->from('{{customer}}')->count();
356
357
        $this->assertGreaterThanOrEqual($attemptsInsertRows, $insertedRowsCount);
358
    }
359
360
    public function testBatchInsertWithYield(): void
361
    {
362
        $db = $this->getConnection(true);
363
364
        $rows = (
365
            static function () {
366
                yield ['[email protected]', 'test name', 'test address'];
367
            }
368
        )();
369
        $command = $db->createCommand();
370
        $command->batchInsert('{{customer}}', ['email', 'name', 'address'], $rows);
371
372
        $this->assertSame(1, $command->execute());
373
    }
374
375
    /**
376
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::createIndex()
377
     */
378
    public function testCreateIndex(
379
        string $name,
380
        string $tableName,
381
        array|string $column,
382
        string|null $indexType,
383
        string|null $indexMethod,
384
    ): void {
385
        $db = $this->getConnection();
386
387
        $command = $db->createCommand();
388
        $schema = $db->getSchema();
389
390
        if ($schema->getTableSchema($tableName) !== null) {
391
            $command->dropTable($tableName)->execute();
392
        }
393
394
        $command->createTable($tableName, ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
395
396
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
397
398
        $command->createIndex($name, $tableName, $column, $indexType, $indexMethod)->execute();
399
400
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always false.
Loading history...
401
            $column = [$column];
402
        }
403
404
        $this->assertSame($column, $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
405
406
        if ($indexType === 'UNIQUE') {
407
            $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
408
        } else {
409
            $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
410
        }
411
    }
412
413
    public function testCreateTable(): void
414
    {
415
        $db = $this->getConnection();
416
417
        $command = $db->createCommand();
418
        $schema = $db->getSchema();
419
420
        if ($schema->getTableSchema('{{testCreateTable}}', true) !== null) {
421
            $command->dropTable('{{testCreateTable}}')->execute();
422
        }
423
424
        $command->createTable(
425
            '{{testCreateTable}}',
426
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER],
427
        )->execute();
428
        $command->insert('{{testCreateTable}}', ['bar' => 1])->execute();
429
        $records = $command->setSql(
430
            <<<SQL
431
            SELECT [[id]], [[bar]] FROM [[testCreateTable]];
432
            SQL
433
        )->queryAll();
434
435
        $this->assertEquals([['id' => 1, 'bar' => 1]], $records);
436
    }
437
438
    public function testCreateView(): void
439
    {
440
        $db = $this->getConnection();
441
442
        $command = $db->createCommand();
443
        $schema = $db->getSchema();
444
        $subQuery = (new Query($db))->select('{{bar}}')->from('{{testCreateViewTable}}')->where(['>', 'bar', '5']);
445
446
        if ($schema->getTableSchema('{{testCreateView}}') !== null) {
447
            $command->dropView('{{testCreateView}}')->execute();
448
        }
449
450
        if ($schema->getTableSchema('{{testCreateViewTable}}')) {
451
            $command->dropTable('{{testCreateViewTable}}')->execute();
452
        }
453
454
        $command->createTable(
455
            '{{testCreateViewTable}}',
456
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER],
457
        )->execute();
458
        $command->insert('{{testCreateViewTable}}', ['bar' => 1])->execute();
459
        $command->insert('{{testCreateViewTable}}', ['bar' => 6])->execute();
460
        $command->createView('{{testCreateView}}', $subQuery)->execute();
461
        $records = $command->setSql(
462
            <<<SQL
463
            SELECT [[bar]] FROM {{testCreateView}};
464
            SQL
465
        )->queryAll();
466
467
        $this->assertEquals([['bar' => 6]], $records);
468
    }
469
470
    public function testDataReaderRewindException(): void
471
    {
472
        $db = $this->getConnection(true);
473
474
        $this->expectException(InvalidCallException::class);
475
        $this->expectExceptionMessage('DataReader cannot rewind. It is a forward-only reader.');
476
477
        $command = $db->createCommand();
478
        $reader = $command->setSql(
479
            <<<SQL
480
            SELECT * FROM {{customer}}
481
            SQL
482
        )->query();
483
        $reader->next();
484
        $reader->rewind();
485
    }
486
487
    public function testDelete(): void
488
    {
489
        $db = $this->getConnection(true);
490
491
        $command = $db->createCommand();
492
        $command->delete('{{customer}}', ['id' => 2])->execute();
493
        $chekSql = <<<SQL
494
        SELECT COUNT([[id]]) FROM [[customer]]
495
        SQL;
496
        $command->setSql($chekSql);
497
498
        $this->assertSame('2', $command->queryScalar());
499
500
        $command->delete('{{customer}}', ['id' => 3])->execute();
501
        $command->setSql($chekSql);
502
503
        $this->assertSame('1', $command->queryScalar());
504
    }
505
506
    public function testDropCheck()
507
    {
508
        $db = $this->getConnection();
509
510
        $command = $db->createCommand();
511
        $schema = $db->getSchema();
512
513
        if ($schema->getTableSchema('{{test_ck}}') !== null) {
514
            $command->dropTable('{{test_ck}}')->execute();
515
        }
516
517
        $command->createTable('{{test_ck}}', ['int1' => 'integer'])->execute();
518
519
        $this->assertEmpty($schema->getTableChecks('{{test_ck}}', true));
520
521
        $command->addCheck('{{test_ck_constraint}}', '{{test_ck}}', 'int1 > 1')->execute();
522
523
        $this->assertMatchesRegularExpression(
524
            '/^.*int1.*>.*1.*$/',
525
            $schema->getTableChecks('{{test_ck}}', true)[0]->getExpression(),
526
        );
527
528
        $command->dropCheck('{{test_ck_constraint}}', '{{test_ck}}')->execute();
529
530
        $this->assertEmpty($schema->getTableChecks('{{test_ck}}', true));
531
    }
532
533
    public function testDropColumn(): void
534
    {
535
        $db = $this->getConnection();
536
537
        $command = $db->createCommand();
538
        $schema = $db->getSchema();
539
540
        if ($schema->getTableSchema('{{testDropColumn}}', true) !== null) {
541
            $command->dropTable('{{testDropColumn}}')->execute();
542
        }
543
544
        $command->createTable(
545
            '{{testDropColumn}}',
546
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER, 'baz' => Schema::TYPE_INTEGER],
547
        )->execute();
548
        $command->dropColumn('{{testDropColumn}}', 'bar')->execute();
549
550
        $this->assertArrayNotHasKey('bar', $schema->getTableSchema('{{testDropColumn}}')->getColumns());
551
        $this->assertArrayHasKey('baz', $schema->getTableSchema('{{testDropColumn}}')->getColumns());
552
    }
553
554
    public function testDropCommentFromColumn(): void
555
    {
556
        $db = $this->getConnection(true);
557
558
        $command = $db->createCommand();
559
        $schema = $db->getSchema();
560
        $command->addCommentOnColumn('{{customer}}', 'id', 'Primary key.')->execute();
561
        $commentOnColumn = match ($db->getName()) {
562
            'mysql', 'pgsql' => ['comment' => $schema->getTableSchema('{{customer}}')->getColumn('id')->getComment()],
563
            'sqlsrv' => DbHelper::getCommmentsFromColumn('customer', 'id', $db),
564
        };
565
566
        $this->assertSame(['comment' => 'Primary key.'], $commentOnColumn);
567
568
        $command->dropCommentFromColumn('{{customer}}', 'id')->execute();
569
        $commentOnColumn = match ($db->getName()) {
570
            'mysql', 'pgsql' => $schema->getTableSchema('{{customer}}')->getColumn('id')->getComment(),
571
            'sqlsrv' => DbHelper::getCommmentsFromColumn('customer', 'id', $db),
572
        };
573
574
        $this->assertEmpty($commentOnColumn);
575
    }
576
577
    public function testDropCommentFromTable(): void
578
    {
579
        $db = $this->getConnection(true);
580
581
        $command = $db->createCommand();
582
        $command->addCommentOnTable('{{customer}}', 'Customer table.')->execute();
583
        $commentOnTable = DbHelper::getCommmentsFromTable('customer', $db);
584
585
        $this->assertSame(['comment' => 'Customer table.'], $commentOnTable);
586
587
        $command->dropCommentFromTable('{{customer}}')->execute();
588
        $commentOnTable = DbHelper::getCommmentsFromTable('customer', $db);
589
590
        $this->assertNull($commentOnTable);
591
    }
592
593
    public function testDropDefaultValue(): void
594
    {
595
        $db = $this->getConnection();
596
597
        $command = $db->createCommand();
598
        $schema = $db->getSchema();
599
600
        if ($schema->getTableSchema('{{test_def}}') !== null) {
601
            $command->dropTable('{{test_def}}')->execute();
602
        }
603
604
        $command->createTable('{{test_def}}', ['int1' => 'integer'])->execute();
605
606
        $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}', true));
607
608
        $command->addDefaultValue('{{test_def_constraint}}', '{{test_def}}', 'int1', 41)->execute();
609
610
        $this->assertMatchesRegularExpression(
611
            '/^.*41.*$/',
612
            $schema->getTableDefaultValues('{{test_def}}', true)[0]->getValue(),
613
        );
614
615
        $command->dropDefaultValue('{{test_def_constraint}}', '{{test_def}}')->execute();
616
617
        $this->assertEmpty($schema->getTableDefaultValues('{{test_def}}', true));
618
    }
619
620
    public function testDropForeignKey(): void
621
    {
622
        $db = $this->getConnection();
623
624
        $command = $db->createCommand();
625
        $schema = $db->getSchema();
626
627
        if ($schema->getTableSchema('{{test_fk}}') !== null) {
628
            $command->dropTable('{{test_fk}}')->execute();
629
        }
630
631
        $command->createTable('{{test_fk}}', ['id' => Schema::TYPE_PK, 'int1' => 'integer'])->execute();
632
633
        $this->assertEmpty($schema->getTableForeignKeys('{{test_fk}}', true));
634
635
        $command->addForeignKey('{{test_fk_constraint}}', '{{test_fk}}', 'int1', '{{test_fk}}', 'id')->execute();
636
637
        $this->assertNotEmpty($schema->getTableForeignKeys('{{test_fk}}', true));
638
639
        $command->dropForeignKey('{{test_fk_constraint}}', '{{test_fk}}')->execute();
640
641
        $this->assertEmpty($schema->getTableForeignKeys('{{test_fk}}', true));
642
    }
643
644
    public function testDropIndex(): void
645
    {
646
        $db = $this->getConnection();
647
648
        $command = $db->createCommand();
649
        $schema = $db->getSchema();
650
651
        if ($schema->getTableSchema('{{test_idx}}') !== null) {
652
            $command->dropTable('{{test_idx}}')->execute();
653
        }
654
655
        $command->createTable('{{test_idx}}', ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
656
657
        $this->assertEmpty($schema->getTableIndexes('{[test_idx}}', true));
658
659
        $command->createIndex('{{test_idx_constraint}}', '{{test_idx}}', ['int1', 'int2'], 'UNIQUE')->execute();
660
661
        $this->assertSame(['int1', 'int2'], $schema->getTableIndexes('{{test_idx}}', true)[0]->getColumnNames());
662
        $this->assertTrue($schema->getTableIndexes('{{test_idx}}', true)[0]->isUnique());
663
664
        $command->dropIndex('{{test_idx_constraint}}', '{{test_idx}}')->execute();
665
666
        $this->assertEmpty($schema->getTableIndexes('{{test_idx}}', true));
667
    }
668
669
    public function testDropPrimaryKey(): void
670
    {
671
        $db = $this->getConnection();
672
673
        $command = $db->createCommand();
674
        $schema = $db->getSchema();
675
676
        if ($schema->getTableSchema('{{test_pk}}') !== null) {
677
            $command->dropTable('{{test_pk}}')->execute();
678
        }
679
680
        $command->createTable('{{test_pk}}', ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
681
682
        $this->assertEmpty($schema->getTableSchema('{{test_pk}}', true)->getPrimaryKey());
683
684
        $command->addPrimaryKey('{{test_pk_constraint}}', '{{test_pk}}', ['int1', 'int2'])->execute();
685
686
        $this->assertSame(['int1', 'int2'], $schema->getTableSchema('{{test_pk}}', true)->getColumnNames());
687
688
        $command->dropPrimaryKey('{{test_pk_constraint}}', '{{test_pk}}')->execute();
689
690
        $this->assertEmpty($schema->getTableSchema('{{test_pk}}', true)->getPrimaryKey());
691
    }
692
693
    public function testDropTable(): void
694
    {
695
        $db = $this->getConnection();
696
697
        $command = $db->createCommand();
698
        $schema = $db->getSchema();
699
700
        if ($schema->getTableSchema('{{testDropTable}}') !== null) {
701
            $command->dropTable('{{testDropTable}}')->execute();
702
        }
703
704
        $command->createTable('{{testDropTable}}', ['id' => Schema::TYPE_PK, 'foo' => 'integer'])->execute();
705
706
        $this->assertNotNull($schema->getTableSchema('{{testDropTable}}', true));
707
708
        $command->dropTable('{{testDropTable}}')->execute();
709
710
        $this->assertNull($schema->getTableSchema('{{testDropTable}}', true));
711
    }
712
713
    public function testDropUnique(): void
714
    {
715
        $db = $this->getConnection();
716
717
        $command = $db->createCommand();
718
        $schema = $db->getSchema();
719
720
        if ($schema->getTableSchema('{{test_uq}}') !== null) {
721
            $command->dropTable('{{test_uq}}')->execute();
722
        }
723
724
        $command->createTable('{{test_uq}}', ['int1' => 'integer not null', 'int2' => 'integer not null'])->execute();
725
726
        $this->assertEmpty($schema->getTableUniques('{{test_uq}}', true));
727
728
        $command->addUnique('{{test_uq_constraint}}', '{{test_uq}}', ['int1'])->execute();
729
730
        $this->assertSame(['int1'], $schema->getTableUniques('{{test_uq}}', true)[0]->getColumnNames());
731
732
        $command->dropUnique('{{test_uq_constraint}}', '{{test_uq}}')->execute();
733
734
        $this->assertEmpty($schema->getTableUniques('{{test_uq}}', true));
735
    }
736
737
    public function testDropView(): void
738
    {
739
        $db = $this->getConnection(true);
740
741
        /* since it already exists in the fixtures */
742
        $viewName = '{{animal_view}}';
743
744
        $schema = $db->getSchema();
745
746
        $this->assertNotNull($schema->getTableSchema($viewName));
747
748
        $db->createCommand()->dropView($viewName)->execute();
749
750
        $this->assertNull($schema->getTableSchema($viewName));
751
    }
752
753
    public function testExecute(): void
754
    {
755
        $db = $this->getConnection(true);
756
757
        $command = $db->createCommand();
758
        $command->setSql(
759
            <<<SQL
760
            INSERT INTO [[customer]] ([[email]], [[name]], [[address]]) VALUES ('[email protected]', 'user4', 'address4')
761
            SQL
762
        );
763
764
        $this->assertSame(1, $command->execute());
765
766
        $command = $command->setSql(
767
            <<<SQL
768
            SELECT COUNT(*) FROM [[customer]] WHERE [[name]] = 'user4'
769
            SQL
770
        );
771
772
        $this->assertEquals(1, $command->queryScalar());
773
774
        $command->setSql('bad SQL');
775
        $message = match ($db->getName()) {
776
            'pgsql' => 'SQLSTATE[42601]',
777
            'sqlite' => 'SQLSTATE[HY000]',
778
            default => 'SQLSTATE[42000]',
779
        };
780
781
        $this->expectException(Exception::class);
782
        $this->expectExceptionMessage($message);
783
784
        $command->execute();
785
    }
786
787
    public function testExecuteWithoutSql(): void
788
    {
789
        $db = $this->getConnection();
790
791
        $command = $db->createCommand();
792
        $result = $command->setSql('')->execute();
793
794
        $this->assertSame(0, $result);
795
    }
796
797
    public function testExecuteWithTransaction(): void
798
    {
799
        $db = $this->getConnection(true);
800
801
        $this->assertNull($db->getTransaction());
802
803
        $command = $db->createCommand(
804
            <<<SQL
805
            INSERT INTO {{profile}} ([[description]]) VALUES('command transaction 1')
806
            SQL,
807
        );
808
809
        Assert::invokeMethod($command, 'requireTransaction');
810
811
        $command->execute();
812
813
        $this->assertNull($db->getTransaction());
814
815
        $this->assertEquals(
816
            1,
817
            $db->createCommand(
818
                <<<SQL
819
                SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction 1'
820
                SQL,
821
            )->queryScalar(),
822
        );
823
824
        $command = $db->createCommand(
825
            <<<SQL
826
            INSERT INTO {{profile}} ([[description]]) VALUES('command transaction 2')
827
            SQL,
828
        );
829
830
        Assert::invokeMethod($command, 'requireTransaction', [TransactionInterface::READ_UNCOMMITTED]);
831
832
        $command->execute();
833
834
        $this->assertNull($db->getTransaction());
835
836
        $this->assertEquals(
837
            1,
838
            $db->createCommand(
839
                <<<SQL
840
                SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction 2'
841
                SQL,
842
            )->queryScalar(),
843
        );
844
    }
845
846
    public function testInsert(): void
847
    {
848
        $db = $this->getConnection(true);
849
850
        $db->createCommand(
851
            <<<SQL
852
            DELETE FROM {{customer}}
853
            SQL
854
        )->execute();
855
        $command = $db->createCommand();
856
        $command
857
            ->insert('{{customer}}', ['email' => '[email protected]', 'name' => 'test', 'address' => 'test address'])
858
            ->execute();
859
860
        $this->assertEquals(
861
            1,
862
            $db->createCommand(
863
                <<<SQL
864
                SELECT COUNT(*) FROM {{customer}};
865
                SQL
866
            )->queryScalar(),
867
        );
868
869
        $record = $db->createCommand(
870
            <<<SQL
871
            SELECT [[email]], [[name]], [[address]] FROM {{customer}}
872
            SQL
873
        )->queryOne();
874
875
        $this->assertSame(['email' => '[email protected]', 'name' => 'test', 'address' => 'test address'], $record);
876
    }
877
878
    public function testInsertExpression(): void
879
    {
880
        $db = $this->getConnection(true);
881
882
        $command = $db->createCommand();
883
        $command->setSql(
884
            <<<SQL
885
            DELETE FROM [[order_with_null_fk]]
886
            SQL
887
        )->execute();
888
        $expression = match ($db->getName()) {
889
            'mysql' => 'YEAR(NOW())',
890
            'pgsql' => "EXTRACT(YEAR FROM TIMESTAMP 'now')",
891
            'sqlite' => "strftime('%Y')",
892
            'sqlsrv' => 'YEAR(GETDATE())',
893
        };
894
        $command->insert(
895
            '{{order_with_null_fk}}',
896
            ['created_at' => new Expression($expression), 'total' => 1],
897
        )->execute();
898
899
        $this->assertEquals(
900
            1,
901
            $command->setSql(
902
                <<<SQL
903
                SELECT COUNT(*) FROM {{order_with_null_fk}}
904
                SQL
905
            )->queryScalar(),
906
        );
907
908
        $record = $command->setSql(
909
            <<<SQL
910
            SELECT [[created_at]] FROM {{order_with_null_fk}}
911
            SQL
912
        )->queryOne();
913
914
        $this->assertEquals(['created_at' => date('Y')], $record);
915
    }
916
917
    public function testsInsertQueryAsColumnValue(): void
918
    {
919
        $db = $this->getConnection(true);
920
921
        $command = $db->createCommand();
922
        $time = time();
923
        $command->setSql(
924
            <<<SQL
925
            DELETE FROM [[order_with_null_fk]]
926
            SQL
927
        )->execute();
928
        $command->insert('{{order}}', ['customer_id' => 1, 'created_at' => $time, 'total' => 42])->execute();
929
930
        if ($db->getName() === 'pgsql') {
931
            $orderId = $db->getLastInsertID('public.order_id_seq');
932
        } else {
933
            $orderId = $db->getLastInsertID();
934
        }
935
936
        $columnValueQuery = (new Query($db))->select('{{created_at}}')->from('{{order}}')->where(['id' => $orderId]);
937
        $command->insert(
938
            '{{order_with_null_fk}}',
939
            ['customer_id' => $orderId, 'created_at' => $columnValueQuery, 'total' => 42],
940
        )->execute();
941
942
        $this->assertEquals(
943
            $time,
944
            $command->setSql(
945
                <<<SQL
946
                SELECT [[created_at]] FROM [[order_with_null_fk]] WHERE [[customer_id]] = :id
947
                SQL
948
            )->bindValues([':id' => $orderId])->queryScalar(),
949
        );
950
951
        $command->setSql(
952
            <<<SQL
953
            DELETE FROM [[order_with_null_fk]]
954
            SQL
955
        )->execute();
956
        $command->setSql(
957
            <<<SQL
958
            DELETE FROM [[order]]
959
            SQL
960
        )->execute();
961
    }
962
963
    public function testInsertSelect(): void
964
    {
965
        $db = $this->getConnection(true);
966
967
        $command = $db->createCommand();
968
        $command->setSql(
969
            <<<SQL
970
            DELETE FROM {{customer}}
971
            SQL
972
        )->execute();
973
        $command->insert(
974
            '{{customer}}',
975
            ['email' => '[email protected]', 'name' => 'test', 'address' => 'test address']
976
        )->execute();
977
        $query = (new Query($db))
978
            ->select(['{{customer}}.{{email}} as name', '{{name}} as email', '{{address}}'])
979
            ->from('{{customer}}')
980
            ->where(['and', ['<>', 'name', 'foo'], ['status' => [0, 1, 2, 3]]]);
981
        $command->insert('{{customer}}', $query)->execute();
982
983
        $this->assertEquals(
984
            2,
985
            $command->setSql(
986
                <<<SQL
987
                SELECT COUNT(*) FROM {{customer}}
988
                SQL
989
            )->queryScalar(),
990
        );
991
992
        $record = $command->setSql(
993
            <<<SQL
994
            SELECT [[email]], [[name]], [[address]] FROM {{customer}}
995
            SQL
996
        )->queryAll();
997
998
        $this->assertSame(
999
            [
1000
                ['email' => '[email protected]', 'name' => 'test', 'address' => 'test address'],
1001
                ['email' => 'test', 'name' => '[email protected]', 'address' => 'test address'],
1002
            ],
1003
            $record,
1004
        );
1005
    }
1006
1007
    public function testInsertSelectAlias(): void
1008
    {
1009
        $db = $this->getConnection(true);
1010
1011
        $command = $db->createCommand();
1012
        $command->setSql(
1013
            <<<SQL
1014
            DELETE FROM [[customer]]
1015
            SQL
1016
        )->execute();
1017
        $command->insert(
1018
            '{{customer}}',
1019
            [
1020
                'email' => '[email protected]',
1021
                'name' => 'test',
1022
                'address' => 'test address',
1023
            ]
1024
        )->execute();
1025
        $query = (new Query($db))
1026
            ->select(['email' => '{{customer}}.{{email}}', 'address' => 'name', 'name' => 'address'])
1027
            ->from('{{customer}}')
1028
            ->where(['and', ['<>', 'name', 'foo'], ['status' => [0, 1, 2, 3]]]);
1029
        $command->insert('{{customer}}', $query)->execute();
1030
1031
        $this->assertEquals(
1032
            2,
1033
            $command->setSql(
1034
                <<<SQL
1035
                SELECT COUNT(*) FROM [[customer]]
1036
                SQL
1037
            )->queryScalar(),
1038
        );
1039
1040
        $record = $command->setSql(
1041
            <<<SQL
1042
            SELECT [[email]], [[name]], [[address]] FROM [[customer]]
1043
            SQL
1044
        )->queryAll();
1045
1046
        $this->assertSame(
1047
            [
1048
                ['email' => '[email protected]', 'name' => 'test', 'address' => 'test address'],
1049
                ['email' => '[email protected]', 'name' => 'test address', 'address' => 'test'],
1050
            ],
1051
            $record,
1052
        );
1053
    }
1054
1055
    /**
1056
     * Test INSERT INTO ... SELECT SQL statement with wrong query object.
1057
     *
1058
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::invalidSelectColumns()
1059
     *
1060
     * @throws Exception
1061
     * @throws Throwable
1062
     */
1063
    public function testInsertSelectFailed(array|ExpressionInterface|string $invalidSelectColumns): void
1064
    {
1065
        $db = $this->getConnection();
1066
1067
        $query = new Query($db);
1068
        $query->select($invalidSelectColumns)->from('{{customer}}');
1069
        $command = $db->createCommand();
1070
1071
        $this->expectException(InvalidArgumentException::class);
1072
        $this->expectExceptionMessage('Expected select query object with enumerated (named) parameters');
1073
1074
        $command->insert('{{customer}}', $query)->execute();
1075
    }
1076
1077
    public function testInsertToBlob(): void
1078
    {
1079
        $db = $this->getConnection(true);
1080
1081
        $command = $db->createCommand();
1082
        $command->delete('{{type}}')->execute();
1083
        $columns = [
1084
            'int_col' => 1,
1085
            'char_col' => 'test',
1086
            'float_col' => 3.14,
1087
            'bool_col' => true,
1088
            'blob_col' => serialize(['test' => 'data', 'num' => 222]),
1089
        ];
1090
        $command->insert('{{type}}', $columns)->execute();
1091
        $result = $command->setSql(
1092
            <<<SQL
1093
            SELECT [[blob_col]] FROM {{type}}
1094
            SQL
1095
        )->queryOne();
1096
1097
        $this->assertIsArray($result);
1098
1099
        $resultBlob = is_resource($result['blob_col']) ? stream_get_contents($result['blob_col']) : $result['blob_col'];
1100
1101
        $this->assertSame($columns['blob_col'], $resultBlob);
1102
    }
1103
1104
    public function testIntegrityViolation(): void
1105
    {
1106
        $db = $this->getConnection(true);
1107
1108
        $this->expectException(IntegrityException::class);
1109
1110
        $command = $db->createCommand(
1111
            <<<SQL
1112
            INSERT INTO [[profile]] ([[id]], [[description]]) VALUES (123, 'duplicate')
1113
            SQL
1114
        );
1115
        $command->execute();
1116
        $command->execute();
1117
    }
1118
1119
    public function testNoTablenameReplacement(): void
1120
    {
1121
        $db = $this->getConnection(true);
1122
1123
        $command = $db->createCommand();
1124
        $command->insert(
1125
            '{{customer}}',
1126
            ['name' => 'Some {{weird}} name', 'email' => '[email protected]', 'address' => 'Some {{%weird}} address']
1127
        )->execute();
1128
1129
        if ($db->getName() === 'pgsql') {
1130
            $customerId = $db->getLastInsertID('public.customer_id_seq');
1131
        } else {
1132
            $customerId = $db->getLastInsertID();
1133
        }
1134
1135
        $customer = $command->setSql(
1136
            <<<SQL
1137
            SELECT [[name]], [[email]], [[address]] FROM {{customer}} WHERE [[id]]=:id
1138
            SQL,
1139
        )->bindValues([':id' => $customerId])->queryOne();
1140
1141
        $this->assertIsArray($customer);
1142
        $this->assertSame('Some {{weird}} name', $customer['name']);
1143
        $this->assertSame('Some {{%weird}} address', $customer['address']);
1144
1145
        $command->update(
1146
            '{{customer}}',
1147
            ['name' => 'Some {{updated}} name', 'address' => 'Some {{%updated}} address'],
1148
            ['id' => $customerId]
1149
        )->execute();
1150
        $customer = $command->setSql(
1151
            <<<SQL
1152
            SELECT [[name]], [[email]], [[address]] FROM {{customer}} WHERE [[id]] = :id
1153
            SQL
1154
        )->bindValues([':id' => $customerId])->queryOne();
1155
1156
        $this->assertIsArray($customer);
1157
        $this->assertSame('Some {{updated}} name', $customer['name']);
1158
        $this->assertSame('Some {{%updated}} address', $customer['address']);
1159
    }
1160
1161
    public function testQuery(): void
1162
    {
1163
        $db = $this->getConnection(true);
1164
1165
        $command = $db->createCommand();
1166
        $command->setSql(
1167
            <<<SQL
1168
            SELECT * FROM [[customer]]
1169
            SQL
1170
        );
1171
1172
        $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...
1173
1174
        $reader = $command->query();
1175
1176
        // check tests that the reader is a valid iterator
1177
        if ($db->getName() !== 'sqlite' && $db->getName() !== 'pgsql' && $db->getName() !== 'sqlsrv') {
1178
            $this->assertEquals(3, $reader->count());
1179
        }
1180
1181
        $this->assertNotNull($command->getPdoStatement());
1182
        $this->assertInstanceOf(DataReaderInterface::class, $reader);
1183
        $this->assertIsInt($reader->count());
1184
1185
        $expectedRow = 6;
1186
1187
        if ($db->getName() === 'pgsql') {
1188
            $expectedRow = 7;
1189
        }
1190
1191
        foreach ($reader as $row) {
1192
            $this->assertIsArray($row);
1193
            $this->assertCount($expectedRow, $row);
1194
        }
1195
1196
        $command = $db->createCommand('bad SQL');
1197
1198
        $this->expectException(Exception::class);
1199
1200
        $command->query();
1201
    }
1202
1203
    public function testQueryAll(): void
1204
    {
1205
        $db = $this->getConnection(true);
1206
1207
        $command = $db->createCommand();
1208
        $command->setSql(
1209
            <<<SQL
1210
            SELECT * FROM {{customer}}
1211
            SQL
1212
        );
1213
        $rows = $command->queryAll();
1214
1215
        $expectedRow = 6;
1216
1217
        if ($db->getName() === 'pgsql') {
1218
            $expectedRow = 7;
1219
        }
1220
1221
        $this->assertIsArray($rows);
1222
        $this->assertCount(3, $rows);
1223
        $this->assertIsArray($rows[0]);
1224
        $this->assertCount($expectedRow, $rows[0]);
1225
1226
        $command->setSql('bad SQL');
1227
1228
        $this->expectException(Exception::class);
1229
1230
        $command->queryAll();
1231
        $command->setSql(
1232
            <<<SQL
1233
            SELECT * FROM {{customer}} where id = 100
1234
            SQL
1235
        );
1236
        $rows = $command->queryAll();
1237
1238
        $this->assertIsArray($rows);
1239
        $this->assertCount(0, $rows);
1240
        $this->assertSame([], $rows);
1241
    }
1242
1243
    public function testQueryCache(): void
1244
    {
1245
        $db = $this->getConnection(true);
1246
1247
        $query = (new Query($db))->select(['{{name}}'])->from('{{customer}}');
1248
        $command = $db->createCommand();
1249
        $update = $command->setSql(
1250
            <<<SQL
1251
            UPDATE [[customer]] SET [[name]] = :name WHERE [[id]] = :id
1252
            SQL
1253
        );
1254
1255
        $this->assertSame('user1', $query->where(['id' => 1])->scalar(), 'Asserting initial value');
1256
1257
        /* No cache */
1258
        $update->bindValues([':id' => 1, ':name' => 'user11'])->execute();
1259
1260
        $this->assertSame(
1261
            'user11',
1262
            $query->where(['id' => 1])->scalar(),
1263
            'Query reflects DB changes when caching is disabled',
1264
        );
1265
1266
        /* Connection cache */
1267
        $db->cache(
1268
            static function (ConnectionPDOInterface $db) use ($query, $update) {
1269
                self::assertSame('user2', $query->where(['id' => 2])->scalar(), 'Asserting initial value for user #2');
1270
1271
                $update->bindValues([':id' => 2, ':name' => 'user22'])->execute();
1272
1273
                self::assertSame(
1274
                    'user2',
1275
                    $query->where(['id' => 2])->scalar(),
1276
                    'Query does NOT reflect DB changes when wrapped in connection caching',
1277
                );
1278
1279
                $db->noCache(
1280
                    static function () use ($query) {
1281
                        self::assertSame(
1282
                            'user22',
1283
                            $query->where(['id' => 2])->scalar(),
1284
                            'Query reflects DB changes when wrapped in connection caching and noCache simultaneously',
1285
                        );
1286
                    }
1287
                );
1288
1289
                self::assertSame(
1290
                    'user2',
1291
                    $query->where(['id' => 2])->scalar(),
1292
                    'Cache does not get changes after getting newer data from DB in noCache block.',
1293
                );
1294
            },
1295
            10,
1296
        );
1297
1298
        $db->queryCacheEnable(false);
1299
1300
        $db->cache(
1301
            static function () use ($query, $update) {
1302
                self::assertSame(
1303
                    'user22',
1304
                    $query->where(['id' => 2])->scalar(),
1305
                    'When cache is disabled for the whole connection, Query inside cache block does not get cached',
1306
                );
1307
1308
                $update->bindValues([':id' => 2, ':name' => 'user2'])->execute();
1309
1310
                self::assertSame('user2', $query->where(['id' => 2])->scalar());
1311
            },
1312
            10,
1313
        );
1314
1315
        $db->queryCacheEnable(true);
1316
        $query->cache();
1317
1318
        $this->assertSame('user11', $query->where(['id' => 1])->scalar());
1319
1320
        $update->bindValues([':id' => 1, ':name' => 'user1'])->execute();
1321
1322
        $this->assertSame(
1323
            'user11',
1324
            $query->where(['id' => 1])->scalar(),
1325
            'When both Connection and Query have cache enabled, we get cached value',
1326
        );
1327
        $this->assertSame(
1328
            'user1',
1329
            $query->noCache()->where(['id' => 1])->scalar(),
1330
            'When Query has disabled cache, we get actual data',
1331
        );
1332
1333
        $db->cache(
1334
            static function () use ($query) {
1335
                self::assertSame('user1', $query->noCache()->where(['id' => 1])->scalar());
1336
                self::assertSame('user11', $query->cache()->where(['id' => 1])->scalar());
1337
            },
1338
            10,
1339
        );
1340
    }
1341
1342
    public function testQueryColumn(): void
1343
    {
1344
        $db = $this->getConnection(true);
1345
1346
        $command = $db->createCommand();
1347
        $command->setSql(
1348
            <<<SQL
1349
            SELECT * FROM [[customer]]
1350
            SQL
1351
        );
1352
        $rows = $command->queryColumn();
1353
1354
        $this->assertIsArray($rows);
1355
        $this->assertCount(3, $rows);
1356
        $this->assertEquals('1', $rows[0]);
1357
1358
        $command->setSql('bad SQL');
1359
1360
        $this->expectException(Exception::class);
1361
1362
        $command->queryColumn();
1363
        $command->setSql(
1364
            <<<SQL
1365
            SELECT * FROM [[customer]] where id = 100
1366
            SQL
1367
        );
1368
        $rows = $command->queryColumn();
1369
1370
        $this->assertIsArray($rows);
1371
        $this->assertCount(0, $rows);
1372
        $this->assertSame([], $rows);
1373
    }
1374
1375
    public function testQueryOne(): void
1376
    {
1377
        $db = $this->getConnection(true);
1378
1379
        $command = $db->createCommand();
1380
        $sql = <<<SQL
1381
        SELECT * FROM [[customer]] ORDER BY [[id]]
1382
        SQL;
1383
        $row = $command->setSql($sql)->queryOne();
1384
1385
        $this->assertIsArray($row);
1386
        $this->assertEquals(1, $row['id']);
1387
        $this->assertEquals('user1', $row['name']);
1388
1389
        $command->setSql($sql)->prepare();
1390
        $row = $command->queryOne();
1391
1392
        $this->assertIsArray($row);
1393
        $this->assertEquals(1, $row['id']);
1394
        $this->assertEquals('user1', $row['name']);
1395
1396
        $sql = <<<SQL
1397
        SELECT * FROM [[customer]] WHERE [[id]] = 10
1398
        SQL;
1399
        $command = $command->setSql($sql);
1400
1401
        $this->assertNull($command->queryOne());
1402
    }
1403
1404
    public function testQueryScalar(): void
1405
    {
1406
        $db = $this->getConnection(true);
1407
1408
        $command = $db->createCommand();
1409
        $sql = <<<SQL
1410
        SELECT * FROM [[customer]] ORDER BY [[id]]
1411
        SQL;
1412
1413
        $this->assertEquals(1, $command->setSql($sql)->queryScalar());
1414
1415
        $sql = <<<SQL
1416
        SELECT [[id]] FROM [[customer]] ORDER BY [[id]]
1417
        SQL;
1418
        $command->setSql($sql)->prepare();
1419
1420
        $this->assertEquals(1, $command->queryScalar());
1421
1422
        $command = $command->setSql(
1423
            <<<SQL
1424
            SELECT [[id]] FROM [[customer]] WHERE [[id]] = 10
1425
            SQL
1426
        );
1427
1428
        $this->assertFalse($command->queryScalar());
1429
    }
1430
1431
    public function testRenameColumn(): void
1432
    {
1433
        $db = $this->getConnection(true);
1434
1435
        $command = $db->createCommand();
1436
        $schema = $db->getSchema();
1437
1438
        $command->renameColumn('{{customer}}', 'address', 'address_city')->execute();
1439
1440
        $this->assertContains('address_city', $schema->getTableSchema('{{customer}}')->getColumnNames());
1441
        $this->assertNotContains('address', $schema->getTableSchema('{{customer}}')->getColumnNames());
1442
    }
1443
1444
    public function testRenameTable(): void
1445
    {
1446
        $db = $this->getConnection(true);
1447
1448
        $command = $db->createCommand();
1449
        $schema = $db->getSchema();
1450
1451
        if ($schema->getTableSchema('{{new_type}}') !== null) {
1452
            $command->dropTable('{{new_type}}')->execute();
1453
        }
1454
1455
        $this->assertNotNull($schema->getTableSchema('{{type}}'));
1456
        $this->assertNull($schema->getTableSchema('{{new_type}}'));
1457
1458
        $command->renameTable('{{type}}', '{{new_type}}')->execute();
1459
1460
        $this->assertNull($schema->getTableSchema('{{type}}', true));
1461
        $this->assertNotNull($schema->getTableSchema('{{new_type}}', true));
1462
    }
1463
1464
    public function testSetRetryHandler(): void
1465
    {
1466
        $db = $this->getConnection(true);
1467
1468
        $command = $db->createCommand();
1469
1470
        $this->assertNull($db->getTransaction());
1471
1472
        $command->setSql(
1473
            <<<SQL
1474
            INSERT INTO [[profile]] ([[description]]) VALUES('command retry')
1475
            SQL
1476
        )->execute();
1477
1478
        $this->assertNull($db->getTransaction());
1479
        $this->assertEquals(
1480
            1,
1481
            $command->setSql(
1482
                <<<SQL
1483
                SELECT COUNT(*) FROM [[profile]] WHERE [[description]] = 'command retry'
1484
                SQL
1485
            )->queryScalar()
1486
        );
1487
1488
        $attempts = null;
1489
        $hitHandler = false;
1490
        $hitCatch = false;
1491
        $command->setSql(
1492
            <<<SQL
1493
            INSERT INTO [[profile]] ([[id]], [[description]]) VALUES(1, 'command retry')
1494
            SQL
1495
        );
1496
1497
        Assert::invokeMethod(
1498
            $command,
1499
            'setRetryHandler',
1500
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
1501
                $attempts = $attempt;
1502
                $hitHandler = true;
1503
1504
                return $attempt <= 2;
1505
            }]
1506
        );
1507
1508
        try {
1509
            $command->execute();
1510
        } catch (Exception $e) {
1511
            $hitCatch = true;
1512
1513
            $this->assertInstanceOf(IntegrityException::class, $e);
1514
        }
1515
1516
        $this->assertNull($db->getTransaction());
1517
        $this->assertSame(3, $attempts);
1518
        $this->assertTrue($hitHandler);
1519
        $this->assertTrue($hitCatch);
1520
    }
1521
1522
    public function testTransaction(): void
1523
    {
1524
        $db = $this->getConnection(true);
1525
1526
        $this->assertNull($db->getTransaction());
1527
1528
        $command = $db->createCommand();
1529
        $command = $command->setSql(
1530
            <<<SQL
1531
            INSERT INTO [[profile]] ([[description]]) VALUES('command transaction')
1532
            SQL
1533
        );
1534
1535
        Assert::invokeMethod($command, 'requireTransaction');
1536
1537
        $command->execute();
1538
1539
        $this->assertNull($db->getTransaction());
1540
        $this->assertEquals(
1541
            1,
1542
            $command->setSql(
1543
                <<<SQL
1544
                SELECT COUNT(*) FROM [[profile]] WHERE [[description]] = 'command transaction'
1545
                SQL
1546
            )->queryScalar(),
1547
        );
1548
    }
1549
1550
    public function testTruncateTable(): void
1551
    {
1552
        $db = $this->getConnection(true);
1553
1554
        $command = $db->createCommand();
1555
        $rows = $command->setSql(
1556
            <<<SQL
1557
            SELECT * FROM [[animal]]
1558
            SQL
1559
        )->queryAll();
1560
1561
        $this->assertCount(2, $rows);
1562
1563
        $command->truncateTable('{{animal}}')->execute();
1564
        $rows = $command->setSql(
1565
            <<<SQL
1566
            SELECT * FROM {{animal}}
1567
            SQL
1568
        )->queryAll();
1569
1570
        $this->assertCount(0, $rows);
1571
    }
1572
1573
    /**
1574
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::update()
1575
     */
1576
    public function testUpdate(
1577
        string $table,
1578
        array $columns,
1579
        array|string $conditions,
1580
        array $params,
1581
        string $expected
1582
    ): void {
1583
        $db = $this->getConnection();
1584
1585
        $command = $db->createCommand();
1586
        $sql = $command->update($table, $columns, $conditions, $params)->getSql();
1587
1588
        $this->assertSame($expected, $sql);
1589
    }
1590
1591
    /**
1592
     * @dataProvider \Yiisoft\Db\Tests\Provider\CommandProvider::upsert()
1593
     */
1594
    public function testUpsert(array $firstData, array $secondData): void
1595
    {
1596
        $db = $this->getConnection(true);
1597
1598
        $command = $db->createCommand();
1599
1600
        $this->assertEquals(
1601
            0,
1602
            $command->setSql(
1603
                <<<SQL
1604
                SELECT COUNT(*) FROM [[T_upsert]]
1605
                SQL,
1606
            )->queryScalar()
1607
        );
1608
1609
        $this->performAndCompareUpsertResult($db, $firstData);
1610
1611
        $this->assertEquals(
1612
            1,
1613
            $command->setSql(
1614
                <<<SQL
1615
                SELECT COUNT(*) FROM [[T_upsert]]
1616
                SQL,
1617
            )->queryScalar()
1618
        );
1619
1620
        $this->performAndCompareUpsertResult($db, $secondData);
1621
    }
1622
1623
    protected function performAndCompareUpsertResult(ConnectionPDOInterface $db, array $data): void
1624
    {
1625
        $params = $data['params'];
1626
        $expected = $data['expected'] ?? $params[1];
1627
1628
        $command = $db->createCommand();
1629
1630
        call_user_func_array([$command, 'upsert'], $params);
1631
1632
        $command->execute();
1633
1634
        $actual = (new Query($db))
1635
            ->select(['email', 'address' => new Expression($this->upsertTestCharCast), 'status'])
1636
            ->from('{{T_upsert}}')
1637
            ->one();
1638
        $this->assertEquals($expected, $actual, $this->upsertTestCharCast);
1639
    }
1640
}
1641