Passed
Pull Request — master (#163)
by Wilmer
10:25
created

TestCommandTrait::testInsertExpression()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 37
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 25
c 1
b 0
f 0
nc 5
nop 0
dl 0
loc 37
rs 9.2088
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\TestUtility;
6
7
use PDO;
8
use Throwable;
9
use Yiisoft\Db\Connection\Connection;
10
use Yiisoft\Db\Data\DataReader;
11
use Yiisoft\Db\Exception\Exception;
12
use Yiisoft\Db\Exception\IntegrityException;
13
use Yiisoft\Db\Exception\InvalidArgumentException;
14
use Yiisoft\Db\Expression\Expression;
15
use Yiisoft\Db\Query\Query;
16
use Yiisoft\Db\Schema\Schema;
17
18
use function call_user_func;
19
use function call_user_func_array;
20
use function date;
21
use function is_array;
22
use function range;
23
use function rtrim;
24
use function setlocale;
25
use function time;
26
27
trait TestCommandTrait
28
{
29
    protected string $upsertTestCharCast = 'CAST([[address]] AS VARCHAR(255))';
30
31
    public function testConstruct(): void
32
    {
33
        $db = $this->getConnection();
0 ignored issues
show
Bug introduced by
It seems like getConnection() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

33
        /** @scrutinizer ignore-call */ 
34
        $db = $this->getConnection();
Loading history...
34
35
        /* null */
36
        $command = $db->createCommand();
37
38
        $this->assertNull($command->getSql());
0 ignored issues
show
Bug introduced by
It seems like assertNull() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

38
        $this->/** @scrutinizer ignore-call */ 
39
               assertNull($command->getSql());
Loading history...
39
40
        /* string */
41
        $sql = 'SELECT * FROM customer';
42
43
        $command = $db->createCommand($sql);
44
45
        $this->assertEquals($sql, $command->getSql());
0 ignored issues
show
Bug introduced by
It seems like assertEquals() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

45
        $this->/** @scrutinizer ignore-call */ 
46
               assertEquals($sql, $command->getSql());
Loading history...
46
    }
47
48
    public function testGetSetSql(): void
49
    {
50
        $db = $this->getConnection();
51
52
        $sql = 'SELECT * FROM customer';
53
54
        $command = $db->createCommand($sql);
55
56
        $this->assertEquals($sql, $command->getSql());
57
58
        $sql2 = 'SELECT * FROM order';
59
60
        $command->setSql($sql2);
61
62
        $this->assertEquals($sql2, $command->getSql());
63
    }
64
65
    public function testPrepareCancel(): void
66
    {
67
        $db = $this->getConnection();
68
69
        $command = $db->createCommand('SELECT * FROM {{customer}}');
70
71
        $this->assertNull($command->getPdoStatement());
72
73
        $command->prepare();
74
75
        $this->assertNotNull($command->getPdoStatement());
0 ignored issues
show
Bug introduced by
It seems like assertNotNull() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

75
        $this->/** @scrutinizer ignore-call */ 
76
               assertNotNull($command->getPdoStatement());
Loading history...
76
77
        $command->cancel();
78
79
        $this->assertNull($command->getPdoStatement());
80
    }
81
82
    public function testExecute(): void
83
    {
84
        $db = $this->getConnection(true);
85
86
        $sql = 'INSERT INTO {{customer}}([[email]], [[name]], [[address]]) VALUES (\'[email protected]\', \'user4\', \'address4\')';
87
88
        $command = $db->createCommand($sql);
89
90
        $this->assertEquals(1, $command->execute());
91
92
        $sql = 'SELECT COUNT(*) FROM {{customer}} WHERE [[name]] = \'user4\'';
93
94
        $command = $db->createCommand($sql);
95
96
        $this->assertEquals(1, $command->queryScalar());
97
98
        $command = $db->createCommand('bad SQL');
99
100
        $this->expectException(Exception::class);
0 ignored issues
show
Bug introduced by
It seems like expectException() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

100
        $this->/** @scrutinizer ignore-call */ 
101
               expectException(Exception::class);
Loading history...
101
102
        $command->execute();
103
    }
104
105
    public function testQuery(): void
106
    {
107
        $db = $this->getConnection(true);
108
109
        /* query */
110
        $sql = 'SELECT * FROM {{customer}}';
111
112
        $reader = $db->createCommand($sql)->Query();
113
114
        $this->assertInstanceOf(DataReader::class, $reader);
0 ignored issues
show
Bug introduced by
It seems like assertInstanceOf() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

114
        $this->/** @scrutinizer ignore-call */ 
115
               assertInstanceOf(DataReader::class, $reader);
Loading history...
115
116
        /* queryAll */
117
        $rows = $db->createCommand('SELECT * FROM {{customer}}')->queryAll();
118
119
        $this->assertCount(3, $rows);
0 ignored issues
show
Bug introduced by
It seems like assertCount() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
        $this->/** @scrutinizer ignore-call */ 
120
               assertCount(3, $rows);
Loading history...
120
121
        $row = $rows[2];
122
123
        $this->assertEquals(3, $row['id']);
124
        $this->assertEquals('user3', $row['name']);
125
126
        $rows = $db->createCommand('SELECT * FROM {{customer}} WHERE [[id]] = 10')->queryAll();
127
128
        $this->assertEquals([], $rows);
129
130
        /* queryOne */
131
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
132
133
        $row = $db->createCommand($sql)->queryOne();
134
135
        $this->assertEquals(1, $row['id']);
136
        $this->assertEquals('user1', $row['name']);
137
138
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
139
140
        $command = $db->createCommand($sql);
141
142
        $command->prepare();
143
144
        $row = $command->queryOne();
145
146
        $this->assertEquals(1, $row['id']);
147
        $this->assertEquals('user1', $row['name']);
148
149
        $sql = 'SELECT * FROM {{customer}} WHERE [[id]] = 10';
150
151
        $command = $db->createCommand($sql);
152
153
        $this->assertFalse($command->queryOne());
0 ignored issues
show
Bug introduced by
It seems like assertFalse() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
        $this->/** @scrutinizer ignore-call */ 
154
               assertFalse($command->queryOne());
Loading history...
154
155
        /* queryColumn */
156
        $sql = 'SELECT * FROM {{customer}}';
157
158
        $column = $db->createCommand($sql)->queryColumn();
159
160
        $this->assertEquals(range(1, 3), $column);
161
162
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
163
164
        $this->assertEquals([], $command->queryColumn());
165
166
        /* queryScalar */
167
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
168
169
        $this->assertEquals($db->createCommand($sql)->queryScalar(), 1);
170
171
        $sql = 'SELECT [[id]] FROM {{customer}} ORDER BY [[id]]';
172
173
        $command = $db->createCommand($sql);
174
175
        $command->prepare();
176
177
        $this->assertEquals(1, $command->queryScalar());
178
179
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
180
181
        $this->assertFalse($command->queryScalar());
182
183
        $command = $db->createCommand('bad SQL');
184
185
        $this->expectException(Exception::class);
186
187
        $command->Query();
188
    }
189
190
    /**
191
     * Test whether param binding works in other places than WHERE.
192
     *
193
     * @dataProvider paramsNonWhereProvider
194
     *
195
     * @param string $sql
196
     */
197
    public function testBindParamsNonWhere(string $sql): void
198
    {
199
        $db = $this->getConnection();
200
201
        $db->createCommand()->insert(
202
            'customer',
203
            [
204
                'name' => 'testParams',
205
                'email' => '[email protected]',
206
                'address' => '1'
207
            ]
208
        )->execute();
209
210
        $params = [
211
            ':email' => '[email protected]',
212
            ':len'   => 5,
213
        ];
214
215
        $command = $db->createCommand($sql, $params);
216
217
        $this->assertEquals('Params', $command->queryScalar());
218
    }
219
220
    public function testFetchMode(): void
221
    {
222
        $db = $this->getConnection();
223
224
        /* default: FETCH_ASSOC */
225
        $sql = 'SELECT * FROM {{customer}}';
226
227
        $command = $db->createCommand($sql);
228
229
        $result = $command->queryOne();
230
231
        $this->assertTrue(is_array($result) && isset($result['id']));
0 ignored issues
show
Bug introduced by
It seems like assertTrue() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

231
        $this->/** @scrutinizer ignore-call */ 
232
               assertTrue(is_array($result) && isset($result['id']));
Loading history...
232
233
        /* FETCH_OBJ, customized via fetchMode property */
234
        $sql = 'SELECT * FROM {{customer}}';
235
236
        $command = $db->createCommand($sql);
237
238
        $command->setFetchMode(PDO::FETCH_OBJ);
239
240
        $result = $command->queryOne();
241
242
        $this->assertIsObject($result);
0 ignored issues
show
Bug introduced by
It seems like assertIsObject() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

242
        $this->/** @scrutinizer ignore-call */ 
243
               assertIsObject($result);
Loading history...
243
244
        /* FETCH_NUM, customized in query method */
245
        $sql = 'SELECT * FROM {{customer}}';
246
247
        $command = $db->createCommand($sql);
248
249
        $result = $command->queryOne(PDO::FETCH_NUM);
250
251
        $this->assertTrue(is_array($result) && isset($result[0]));
252
    }
253
254
    public function testBatchInsert(): void
255
    {
256
        $db = $this->getConnection();
257
258
        $command = $db->createCommand();
259
260
        $command->batchInsert(
261
            '{{customer}}',
262
            ['email', 'name', 'address'],
263
            [
264
                ['[email protected]', 't1', 't1 address'],
265
                ['[email protected]', null, false],
266
            ]
267
        );
268
269
        $this->assertEquals(2, $command->execute());
270
271
        /**
272
         * {@see https://github.com/yiisoft/yii2/issues/11693}
273
         */
274
        $command = $this->getConnection()->createCommand();
275
276
        $command->batchInsert(
277
            '{{customer}}',
278
            ['email', 'name', 'address'],
279
            []
280
        );
281
282
        $this->assertEquals(0, $command->execute());
283
    }
284
285
    public function testBatchInsertWithYield(): void
286
    {
287
        $rows = call_user_func(static function () {
288
            if (false) {
289
                yield [];
290
            }
291
        });
292
293
        $command = $this->getConnection()->createCommand();
294
295
        $command->batchInsert(
296
            '{{customer}}',
297
            ['email', 'name', 'address'],
298
            $rows
299
        );
300
301
        $this->assertEquals(0, $command->execute());
302
    }
303
304
    /**
305
     * Test batch insert with different data types.
306
     *
307
     * Ensure double is inserted with `.` decimal separator.
308
     *
309
     * {@see https://github.com/yiisoft/yii2/issues/6526}
310
     */
311
    public function testBatchInsertDataTypesLocale(): void
312
    {
313
        $locale = setlocale(LC_NUMERIC, 0);
314
315
        if (false === $locale) {
316
            $this->markTestSkipped('Your platform does not support locales.');
0 ignored issues
show
Bug introduced by
It seems like markTestSkipped() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

316
            $this->/** @scrutinizer ignore-call */ 
317
                   markTestSkipped('Your platform does not support locales.');
Loading history...
317
        }
318
319
        $db = $this->getConnection();
320
321
        try {
322
            /* This one sets decimal mark to comma sign */
323
            setlocale(LC_NUMERIC, 'ru_RU.utf8');
324
325
            $cols = ['int_col', 'char_col', 'float_col', 'bool_col'];
326
327
            $data = [
328
                [1, 'A', 9.735, true],
329
                [2, 'B', -2.123, false],
330
                [3, 'C', 2.123, false],
331
            ];
332
333
            /* clear data in "type" table */
334
            $db->createCommand()->delete('type')->execute();
335
336
            /* batch insert on "type" table */
337
            $db->createCommand()->batchInsert('type', $cols, $data)->execute();
338
339
            $data = $db->createCommand(
340
                'SELECT int_col, char_col, float_col, bool_col FROM {{type}} WHERE [[int_col]] IN (1,2,3)
341
                ORDER BY [[int_col]];'
342
            )->queryAll();
343
344
            $this->assertCount(3, $data);
345
            $this->assertEquals(1, $data[0]['int_col']);
346
            $this->assertEquals(2, $data[1]['int_col']);
347
            $this->assertEquals(3, $data[2]['int_col']);
348
349
            /* rtrim because Postgres padds the column with whitespace */
350
            $this->assertEquals('A', rtrim($data[0]['char_col']));
351
            $this->assertEquals('B', rtrim($data[1]['char_col']));
352
            $this->assertEquals('C', rtrim($data[2]['char_col']));
353
            $this->assertEquals('9.735', $data[0]['float_col']);
354
            $this->assertEquals('-2.123', $data[1]['float_col']);
355
            $this->assertEquals('2.123', $data[2]['float_col']);
356
            $this->assertEquals('1', $data[0]['bool_col']);
357
            $this->assertIsOneOf($data[1]['bool_col'], ['0', false]);
0 ignored issues
show
Bug introduced by
It seems like assertIsOneOf() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

357
            $this->/** @scrutinizer ignore-call */ 
358
                   assertIsOneOf($data[1]['bool_col'], ['0', false]);
Loading history...
358
            $this->assertIsOneOf($data[2]['bool_col'], ['0', false]);
359
        } catch (Exception $e) {
360
            setlocale(LC_NUMERIC, $locale);
361
362
            throw $e;
363
        } catch (Throwable $e) {
364
            setlocale(LC_NUMERIC, $locale);
365
366
            throw $e;
367
        }
368
369
        setlocale(LC_NUMERIC, $locale);
370
    }
371
372
    /**
373
     * Make sure that `{{something}}` in values will not be encoded.
374
     *
375
     * @dataProvider batchInsertSqlProvider
376
     *
377
     * @param mixed $table
378
     * @param mixed $columns
379
     * @param mixed $values
380
     * @param mixed $expected
381
     * @param array $expectedParams
382
     *
383
     * {@see https://github.com/yiisoft/yii2/issues/11242}
384
     */
385
    public function testBatchInsertSQL($table, $columns, $values, $expected, array $expectedParams = []): void
386
    {
387
        $db = $this->getConnection();
388
389
        $command = $db->createCommand();
390
391
        $command->batchInsert($table, $columns, $values);
392
393
        $command->prepare(false);
394
395
        $this->assertSame($expected, $command->getSql());
0 ignored issues
show
Bug introduced by
It seems like assertSame() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

395
        $this->/** @scrutinizer ignore-call */ 
396
               assertSame($expected, $command->getSql());
Loading history...
396
        $this->assertSame($expectedParams, $command->getParams());
397
    }
398
399
    public function testInsert(): void
400
    {
401
        $db = $this->getConnection();
402
403
        $db->createCommand('DELETE FROM {{customer}}')->execute();
404
405
        $command = $db->createCommand();
406
407
        $command->insert(
408
            '{{customer}}',
409
            [
410
                'email'   => '[email protected]',
411
                'name'    => 'test',
412
                'address' => 'test address',
413
            ]
414
        )->execute();
415
416
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{customer}};')->queryScalar());
417
418
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryOne();
419
420
        $this->assertEquals([
421
            'email'   => '[email protected]',
422
            'name'    => 'test',
423
            'address' => 'test address',
424
        ], $record);
425
    }
426
427
    /**
428
     * verify that {{}} are not going to be replaced in parameters.
429
     */
430
    public function testNoTablenameReplacement(): void
431
    {
432
        $db = $this->getConnection(true);
433
434
        $db->createCommand()->insert(
435
            '{{customer}}',
436
            [
437
                'name'    => 'Some {{weird}} name',
438
                'email'   => '[email protected]',
439
                'address' => 'Some {{%weird}} address',
440
            ]
441
        )->execute();
442
443
        if ($db->getDriverName() === 'pgsql') {
444
            $customerId = $db->getLastInsertID('public.customer_id_seq');
445
        } else {
446
            $customerId = $db->getLastInsertID();
447
        }
448
449
        $customer = $db->createCommand('SELECT * FROM {{customer}} WHERE id=' . $customerId)->queryOne();
450
451
        $this->assertEquals('Some {{weird}} name', $customer['name']);
452
        $this->assertEquals('Some {{%weird}} address', $customer['address']);
453
454
        $db->createCommand()->update(
455
            '{{customer}}',
456
            [
457
                'name'    => 'Some {{updated}} name',
458
                'address' => 'Some {{%updated}} address',
459
            ],
460
            ['id' => $customerId]
461
        )->execute();
462
463
        $customer = $db->createCommand('SELECT * FROM {{customer}} WHERE id=' . $customerId)->queryOne();
464
465
        $this->assertEquals('Some {{updated}} name', $customer['name']);
466
        $this->assertEquals('Some {{%updated}} address', $customer['address']);
467
    }
468
469
    /**
470
     * Test INSERT INTO ... SELECT SQL statement.
471
     */
472
    public function testInsertSelect(): void
473
    {
474
        $db = $this->getConnection();
475
476
        $db->createCommand('DELETE FROM {{customer}}')->execute();
477
478
        $command = $db->createCommand();
479
480
        $command->insert(
481
            '{{customer}}',
482
            [
483
                'email'   => '[email protected]',
484
                'name'    => 'test',
485
                'address' => 'test address',
486
            ]
487
        )->execute();
488
489
        $query = new Query($db);
490
491
        $query->select(
492
            [
493
                '{{customer}}.[[email]] as name',
494
                '[[name]] as email',
495
                '[[address]]',
496
            ]
497
        )
498
            ->from('{{customer}}')
499
            ->where([
500
                'and',
501
                ['<>', 'name', 'foo'],
502
                ['status' => [0, 1, 2, 3]],
503
            ]);
504
505
        $command = $db->createCommand();
506
507
        $command->insert(
508
            '{{customer}}',
509
            $query
510
        )->execute();
511
512
        $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{customer}}')->queryScalar());
513
514
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryAll();
515
516
        $this->assertEquals([
517
            [
518
                'email'   => '[email protected]',
519
                'name'    => 'test',
520
                'address' => 'test address',
521
            ],
522
            [
523
                'email'   => 'test',
524
                'name'    => '[email protected]',
525
                'address' => 'test address',
526
            ],
527
        ], $record);
528
    }
