Passed
Pull Request — master (#380)
by Wilmer
04:47 queued 01:51
created

CommonCommandTest::testBatchInsertFailsOld()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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