Passed
Push — master ( 2d3e9c...9607e5 )
by Alexander
02:50
created

TestCommandTrait::upsertProviderTrait()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 216
Code Lines 141

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 141
nc 1
nop 0
dl 0
loc 216
ccs 0
cts 55
cp 0
crap 2
rs 8
c 0
b 0
f 0

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
    public function testConstruct(): void
28
    {
29 4
        $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 4
        /* null */
32
        $command = $db->createCommand();
33
34 4
        $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 4
        /* string */
37
        $sql = 'SELECT * FROM customer';
38
39 4
        $command = $db->createCommand($sql);
40
41 4
        $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 4
44 4
    public function testGetSetSql(): void
45
    {
46 4
        $db = $this->getConnection();
47
48 4
        $sql = 'SELECT * FROM customer';
49
50 4
        $command = $db->createCommand($sql);
51
52 4
        $this->assertEquals($sql, $command->getSql());
53
54 4
        $sql2 = 'SELECT * FROM order';
55
56 4
        $command->setSql($sql2);
57
58 4
        $this->assertEquals($sql2, $command->getSql());
59
    }
60 4
61 4
    public function testPrepareCancel(): void
62
    {
63 4
        $db = $this->getConnection();
64
65 4
        $command = $db->createCommand('SELECT * FROM {{customer}}');
66
67 4
        $this->assertNull($command->getPdoStatement());
68
69 4
        $command->prepare();
70
71 4
        $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 4
        $command->cancel();
74
75 4
        $this->assertNull($command->getPdoStatement());
76
    }
77 4
78 4
    public function testExecute(): void
79
    {
80 4
        $db = $this->getConnection(true);
81
82 4
        $sql = 'INSERT INTO {{customer}}([[email]], [[name]], [[address]])'
83
            . ' VALUES (\'[email protected]\', \'user4\', \'address4\')';
84
85 4
        $command = $db->createCommand($sql);
86
87 4
        $this->assertEquals(1, $command->execute());
88
89 4
        $sql = 'SELECT COUNT(*) FROM {{customer}} WHERE [[name]] = \'user4\'';
90
91 4
        $command = $db->createCommand($sql);
92
93 4
        $this->assertEquals(1, $command->queryScalar());
94
95 4
        $command = $db->createCommand('bad SQL');
96
97 4
        $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 4
        $command->execute();
100
    }
101 4
102
    public function testQuery(): void
103
    {
104 4
        $db = $this->getConnection(true);
105
106 4
        /* query */
107
        $sql = 'SELECT * FROM {{customer}}';
108
109 4
        $reader = $db->createCommand($sql)->Query();
110
111 4
        $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 4
        /* queryAll */
114
        $rows = $db->createCommand('SELECT * FROM {{customer}}')->queryAll();
115
116 4
        $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 4
        $row = $rows[2];
119
120 4
        $this->assertEquals(3, $row['id']);
121
        $this->assertEquals('user3', $row['name']);
122 4
123 4
        $rows = $db->createCommand('SELECT * FROM {{customer}} WHERE [[id]] = 10')->queryAll();
124
125 4
        $this->assertEquals([], $rows);
126
127 4
        /* queryOne */
128
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
129
130 4
        $row = $db->createCommand($sql)->queryOne();
131
132 4
        $this->assertEquals(1, $row['id']);
133
        $this->assertEquals('user1', $row['name']);
134 4
135 4
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
136
137 4
        $command = $db->createCommand($sql);
138
139 4
        $command->prepare();
140
141 4
        $row = $command->queryOne();
142
143 4
        $this->assertEquals(1, $row['id']);
144
        $this->assertEquals('user1', $row['name']);
145 4
146 4
        $sql = 'SELECT * FROM {{customer}} WHERE [[id]] = 10';
147
148 4
        $command = $db->createCommand($sql);
149
150 4
        $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 4
        /* queryColumn */
153
        $sql = 'SELECT * FROM {{customer}}';
154
155 4
        $column = $db->createCommand($sql)->queryColumn();
156
157 4
        $this->assertEquals(range(1, 3), $column);
158
159 4
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
160
161 4
        $this->assertEquals([], $command->queryColumn());
162
163 4
        /* queryScalar */
164
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
165
166 4
        $this->assertEquals($db->createCommand($sql)->queryScalar(), 1);
167
168 4
        $sql = 'SELECT [[id]] FROM {{customer}} ORDER BY [[id]]';
169
170 4
        $command = $db->createCommand($sql);
171
172 4
        $command->prepare();
173
174 4
        $this->assertEquals(1, $command->queryScalar());
175
176 4
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
177
178 4
        $this->assertFalse($command->queryScalar());
179
180 4
        $command = $db->createCommand('bad SQL');
181
182 4
        $this->expectException(Exception::class);
183
184 4
        $command->Query();
185
    }
186 4
187
    public function testFetchMode(): void
188
    {
189 4
        $db = $this->getConnection();
190
191 4
        /* default: FETCH_ASSOC */
192
        $sql = 'SELECT * FROM {{customer}}';
193
194 4
        $command = $db->createCommand($sql);
195
196 4
        $result = $command->queryOne();
197
198 4
        $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 4
        /* FETCH_OBJ, customized via fetchMode property */
201
        $sql = 'SELECT * FROM {{customer}}';
202
203 4
        $command = $db->createCommand($sql);
204
205 4
        $command->setFetchMode(PDO::FETCH_OBJ);
206
207 4
        $result = $command->queryOne();
208
209 4
        $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 4
        /* FETCH_NUM, customized in query method */
212
        $sql = 'SELECT * FROM {{customer}}';
213
214 4
        $command = $db->createCommand($sql);
215
216 4
        $result = $command->queryOne(PDO::FETCH_NUM);
217
218 4
        $this->assertTrue(is_array($result) && isset($result[0]));
219
    }
220 4
221 4
    public function testBatchInsert(): void
222
    {
223 4
        $db = $this->getConnection();
224
225 4
        $command = $db->createCommand();
226
227 4
        $command->batchInsert(
228
            '{{customer}}',
229 4
            ['email', 'name', 'address'],
230 4
            [
231 4
                ['[email protected]', 't1', 't1 address'],
232
                ['[email protected]', null, false],
233 4
            ]
234
        );
235
236
        $this->assertEquals(2, $command->execute());
237
238 4
        /**
239
         * {@see https://github.com/yiisoft/yii2/issues/11693}
240
         */
241
        $command = $this->getConnection()->createCommand();
242
243 4
        $command->batchInsert(
244
            '{{customer}}',
245 4
            ['email', 'name', 'address'],
246 4
            []
247 4
        );
248 4
249
        $this->assertEquals(0, $command->execute());
250
    }
251 4
252 4
    public function testBatchInsertWithYield(): void
253
    {
254 4
        $rows = (static function () {
255
            if (false) {
256 4
                yield [];
257 4
            }
258
        })();
259
260 4
        $command = $this->getConnection()->createCommand();
261
262 4
        $command->batchInsert(
263
            '{{customer}}',
264 4
            ['email', 'name', 'address'],
265 4
            $rows
266 4
        );
267
268
        $this->assertEquals(0, $command->execute());
269
    }
270 4
271 4
    /**
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
    public function testBatchInsertDataTypesLocale(): void
279
    {
280 4
        $locale = setlocale(LC_NUMERIC, 0);
281
282 4
        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 4
        }
285
286
        $db = $this->getConnection(true);
287
288 4
        try {
289
            /* This one sets decimal mark to comma sign */
290
            setlocale(LC_NUMERIC, 'ru_RU.utf8');
291
292 4
            $cols = ['int_col', 'char_col', 'float_col', 'bool_col'];
293
294 4
            $data = [
295
                [1, 'A', 9.735, true],
296
                [2, 'B', -2.123, false],
297 4
                [3, 'C', 2.123, false],
298
            ];
299
300
            /* clear data in "type" table */
301
            $db->createCommand()->delete('type')->execute();
302
303 4
            /* change, for point oracle. */
304
            if ($db->getDriverName() === 'oci') {
305
                $db->createCommand("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'")->execute();
306 4
            }
307
308 4
            /* batch insert on "type" table */
309 4
            $db->createCommand()->batchInsert('type', $cols, $data)->execute();
310
311 4
            $data = $db->createCommand(
312
                'SELECT [[int_col]], [[char_col]], [[float_col]], [[bool_col]] ' .
313 4
                'FROM {{type}} WHERE [[int_col]] IN (1,2,3) ORDER BY [[int_col]]'
314 4
            )->queryAll();
315 4
316 4
            $this->assertCount(3, $data);
317
            $this->assertEquals(1, $data[0]['int_col']);
318
            $this->assertEquals(2, $data[1]['int_col']);
319 4
            $this->assertEquals(3, $data[2]['int_col']);
320 4
321 4
            /* rtrim because Postgres padds the column with whitespace */
322 4
            $this->assertEquals('A', rtrim($data[0]['char_col']));
323 4
            $this->assertEquals('B', rtrim($data[1]['char_col']));
324 4
            $this->assertEquals('C', rtrim($data[2]['char_col']));
325 4
            $this->assertEquals('9.735', $data[0]['float_col']);
326 4
            $this->assertEquals('-2.123', $data[1]['float_col']);
327 4
            $this->assertEquals('2.123', $data[2]['float_col']);
328
            $this->assertEquals('1', $data[0]['bool_col']);
329
            $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
            $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 4
            throw $e;
339 4
        }
