Passed
Pull Request — master (#163)
by Wilmer
12:24
created

TestCommandTrait::testInsert()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 16
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 26
rs 9.7333
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
    public function testConstruct(): void
30
    {
31
        $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
        $command = $db->createCommand();
35
36
        $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
        $sql = 'SELECT * FROM customer';
40
41
        $command = $db->createCommand($sql);
42
43
        $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
    }
45
46
    public function testGetSetSql(): void
47
    {
48
        $db = $this->getConnection();
49
50
        $sql = 'SELECT * FROM customer';
51
52
        $command = $db->createCommand($sql);
53
54
        $this->assertEquals($sql, $command->getSql());
55
56
        $sql2 = 'SELECT * FROM order';
57
58
        $command->setSql($sql2);
59
60
        $this->assertEquals($sql2, $command->getSql());
61
    }
62
63
    public function testPrepareCancel(): void
64
    {
65
        $db = $this->getConnection();
66
67
        $command = $db->createCommand('SELECT * FROM {{customer}}');
68
69
        $this->assertNull($command->getPdoStatement());
70
71
        $command->prepare();
72
73
        $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
        $command->cancel();
76
77
        $this->assertNull($command->getPdoStatement());
78
    }
79
80
    public function testExecute(): void
81
    {
82
        $db = $this->getConnection(true);
83
84
        $sql = 'INSERT INTO {{customer}}([[email]], [[name]], [[address]])'
85
            . ' VALUES (\'[email protected]\', \'user4\', \'address4\')';
86
87
        $command = $db->createCommand($sql);
88
89
        $this->assertEquals(1, $command->execute());
90
91
        $sql = 'SELECT COUNT(*) FROM {{customer}} WHERE [[name]] = \'user4\'';
92
93
        $command = $db->createCommand($sql);
94
95
        $this->assertEquals(1, $command->queryScalar());
96
97
        $command = $db->createCommand('bad SQL');
98
99
        $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
        $command->execute();
102
    }
103
104
    public function testQuery(): void
105
    {
106
        $db = $this->getConnection(true);
107
108
        /* query */
109
        $sql = 'SELECT * FROM {{customer}}';
110
111
        $reader = $db->createCommand($sql)->Query();
112
113
        $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
        $rows = $db->createCommand('SELECT * FROM {{customer}}')->queryAll();
117
118
        $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
        $row = $rows[2];
121
122
        $this->assertEquals(3, $row['id']);
123
        $this->assertEquals('user3', $row['name']);
124
125
        $rows = $db->createCommand('SELECT * FROM {{customer}} WHERE [[id]] = 10')->queryAll();
126
127
        $this->assertEquals([], $rows);
128
129
        /* queryOne */
130
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
131
132
        $row = $db->createCommand($sql)->queryOne();
133
134
        $this->assertEquals(1, $row['id']);
135
        $this->assertEquals('user1', $row['name']);
136
137
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
138
139
        $command = $db->createCommand($sql);
140
141
        $command->prepare();
142
143
        $row = $command->queryOne();
144
145
        $this->assertEquals(1, $row['id']);
146
        $this->assertEquals('user1', $row['name']);
147
148
        $sql = 'SELECT * FROM {{customer}} WHERE [[id]] = 10';
149
150
        $command = $db->createCommand($sql);
151
152
        $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
        $sql = 'SELECT * FROM {{customer}}';
156
157
        $column = $db->createCommand($sql)->queryColumn();
158
159
        $this->assertEquals(range(1, 3), $column);
160
161
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
162
163
        $this->assertEquals([], $command->queryColumn());
164
165
        /* queryScalar */
166
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
167
168
        $this->assertEquals($db->createCommand($sql)->queryScalar(), 1);
169
170
        $sql = 'SELECT [[id]] FROM {{customer}} ORDER BY [[id]]';
171
172
        $command = $db->createCommand($sql);
173
174
        $command->prepare();
175
176
        $this->assertEquals(1, $command->queryScalar());
177
178
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
179
180
        $this->assertFalse($command->queryScalar());
181
182
        $command = $db->createCommand('bad SQL');
183
184
        $this->expectException(Exception::class);
185
186
        $command->Query();
187
    }
188
189
    public function testFetchMode(): void
190
    {
191
        $db = $this->getConnection();
192
193
        /* default: FETCH_ASSOC */
194
        $sql = 'SELECT * FROM {{customer}}';
195
196
        $command = $db->createCommand($sql);
197
198
        $result = $command->queryOne();
199
200
        $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
        $sql = 'SELECT * FROM {{customer}}';
204
205
        $command = $db->createCommand($sql);
206
207
        $command->setFetchMode(PDO::FETCH_OBJ);
208
209
        $result = $command->queryOne();
210
211
        $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
        $sql = 'SELECT * FROM {{customer}}';
215
216
        $command = $db->createCommand($sql);
217
218
        $result = $command->queryOne(PDO::FETCH_NUM);
219
220
        $this->assertTrue(is_array($result) && isset($result[0]));
221
    }
222
223
    public function testBatchInsert(): void
224
    {
225
        $db = $this->getConnection();
226
227
        $command = $db->createCommand();
228
229
        $command->batchInsert(
230
            '{{customer}}',
231
            ['email', 'name', 'address'],
232
            [
233
                ['[email protected]', 't1', 't1 address'],
234
                ['[email protected]', null, false],
235
            ]
236
        );
237
238
        $this->assertEquals(2, $command->execute());
239
240
        /**
241
         * {@see https://github.com/yiisoft/yii2/issues/11693}
242
         */
243
        $command = $this->getConnection()->createCommand();
244
245
        $command->batchInsert(
246
            '{{customer}}',
247
            ['email', 'name', 'address'],
248
            []
249
        );
250
251
        $this->assertEquals(0, $command->execute());
252
    }
253
254
    public function testBatchInsertWithYield(): void
255
    {
256
        $rows = call_user_func(static function () {
257
            if (false) {
258
                yield [];
259
            }
260
        });
261
262
        $command = $this->getConnection()->createCommand();
263
264
        $command->batchInsert(
265
            '{{customer}}',
266
            ['email', 'name', 'address'],
267
            $rows
268
        );
269
270
        $this->assertEquals(0, $command->execute());
271
    }
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
    public function testBatchInsertDataTypesLocale(): void
281
    {
282
        $locale = setlocale(LC_NUMERIC, 0);
283
284
        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
        $db = $this->getConnection();
289
290
        try {
291
            /* This one sets decimal mark to comma sign */
292
            setlocale(LC_NUMERIC, 'ru_RU.utf8');
293
294
            $cols = ['int_col', 'char_col', 'float_col', 'bool_col'];
295
296
            $data = [
297
                [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
            $db->createCommand()->delete('type')->execute();
304
305
            /* batch insert on "type" table */
306
            $db->createCommand()->batchInsert('type', $cols, $data)->execute();
307
308
            $data = $db->createCommand(
309
                'SELECT int_col, char_col, float_col, bool_col FROM {{type}} WHERE [[int_col]] IN (1,2,3)
310
                ORDER BY [[int_col]];'
311
            )->queryAll();
312
313
            $this->assertCount(3, $data);
314
            $this->assertEquals(1, $data[0]['int_col']);
315
            $this->assertEquals(2, $data[1]['int_col']);
316
            $this->assertEquals(3, $data[2]['int_col']);
317
318
            /* rtrim because Postgres padds the column with whitespace */
319
            $this->assertEquals('A', rtrim($data[0]['char_col']));
320
            $this->assertEquals('B', rtrim($data[1]['char_col']));
321
            $this->assertEquals('C', rtrim($data[2]['char_col']));
322
            $this->assertEquals('9.735', $data[0]['float_col']);
323
            $this->assertEquals('-2.123', $data[1]['float_col']);
324
            $this->assertEquals('2.123', $data[2]['float_col']);
325
            $this->assertEquals('1', $data[0]['bool_col']);
326
            $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

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

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

935
            $db->/** @scrutinizer ignore-call */ 
936
                 noCache(function () use ($command) {
Loading history...
936
                $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
937
            });
938
939
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
940
        }, 10);
941
942
        $db->setEnableQueryCache(false);
943
944
        $db->cache(function () use ($command, $update) {
945
            $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
946
            $update->bindValues([':id' => 2, ':name' => 'user2'])->execute();
947
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
948
        }, 10);
949
950
        $db->setEnableQueryCache(true);
951
952
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id')->cache();
953
954
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
955
956
        $update->bindValues([':id' => 1, ':name' => 'user1'])->execute();
957
958
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
959
        $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
960
961
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
962
963
        $db->cache(function () use ($command) {
964
            $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
965
            $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
966
        }, 10);
967
    }
968
969
    public function testColumnCase(): void
970
    {
971
        $db = $this->getConnection();
972
973
        $this->assertEquals(PDO::CASE_NATURAL, $db->getSlavePdo()->getAttribute(PDO::ATTR_CASE));
974
975
        $sql = 'SELECT [[customer_id]], [[total]] FROM {{order}}';
976
977
        $rows = $db->createCommand($sql)->queryAll();
978
979
        $this->assertTrue(isset($rows[0]));
980
        $this->assertTrue(isset($rows[0]['customer_id']));
981
        $this->assertTrue(isset($rows[0]['total']));
982
983
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
984
985
        $rows = $db->createCommand($sql)->queryAll();
986
987
        $this->assertTrue(isset($rows[0]));
988
        $this->assertTrue(isset($rows[0]['customer_id']));
989
        $this->assertTrue(isset($rows[0]['total']));
990
991
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
992
993
        $rows = $db->createCommand($sql)->queryAll();
994
995
        $this->assertTrue(isset($rows[0]));
996
        $this->assertTrue(isset($rows[0]['CUSTOMER_ID']));
997
        $this->assertTrue(isset($rows[0]['TOTAL']));
998
    }
999
1000
    public function testTransaction(): void
1001
    {
1002
        $db = $this->getConnection();
1003
1004
        $this->assertNull($db->getTransaction());
1005
1006
        $command = $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command transaction')");
1007
1008
        $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

1008
        $this->/** @scrutinizer ignore-call */ 
1009
               invokeMethod($command, 'requireTransaction');
Loading history...
1009
1010
        $command->execute();
1011
1012
        $this->assertNull($db->getTransaction());
1013
        $this->assertEquals(
1014
            1,
1015
            $db->createCommand(
1016
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction'"
1017
            )->queryScalar()
1018
        );
1019
    }
1020
1021
    public function testRetryHandler(): void
1022
    {
1023
        $db = $this->getConnection();
1024
1025
        $this->assertNull($db->getTransaction());
1026
1027
        $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command retry')")->execute();
1028
1029
        $this->assertNull($db->getTransaction());
1030
        $this->assertEquals(
1031
            1,
1032
            $db->createCommand(
1033
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command retry'"
1034
            )->queryScalar()
1035
        );
1036
1037
        $attempts = null;
1038
        $hitHandler = false;
1039
        $hitCatch = false;
1040
1041
        $command = $db->createCommand(
1042
            "INSERT INTO {{profile}}([[id]], [[description]]) VALUES(1, 'command retry')"
1043
        );
1044
1045
        $this->invokeMethod(
1046
            $command,
1047
            'setRetryHandler',
1048
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
1049
                $attempts = $attempt;
1050
                $hitHandler = true;
1051
1052
                return $attempt <= 2;
1053
            }]
1054
        );
1055
1056
        try {
1057
            $command->execute();
1058
        } catch (Exception $e) {
1059
            $hitCatch = true;
1060
            $this->assertInstanceOf(IntegrityException::class, $e);
1061
        }
1062
1063
        $this->assertNull($db->getTransaction());
1064
        $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

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