529
530
    /**
531
     * Test INSERT INTO ... SELECT SQL statement with alias syntax.
532
     */
533
    public function testInsertSelectAlias(): void
534
    {
535
        $db = $this->getConnection();
536
537
        $db->createCommand('DELETE FROM {{customer}}')->execute();
538
539
        $command = $db->createCommand();
540
541
        $command->insert(
542
            '{{customer}}',
543
            [
544
                'email'   => '[email protected]',
545
                'name'    => 'test',
546
                'address' => 'test address',
547
            ]
548
        )->execute();
549
550
        $query = new Query($db);
551
552
        $query->select(
553
            [
554
                'email'   => '{{customer}}.[[email]]',
555
                'address' => 'name',
556
                'name'    => 'address',
557
            ]
558
        )
559
            ->from('{{customer}}')
560
            ->where([
561
                'and',
562
                ['<>', 'name', 'foo'],
563
                ['status' => [0, 1, 2, 3]],
564
            ]);
565
566
        $command = $db->createCommand();
567
568
        $command->insert(
569
            '{{customer}}',
570
            $query
571
        )->execute();
572
573
        $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{customer}}')->queryScalar());
574
575
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryAll();
576
577
        $this->assertEquals([
578
            [
579
                'email'   => '[email protected]',
580
                'name'    => 'test',
581
                'address' => 'test address',
582
            ],
583
            [
584
                'email'   => '[email protected]',
585
                'name'    => 'test address',
586
                'address' => 'test',
587
            ],
588
        ], $record);