340
341 4
        setlocale(LC_NUMERIC, $locale);
342
    }
343 4
344
    public function testInsert(): void
345 4
    {
346
        $db = $this->getConnection();
347 4
348
        $db->createCommand('DELETE FROM {{customer}}')->execute();
349 4
350 4
        $command = $db->createCommand();
351
352 4
        $command->insert(
353
            '{{customer}}',
354
            [
355
                'email' => '[email protected]',
356 4
                'name' => 'test',
357
                'address' => 'test address',
358 4
            ]
359
        )->execute();
360 4
361
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{customer}};')->queryScalar());
362 4
363 4
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryOne();
364
365
        $this->assertEquals([
366
            'email' => '[email protected]',
367 4
            'name' => 'test',
368
            'address' => 'test address',
369
        ], $record);
370
    }
371
372 4
    /**
373
     * verify that {{}} are not going to be replaced in parameters.
374 4
     */
375
    public function testNoTablenameReplacement(): void
376 4
    {
377 4
        $db = $this->getConnection(true);
378
379 4
        $db->createCommand()->insert(
380
            '{{customer}}',
381
            [
382
                'name' => 'Some {{weird}} name',
383 4
                'email' => '[email protected]',
384
                'address' => 'Some {{%weird}} address',
385 4
            ]
386 1
        )->execute();
387
388 3
        if ($db->getDriverName() === 'pgsql') {
389
            $customerId = $db->getLastInsertID('public.customer_id_seq');
390
        } else {
391 4
            $customerId = $db->getLastInsertID();
392
        }
393 4
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 4
                'address' => 'Some {{%updated}} address',
404
            ],
