Passed
Pull Request — master (#412)
by Wilmer
36:35 queued 23:53
created

CommonCommandTest::testQueryCache()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 96
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 59
nc 1
nop 0
dl 0
loc 96
rs 8.8945
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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