589
    }
590
591
    /**
592
     * Data provider for testInsertSelectFailed.
593
     *
594
     * @return array
595
     */
596
    public function invalidSelectColumns(): array
597
    {
598
        return [
599
            [[]],
600
            ['*'],
601
            [['*']],
602
        ];
603
    }
604
605
    /**
606
     * Test INSERT INTO ... SELECT SQL statement with wrong query object.
607
     *
608
     * @dataProvider invalidSelectColumns
609
     *
610
     * @param mixed $invalidSelectColumns
611
     */
612
    public function testInsertSelectFailed($invalidSelectColumns): void
613
    {
614
        $db = $this->getConnection();
615
616
        $query = new Query($db);
617
618
        $query->select($invalidSelectColumns)->from('{{customer}}');
619
620
        $command = $db->createCommand();
621
622
        $this->expectException(InvalidArgumentException::class);
623
        $this->expectExceptionMessage('Expected select query object with enumerated (named) parameters');
0 ignored issues
show
Bug introduced by
It seems like expectExceptionMessage() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

623
        $this->/** @scrutinizer ignore-call */ 
624
               expectExceptionMessage('Expected select query object with enumerated (named) parameters');
Loading history...
624
625
        $command->insert(
626
            '{{customer}}',
627
            $query
628
        )->execute();
629
    }