405 4
            ['id' => $customerId]
406
        )->execute();
407 4
408 4
        $customer = $db->createCommand('SELECT * FROM {{customer}} WHERE id=' . $customerId)->queryOne();
409 4
410
        $this->assertEquals('Some {{updated}} name', $customer['name']);
411
        $this->assertEquals('Some {{%updated}} address', $customer['address']);
412
    }
413
414 4
    /**
415
     * Test INSERT INTO ... SELECT SQL statement.
416 4
     */
417
    public function testInsertSelect(): void
418 4
    {
419
        $db = $this->getConnection();
420 4
421
        $db->createCommand('DELETE FROM {{customer}}')->execute();
422 4
423 4
        $command = $db->createCommand();
424
425 4
        $command->insert(
426
            '{{customer}}',
427
            [
428
                'email' => '[email protected]',
429 4
                'name' => 'test',
430
                'address' => 'test address',
431 4
            ]
432
        )->execute();
433 4
434
        $query = new Query($db);
435 4
436
        $query->select(
437
            [
438
                '{{customer}}.[[email]] as name',
439
                '[[name]] as email',
440 4
                '[[address]]',
441 4
            ]
442 4
        )
443
            ->from('{{customer}}')
444
            ->where([
445
                'and',
446
                ['<>', 'name', 'foo'],
447 4
                ['status' => [0, 1, 2, 3]],
448
            ]);
449 4
450 4
        $command = $db->createCommand();
451
452 4
        $command->insert(
453
            '{{customer}}',
454 4
            $query
455
        )->execute();
456 4
457
        $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{customer}}')->queryScalar());
458 4
459
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryAll();
460 4
461
        $this->assertEquals([
462
            [
463
                'email' => '[email protected]',
464
                'name' => 'test',
465
                'address' => 'test address',
466
            ],
467
            [
468
                'email' => 'test',
469
                'name' => '[email protected]',
470 4
                'address' => 'test address',
471
            ],
472
        ], $record);
473
    }
474
475 4
    /**
476
     * Test INSERT INTO ... SELECT SQL statement with alias syntax.
477 4
     */
478
    public function testInsertSelectAlias(): void
