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