630
631
    public function testInsertExpression(): void
632
    {
633
        $db = $this->getConnection();
634
635
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
636
637
        switch ($db->getDriverName()) {
638
            case 'pgsql':
639
                $expression = "EXTRACT(YEAR FROM TIMESTAMP 'now')";
640
                break;
641
            case 'mysql':
642
                $expression = 'YEAR(NOW())';
643
                break;
644
            case 'sqlite':
645
                $expression = "strftime('%Y')";
646
                break;
647
            case 'sqlsrv':
648
                $expression = 'YEAR(GETDATE())';
649
        }
650
651
        $command = $db->createCommand();
652
653
        $command->insert(
654
            '{{order_with_null_fk}}',
655
            [
656
                'created_at' => new Expression($expression),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $expression does not seem to be defined for all execution paths leading up to this point.
Loading history...
657
                'total' => 1,
658
            ]
659
        )->execute();
660
661
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{order_with_null_fk}}')->queryScalar());
662
663
        $record = $db->createCommand('SELECT [[created_at]] FROM {{order_with_null_fk}}')->queryOne();
664
665
        $this->assertEquals([
666
            'created_at' => date('Y'),
667
        ], $record);
668
    }
669
670
    public function testsInsertQueryAsColumnValue(): void
671
    {
672
        $time = time();
673
674
        $db = $this->getConnection();
675
676
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
677
678
        $command = $db->createCommand();
679
680
        $command->insert('{{order}}', [
681
            'customer_id' => 1,
682
            'created_at'  => $time,
683
            'total'       => 42,
684
        ])->execute();
685
686
        if ($db->getDriverName() === 'pgsql') {
687
            $orderId = $db->getLastInsertID('public.order_id_seq');
688
        } else {
689
            $orderId = $db->getLastInsertID();
690
        }
691
692
        $columnValueQuery = new Query($db);
693
694
        $columnValueQuery->select('created_at')->from('{{order}}')->where(['id' => $orderId]);
695
696
        $command = $db->createCommand();
697
698
        $command->insert(
699
            '{{order_with_null_fk}}',
700
            [
701
                'customer_id' => $orderId,
702
                'created_at'  => $columnValueQuery,
703
                'total'       => 42,
704
            ]
705
        )->execute();
706
707
        $this->assertEquals(
708
            $time,
709
            $db->createCommand(
710
                'SELECT [[created_at]] FROM {{order_with_null_fk}} WHERE [[customer_id]] = ' . $orderId
711
            )->queryScalar()
712
        );
713
714
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
715
        $db->createCommand('DELETE FROM {{order}} WHERE [[id]] = ' . $orderId)->execute();
716
    }
717
718
    public function testCreateTable(): void
719
    {
720
        $db = $this->getConnection();
721
722
        if ($db->getSchema()->getTableSchema('testCreateTable') !== null) {
723
            $db->createCommand()->dropTable('testCreateTable')->execute();
724
        }
725
726
        $db->createCommand()->createTable(
727
            'testCreateTable',
728
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER]
729
        )->execute();
730
731
        $db->createCommand()->insert('testCreateTable', ['bar' => 1])->execute();
732
733
        $records = $db->createCommand('SELECT [[id]], [[bar]] FROM {{testCreateTable}};')->queryAll();
734
735
        $this->assertEquals([
736
            ['id' => 1, 'bar' => 1],
737
        ], $records);
738
    }
739
740
    public function testDropTable(): void
741
    {
742
        $db = $this->getConnection();
743
744
        $tableName = 'type';
745
746
        $this->assertNotNull($db->getSchema()->getTableSchema($tableName));
747
748
        $db->createCommand()->dropTable($tableName)->execute();
749
750
        $this->assertNull($db->getSchema()->getTableSchema($tableName));
751
    }
752
753
    public function testTruncateTable(): void
754
    {
755
        $db = $this->getConnection();
756
757
        $rows = $db->createCommand('SELECT * FROM {{animal}}')->queryAll();
758
759
        $this->assertCount(2, $rows);
760
761
        $db->createCommand()->truncateTable('animal')->execute();
762
763
        $rows = $db->createCommand('SELECT * FROM {{animal}}')->queryAll();
764
765
        $this->assertCount(0, $rows);
766
    }
