Passed
Push — master ( f4eeb8...0d22ee )
by Sergei
11:47 queued 01:39
created

TestCommandTrait::testTransaction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 0
dl 0
loc 18
ccs 10
cts 10
cp 1
crap 1
rs 9.9332
c 0
b 0
f 0
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\ConnectionInterface;
10
use Yiisoft\Db\Data\DataReader;
11
use Yiisoft\Db\Exception\Exception;
12
use Yiisoft\Db\Exception\IntegrityException;
13
use Yiisoft\Db\Expression\Expression;
14
use Yiisoft\Db\Query\Query;
15
use Yiisoft\Db\Schema\Schema;
16
17
use function call_user_func_array;
18
use function date;
19
use function is_array;
20
use function range;
21
use function rtrim;
22
use function setlocale;
23
use function time;
24
25
trait TestCommandTrait
26
{
27 5
    public function testConstruct(): void
28
    {
29 5
        $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

29
        /** @scrutinizer ignore-call */ 
30
        $db = $this->getConnection();
Loading history...
30
31
        /* null */
32 5
        $command = $db->createCommand();
33
34 5
        $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

34
        $this->/** @scrutinizer ignore-call */ 
35
               assertNull($command->getSql());
Loading history...
35
36
        /* string */
37 5
        $sql = 'SELECT * FROM customer';
38
39 5
        $command = $db->createCommand($sql);
40
41 5
        $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

41
        $this->/** @scrutinizer ignore-call */ 
42
               assertEquals($sql, $command->getSql());
Loading history...
42
    }
43
44 5
    public function testGetSetSql(): void
45
    {
46 5
        $db = $this->getConnection();
47
48 5
        $sql = 'SELECT * FROM customer';
49
50 5
        $command = $db->createCommand($sql);
51
52 5
        $this->assertEquals($sql, $command->getSql());
53
54 5
        $sql2 = 'SELECT * FROM order';
55
56 5
        $command->setSql($sql2);
57
58 5
        $this->assertEquals($sql2, $command->getSql());
59
    }
60
61 5
    public function testPrepareCancel(): void
62
    {
63 5
        $db = $this->getConnection();
64
65 5
        $command = $db->createCommand('SELECT * FROM {{customer}}');
66
67 5
        $this->assertNull($command->getPdoStatement());
68
69 5
        $command->prepare();
70
71 5
        $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

71
        $this->/** @scrutinizer ignore-call */ 
72
               assertNotNull($command->getPdoStatement());
Loading history...
72
73 5
        $command->cancel();
74
75 5
        $this->assertNull($command->getPdoStatement());
76
    }
77
78 5
    public function testExecute(): void
79
    {
80 5
        $db = $this->getConnection(true);
81
82
        $sql = 'INSERT INTO {{customer}}([[email]], [[name]], [[address]])'
83
            . ' VALUES (\'[email protected]\', \'user4\', \'address4\')';
84
85 5
        $command = $db->createCommand($sql);
86
87 5
        $this->assertEquals(1, $command->execute());
88
89 5
        $sql = 'SELECT COUNT(*) FROM {{customer}} WHERE [[name]] = \'user4\'';
90
91 5
        $command = $db->createCommand($sql);
92
93 5
        $this->assertEquals(1, $command->queryScalar());
94
95 5
        $command = $db->createCommand('bad SQL');
96
97 5
        $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

97
        $this->/** @scrutinizer ignore-call */ 
98
               expectException(Exception::class);
Loading history...
98
99 5
        $command->execute();
100
    }
101
102 5
    public function testQuery(): void
103
    {
104 5
        $db = $this->getConnection(true);
105
106
        /* query */
107 5
        $sql = 'SELECT * FROM {{customer}}';
108
109 5
        $reader = $db->createCommand($sql)->Query();
110
111 5
        $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

111
        $this->/** @scrutinizer ignore-call */ 
112
               assertInstanceOf(DataReader::class, $reader);
Loading history...
112
113
        /* queryAll */
114 5
        $rows = $db->createCommand('SELECT * FROM {{customer}}')->queryAll();
115
116 5
        $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

116
        $this->/** @scrutinizer ignore-call */ 
117
               assertCount(3, $rows);
Loading history...
117
118 5
        $row = $rows[2];
119
120 5
        $this->assertEquals(3, $row['id']);
121 5
        $this->assertEquals('user3', $row['name']);
122
123 5
        $rows = $db->createCommand('SELECT * FROM {{customer}} WHERE [[id]] = 10')->queryAll();
124
125 5
        $this->assertEquals([], $rows);
126
127
        /* queryOne */
128 5
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
129
130 5
        $row = $db->createCommand($sql)->queryOne();
131
132 5
        $this->assertEquals(1, $row['id']);
133 5
        $this->assertEquals('user1', $row['name']);
134
135 5
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
136
137 5
        $command = $db->createCommand($sql);
138
139 5
        $command->prepare();
140
141 5
        $row = $command->queryOne();
142
143 5
        $this->assertEquals(1, $row['id']);
144 5
        $this->assertEquals('user1', $row['name']);
145
146 5
        $sql = 'SELECT * FROM {{customer}} WHERE [[id]] = 10';
147
148 5
        $command = $db->createCommand($sql);
149
150 5
        $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

150
        $this->/** @scrutinizer ignore-call */ 
151
               assertFalse($command->queryOne());
Loading history...
151
152
        /* queryColumn */
153 5
        $sql = 'SELECT * FROM {{customer}}';
154
155 5
        $column = $db->createCommand($sql)->queryColumn();
156
157 5
        $this->assertEquals(range(1, 3), $column);
158
159 5
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
160
161 5
        $this->assertEquals([], $command->queryColumn());
162
163
        /* queryScalar */
164 5
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
165
166 5
        $this->assertEquals($db->createCommand($sql)->queryScalar(), 1);
167
168 5
        $sql = 'SELECT [[id]] FROM {{customer}} ORDER BY [[id]]';
169
170 5
        $command = $db->createCommand($sql);
171
172 5
        $command->prepare();
173
174 5
        $this->assertEquals(1, $command->queryScalar());
175
176 5
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
177
178 5
        $this->assertFalse($command->queryScalar());
179
180 5
        $command = $db->createCommand('bad SQL');
181
182 5
        $this->expectException(Exception::class);
183
184 5
        $command->Query();
185
    }
186
187 5
    public function testFetchMode(): void
188
    {
189 5
        $db = $this->getConnection();
190
191
        /* default: FETCH_ASSOC */
192 5
        $sql = 'SELECT * FROM {{customer}}';
193
194 5
        $command = $db->createCommand($sql);
195
196 5
        $result = $command->queryOne();
197
198 5
        $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

198
        $this->/** @scrutinizer ignore-call */ 
199
               assertTrue(is_array($result) && isset($result['id']));
Loading history...
199
200
        /* FETCH_OBJ, customized via fetchMode property */
201 5
        $sql = 'SELECT * FROM {{customer}}';
202
203 5
        $command = $db->createCommand($sql);
204
205 5
        $command->setFetchMode(PDO::FETCH_OBJ);
206
207 5
        $result = $command->queryOne();
208
209 5
        $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

209
        $this->/** @scrutinizer ignore-call */ 
210
               assertIsObject($result);
Loading history...
210
211
        /* FETCH_NUM, customized in query method */
212 5
        $sql = 'SELECT * FROM {{customer}}';
213
214 5
        $command = $db->createCommand($sql);
215
216 5
        $result = $command->queryOne(PDO::FETCH_NUM);
217
218 5
        $this->assertTrue(is_array($result) && isset($result[0]));
219
    }
220
221 5
    public function testBatchInsert(): void
222
    {
223 5
        $db = $this->getConnection();
224
225 5
        $command = $db->createCommand();
226
227 5
        $command->batchInsert(
228
            '{{customer}}',
229 5
            ['email', 'name', 'address'],
230
            [
231 5
                ['[email protected]', 't1', 't1 address'],
232
                ['[email protected]', null, false],
233
            ]
234
        );
235
236 5
        $this->assertEquals(2, $command->execute());
237
238
        /**
239
         * {@see https://github.com/yiisoft/yii2/issues/11693}
240
         */
241 5
        $command = $this->getConnection()->createCommand();
242
243 5
        $command->batchInsert(
244
            '{{customer}}',
245 5
            ['email', 'name', 'address'],
246 5
            []
247
        );
248
249 5
        $this->assertEquals(0, $command->execute());
250
    }
251
252 5
    public function testBatchInsertWithYield(): void
253
    {
254 5
        $rows = (static function () {
255 5
            if (false) {
256
                yield [];
257
            }
258
        })();
259
260 5
        $command = $this->getConnection()->createCommand();
261
262 5
        $command->batchInsert(
263
            '{{customer}}',
264 5
            ['email', 'name', 'address'],
265
            $rows
266
        );
267
268 5
        $this->assertEquals(0, $command->execute());
269
    }
270
271
    /**
272
     * Test batch insert with different data types.
273
     *
274
     * Ensure double is inserted with `.` decimal separator.
275
     *
276
     * {@see https://github.com/yiisoft/yii2/issues/6526}
277
     */
278 5
    public function testBatchInsertDataTypesLocale(): void
279
    {
280 5
        $locale = setlocale(LC_NUMERIC, 0);
281
282 5
        if (false === $locale) {
283
            $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

283
            $this->/** @scrutinizer ignore-call */ 
284
                   markTestSkipped('Your platform does not support locales.');
Loading history...
284
        }
285
286 5
        $db = $this->getConnection(true);
287
288
        try {
289
            /* This one sets decimal mark to comma sign */
290 5
            setlocale(LC_NUMERIC, 'ru_RU.utf8');
291
292 5
            $cols = ['int_col', 'char_col', 'float_col', 'bool_col'];
293
294
            $data = [
295 5
                [1, 'A', 9.735, true],
296
                [2, 'B', -2.123, false],
297
                [3, 'C', 2.123, false],
298
            ];
299
300
            /* clear data in "type" table */
301 5
            $db->createCommand()->delete('type')->execute();
302
303
            /* change, for point oracle. */
304 5
            if ($db->getDriverName() === 'oci') {
305 1
                $db->createCommand("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'")->execute();
306
            }
307
308
            /* batch insert on "type" table */
309 5
            $db->createCommand()->batchInsert('type', $cols, $data)->execute();
310
311 5
            $data = $db->createCommand(
312
                'SELECT [[int_col]], [[char_col]], [[float_col]], [[bool_col]] ' .
313
                'FROM {{type}} WHERE [[int_col]] IN (1,2,3) ORDER BY [[int_col]]'
314 5
            )->queryAll();
315
316 5
            $this->assertCount(3, $data);
317 5
            $this->assertEquals(1, $data[0]['int_col']);
318 5
            $this->assertEquals(2, $data[1]['int_col']);
319 5
            $this->assertEquals(3, $data[2]['int_col']);
320
321
            /* rtrim because Postgres padds the column with whitespace */
322 5
            $this->assertEquals('A', rtrim($data[0]['char_col']));
323 5
            $this->assertEquals('B', rtrim($data[1]['char_col']));
324 5
            $this->assertEquals('C', rtrim($data[2]['char_col']));
325 5
            $this->assertEquals('9.735', $data[0]['float_col']);
326 5
            $this->assertEquals('-2.123', $data[1]['float_col']);
327 5
            $this->assertEquals('2.123', $data[2]['float_col']);
328 5
            $this->assertEquals('1', $data[0]['bool_col']);
329 5
            $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

329
            $this->/** @scrutinizer ignore-call */ 
330
                   assertIsOneOf($data[1]['bool_col'], ['0', false]);
Loading history...
330 5
            $this->assertIsOneOf($data[2]['bool_col'], ['0', false]);
331
        } catch (Exception $e) {
332
            setlocale(LC_NUMERIC, $locale);
333
334
            throw $e;
335
        } catch (Throwable $e) {
336
            setlocale(LC_NUMERIC, $locale);
337
338
            throw $e;
339
        }
340
341 5
        setlocale(LC_NUMERIC, $locale);
342
    }
343
344 4
    public function testInsert(): void
345
    {
346 4
        $db = $this->getConnection();
347
348 4
        $db->createCommand('DELETE FROM {{customer}}')->execute();
349
350 4
        $command = $db->createCommand();
351
352 4
        $command->insert(
353
            '{{customer}}',
354
            [
355 4
                'email' => '[email protected]',
356
                'name' => 'test',
357
                'address' => 'test address',
358
            ]
359 4
        )->execute();
360
361 4
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{customer}};')->queryScalar());
362
363 4
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryOne();
364
365 4
        $this->assertEquals([
366
            'email' => '[email protected]',
367
            'name' => 'test',
368
            'address' => 'test address',
369
        ], $record);
370
    }
