Passed
Pull Request — master (#163)
by Wilmer
11:48
created

TestCommandTrait   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 1376
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 741
c 1
b 0
f 0
dl 0
loc 1376
rs 5.3789
wmc 56

36 Methods

Rating   Name   Duplication   Size   Complexity  
A testsInsertQueryAsColumnValue() 0 46 2
A testBatchInsertDataTypesLocale() 0 59 4
A testNoTablenameReplacement() 0 37 2
A testGetSetSql() 0 15 1
A testInsertSelect() 0 56 1
A testExecute() 0 22 1
A testRenameTable() 0 18 2
A testInsertExpression() 0 37 5
A testConstruct() 0 15 1
A testDropTable() 0 11 1
A testPrepareCancel() 0 15 1
A testQuery() 0 83 1
A testTruncateTable() 0 13 1
A performAndCompareUpsertResult() 0 21 1
A testFetchMode() 0 32 3
A testInsertSelectAlias() 0 56 1
A testBatchInsert() 0 29 1
A testInsert() 0 26 1
A testBatchInsertWithYield() 0 17 2
A testCreateTable() 0 20 2
A getRawSqlProviderTrait() 0 41 1
A testAddDropForeignKey() 0 48 2
A invalidSelectColumnsProviderTrait() 0 6 1
A testCreateDropIndex() 0 52 2
A testCreateView() 0 29 3
A testLastInsertId() 0 11 1
B upsertProviderTrait() 0 216 1
A testIntegrityViolation() 0 12 1
A testRetryHandler() 0 46 2
A testAddDropUnique() 0 31 2
A batchInsertSqlProviderTrait() 0 39 1
A testTransaction() 0 18 1
A testDropView() 0 12 1
A testQueryCache() 0 56 1
A testColumnCase() 0 29 1
A bindParamsNonWhereProviderTrait() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like TestCommandTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TestCommandTrait, and based on these observations, apply Extract Interface, too.

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 testAddDropForeignKey(): void
714
    {
715
        $db = $this->getConnection();
716
717
        $tableName = 'test_fk';
718
        $name = 'test_fk_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 unique',
728
            'int2' => 'integer not null unique',
729
            'int3' => 'integer not null unique',
730
            'int4' => 'integer not null unique',
731
            'unique ([[int1]], [[int2]])',
732
            'unique ([[int3]], [[int4]])',
733
        ])->execute();
734
735
        $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

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

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

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

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