Passed
Pull Request — master (#397)
by Wilmer
10:13 queued 07:33
created

CommonCommandTest::testBatchInsert()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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