371
372
    /**
373
     * verify that {{}} are not going to be replaced in parameters.
374
     */
375 4
    public function testNoTablenameReplacement(): void
376
    {
377 4
        $db = $this->getConnection(true);
378
379 4
        $db->createCommand()->insert(
380
            '{{customer}}',
381
            [
382 4
                'name' => 'Some {{weird}} name',
383
                'email' => '[email protected]',
384
                'address' => 'Some {{%weird}} address',
385
            ]
386 4
        )->execute();
387
388 4
        if ($db->getDriverName() === 'pgsql') {
389 1
            $customerId = $db->getLastInsertID('public.customer_id_seq');
390
        } else {
391 3
            $customerId = $db->getLastInsertID();
392
        }
393
394 4
        $customer = $db->createCommand('SELECT * FROM {{customer}} WHERE id=' . $customerId)->queryOne();
395
396 4
        $this->assertEquals('Some {{weird}} name', $customer['name']);
397 4
        $this->assertEquals('Some {{%weird}} address', $customer['address']);
398
399 4
        $db->createCommand()->update(
400
            '{{customer}}',
401
            [
402 4
                'name' => 'Some {{updated}} name',
403
                'address' => 'Some {{%updated}} address',
404
            ],
405 4
            ['id' => $customerId]
406 4
        )->execute();
407
408 4
        $customer = $db->createCommand('SELECT * FROM {{customer}} WHERE id=' . $customerId)->queryOne();
409
410 4
        $this->assertEquals('Some {{updated}} name', $customer['name']);
411 4
        $this->assertEquals('Some {{%updated}} address', $customer['address']);
412
    }
