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

CommonCommandTest::testQueryScalar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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