479 4
    {
480
        $db = $this->getConnection();
481 4
482
        $db->createCommand('DELETE FROM {{customer}}')->execute();
483 4
484 4
        $command = $db->createCommand();
485
486 4
        $command->insert(
487
            '{{customer}}',
488
            [
489
                'email' => '[email protected]',
490 4
                'name' => 'test',
491
                'address' => 'test address',
492 4
            ]
493
        )->execute();
494 4
495
        $query = new Query($db);
496 4
497
        $query->select(
498
            [
499
                'email' => '{{customer}}.[[email]]',
500
                'address' => 'name',
501 4
                'name' => 'address',
502 4
            ]
503 4
        )
504
            ->from('{{customer}}')
505
            ->where([
506
                'and',
507
                ['<>', 'name', 'foo'],
508 4
                ['status' => [0, 1, 2, 3]],
509
            ]);
510 4
511 4
        $command = $db->createCommand();
512
513 4
        $command->insert(
514
            '{{customer}}',
515 4
            $query
516
        )->execute();
517 4
518
        $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{customer}}')->queryScalar());
519 4
520
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryAll();
521 4
522
        $this->assertEquals([
523
            [
524
                'email' => '[email protected]',
525
                'name' => 'test',
526
                'address' => 'test address',
527
            ],
528
            [
529
                'email' => '[email protected]',
530
                'name' => 'test address',
531 4
                'address' => 'test',
532
            ],
533 4
        ], $record);
534
    }
535 4
536
    public function testInsertExpression(): void
537 4
    {
538
        $db = $this->getConnection();
539 4
540 4
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
541 1
542 1
        switch ($db->getDriverName()) {
543 3
            case 'pgsql':
544 1
                $expression = "EXTRACT(YEAR FROM TIMESTAMP 'now')";
545 1
                break;
546 2
            case 'mysql':
547 1
                $expression = 'YEAR(NOW())';
548 1
                break;
549 1
            case 'sqlite':
550 1
                $expression = "strftime('%Y')";
551
                break;
552
            case 'sqlsrv':
553 4
                $expression = 'YEAR(GETDATE())';
554
        }
555 4
556 4
        $command = $db->createCommand();
557
558 4
        $command->insert(
559 4
            '{{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 4
            ]
564
        )->execute();
565 4
566
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{order_with_null_fk}}')->queryScalar());
567 4
568 4
        $record = $db->createCommand('SELECT [[created_at]] FROM {{order_with_null_fk}}')->queryOne();
569
570 4
        $this->assertEquals([
571
            'created_at' => date('Y'),
572 4
        ], $record);
573
    }
574 4
575
    public function testsInsertQueryAsColumnValue(): void
576 4
    {
577
        $time = time();
578 4
579
        $db = $this->getConnection(true);
580 4
581
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
582 4
583 4
        $command = $db->createCommand();
584 4
585 4
        $command->insert('{{order}}', [
586 4
            'customer_id' => 1,
587
            'created_at' => $time,
588 4
            'total' => 42,
589 1
        ])->execute();
590
591 3
        if ($db->getDriverName() === 'pgsql') {
592
            $orderId = $db->getLastInsertID('public.order_id_seq');
593
        } else {
594 4
            $orderId = $db->getLastInsertID();
595
        }
596 4
597
        $columnValueQuery = new Query($db);
598 4
599
        $columnValueQuery->select('created_at')->from('{{order}}')->where(['id' => $orderId]);
600 4
601 4
        $command = $db->createCommand();
602
603 4
        $command->insert(
604 4
            '{{order_with_null_fk}}',
605 4
            [
606
                'customer_id' => $orderId,
607 4
                'created_at' => $columnValueQuery,
608
                'total' => 42,
609 4
            ]
610
        )->execute();
611 4
612 4
        $this->assertEquals(
613 4
            $time,
614
            $db->createCommand(
615
                'SELECT [[created_at]] FROM {{order_with_null_fk}} WHERE [[customer_id]] = ' . $orderId
616 4
            )->queryScalar()
617 4
        );
618 4
619
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
620 4
        $db->createCommand('DELETE FROM {{order}} WHERE [[id]] = ' . $orderId)->execute();
621
    }
622 4
623
    public function testCreateTable(): void
624 4
    {
625
        $db = $this->getConnection();
626
627
        if ($db->getSchema()->getTableSchema('testCreateTable') !== null) {
628 4
            $db->createCommand()->dropTable('testCreateTable')->execute();
629 4
        }
630 4
631 4
        $db->createCommand()->createTable(
632
            'testCreateTable',
633 4
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER]
634
        )->execute();
