Passed
Pull Request — master (#201)
by Wilmer
02:04
created

TestCommandTrait::testsInsertQueryAsColumnValue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 46
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 29
nc 2
nop 0
dl 0
loc 46
ccs 28
cts 28
cp 1
crap 2
rs 9.456
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\TestUtility;
6
7
use PDO;
8
use Throwable;
9
use Yiisoft\Db\Connection\ConnectionInterface;
10
use Yiisoft\Db\Data\DataReader;
11
use Yiisoft\Db\Exception\Exception;
12
use Yiisoft\Db\Exception\IntegrityException;
13
use Yiisoft\Db\Exception\InvalidArgumentException;
14
use Yiisoft\Db\Expression\Expression;
15
use Yiisoft\Db\Query\Query;
16
use Yiisoft\Db\Schema\Schema;
17
18
use function call_user_func;
19
use function call_user_func_array;
20
use function date;
21
use function is_array;
22
use function range;
23
use function rtrim;
24
use function setlocale;
25
use function time;
26
27
trait TestCommandTrait
28
{
29 4
    public function testConstruct(): void
30
    {
31 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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