767
768
    public function testRenameTable(): void
769
    {
770
        $db = $this->getConnection(true);
771
772
        $fromTableName = 'type';
773
        $toTableName = 'new_type';
774
775
        if ($db->getSchema()->getTableSchema($toTableName) !== null) {
776
            $db->createCommand()->dropTable($toTableName)->execute();
777
        }
778
779
        $this->assertNotNull($db->getSchema()->getTableSchema($fromTableName));
780
        $this->assertNull($db->getSchema()->getTableSchema($toTableName));
781
782
        $db->createCommand()->renameTable($fromTableName, $toTableName)->execute();
783
784
        $this->assertNull($db->getSchema()->getTableSchema($fromTableName, true));
785
        $this->assertNotNull($db->getSchema()->getTableSchema($toTableName, true));
786
    }
787
788
    public function upsertProvider(): array
789
    {
790
        $db = $this->getConnection();
791
792
        return [
793
            'regular values' => [
794
                [
795
                    'params' => [
796
                        'T_upsert',
797
                        [
798
                            'email'   => '[email protected]',
799
                            'address' => 'Earth',
800
                            'status'  => 3,
801
                        ],
802
                    ],
803
                ],
804
                [
805
                    'params' => [
806
                        'T_upsert',
807
                        [
808
                            'email'   => '[email protected]',
809
                            'address' => 'Universe',
810
                            'status'  => 1,
811
                        ],
812
                    ],
813
                ],
814
            ],
815
            'regular values with update part' => [
816
                [
817
                    'params' => [
818
                        'T_upsert',
819
                        [
820
                            'email'   => '[email protected]',
821
                            'address' => 'Earth',
822
                            'status'  => 3,
823
                        ],
824
                        [
825
                            'address' => 'Moon',
826
                            'status'  => 2,
827
                        ],
828
                    ],
829
                ],
830
                [
831
                    'params' => [
832
                        'T_upsert',
833
                        [
834
                            'email'   => '[email protected]',
835
                            'address' => 'Universe',
836
                            'status'  => 1,
837
                        ],
838
                        [
839
                            'address' => 'Moon',
840
                            'status'  => 2,
841
                        ],
842
                    ],
843
                    'expected' => [
844
                        'email'   => '[email protected]',
845
                        'address' => 'Moon',
846
                        'status'  => 2,
847
                    ],
848
                ],
849
            ],
850
            'regular values without update part' => [
851
                [
852
                    'params' => [
853
                        'T_upsert',
854
                        [
855
                            'email'   => '[email protected]',
856
                            'address' => 'Earth',
857
                            'status'  => 3,
858
                        ],
859
                        false,
860
                    ],
861
                ],
862
                [
863
                    'params' => [
864
                        'T_upsert',
865
                        [
866
                            'email'   => '[email protected]',
867
                            'address' => 'Universe',
868
                            'status'  => 1,
869
                        ],
870
                        false,
871
                    ],
872
                    'expected' => [
873
                        'email'   => '[email protected]',
874
                        'address' => 'Earth',
875
                        'status'  => 3,
876
                    ],
877
                ],
878
            ],
879
            'query' => [
880
                [
881
                    'params' => [
882
                        'T_upsert',
883
                        (new Query($db))
884
                            ->select([
885
                                'email',
886
                                'address',
887
                                'status' => new Expression('1'),
888
                            ])
889
                            ->from('customer')
890
                            ->where(['name' => 'user1'])
891
                            ->limit(1),
892
                    ],
893
                    'expected' => [
894
                        'email'   => '[email protected]',
895
                        'address' => 'address1',
896
                        'status'  => 1,
897
                    ],
898
                ],
899
                [
900
                    'params' => [
901
                        'T_upsert',
902
                        (new Query($db))
903
                            ->select([
904
                                'email',
905
                                'address',
906
                                'status' => new Expression('2'),
907
                            ])
908
                            ->from('customer')
909
                            ->where(['name' => 'user1'])
910
                            ->limit(1),
911
                    ],
912
                    'expected' => [
913
                        'email'   => '[email protected]',
914
                        'address' => 'address1',
915
                        'status'  => 2,
916
                    ],
917
                ],
918
            ],
919
            'query with update part' => [
920
                [
921
                    'params' => [
922
                        'T_upsert',
923
                        (new Query($db))
924
                            ->select([
925
                                'email',
926
                                'address',
927
                                'status' => new Expression('1'),
928
                            ])
929
                            ->from('customer')
930
                            ->where(['name' => 'user1'])
931
                            ->limit(1),
932
                        [
933
                            'address' => 'Moon',
934
                            'status'  => 2,
935
                        ],
936
                    ],
937
                    'expected' => [
938
                        'email'   => '[email protected]',
939
                        'address' => 'address1',
940
                        'status'  => 1,
941
                    ],
942
                ],
943
                [
944
                    'params' => [
945
                        'T_upsert',
946
                        (new Query($db))
947
                            ->select([
948
                                'email',
949
                                'address',
950
                                'status' => new Expression('3'),
951
                            ])
952
                            ->from('customer')
953
                            ->where(['name' => 'user1'])
954
                            ->limit(1),
955
                        [
956
                            'address' => 'Moon',
957
                            'status'  => 2,
958
                        ],
959
                    ],
960
                    'expected' => [
961
                        'email'   => '[email protected]',
962
                        'address' => 'Moon',
963
                        'status'  => 2,
964
                    ],
965
                ],
966
            ],
967
            'query without update part' => [
968
                [
969
                    'params' => [
970
                        'T_upsert',
971
                        (new Query($db))
972
                            ->select([
973
                                'email',
974
                                'address',
975
                                'status' => new Expression('1'),
976
                            ])
977
                            ->from('customer')
978
                            ->where(['name' => 'user1'])
979
                            ->limit(1),
980
                        false,
981
                    ],
982
                    'expected' => [
983
                        'email'   => '[email protected]',
984
                        'address' => 'address1',
985
                        'status'  => 1,
986
                    ],
987
                ],
988
                [
989
                    'params' => [
990
                        'T_upsert',
991
                        (new Query($db))
992
                            ->select([
993
                                'email',
994
                                'address',
995
                                'status' => new Expression('2'),
996
                            ])
997
                            ->from('customer')
998
                            ->where(['name' => 'user1'])
999
                            ->limit(1),
1000
                        false,
1001
                    ],
1002
                    'expected' => [
1003
                        'email'   => '[email protected]',
1004
                        'address' => 'address1',
1005
                        'status'  => 1,
1006
                    ],
1007
                ],
1008
            ],
1009
        ];
1010
    }