635 4
636
        $db->createCommand()->insert('testCreateTable', ['bar' => 1])->execute();
637 4
638 4
        $records = $db->createCommand('SELECT [[id]], [[bar]] FROM {{testCreateTable}};')->queryAll();
639
640 4
        $this->assertEquals([
641
            ['id' => 1, 'bar' => 1],
642 4
        ], $records);
643
    }
644 4
645
    public function testDropTable(): void
646 4
    {
647
        $db = $this->getConnection();
648 4
649
        $tableName = 'type';
650 4
651
        $this->assertNotNull($db->getSchema()->getTableSchema($tableName));
652 4
653 4
        $db->createCommand()->dropTable($tableName)->execute();
654
655 4
        $this->assertNull($db->getSchema()->getTableSchema($tableName));
656
    }
657 4
658
    public function testTruncateTable(): void
659 4
    {
660
        $db = $this->getConnection();
661 4
662
        $rows = $db->createCommand('SELECT * FROM {{animal}}')->queryAll();
663 4
664
        $this->assertCount(2, $rows);
665 4
666
        $db->createCommand()->truncateTable('animal')->execute();
667 4
668 4
        $rows = $db->createCommand('SELECT * FROM {{animal}}')->queryAll();
669
670 4
        $this->assertCount(0, $rows);
671
    }
672 4
673
    public function testRenameTable(): void
674 4
    {
675 4
        $db = $this->getConnection(true);
676
677 4
        $fromTableName = 'type';
678
        $toTableName = 'new_type';
679
680
        if ($db->getSchema()->getTableSchema($toTableName) !== null) {
681 4
            $db->createCommand()->dropTable($toTableName)->execute();
682 4
        }
683
684 4
        $this->assertNotNull($db->getSchema()->getTableSchema($fromTableName));
685
        $this->assertNull($db->getSchema()->getTableSchema($toTableName));
686 4
687 4
        $db->createCommand()->renameTable($fromTableName, $toTableName)->execute();
688 4
689
        $this->assertNull($db->getSchema()->getTableSchema($fromTableName, true));
690 24
        $this->assertNotNull($db->getSchema()->getTableSchema($toTableName, true));
691
    }
692 24
693 24
    protected function performAndCompareUpsertResult(ConnectionInterface $db, array $data): void
694
    {
695 24
        $params = $data['params'];
696
        $expected = $data['expected'] ?? $params[1];
697 24
698
        $command = $db->createCommand();
699 24
700
        call_user_func_array([$command, 'upsert'], $params);
701 24
702 24
        $command->execute();
703 24
704 24
        $actual = (new Query($db))
705 24
            ->select([
706
                'email',
707 24
                'address' => new Expression($this->upsertTestCharCast),
708 24
                'status',
709
            ])
710 24
            ->from('T_upsert')
711 24
            ->one();
712
713 4
        $this->assertEquals($expected, $actual, $this->upsertTestCharCast);
714
    }
715 4
716
    public function testAddDropForeignKey(): void
717 4
    {
718 4
        $db = $this->getConnection();
719
720 4
        $tableName = 'test_fk';
721
        $name = 'test_fk_constraint';
722 4
723
        $schema = $db->getSchema();
724
725
        if ($schema->getTableSchema($tableName) !== null) {
726 4
            $db->createCommand()->dropTable($tableName)->execute();
727 4
        }
728
729
        $db->createCommand()->createTable($tableName, [
730
            'int1' => 'integer not null unique',
731
            'int2' => 'integer not null unique',
732
            'int3' => 'integer not null unique',
733 4
            'int4' => 'integer not null unique',
734
            'unique ([[int1]], [[int2]])',
735 4
            'unique ([[int3]], [[int4]])',
736
        ])->execute();
737 4
738
        $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 4
740 4
        $db->createCommand()->addForeignKey($name, $tableName, ['int1'], $tableName, ['int3'])->execute();
741
742 4
        $this->assertEquals(['int1'], $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames());
743
        $this->assertEquals(['int3'], $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames());
744 4
745
        $db->createCommand()->dropForeignKey($name, $tableName)->execute();
746 4
747
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
748
749 4
        $db->createCommand()->addForeignKey(
750
            $name,
751 4
            $tableName,
752 4
            ['int1', 'int2'],
753
            $tableName,
754 4
            ['int3', 'int4']
755 4
        )->execute();
756 4
757
        $this->assertEquals(
758 4
            ['int1', 'int2'],
759 4
            $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames()
760 4
        );
761
        $this->assertEquals(
762 4
            ['int3', 'int4'],
763
            $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames()
764 4
        );
765
    }
