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

TestCommandTrait::testCreateDropIndex()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 52
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 2.0001

Importance

Changes 0
Metric Value
cc 2
eloc 30
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 52
ccs 28
cts 29
cp 0.9655
crap 2.0001
rs 9.44

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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