Passed
Push — master ( 9607e5...058f8b )
by Wilmer
09:15
created

TestCommandTrait::testNoTablenameReplacement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 37
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 2

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

150
        $this->/** @scrutinizer ignore-call */ 
151
               assertFalse($command->queryOne());
Loading history...
151
152
        /* queryColumn */
153 5
        $sql = 'SELECT * FROM {{customer}}';
154
155 5
        $column = $db->createCommand($sql)->queryColumn();
156
157 5
        $this->assertEquals(range(1, 3), $column);
158
159 5
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
160
161 5
        $this->assertEquals([], $command->queryColumn());
162
163
        /* queryScalar */
164 5
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
165
166 5
        $this->assertEquals($db->createCommand($sql)->queryScalar(), 1);
167
168 5
        $sql = 'SELECT [[id]] FROM {{customer}} ORDER BY [[id]]';
169
170 5
        $command = $db->createCommand($sql);
171
172 5
        $command->prepare();
173
174 5
        $this->assertEquals(1, $command->queryScalar());
175
176 5
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
177
178 5
        $this->assertFalse($command->queryScalar());
179
180 5
        $command = $db->createCommand('bad SQL');
181
182 5
        $this->expectException(Exception::class);
183
184 5
        $command->Query();
185
    }
186
187 5
    public function testFetchMode(): void
188
    {
189 5
        $db = $this->getConnection();
190
191
        /* default: FETCH_ASSOC */
192 5
        $sql = 'SELECT * FROM {{customer}}';
193
194 5
        $command = $db->createCommand($sql);
195
196 5
        $result = $command->queryOne();
197
198 5
        $this->assertTrue(is_array($result) && isset($result['id']));
0 ignored issues
show
Bug introduced by
It seems like assertTrue() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

198
        $this->/** @scrutinizer ignore-call */ 
199
               assertTrue(is_array($result) && isset($result['id']));
Loading history...
199
200
        /* FETCH_OBJ, customized via fetchMode property */
201 5
        $sql = 'SELECT * FROM {{customer}}';
202
203 5
        $command = $db->createCommand($sql);
204
205 5
        $command->setFetchMode(PDO::FETCH_OBJ);
206
207 5
        $result = $command->queryOne();
208
209 5
        $this->assertIsObject($result);
0 ignored issues
show
Bug introduced by
It seems like assertIsObject() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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 4
        }, 10);
911
912 4
        $db->setEnableQueryCache(false);
913
914 4
        $db->cache(function () use ($command, $update) {
915 4
            $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
916 4
            $update->bindValues([':id' => 2, ':name' => 'user2'])->execute();
917 4
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
918 4
        }, 10);
919
920 4
        $db->setEnableQueryCache(true);
921
922 4
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id')->cache();
923
924 4
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
925
926 4
        $update->bindValues([':id' => 1, ':name' => 'user1'])->execute();
927
928 4
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
929 4
        $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
930
931 4
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
932
933 4
        $db->cache(function () use ($command) {
934 4
            $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
935 4
            $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
936 4
        }, 10);
937 4
    }
938
939 4
    public function testColumnCase(): void
940
    {
941 4
        $db = $this->getConnection();
942
943 4
        $this->assertEquals(PDO::CASE_NATURAL, $db->getSlavePdo()->getAttribute(PDO::ATTR_CASE));
944
945 4
        $sql = 'SELECT [[customer_id]], [[total]] FROM {{order}}';
946
947 4
        $rows = $db->createCommand($sql)->queryAll();
948
949 4
        $this->assertTrue(isset($rows[0]));
950 4
        $this->assertTrue(isset($rows[0]['customer_id']));
951 4
        $this->assertTrue(isset($rows[0]['total']));
952
953 4
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
954
955 4
        $rows = $db->createCommand($sql)->queryAll();
956
957 4
        $this->assertTrue(isset($rows[0]));
958 4
        $this->assertTrue(isset($rows[0]['customer_id']));
959 4
        $this->assertTrue(isset($rows[0]['total']));
960
961 4
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
962
963 4
        $rows = $db->createCommand($sql)->queryAll();
964
965 4
        $this->assertTrue(isset($rows[0]));
966 4
        $this->assertTrue(isset($rows[0]['CUSTOMER_ID']));
967 4
        $this->assertTrue(isset($rows[0]['TOTAL']));
968 4
    }
969
970 5
    public function testTransaction(): void
971
    {
972 5
        $db = $this->getConnection();
973
974 5
        $this->assertNull($db->getTransaction());
975
976 5
        $command = $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command transaction')");
977
978 5
        $this->invokeMethod($command, 'requireTransaction');
0 ignored issues
show
Bug introduced by
It seems like invokeMethod() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

978
        $this->/** @scrutinizer ignore-call */ 
979
               invokeMethod($command, 'requireTransaction');
Loading history...
979
980 5
        $command->execute();
981
982 5
        $this->assertNull($db->getTransaction());
983 5
        $this->assertEquals(
984 5
            1,
985 5
            $db->createCommand(
986 5
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction'"
987 5
            )->queryScalar()
988
        );
989 5
    }