1011
1012
    /**
1013
     * @dataProvider upsertProvider
1014
     *
1015
     * @param array $firstData
1016
     * @param array $secondData
1017
     */
1018
    public function testUpsert(array $firstData, array $secondData): void
1019
    {
1020
        $db = $this->getConnection(true);
1021
1022
        $this->assertEquals(0, $db->createCommand('SELECT COUNT(*) FROM {{T_upsert}}')->queryScalar());
1023
1024
        $this->performAndCompareUpsertResult($db, $firstData);
1025
1026
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{T_upsert}}')->queryScalar());
1027
1028
        $this->performAndCompareUpsertResult($db, $secondData);
1029
    }
1030
1031
    protected function performAndCompareUpsertResult(Connection $db, array $data): void
1032
    {
1033
        $params = $data['params'];
1034
        $expected = $data['expected'] ?? $params[1];
1035
1036
        $command = $db->createCommand();
1037
1038
        call_user_func_array([$command, 'upsert'], $params);
1039
1040
        $command->execute();
1041
1042
        $actual = (new Query($db))
1043
            ->select([
1044
                'email',
1045
                'address' => new Expression($this->upsertTestCharCast),
1046
                'status',
1047
            ])
1048
            ->from('T_upsert')
1049
            ->one();
1050
1051
        $this->assertEquals($expected, $actual, $this->upsertTestCharCast);
1052
    }
1053
1054
    public function testAddDropPrimaryKey(): void
1055
    {
1056
        $db = $this->getConnection();
1057
1058
        $tableName = 'test_pk';
1059
        $name = 'test_pk_constraint';
1060
1061
        $schema = $db->getSchema();
1062
1063
        if ($schema->getTableSchema($tableName) !== null) {
1064
            $db->createCommand()->dropTable($tableName)->execute();
1065
        }
1066
1067
        $db->createCommand()->createTable($tableName, [
1068
            'int1' => 'integer not null',
1069
            'int2' => 'integer not null',
1070
        ])->execute();
1071
1072
        $this->assertNull($schema->getTablePrimaryKey($tableName, true));
1073
1074
        $db->createCommand()->addPrimaryKey($name, $tableName, ['int1'])->execute();
1075
1076
        $this->assertEquals(['int1'], $schema->getTablePrimaryKey($tableName, true)->getColumnNames());
1077
1078
        $db->createCommand()->dropPrimaryKey($name, $tableName)->execute();
1079
1080
        $this->assertNull($schema->getTablePrimaryKey($tableName, true));
1081
1082
        $db->createCommand()->addPrimaryKey($name, $tableName, ['int1', 'int2'])->execute();
1083
1084
        $this->assertEquals(['int1', 'int2'], $schema->getTablePrimaryKey($tableName, true)->getColumnNames());
1085
    }
1086
1087
    public function testAddDropForeignKey(): void
1088
    {
1089
        $db = $this->getConnection();
1090
1091
        $tableName = 'test_fk';
1092
        $name = 'test_fk_constraint';
1093
1094
        $schema = $db->getSchema();
1095
1096
        if ($schema->getTableSchema($tableName) !== null) {
1097
            $db->createCommand()->dropTable($tableName)->execute();
1098
        }
1099
1100
        $db->createCommand()->createTable($tableName, [
1101
            'int1' => 'integer not null unique',
1102
            'int2' => 'integer not null unique',
1103
            'int3' => 'integer not null unique',
1104
            'int4' => 'integer not null unique',
1105
            'unique ([[int1]], [[int2]])',
1106
            'unique ([[int3]], [[int4]])',
1107
        ])->execute();
1108
1109
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
0 ignored issues
show
Bug introduced by
It seems like assertEmpty() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1109
        $this->/** @scrutinizer ignore-call */ 
1110
               assertEmpty($schema->getTableForeignKeys($tableName, true));
Loading history...
1110
1111
        $db->createCommand()->addForeignKey($name, $tableName, ['int1'], $tableName, ['int3'])->execute();
1112
1113
        $this->assertEquals(['int1'], $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames());
1114
        $this->assertEquals(['int3'], $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames());
1115
1116
        $db->createCommand()->dropForeignKey($name, $tableName)->execute();
1117
1118
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
1119
1120
        $db->createCommand()->addForeignKey(
1121
            $name,
1122
            $tableName,
1123
            ['int1', 'int2'],
1124
            $tableName,
1125
            ['int3', 'int4']
1126
        )->execute();
1127
1128
        $this->assertEquals(
1129
            ['int1', 'int2'],
1130
            $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames()
1131
        );
1132
        $this->assertEquals(
1133
            ['int3', 'int4'],
1134
            $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames()
1135
        );
1136
    }
1137
1138
    public function testCreateDropIndex(): void
1139
    {
1140
        $db = $this->getConnection();
1141
1142
        $tableName = 'test_idx';
1143
        $name = 'test_idx_constraint';
1144
1145
        $schema = $db->getSchema();
1146
1147
        if ($schema->getTableSchema($tableName) !== null) {
1148
            $db->createCommand()->dropTable($tableName)->execute();
1149
        }
1150
1151
        $db->createCommand()->createTable($tableName, [
1152
            'int1' => 'integer not null',
1153
            'int2' => 'integer not null',
1154
        ])->execute();
1155
1156
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
1157
1158
        $db->createCommand()->createIndex($name, $tableName, ['int1'])->execute();
1159
1160
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
1161
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
1162
1163
        $db->createCommand()->dropIndex($name, $tableName)->execute();
1164
1165
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
1166
1167
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'])->execute();
1168
1169
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
1170
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
1171
1172
        $db->createCommand()->dropIndex($name, $tableName)->execute();
1173
1174
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
1175
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
1176
1177
        $db->createCommand()->createIndex($name, $tableName, ['int1'], true)->execute();
1178
1179
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
1180
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
1181
1182
        $db->createCommand()->dropIndex($name, $tableName)->execute();
1183
1184
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
1185
1186
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'], true)->execute();
1187
1188
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
1189
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
1190
    }