766 4
767
    public function testCreateDropIndex(): void
768 4
    {
769 4
        $db = $this->getConnection();
770
771 4
        $tableName = 'test_idx';
772
        $name = 'test_idx_constraint';
773 4
774
        $schema = $db->getSchema();
775
776
        if ($schema->getTableSchema($tableName) !== null) {
777 4
            $db->createCommand()->dropTable($tableName)->execute();
778 4
        }
779
780 4
        $db->createCommand()->createTable($tableName, [
781
            'int1' => 'integer not null',
782 4
            'int2' => 'integer not null',
783
        ])->execute();
784 4
785
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
786 4
787 4
        $db->createCommand()->createIndex($name, $tableName, ['int1'])->execute();
788
789 4
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
790
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
791 4
792
        $db->createCommand()->dropIndex($name, $tableName)->execute();
793 4
794
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
795 4
796 4
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'])->execute();
797
798 4
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
799
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
800 4
801 4
        $db->createCommand()->dropIndex($name, $tableName)->execute();
802
803 4
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
804
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
805 4
806 4
        $db->createCommand()->createIndex($name, $tableName, ['int1'], true)->execute();
807
808 4
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
809
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
810 4
811
        $db->createCommand()->dropIndex($name, $tableName)->execute();
812 4
813
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
814 4
815 4
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'], true)->execute();
816 4
817
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
818 4
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
819
    }
820 4
821
    public function testAddDropUnique(): void
822 4
    {
823 4
        $db = $this->getConnection();
824
825 4
        $tableName = 'test_uq';
826
        $name = 'test_uq_constraint';
827 4
828
        $schema = $db->getSchema();
829
830
        if ($schema->getTableSchema($tableName) !== null) {
831 4
            $db->createCommand()->dropTable($tableName)->execute();
832 4
        }
833
834 4
        $db->createCommand()->createTable($tableName, [
835
            'int1' => 'integer not null',
836 4
            'int2' => 'integer not null',
837
        ])->execute();
838 4
839
        $this->assertEmpty($schema->getTableUniques($tableName, true));
840 4
841
        $db->createCommand()->addUnique($name, $tableName, ['int1'])->execute();
842 4
843
        $this->assertEquals(['int1'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
844 4
845
        $db->createCommand()->dropUnique($name, $tableName)->execute();
846 4
847
        $this->assertEmpty($schema->getTableUniques($tableName, true));
848 4
849 4
        $db->createCommand()->addUnique($name, $tableName, ['int1', 'int2'])->execute();
850
851 4
        $this->assertEquals(['int1', 'int2'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
852
    }
853 4
854
    public function testIntegrityViolation(): void
855 4
    {
856
        $this->expectException(IntegrityException::class);
857 4
858
        $db = $this->getConnection();
859 4
860
        $sql = 'INSERT INTO {{profile}}([[id]], [[description]]) VALUES (123, \'duplicate\')';
861 4
862 3
        $command = $db->createCommand($sql);
863
864
        $command->execute();
865 3
        $command->execute();
866
    }
867 3
868
    public function testLastInsertId(): void
869 3
    {
870
        $db = $this->getConnection(true);
871 3
872
        $sql = 'INSERT INTO {{profile}}([[description]]) VALUES (\'non duplicate\')';
873 3
874
        $command = $db->createCommand($sql);
875 3
876 3
        $command->execute();
877
878 4
        $this->assertEquals(3, $db->getSchema()->getLastInsertID());
879
    }
880 4
881
    public function testQueryCache(): void
882 4
    {
883 4
        $db = $this->getConnection();
884
885 4
        $db->setEnableQueryCache(true);
886
        $db->setQueryCache($this->cache);
887 4
888
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
889 4
890
        $this->assertEquals('user1', $command->bindValue(':id', 1)->queryScalar());
891 4
892
        $update = $db->createCommand('UPDATE {{customer}} SET [[name]] = :name WHERE [[id]] = :id');
893 4
894
        $update->bindValues([':id' => 1, ':name' => 'user11'])->execute();
895 4
896 4
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
897
898 4
        $db->cache(function (ConnectionInterface $db) use ($command, $update) {
899
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
900 4
901
            $update->bindValues([':id' => 2, ':name' => 'user22'])->execute();
902 4
903 4
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
904 4
905
            $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

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

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

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