413
414
    /**
415
     * Test INSERT INTO ... SELECT SQL statement.
416
     */
417 5
    public function testInsertSelect(): void
418
    {
419 5
        $db = $this->getConnection();
420
421 5
        $db->createCommand('DELETE FROM {{customer}}')->execute();
422
423 5
        $command = $db->createCommand();
424
425 5
        $command->insert(
426
            '{{customer}}',
427
            [
428 5
                'email' => '[email protected]',
429
                'name' => 'test',
430
                'address' => 'test address',
431
            ]
432 5
        )->execute();
433
434 5
        $query = new Query($db);
435
436 5
        $query->select(
437
            [
438 5
                '{{customer}}.[[email]] as name',
439
                '[[name]] as email',
440
                '[[address]]',
441
            ]
442
        )
443 5
            ->from('{{customer}}')
444 5
            ->where([
445
                'and',
446
                ['<>', 'name', 'foo'],
447
                ['status' => [0, 1, 2, 3]],
448
            ]);
449
450 5
        $command = $db->createCommand();
451
452 5
        $command->insert(
453
            '{{customer}}',
454
            $query
455 5
        )->execute();
456
457 5
        $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{customer}}')->queryScalar());
458
459 5
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryAll();
460
461 5
        $this->assertEquals([
462
            [
463 5
                'email' => '[email protected]',
464
                'name' => 'test',
465
                'address' => 'test address',
466
            ],
467
            [
468
                'email' => 'test',
469
                'name' => '[email protected]',
470
                'address' => 'test address',
471
            ],
472
        ], $record);
473
    }
474
475
    /**
476
     * Test INSERT INTO ... SELECT SQL statement with alias syntax.
477
     */
478 4
    public function testInsertSelectAlias(): void
479
    {
480 4
        $db = $this->getConnection();
481
482 4
        $db->createCommand('DELETE FROM {{customer}}')->execute();
483
484 4
        $command = $db->createCommand();
485
486 4
        $command->insert(
487
            '{{customer}}',
488
            [
489 4
                'email' => '[email protected]',
490
                'name' => 'test',
491
                'address' => 'test address',
492
            ]
493 4
        )->execute();
494
495 4
        $query = new Query($db);
496
497 4
        $query->select(
498
            [
499 4
                'email' => '{{customer}}.[[email]]',
500
                'address' => 'name',
501
                'name' => 'address',
502
            ]
503
        )
504 4
            ->from('{{customer}}')
505 4
            ->where([
506
                'and',
507
                ['<>', 'name', 'foo'],
508
                ['status' => [0, 1, 2, 3]],
509
            ]);
510
511 4
        $command = $db->createCommand();
512
513 4
        $command->insert(
514
            '{{customer}}',
515
            $query
516 4
        )->execute();
517
518 4
        $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{customer}}')->queryScalar());
519
520 4
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryAll();
521
522 4
        $this->assertEquals([
523
            [
524 4
                'email' => '[email protected]',
525
                'name' => 'test',
526
                'address' => 'test address',
527
            ],
528
            [
529
                'email' => '[email protected]',
530
                'name' => 'test address',
531
                'address' => 'test',
532
            ],
533
        ], $record);
534
    }
535
536 4
    public function testInsertExpression(): void