1191
1192
    public function testAddDropUnique(): void
1193
    {
1194
        $db = $this->getConnection();
1195
1196
        $tableName = 'test_uq';
1197
        $name = 'test_uq_constraint';
1198
1199
        $schema = $db->getSchema();
1200
1201
        if ($schema->getTableSchema($tableName) !== null) {
1202
            $db->createCommand()->dropTable($tableName)->execute();
1203
        }
1204
1205
        $db->createCommand()->createTable($tableName, [
1206
            'int1' => 'integer not null',
1207
            'int2' => 'integer not null',
1208
        ])->execute();
1209
1210
        $this->assertEmpty($schema->getTableUniques($tableName, true));
1211
1212
        $db->createCommand()->addUnique($name, $tableName, ['int1'])->execute();
1213
1214
        $this->assertEquals(['int1'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
1215
1216
        $db->createCommand()->dropUnique($name, $tableName)->execute();
1217
1218
        $this->assertEmpty($schema->getTableUniques($tableName, true));
1219
1220
        $db->createCommand()->addUnique($name, $tableName, ['int1', 'int2'])->execute();
1221
1222
        $this->assertEquals(['int1', 'int2'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
1223
    }
1224
1225
    public function testAddDropCheck(): void
1226
    {
1227
        $db = $this->getConnection();
1228
1229
        $tableName = 'test_ck';
1230
        $name = 'test_ck_constraint';
1231
1232
        $schema = $db->getSchema();
1233
1234
        if ($schema->getTableSchema($tableName) !== null) {
1235
            $db->createCommand()->dropTable($tableName)->execute();
1236
        }
1237
1238
        $db->createCommand()->createTable($tableName, [
1239
            'int1' => 'integer',
1240
        ])->execute();
1241
1242
        $this->assertEmpty($schema->getTableChecks($tableName, true));
1243
1244
        $db->createCommand()->addCheck($name, $tableName, '[[int1]] > 1')->execute();
1245
1246
        $this->assertMatchesRegularExpression(
0 ignored issues
show
Bug introduced by
It seems like assertMatchesRegularExpression() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1246
        $this->/** @scrutinizer ignore-call */ 
1247
               assertMatchesRegularExpression(
Loading history...
1247
            '/^.*int1.*>.*1.*$/',
1248
            $schema->getTableChecks($tableName, true)[0]->getExpression()
1249
        );
1250
1251
        $db->createCommand()->dropCheck($name, $tableName)->execute();
1252
1253
        $this->assertEmpty($schema->getTableChecks($tableName, true));
1254
    }
1255
1256
    public function testIntegrityViolation(): void
1257
    {
1258
        $this->expectException(IntegrityException::class);
1259
1260
        $db = $this->getConnection();
1261
1262
        $sql = 'INSERT INTO {{profile}}([[id]], [[description]]) VALUES (123, \'duplicate\')';
1263
1264
        $command = $db->createCommand($sql);
1265
1266
        $command->execute();
1267
        $command->execute();
1268
    }
1269
1270
    public function testLastInsertId(): void
1271
    {
1272
        $db = $this->getConnection();
1273
1274
        $sql = 'INSERT INTO {{profile}}([[description]]) VALUES (\'non duplicate\')';
1275
1276
        $command = $db->createCommand($sql);
1277
1278
        $command->execute();
1279
1280
        $this->assertEquals(3, $db->getSchema()->getLastInsertID());
1281
    }
1282
1283
    public function testQueryCache(): void
1284
    {
1285
        $db = $this->getConnection();
1286
1287
        $db->setEnableQueryCache(true);
1288
        $db->setQueryCache($this->cache);
1289
1290
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
1291
1292
        $this->assertEquals('user1', $command->bindValue(':id', 1)->queryScalar());
1293
1294
        $update = $db->createCommand('UPDATE {{customer}} SET [[name]] = :name WHERE [[id]] = :id');
1295
1296
        $update->bindValues([':id' => 1, ':name' => 'user11'])->execute();
1297
1298
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
1299
1300
        $db->cache(function (Connection $db) use ($command, $update) {
1301
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
1302
1303
            $update->bindValues([':id' => 2, ':name' => 'user22'])->execute();
1304
1305
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
1306
1307
            $db->noCache(function () use ($command) {
1308
                $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
1309
            });
1310
1311
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
1312
        }, 10);
1313
1314
        $db->setEnableQueryCache(false);
1315
1316
        $db->cache(function () use ($command, $update) {
1317
            $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
1318
            $update->bindValues([':id' => 2, ':name' => 'user2'])->execute();
1319
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
1320
        }, 10);
1321
1322
        $db->setEnableQueryCache(true);
1323
1324
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id')->cache();
1325
1326
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
1327
1328
        $update->bindValues([':id' => 1, ':name' => 'user1'])->execute();
1329
1330
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
1331
        $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
1332
1333
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
1334
1335
        $db->cache(function () use ($command) {
1336
            $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
1337
            $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
1338
        }, 10);
1339
    }
1340
1341
    public function testColumnCase(): void
1342
    {
1343
        $db = $this->getConnection();
1344
1345
        $this->assertEquals(PDO::CASE_NATURAL, $db->getSlavePdo()->getAttribute(PDO::ATTR_CASE));
1346
1347
        $sql = 'SELECT [[customer_id]], [[total]] FROM {{order}}';
1348
1349
        $rows = $db->createCommand($sql)->queryAll();
1350
1351
        $this->assertTrue(isset($rows[0]));
1352
        $this->assertTrue(isset($rows[0]['customer_id']));
1353
        $this->assertTrue(isset($rows[0]['total']));
1354
1355
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
1356
1357
        $rows = $db->createCommand($sql)->queryAll();
1358
1359
        $this->assertTrue(isset($rows[0]));
1360
        $this->assertTrue(isset($rows[0]['customer_id']));
1361
        $this->assertTrue(isset($rows[0]['total']));
1362
1363
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
1364
1365
        $rows = $db->createCommand($sql)->queryAll();
1366
1367
        $this->assertTrue(isset($rows[0]));
1368
        $this->assertTrue(isset($rows[0]['CUSTOMER_ID']));
1369
        $this->assertTrue(isset($rows[0]['TOTAL']));
1370
    }
1371
1372
    /**
1373
     * Data provider for {@see testGetRawSql()}.
1374
     *
1375
     * @return array test data
1376
     */
1377
    public function dataProviderGetRawSql(): array
1378
    {
1379
        return [
1380
            [
1381
                'SELECT * FROM customer WHERE id = :id',
1382
                [':id' => 1],
1383
                'SELECT * FROM customer WHERE id = 1',
1384
            ],
1385
            [
1386
                'SELECT * FROM customer WHERE id = :id',
1387
                ['id' => 1],
1388
                'SELECT * FROM customer WHERE id = 1',
1389
            ],
1390
            [
1391
                'SELECT * FROM customer WHERE id = :id',
1392
                ['id' => null],
1393
                'SELECT * FROM customer WHERE id = NULL',
1394
            ],
1395
            [
1396
                'SELECT * FROM customer WHERE id = :base OR id = :basePrefix',
1397
                [
1398
                    'base'       => 1,
1399
                    'basePrefix' => 2,
1400
                ],
1401
                'SELECT * FROM customer WHERE id = 1 OR id = 2',
1402
            ],
1403
            /**
1404
             * {@see https://github.com/yiisoft/yii2/issues/9268}
1405
             */
1406
            [
1407
                'SELECT * FROM customer WHERE active = :active',
1408
                [':active' => false],
1409
                'SELECT * FROM customer WHERE active = FALSE',
1410
            ],
1411
            /**
1412
             * {@see https://github.com/yiisoft/yii2/issues/15122}
1413
             */
1414
            [
1415
                'SELECT * FROM customer WHERE id IN (:ids)',
1416
                [':ids' => new Expression(implode(', ', [1, 2]))],
1417
                'SELECT * FROM customer WHERE id IN (1, 2)',
1418
            ],
1419
        ];
1420
    }
1421
1422
    /**
1423
     * @dataProvider dataProviderGetRawSql
1424
     *
1425
     * @param string $sql
1426
     * @param array  $params
1427
     * @param string $expectedRawSql
1428
     *
1429
     * {@see https://github.com/yiisoft/yii2/issues/8592}
1430
     */
1431
    public function testGetRawSql(string $sql, array $params, string $expectedRawSql): void
1432
    {
1433
        $db = $this->getConnection();
1434
1435
        $command = $db->createCommand($sql, $params);
1436
1437
        $this->assertEquals($expectedRawSql, $command->getRawSql());
1438
    }
1439
1440
    public function testTransaction(): void
1441
    {
1442
        $db = $this->getConnection();
1443
1444
        $this->assertNull($db->getTransaction());
1445
1446
        $command = $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command transaction')");
1447
1448
        $this->invokeMethod($command, 'requireTransaction');
0 ignored issues
show
Bug introduced by
It seems like invokeMethod() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1448
        $this->/** @scrutinizer ignore-call */ 
1449
               invokeMethod($command, 'requireTransaction');
Loading history...
1449
1450
        $command->execute();
1451
1452
        $this->assertNull($db->getTransaction());
1453
        $this->assertEquals(
1454
            1,
1455
            $db->createCommand(
1456
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction'"
1457
            )->queryScalar()
1458
        );
1459
    }
1460
1461
    public function testRetryHandler(): void
1462
    {
1463
        $db = $this->getConnection();
1464
1465
        $this->assertNull($db->getTransaction());
1466
1467
        $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command retry')")->execute();
1468
1469
        $this->assertNull($db->getTransaction());
1470
        $this->assertEquals(
1471
            1,
1472
            $db->createCommand(
1473
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command retry'"
1474
            )->queryScalar()
1475
        );
1476
1477
        $attempts = null;
1478
        $hitHandler = false;
1479
        $hitCatch = false;
1480
1481
        $command = $db->createCommand(
1482
            "INSERT INTO {{profile}}([[id]], [[description]]) VALUES(1, 'command retry')"
1483
        );
1484
1485
        $this->invokeMethod(
1486
            $command,
1487
            'setRetryHandler',
1488
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
1489
                $attempts = $attempt;
1490
                $hitHandler = true;
1491
1492
                return $attempt <= 2;
1493
            }]
1494
        );
1495
1496
        try {
1497
            $command->execute();
1498
        } catch (Exception $e) {
1499
            $hitCatch = true;
1500
            $this->assertInstanceOf(IntegrityException::class, $e);
1501
        }
1502
1503
        $this->assertNull($db->getTransaction());
1504
        $this->assertSame(3, $attempts);
1505
        $this->assertTrue($hitHandler);
1506
        $this->assertTrue($hitCatch);
1507
    }
1508
1509
    public function testCreateView(): void
1510
    {
1511
        $db = $this->getConnection();
1512
1513
        $subquery = (new Query($db))
1514
            ->select('bar')
1515
            ->from('testCreateViewTable')
1516
            ->where(['>', 'bar', '5']);
1517
1518
        if ($db->getSchema()->getTableSchema('testCreateView') !== null) {
1519
            $db->createCommand()->dropView('testCreateView')->execute();
1520
        }
1521
1522
        if ($db->getSchema()->getTableSchema('testCreateViewTable')) {
1523
            $db->createCommand()->dropTable('testCreateViewTable')->execute();
1524
        }
1525
1526
        $db->createCommand()->createTable('testCreateViewTable', [
1527
            'id'  => Schema::TYPE_PK,
1528
            'bar' => Schema::TYPE_INTEGER,
1529
        ])->execute();
1530
1531
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 1])->execute();
1532
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 6])->execute();
1533
        $db->createCommand()->createView('testCreateView', $subquery)->execute();
1534
1535
        $records = $db->createCommand('SELECT [[bar]] FROM {{testCreateView}};')->queryAll();
1536
1537
        $this->assertEquals([['bar' => 6]], $records);
1538
    }
1539
1540
    public function testDropView(): void
1541
    {
1542
        $db = $this->getConnection();
1543
1544
        $viewName = 'animal_view'; // since it already exists in the fixtures
1545
1546
        $this->assertNotNull($db->getSchema()->getTableSchema($viewName));
1547
1548
        $db->createCommand()->dropView($viewName)->execute();
1549
1550
        $this->assertNull($db->getSchema()->getTableSchema($viewName));
1551
    }
1552
}
1553