990
991 5
    public function testRetryHandler(): void
992
    {
993 5
        $db = $this->getConnection();
994
995 5
        $this->assertNull($db->getTransaction());
996
997 5
        $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command retry')")->execute();
998
999 5
        $this->assertNull($db->getTransaction());
1000 5
        $this->assertEquals(
1001 5
            1,
1002 5
            $db->createCommand(
1003 5
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command retry'"
1004 5
            )->queryScalar()
1005
        );
1006
1007 5
        $attempts = null;
1008 5
        $hitHandler = false;
1009 5
        $hitCatch = false;
1010
1011 5
        $command = $db->createCommand(
1012 5
            "INSERT INTO {{profile}}([[id]], [[description]]) VALUES(1, 'command retry')"
1013
        );
1014
1015 5
        $this->invokeMethod(
1016
            $command,
1017 5
            'setRetryHandler',
1018 5
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
1019 5
                $attempts = $attempt;
1020 5
                $hitHandler = true;
1021
1022 5
                return $attempt <= 2;
1023 5
            }]
1024
        );
1025
1026
        try {
1027 5
            $command->execute();
1028 5
        } catch (Exception $e) {
1029 5
            $hitCatch = true;
1030 5
            $this->assertInstanceOf(IntegrityException::class, $e);
1031
        }
1032
1033 5
        $this->assertNull($db->getTransaction());
1034 5
        $this->assertSame(3, $attempts);
0 ignored issues
show
Bug introduced by
It seems like assertSame() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

1034
        $this->/** @scrutinizer ignore-call */ 
1035
               assertSame(3, $attempts);
Loading history...
1035 5
        $this->assertTrue($hitHandler);
1036 5
        $this->assertTrue($hitCatch);
1037 5
    }
1038
1039 4
    public function testCreateView(): void
1040
    {
1041 4
        $db = $this->getConnection();
1042
1043 4
        $subquery = (new Query($db))
1044 4
            ->select('bar')
1045 4
            ->from('testCreateViewTable')
1046 4
            ->where(['>', 'bar', '5']);
1047
1048 4
        if ($db->getSchema()->getTableSchema('testCreateView') !== null) {
1049
            $db->createCommand()->dropView('testCreateView')->execute();
1050
        }
1051
1052 4
        if ($db->getSchema()->getTableSchema('testCreateViewTable')) {
1053
            $db->createCommand()->dropTable('testCreateViewTable')->execute();
1054
        }
1055
1056 4
        $db->createCommand()->createTable('testCreateViewTable', [
1057 4
            'id' => Schema::TYPE_PK,
1058
            'bar' => Schema::TYPE_INTEGER,
1059 4
        ])->execute();
1060
1061 4
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 1])->execute();
1062 4
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 6])->execute();
1063 4
        $db->createCommand()->createView('testCreateView', $subquery)->execute();
1064
1065 4
        $records = $db->createCommand('SELECT [[bar]] FROM {{testCreateView}};')->queryAll();
1066
1067 4
        $this->assertEquals([['bar' => 6]], $records);
1068 4
    }
1069
1070 5
    public function testDropView(): void
1071
    {
1072 5
        $db = $this->getConnection();
1073
1074
        /* since it already exists in the fixtures */
1075 5
        $viewName = 'animal_view';
1076
1077 5
        $this->assertNotNull($db->getSchema()->getTableSchema($viewName));
1078
1079 5
        $db->createCommand()->dropView($viewName)->execute();
1080
1081 5
        $this->assertNull($db->getSchema()->getTableSchema($viewName));
1082 5
    }
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 2
    public function testAlterTable(): void
1413
    {
1414 2
        $db = $this->getConnection();
1415
1416 2
        if ($db->getDriverName() === 'sqlite') {
1417
            $this->markTestSkipped('Sqlite does not support alterTable');
1418
        }
1419
1420 2
        if ($db->getSchema()->getTableSchema('testAlterTable') !== null) {
1421
            $db->createCommand()->dropTable('testAlterTable')->execute();
1422
        }
1423
1424 2
        $db->createCommand()->createTable(
1425 2
            'testAlterTable',
1426
            [
1427 2
                'id' => Schema::TYPE_PK,
1428
                'bar' => Schema::TYPE_INTEGER,
1429
            ]
1430 2
        )->execute();
1431
1432 2
        $db->createCommand()->insert('testAlterTable', ['bar' => 1])->execute();
1433
1434 2
        $db->createCommand()->alterColumn('testAlterTable', 'bar', Schema::TYPE_STRING)->execute();
1435
1436 2
        $db->createCommand()->insert('testAlterTable', ['bar' => 'hello'])->execute();
1437
1438 2
        $records = $db->createCommand('SELECT [[id]], [[bar]] FROM {{testAlterTable}}')->queryAll();
1439 2
        $this->assertEquals([
1440 2
            ['id' => 1, 'bar' => 1],
1441
            ['id' => 2, 'bar' => 'hello'],
1442
        ], $records);
1443 2
    }
1444
}
1445