537
    {
538 4
        $db = $this->getConnection();
539
540 4
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
541
542 4
        switch ($db->getDriverName()) {
543 4
            case 'pgsql':
544 1
                $expression = "EXTRACT(YEAR FROM TIMESTAMP 'now')";
545 1
                break;
546 3
            case 'mysql':
547 1
                $expression = 'YEAR(NOW())';
548 1
                break;
549 2
            case 'sqlite':
550 1
                $expression = "strftime('%Y')";
551 1
                break;
552 1
            case 'sqlsrv':
553 1
                $expression = 'YEAR(GETDATE())';
554
        }
555
556 4
        $command = $db->createCommand();
557
558 4
        $command->insert(
559
            '{{order_with_null_fk}}',
560
            [
561 4
                '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...
562
                'total' => 1,
563
            ]
564 4
        )->execute();
565
566 4
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{order_with_null_fk}}')->queryScalar());
567
568 4
        $record = $db->createCommand('SELECT [[created_at]] FROM {{order_with_null_fk}}')->queryOne();
569
570 4
        $this->assertEquals([
571 4
            'created_at' => date('Y'),
572
        ], $record);
573
    }
574
575 4
    public function testsInsertQueryAsColumnValue(): void
576
    {
577 4
        $time = time();
578
579 4
        $db = $this->getConnection(true);
580
581 4
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
582
583 4
        $command = $db->createCommand();
584
585 4
        $command->insert('{{order}}', [
586
            'customer_id' => 1,
587
            'created_at' => $time,
588
            'total' => 42,
589 4
        ])->execute();
590
591 4
        if ($db->getDriverName() === 'pgsql') {
592 1
            $orderId = $db->getLastInsertID('public.order_id_seq');
593
        } else {
594 3
            $orderId = $db->getLastInsertID();
595
        }
596
597 4
        $columnValueQuery = new Query($db);
598
599 4
        $columnValueQuery->select('created_at')->from('{{order}}')->where(['id' => $orderId]);
600
601 4
        $command = $db->createCommand();
602
603 4
        $command->insert(
604
            '{{order_with_null_fk}}',
605
            [
606 4
                'customer_id' => $orderId,
607
                'created_at' => $columnValueQuery,
608
                'total' => 42,
609
            ]
610 4
        )->execute();
611
612 4
        $this->assertEquals(
613
            $time,
614 4
            $db->createCommand(
615 4
                'SELECT [[created_at]] FROM {{order_with_null_fk}} WHERE [[customer_id]] = ' . $orderId
616 4
            )->queryScalar()
617
        );
618
619 4
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
620 4
        $db->createCommand('DELETE FROM {{order}} WHERE [[id]] = ' . $orderId)->execute();
621
    }
622
623 4
    public function testCreateTable(): void
624
    {
625 4
        $db = $this->getConnection();
626
627 4
        if ($db->getSchema()->getTableSchema('testCreateTable') !== null) {
628
            $db->createCommand()->dropTable('testCreateTable')->execute();
629
        }
630
631 4
        $db->createCommand()->createTable(
632
            'testCreateTable',
633 4
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER]
634 4
        )->execute();
635
636 4
        $db->createCommand()->insert('testCreateTable', ['bar' => 1])->execute();
637
638 4
        $records = $db->createCommand('SELECT [[id]], [[bar]] FROM {{testCreateTable}};')->queryAll();
639
640 4
        $this->assertEquals([
641 4
            ['id' => 1, 'bar' => 1],
642
        ], $records);
643
    }
644
645 5
    public function testDropTable(): void
646
    {
647 5
        $db = $this->getConnection();
648
649 5
        $tableName = 'type';
650
651 5
        $this->assertNotNull($db->getSchema()->getTableSchema($tableName));
652
653 5
        $db->createCommand()->dropTable($tableName)->execute();
654
655 5
        $this->assertNull($db->getSchema()->getTableSchema($tableName));
656
    }
657
658 5
    public function testTruncateTable(): void
659
    {
660 5
        $db = $this->getConnection();
661
662 5
        $rows = $db->createCommand('SELECT * FROM {{animal}}')->queryAll();
663
664 5
        $this->assertCount(2, $rows);
665
666 5
        $db->createCommand()->truncateTable('animal')->execute();
667
668 5
        $rows = $db->createCommand('SELECT * FROM {{animal}}')->queryAll();
669
670 5
        $this->assertCount(0, $rows);
671
    }
672
673 5
    public function testRenameTable(): void
674
    {
675 5
        $db = $this->getConnection(true);
676
677 5
        $fromTableName = 'type';
678 5
        $toTableName = 'new_type';
679
680 5
        if ($db->getSchema()->getTableSchema($toTableName) !== null) {
681
            $db->createCommand()->dropTable($toTableName)->execute();
682
        }
683
684 5
        $this->assertNotNull($db->getSchema()->getTableSchema($fromTableName));
685 5
        $this->assertNull($db->getSchema()->getTableSchema($toTableName));
686
687 5
        $db->createCommand()->renameTable($fromTableName, $toTableName)->execute();
688
689 5
        $this->assertNull($db->getSchema()->getTableSchema($fromTableName, true));
690 5
        $this->assertNotNull($db->getSchema()->getTableSchema($toTableName, true));
691
    }
692
693 30
    protected function performAndCompareUpsertResult(ConnectionInterface $db, array $data): void
694
    {
695 30
        $params = $data['params'];
696 30
        $expected = $data['expected'] ?? $params[1];
697
698 30
        $command = $db->createCommand();
699
700 30
        call_user_func_array([$command, 'upsert'], $params);
701
702 30
        $command->execute();
703
704 30
        $actual = (new Query($db))
705 30
            ->select([
706
                'email',
707 30
                'address' => new Expression($this->upsertTestCharCast),
708
                'status',
709
            ])
710 30
            ->from('T_upsert')
711 30
            ->one();
712
713 30
        $this->assertEquals($expected, $actual, $this->upsertTestCharCast);
714
    }
715
716 5
    public function testAddDropForeignKey(): void
717
    {
718 5
        $db = $this->getConnection();
719
720 5
        $tableName = 'test_fk';
721 5
        $name = 'test_fk_constraint';
722
723 5
        $schema = $db->getSchema();
724
725 5
        if ($schema->getTableSchema($tableName) !== null) {
726
            $db->createCommand()->dropTable($tableName)->execute();
727
        }
728
729 5
        $db->createCommand()->createTable($tableName, [
730
            'int1' => 'integer not null unique',
731
            'int2' => 'integer not null unique',
732
            'int3' => 'integer not null unique',
733
            'int4' => 'integer not null unique',
734
            'unique ([[int1]], [[int2]])',
735
            'unique ([[int3]], [[int4]])',
736 5
        ])->execute();
737
738 5
        $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

738
        $this->/** @scrutinizer ignore-call */ 
739
               assertEmpty($schema->getTableForeignKeys($tableName, true));
Loading history...
739
740 5
        $db->createCommand()->addForeignKey($name, $tableName, ['int1'], $tableName, ['int3'])->execute();
741
742 5
        $this->assertEquals(['int1'], $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames());
743 5
        $this->assertEquals(['int3'], $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames());
744
745 5
        $db->createCommand()->dropForeignKey($name, $tableName)->execute();
746
747 5
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
748
749 5
        $db->createCommand()->addForeignKey(
750
            $name,
751
            $tableName,
752 5
            ['int1', 'int2'],
753
            $tableName,
754 5
            ['int3', 'int4']
755 5
        )->execute();
756
757 5
        $this->assertEquals(
758 5
            ['int1', 'int2'],
759 5
            $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames()
760
        );
761 5
        $this->assertEquals(
762 5
            ['int3', 'int4'],
763 5
            $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames()
764
        );
765
    }
766
767 5
    public function testCreateDropIndex(): void
768
    {
769 5
        $db = $this->getConnection();
770
771 5
        $tableName = 'test_idx';
772 5
        $name = 'test_idx_constraint';
773
774 5
        $schema = $db->getSchema();
775
776 5
        if ($schema->getTableSchema($tableName) !== null) {
777
            $db->createCommand()->dropTable($tableName)->execute();
778
        }
779
780 5
        $db->createCommand()->createTable($tableName, [
781
            'int1' => 'integer not null',
782
            'int2' => 'integer not null',
783 5
        ])->execute();
784
785 5
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
786
787 5
        $db->createCommand()->createIndex($name, $tableName, ['int1'])->execute();
788
789 5
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
790 5
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
791
792 5
        $db->createCommand()->dropIndex($name, $tableName)->execute();
793
794 5
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
795
796 5
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'])->execute();
797
798 5
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
799 5
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
800
801 5
        $db->createCommand()->dropIndex($name, $tableName)->execute();
802
803 5
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
804 5
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
805
806 5
        $db->createCommand()->createIndex($name, $tableName, ['int1'], true)->execute();
807
808 5
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
809 5
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
810
811 5
        $db->createCommand()->dropIndex($name, $tableName)->execute();
812
813 5
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
814
815 5
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'], true)->execute();
816
817 5
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
818 5
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
819
    }
820
821 5
    public function testAddDropUnique(): void
822
    {
823 5
        $db = $this->getConnection();
824
825 5
        $tableName = 'test_uq';
826 5
        $name = 'test_uq_constraint';
827
828 5
        $schema = $db->getSchema();
829
830 5
        if ($schema->getTableSchema($tableName) !== null) {
831
            $db->createCommand()->dropTable($tableName)->execute();
832
        }
833
834 5
        $db->createCommand()->createTable($tableName, [
835
            'int1' => 'integer not null',
836
            'int2' => 'integer not null',
837 5
        ])->execute();
838
839 5
        $this->assertEmpty($schema->getTableUniques($tableName, true));
840
841 5
        $db->createCommand()->addUnique($name, $tableName, ['int1'])->execute();
842
843 5
        $this->assertEquals(['int1'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
844
845 5
        $db->createCommand()->dropUnique($name, $tableName)->execute();
846
847 5
        $this->assertEmpty($schema->getTableUniques($tableName, true));
848
849 5
        $db->createCommand()->addUnique($name, $tableName, ['int1', 'int2'])->execute();
850
851 5
        $this->assertEquals(['int1', 'int2'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
852
    }
853
854 5
    public function testIntegrityViolation(): void
855
    {
856 5
        $this->expectException(IntegrityException::class);
857
858 5
        $db = $this->getConnection();
859
860 5
        $sql = 'INSERT INTO {{profile}}([[id]], [[description]]) VALUES (123, \'duplicate\')';
861
862 5
        $command = $db->createCommand($sql);
863
864 5
        $command->execute();
865 4
        $command->execute();
866
    }
867
868 3
    public function testLastInsertId(): void
869
    {
870 3
        $db = $this->getConnection(true);
871
872 3
        $sql = 'INSERT INTO {{profile}}([[description]]) VALUES (\'non duplicate\')';
873
874 3
        $command = $db->createCommand($sql);
875
876 3
        $command->execute();
877
878 3
        $this->assertEquals(3, $db->getSchema()->getLastInsertID());
879
    }
880
881 4
    public function testQueryCache(): void
882
    {
883 4
        $db = $this->getConnection(true);
884
885 4
        $this->queryCache->setEnable(true);
886
887 4
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
888
889 4
        $this->assertEquals('user1', $command->bindValue(':id', 1)->queryScalar());
890
891 4
        $update = $db->createCommand('UPDATE {{customer}} SET [[name]] = :name WHERE [[id]] = :id');
892
893 4
        $update->bindValues([':id' => 1, ':name' => 'user11'])->execute();
894
895 4
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
896
897 4
        $db->cache(function (ConnectionInterface $db) use ($command, $update) {
898 4
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
899
900 4
            $update->bindValues([':id' => 2, ':name' => 'user22'])->execute();
901
902 4
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
903
904 4
            $db->noCache(function () use ($command) {
0 ignored issues
show
Bug introduced by
The method noCache() does not exist on Yiisoft\Db\Connection\ConnectionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\Db\Connection\ConnectionInterface. ( Ignorable by Annotation )

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

904
            $db->/** @scrutinizer ignore-call */ 
905
                 noCache(function () use ($command) {
Loading history...
905 4
                $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
906
            });
907
908 4
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
909
        }, 10);
910
911 4
        $this->queryCache->setEnable(false);
912
913 4
        $db->cache(function () use ($command, $update) {
914 4
            $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
915 4
            $update->bindValues([':id' => 2, ':name' => 'user2'])->execute();
916 4
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
917
        }, 10);
918
919 4
        $this->queryCache->setEnable(true);
920
921 4
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id')->cache();
922
923 4
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
924
925 4
        $update->bindValues([':id' => 1, ':name' => 'user1'])->execute();
926
927 4
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
928 4
        $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
929
930 4
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
931
932 4
        $db->cache(function () use ($command) {
933 4
            $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
934 4
            $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
935
        }, 10);
936
    }
937
938 4
    public function testColumnCase(): void
939
    {
940 4
        $db = $this->getConnection();
941
942 4
        $this->assertEquals(PDO::CASE_NATURAL, $db->getSlavePdo()->getAttribute(PDO::ATTR_CASE));
943
944 4
        $sql = 'SELECT [[customer_id]], [[total]] FROM {{order}}';
945
946 4
        $rows = $db->createCommand($sql)->queryAll();
947
948 4
        $this->assertTrue(isset($rows[0]));
949 4
        $this->assertTrue(isset($rows[0]['customer_id']));
950 4
        $this->assertTrue(isset($rows[0]['total']));
951
952 4
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
953
954 4
        $rows = $db->createCommand($sql)->queryAll();
955
956 4
        $this->assertTrue(isset($rows[0]));
957 4
        $this->assertTrue(isset($rows[0]['customer_id']));
958 4
        $this->assertTrue(isset($rows[0]['total']));
959
960 4
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
961
962 4
        $rows = $db->createCommand($sql)->queryAll();
963
964 4
        $this->assertTrue(isset($rows[0]));
965 4
        $this->assertTrue(isset($rows[0]['CUSTOMER_ID']));
966 4
        $this->assertTrue(isset($rows[0]['TOTAL']));
967
    }
968
969 5
    public function testTransaction(): void
970
    {
971 5
        $db = $this->getConnection();
972
973 5
        $this->assertNull($db->getTransaction());
974
975 5
        $command = $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command transaction')");
976
977 5
        $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

977
        $this->/** @scrutinizer ignore-call */ 
978
               invokeMethod($command, 'requireTransaction');
Loading history...
978
979 5
        $command->execute();
980
981 5
        $this->assertNull($db->getTransaction());
982 5
        $this->assertEquals(
983
            1,
984 5
            $db->createCommand(
985
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction'"
986 5
            )->queryScalar()
987
        );
988
    }
989
990 5
    public function testRetryHandler(): void
991
    {
992 5
        $db = $this->getConnection();
993
994 5
        $this->assertNull($db->getTransaction());
995
996 5
        $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command retry')")->execute();
997
998 5
        $this->assertNull($db->getTransaction());
999 5
        $this->assertEquals(
1000
            1,
1001 5
            $db->createCommand(
1002
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command retry'"
1003 5
            )->queryScalar()
1004
        );
1005
1006 5
        $attempts = null;
1007 5
        $hitHandler = false;
1008 5
        $hitCatch = false;
1009
1010 5
        $command = $db->createCommand(
1011
            "INSERT INTO {{profile}}([[id]], [[description]]) VALUES(1, 'command retry')"
1012
        );
1013
1014 5
        $this->invokeMethod(
1015
            $command,
1016
            'setRetryHandler',
1017 5
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
1018 5
                $attempts = $attempt;
1019 5
                $hitHandler = true;
1020
1021 5
                return $attempt <= 2;
1022
            }]
1023
        );
1024
1025
        try {
1026 5
            $command->execute();
1027 5
        } catch (Exception $e) {
1028 5
            $hitCatch = true;
1029 5
            $this->assertInstanceOf(IntegrityException::class, $e);
1030
        }
1031
1032 5
        $this->assertNull($db->getTransaction());
1033 5
        $this->assertSame(3, $attempts);
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

1033
        $this->/** @scrutinizer ignore-call */ 
1034
               assertSame(3, $attempts);
Loading history...
1034 5
        $this->assertTrue($hitHandler);
1035 5
        $this->assertTrue($hitCatch);
1036
    }
1037
1038 4
    public function testCreateView(): void
1039
    {
1040 4
        $db = $this->getConnection();
1041
1042 4
        $subquery = (new Query($db))
1043 4
            ->select('bar')
1044 4
            ->from('testCreateViewTable')
1045 4
            ->where(['>', 'bar', '5']);
1046
1047 4
        if ($db->getSchema()->getTableSchema('testCreateView') !== null) {
1048
            $db->createCommand()->dropView('testCreateView')->execute();
1049
        }
1050
1051 4
        if ($db->getSchema()->getTableSchema('testCreateViewTable')) {
1052
            $db->createCommand()->dropTable('testCreateViewTable')->execute();
1053
        }
1054
1055 4
        $db->createCommand()->createTable('testCreateViewTable', [
1056
            'id' => Schema::TYPE_PK,
1057
            'bar' => Schema::TYPE_INTEGER,
1058 4
        ])->execute();
1059
1060 4
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 1])->execute();
1061 4
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 6])->execute();
1062 4
        $db->createCommand()->createView('testCreateView', $subquery)->execute();
1063
1064 4
        $records = $db->createCommand('SELECT [[bar]] FROM {{testCreateView}};')->queryAll();
1065
1066 4
        $this->assertEquals([['bar' => 6]], $records);
1067
    }
1068
1069 5
    public function testDropView(): void
1070
    {
1071 5
        $db = $this->getConnection();
1072
1073
        /* since it already exists in the fixtures */
1074 5
        $viewName = 'animal_view';
1075
1076 5
        $this->assertNotNull($db->getSchema()->getTableSchema($viewName));
1077
1078 5
        $db->createCommand()->dropView($viewName)->execute();
1079
1080 5
        $this->assertNull($db->getSchema()->getTableSchema($viewName));
1081
    }
1082
1083
    public function batchInsertSqlProviderTrait(): array
1084
    {
1085
        return [
1086
            'issue11242' => [
1087
                'type',
1088
                ['int_col', 'float_col', 'char_col'],
1089
                [['', '', 'Kyiv {{city}}, Ukraine']],
1090
                /**
1091
                 * {@see https://github.com/yiisoft/yii2/issues/11242}
1092
                 *
1093
                 * Make sure curly bracelets (`{{..}}`) in values will not be escaped
1094
                 */
1095
                'expected' => 'INSERT INTO `type` (`int_col`, `float_col`, `char_col`)'
1096
                    . " VALUES (NULL, NULL, 'Kyiv {{city}}, Ukraine')",
1097
            ],
1098
            'wrongBehavior' => [
1099
                '{{%type}}',
1100
                ['{{%type}}.[[int_col]]', '[[float_col]]', 'char_col'],
1101
                [['', '', 'Kyiv {{city}}, Ukraine']],
1102
                /**
1103
                 * Test covers potentially wrong behavior and marks it as expected!.
1104
                 *
1105
                 * In case table name or table column is passed with curly or square bracelets, QueryBuilder can not
1106
                 * determine the table schema and typecast values properly.
1107
                 * TODO: make it work. Impossible without BC breaking for public methods.
1108
                 */
1109
                'expected' => 'INSERT INTO `type` (`type`.`int_col`, `float_col`, `char_col`)'
1110
                    . " VALUES ('', '', 'Kyiv {{city}}, Ukraine')",
1111
            ],
1112
            'batchInsert binds params from expression' => [
1113
                '{{%type}}',
1114
                ['int_col'],
1115
                /**
1116
                 * This example is completely useless. This feature of batchInsert is intended to be used with complex
1117
                 * expression objects, such as JsonExpression.
1118
                 */
1119
                [[new Expression(':qp1', [':qp1' => 42])]],
1120
                'expected' => 'INSERT INTO `type` (`int_col`) VALUES (:qp1)',
1121
                'expectedParams' => [':qp1' => 42],
1122
            ],
1123
        ];
1124
    }
1125
1126
    public function bindParamsNonWhereProviderTrait(): array
1127
    {
1128
        return [
1129
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email GROUP BY SUBSTR(name, :len)'],
1130
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email ORDER BY SUBSTR(name, :len)'],
1131
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email'],
1132
        ];
1133
    }
1134
1135
    public function getRawSqlProviderTrait(): array
1136
    {
1137
        return [
1138
            [
1139
                'SELECT * FROM customer WHERE id = :id',
1140
                [':id' => 1],
1141
                'SELECT * FROM customer WHERE id = 1',
1142
            ],
1143
            [
1144
                'SELECT * FROM customer WHERE id = :id',
1145
                ['id' => 1],
1146
                'SELECT * FROM customer WHERE id = 1',
1147
            ],
1148
            [
1149
                'SELECT * FROM customer WHERE id = :id',
1150
                ['id' => null],
1151
                'SELECT * FROM customer WHERE id = NULL',
1152
            ],
1153
            [
1154
                'SELECT * FROM customer WHERE id = :base OR id = :basePrefix',
1155
                [
1156
                    'base' => 1,
1157
                    'basePrefix' => 2,
1158
                ],
1159
                'SELECT * FROM customer WHERE id = 1 OR id = 2',
1160
            ],
1161
            /**
1162
             * {@see https://github.com/yiisoft/yii2/issues/9268}
1163
             */
1164
            [
1165
                'SELECT * FROM customer WHERE active = :active',
1166
                [':active' => false],
1167
                'SELECT * FROM customer WHERE active = FALSE',
1168
            ],
1169
            /**
1170
             * {@see https://github.com/yiisoft/yii2/issues/15122}
1171
             */
1172
            [
1173
                'SELECT * FROM customer WHERE id IN (:ids)',
1174
                [':ids' => new Expression(implode(', ', [1, 2]))],
1175
                'SELECT * FROM customer WHERE id IN (1, 2)',
1176
            ],
1177
        ];
1178
    }
1179
1180
    public function invalidSelectColumnsProviderTrait(): array
1181
    {
1182
        return [
1183
            [[]],
1184
            ['*'],
1185
            [['*']],
1186
        ];
1187
    }
1188
1189
    public function upsertProviderTrait(): array
1190
    {
1191
        return [
1192
            'regular values' => [
1193
                [
1194
                    'params' => [
1195
                        'T_upsert',
1196
                        [
1197
                            'email' => '[email protected]',
1198
                            'address' => 'Earth',
1199
                            'status' => 3,
1200
                        ],
1201
                    ],
1202
                ],
1203
                [
1204
                    'params' => [
1205
                        'T_upsert',
1206
                        [
1207
                            'email' => '[email protected]',
1208
                            'address' => 'Universe',
1209
                            'status' => 1,
1210
                        ],
1211
                    ],
1212
                ],
1213
            ],
1214
            'regular values with update part' => [
1215
                [
1216
                    'params' => [
1217
                        'T_upsert',
1218
                        [
1219
                            'email' => '[email protected]',
1220
                            'address' => 'Earth',
1221
                            'status' => 3,
1222
                        ],
1223
                        [
1224
                            'address' => 'Moon',
1225
                            'status' => 2,
1226
                        ],
1227
                    ],
1228
                ],
1229
                [
1230
                    'params' => [
1231
                        'T_upsert',
1232
                        [
1233
                            'email' => '[email protected]',
1234
                            'address' => 'Universe',
1235
                            'status' => 1,
1236
                        ],
1237
                        [
1238
                            'address' => 'Moon',
1239
                            'status' => 2,
1240
                        ],
1241
                    ],
1242
                    'expected' => [
1243
                        'email' => '[email protected]',
1244
                        'address' => 'Moon',
1245
                        'status' => 2,
1246
                    ],
1247
                ],
1248
            ],
1249
            'regular values without update part' => [
1250
                [
1251
                    'params' => [
1252
                        'T_upsert',
1253
                        [
1254
                            'email' => '[email protected]',
1255
                            'address' => 'Earth',
1256
                            'status' => 3,
1257
                        ],
1258
                        false,
1259
                    ],
1260
                ],
1261
                [
1262
                    'params' => [
1263
                        'T_upsert',
1264
                        [
1265
                            'email' => '[email protected]',
1266
                            'address' => 'Universe',
1267
                            'status' => 1,
1268
                        ],
1269
                        false,
1270
                    ],
1271
                    'expected' => [
1272
                        'email' => '[email protected]',
1273
                        'address' => 'Earth',
1274
                        'status' => 3,
1275
                    ],
1276
                ],
1277
            ],
1278
            'query' => [
1279
                [
1280
                    'params' => [
1281
                        'T_upsert',
1282
                        (new Query($this->getConnection()))
1283
                            ->select([
1284
                                'email',
1285
                                'address',
1286
                                'status' => new Expression('1'),
1287
                            ])
1288
                            ->from('customer')
1289
                            ->where(['name' => 'user1'])
1290
                            ->limit(1),
1291
                    ],
1292
                    'expected' => [
1293
                        'email' => '[email protected]',
1294
                        'address' => 'address1',
1295
                        'status' => 1,
1296
                    ],
1297
                ],
1298
                [
1299
                    'params' => [
1300
                        'T_upsert',
1301
                        (new Query($this->getConnection()))
1302
                            ->select([
1303
                                'email',
1304
                                'address',
1305
                                'status' => new Expression('2'),
1306
                            ])
1307
                            ->from('customer')
1308
                            ->where(['name' => 'user1'])
1309
                            ->limit(1),
1310
                    ],
1311
                    'expected' => [
1312
                        'email' => '[email protected]',
1313
                        'address' => 'address1',
1314
                        'status' => 2,
1315
                    ],
1316
                ],
1317
            ],
1318
            'query with update part' => [
1319
                [
1320
                    'params' => [
1321
                        'T_upsert',
1322
                        (new Query($this->getConnection()))
1323
                            ->select([
1324
                                'email',
1325
                                'address',
1326
                                'status' => new Expression('1'),
1327
                            ])
1328
                            ->from('customer')
1329
                            ->where(['name' => 'user1'])
1330
                            ->limit(1),
1331
                        [
1332
                            'address' => 'Moon',
1333
                            'status' => 2,
1334
                        ],
1335
                    ],
1336
                    'expected' => [
1337
                        'email' => '[email protected]',
1338
                        'address' => 'address1',
1339
                        'status' => 1,
1340
                    ],
1341
                ],
1342
                [
1343
                    'params' => [
1344
                        'T_upsert',
1345
                        (new Query($this->getConnection()))
1346
                            ->select([
1347
                                'email',
1348
                                'address',
1349
                                'status' => new Expression('3'),
1350
                            ])
1351
                            ->from('customer')
1352
                            ->where(['name' => 'user1'])
1353
                            ->limit(1),
1354
                        [
1355
                            'address' => 'Moon',
1356
                            'status' => 2,
1357
                        ],
1358
                    ],
1359
                    'expected' => [
1360
                        'email' => '[email protected]',
1361
                        'address' => 'Moon',
1362
                        'status' => 2,
1363
                    ],
1364
                ],
1365
            ],
1366
            'query without update part' => [
1367
                [
1368
                    'params' => [
1369
                        'T_upsert',
1370
                        (new Query($this->getConnection()))
1371
                            ->select([
1372
                                'email',
1373
                                'address',
1374
                                'status' => new Expression('1'),
1375
                            ])
1376
                            ->from('customer')
1377
                            ->where(['name' => 'user1'])
1378
                            ->limit(1),
1379
                        false,
1380
                    ],
1381
                    'expected' => [
1382
                        'email' => '[email protected]',
1383
                        'address' => 'address1',
1384
                        'status' => 1,
1385
                    ],
1386
                ],
1387
                [
1388
                    'params' => [
1389
                        'T_upsert',
1390
                        (new Query($this->getConnection()))
1391
                            ->select([
1392
                                'email',
1393
                                'address',
1394
                                'status' => new Expression('2'),
1395
                            ])
1396
                            ->from('customer')
1397
                            ->where(['name' => 'user1'])
1398
                            ->limit(1),
1399
                        false,
1400
                    ],
1401
                    'expected' => [
1402
                        'email' => '[email protected]',
1403
                        'address' => 'address1',
1404
                        'status' => 1,
1405
                    ],
1406
                ],
1407
            ],
1408
        ];
1409
    }
1410
1411 2
    public function testAlterTable(): void
1412
    {
1413 2
        $db = $this->getConnection();
1414
1415 2
        if ($db->getDriverName() === 'sqlite') {
1416
            $this->markTestSkipped('Sqlite does not support alterTable');
1417
        }
1418
1419 2
        if ($db->getSchema()->getTableSchema('testAlterTable') !== null) {
1420
            $db->createCommand()->dropTable('testAlterTable')->execute();
1421
        }
1422
1423 2
        $db->createCommand()->createTable(
1424
            'testAlterTable',
1425
            [
1426 2
                'id' => Schema::TYPE_PK,
1427
                'bar' => Schema::TYPE_INTEGER,
1428
            ]
1429 2
        )->execute();
1430
1431 2
        $db->createCommand()->insert('testAlterTable', ['bar' => 1])->execute();
1432
1433 2
        $db->createCommand()->alterColumn('testAlterTable', 'bar', Schema::TYPE_STRING)->execute();
1434
1435 2
        $db->createCommand()->insert('testAlterTable', ['bar' => 'hello'])->execute();
1436
1437 2
        $records = $db->createCommand('SELECT [[id]], [[bar]] FROM {{testAlterTable}}')->queryAll();
1438 2
        $this->assertEquals([
1439 2
            ['id' => 1, 'bar' => 1],
1440
            ['id' => 2, 'bar' => 'hello'],
1441
        ], $records);
1442
    }
1443
}
1444