Passed
Branch dev (f56f10)
by Wilmer
04:41 queued 01:34
created

TestCommandTrait::testCreateView()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

114
        $this->/** @scrutinizer ignore-call */ 
115
               assertCount(3, $rows);
Loading history...
115
116
        $row = $rows[2];
117
        $this->assertEquals(3, $row['id']);
118
        $this->assertEquals('user3', $row['name']);
119
120
        $rows = $db->createCommand('SELECT * FROM {{customer}} WHERE [[id]] = 10')->queryAll();
121
        $this->assertEquals([], $rows);
122
123
        /* queryOne */
124
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
125
        $row = $db->createCommand($sql)->queryOne();
126
        $this->assertEquals(1, $row['id']);
127
        $this->assertEquals('user1', $row['name']);
128
129
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
130
        $command = $db->createCommand($sql);
131
        $command->prepare();
132
        $row = $command->queryOne();
133
        $this->assertEquals(1, $row['id']);
134
        $this->assertEquals('user1', $row['name']);
135
136
        $sql = 'SELECT * FROM {{customer}} WHERE [[id]] = 10';
137
        $command = $db->createCommand($sql);
138
        $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

138
        $this->/** @scrutinizer ignore-call */ 
139
               assertFalse($command->queryOne());
Loading history...
139
140
        /* queryColumn */
141
        $sql = 'SELECT * FROM {{customer}}';
142
        $column = $db->createCommand($sql)->queryColumn();
143
        $this->assertEquals(range(1, 3), $column);
144
145
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
146
        $this->assertEquals([], $command->queryColumn());
147
148
        /* queryScalar */
149
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
150
        $this->assertEquals($db->createCommand($sql)->queryScalar(), 1);
151
152
        $sql = 'SELECT [[id]] FROM {{customer}} ORDER BY [[id]]';
153
        $command = $db->createCommand($sql);
154
155
        $command->prepare();
156
        $this->assertEquals(1, $command->queryScalar());
157
158
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
159
        $this->assertFalse($command->queryScalar());
160
161
        $command = $db->createCommand('bad SQL');
162
        $this->expectException(Exception::class);
163
        $command->Query();
164
    }
165
166
    public function testFetchMode(): void
167
    {
168
        $db = $this->getConnection();
169
170
        /* default: FETCH_ASSOC */
171
        $sql = 'SELECT * FROM {{customer}}';
172
173
        $command = $db->createCommand($sql);
174
175
        $result = $command->queryOne();
176
177
        $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

177
        $this->/** @scrutinizer ignore-call */ 
178
               assertTrue(is_array($result) && isset($result['id']));
Loading history...
178
179
        /* FETCH_OBJ, customized via fetchMode property */
180
        $sql = 'SELECT * FROM {{customer}}';
181
182
        $command = $db->createCommand($sql);
183
184
        $command->setFetchMode(PDO::FETCH_OBJ);
185
186
        $result = $command->queryOne();
187
188
        $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

188
        $this->/** @scrutinizer ignore-call */ 
189
               assertIsObject($result);
Loading history...
189
190
        /* FETCH_NUM, customized in query method */
191
        $sql = 'SELECT * FROM {{customer}}';
192
193
        $command = $db->createCommand($sql);
194
195
        $result = $command->queryOne(PDO::FETCH_NUM);
196
197
        $this->assertTrue(is_array($result) && isset($result[0]));
198
    }
199
200
    /**
201
     * @throws Exception|InvalidConfigException|Throwable
202
     */
203
    public function testBatchInsert(): void
204
    {
205
        $db = $this->getConnection();
206
207
        $command = $db->createCommand();
208
        $command->batchInsert(
209
            '{{customer}}',
210
            ['email', 'name', 'address'],
211
            [
212
                ['[email protected]', 't1', 't1 address'],
213
                ['[email protected]', null, false],
214
            ]
215
        );
216
        $this->assertEquals(2, $command->execute());
217
218
        /**
219
         * @link https://github.com/yiisoft/yii2/issues/11693
220
         */
221
        $command = $this->getConnection()->createCommand();
222
        $command->batchInsert(
223
            '{{customer}}',
224
            ['email', 'name', 'address'],
225
            []
226
        );
227
        $this->assertEquals(0, $command->execute());
228
    }
229
230
    /**
231
     * @throws Exception|InvalidConfigException|Throwable
232
     */
233
    public function testBatchInsertWithYield(): void
234
    {
235
        $rows = (static function () {
236
            if (false) {
237
                yield [];
238
            }
239
        })();
240
        $command = $this->getConnection()->createCommand();
241
        $command->batchInsert(
242
            '{{customer}}',
243
            ['email', 'name', 'address'],
244
            $rows
245
        );
246
        $this->assertEquals(0, $command->execute());
247
    }
248
249
    /**
250
     * Test batch insert with different data types.
251
     *
252
     * Ensure double is inserted with `.` decimal separator.
253
     *
254
     * @link https://github.com/yiisoft/yii2/issues/6526
255
     */
256
    public function testBatchInsertDataTypesLocale(): void
257
    {
258
        $locale = setlocale(LC_NUMERIC, 0);
259
260
        if (false === $locale) {
261
            $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

261
            $this->/** @scrutinizer ignore-call */ 
262
                   markTestSkipped('Your platform does not support locales.');
Loading history...
262
        }
263
264
        $db = $this->getConnection(true);
265
266
        try {
267
            /* This one sets decimal mark to comma sign */
268
            setlocale(LC_NUMERIC, 'ru_RU.utf8');
269
            $cols = ['int_col', 'char_col', 'float_col', 'bool_col'];
270
            $data = [
271
                [1, 'A', 9.735, true],
272
                [2, 'B', -2.123, false],
273
                [3, 'C', 2.123, false],
274
            ];
275
276
            /* clear data in "type" table */
277
            $db->createCommand()->delete('type')->execute();
278
279
            /* change, for point oracle. */
280
            if ($db->getDriverName() === 'oci') {
281
                $db->createCommand("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'")->execute();
282
            }
283
284
            /* batch insert on "type" table */
285
            $db->createCommand()->batchInsert('type', $cols, $data)->execute();
286
            $data = $db->createCommand(
287
                'SELECT [[int_col]], [[char_col]], [[float_col]], [[bool_col]] ' .
288
                'FROM {{type}} WHERE [[int_col]] IN (1,2,3) ORDER BY [[int_col]]'
289
            )->queryAll();
290
            $this->assertCount(3, $data);
291
            $this->assertEquals(1, $data[0]['int_col']);
292
            $this->assertEquals(2, $data[1]['int_col']);
293
            $this->assertEquals(3, $data[2]['int_col']);
294
295
            /* rtrim because Postgres padds the column with whitespace */
296
            $this->assertEquals('A', rtrim($data[0]['char_col']));
297
            $this->assertEquals('B', rtrim($data[1]['char_col']));
298
            $this->assertEquals('C', rtrim($data[2]['char_col']));
299
            $this->assertEquals('9.735', $data[0]['float_col']);
300
            $this->assertEquals('-2.123', $data[1]['float_col']);
301
            $this->assertEquals('2.123', $data[2]['float_col']);
302
            $this->assertEquals('1', $data[0]['bool_col']);
303
            $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

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

659
        $this->/** @scrutinizer ignore-call */ 
660
               assertEmpty($schema->getTableForeignKeys($tableName, true));
Loading history...
660
661
        $db->createCommand()->addForeignKey($name, $tableName, ['int1'], $tableName, ['int3'])->execute();
662
        $this->assertEquals(['int1'], $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames());
663
        $this->assertEquals(['int3'], $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames());
664
665
        $db->createCommand()->dropForeignKey($name, $tableName)->execute();
666
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
667
668
        $db->createCommand()->addForeignKey(
669
            $name,
670
            $tableName,
671
            ['int1', 'int2'],
672
            $tableName,
673
            ['int3', 'int4']
674
        )->execute();
675
        $this->assertEquals(
676
            ['int1', 'int2'],
677
            $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames()
678
        );
679
        $this->assertEquals(
680
            ['int3', 'int4'],
681
            $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames()
682
        );
683
    }
684
685
    public function testCreateDropIndex(): void
686
    {
687
        $db = $this->getConnection();
688
689
        $tableName = 'test_idx';
690
        $name = 'test_idx_constraint';
691
692
        $schema = $db->getSchema();
693
694
        if ($schema->getTableSchema($tableName) !== null) {
695
            $db->createCommand()->dropTable($tableName)->execute();
696
        }
697
698
        $db->createCommand()->createTable($tableName, [
699
            'int1' => 'integer not null',
700
            'int2' => 'integer not null',
701
        ])->execute();
702
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
703
704
        $db->createCommand()->createIndex($name, $tableName, ['int1'])->execute();
705
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
706
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
707
708
        $db->createCommand()->dropIndex($name, $tableName)->execute();
709
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
710
711
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'])->execute();
712
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
713
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
714
715
        $db->createCommand()->dropIndex($name, $tableName)->execute();
716
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
717
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
718
719
        $db->createCommand()->createIndex($name, $tableName, ['int1'], true)->execute();
720
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
721
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
722
723
        $db->createCommand()->dropIndex($name, $tableName)->execute();
724
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
725
726
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'], true)->execute();
727
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
728
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
729
    }
730
731
    public function testAddDropUnique(): void
732
    {
733
        $db = $this->getConnection();
734
735
        $tableName = 'test_uq';
736
        $name = 'test_uq_constraint';
737
738
        $schema = $db->getSchema();
739
740
        if ($schema->getTableSchema($tableName) !== null) {
741
            $db->createCommand()->dropTable($tableName)->execute();
742
        }
743
744
        $db->createCommand()->createTable($tableName, [
745
            'int1' => 'integer not null',
746
            'int2' => 'integer not null',
747
        ])->execute();
748
        $this->assertEmpty($schema->getTableUniques($tableName, true));
749
750
        $db->createCommand()->addUnique($name, $tableName, ['int1'])->execute();
751
        $this->assertEquals(['int1'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
752
753
        $db->createCommand()->dropUnique($name, $tableName)->execute();
754
        $this->assertEmpty($schema->getTableUniques($tableName, true));
755
756
        $db->createCommand()->addUnique($name, $tableName, ['int1', 'int2'])->execute();
757
        $this->assertEquals(['int1', 'int2'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
758
    }
759
760
    public function testIntegrityViolation(): void
761
    {
762
        $this->expectException(IntegrityException::class);
763
764
        $db = $this->getConnection();
765
766
        $sql = 'INSERT INTO {{profile}}([[id]], [[description]]) VALUES (123, \'duplicate\')';
767
        $command = $db->createCommand($sql);
768
        $command->execute();
769
        $command->execute();
770
    }
771
772
    public function testLastInsertId(): void
773
    {
774
        $db = $this->getConnection(true);
775
776
        $sql = 'INSERT INTO {{profile}}([[description]]) VALUES (\'non duplicate\')';
777
        $command = $db->createCommand($sql);
778
        $command->execute();
779
        $this->assertEquals(3, $db->getSchema()->getLastInsertID());
780
    }
781
782
    public function testQueryCache(): void
783
    {
784
        $db = $this->getConnection(true);
785
786
        $this->queryCache->setEnable(true);
787
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
788
        $this->assertEquals('user1', $command->bindValue(':id', 1)->queryScalar());
789
790
        $update = $db->createCommand('UPDATE {{customer}} SET [[name]] = :name WHERE [[id]] = :id');
791
        $update->bindValues([':id' => 1, ':name' => 'user11'])->execute();
792
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
793
794
        $db->cache(function (ConnectionInterface $db) use ($command, $update) {
795
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
796
797
            $update->bindValues([':id' => 2, ':name' => 'user22'])->execute();
798
799
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
800
801
            $db->noCache(function () use ($command) {
802
                $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
803
            });
804
805
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
806
        }, 10);
807
808
        $this->queryCache->setEnable(false);
809
810
        $db->cache(function () use ($command, $update) {
811
            $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
812
            $update->bindValues([':id' => 2, ':name' => 'user2'])->execute();
813
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
814
        }, 10);
815
816
        $this->queryCache->setEnable(true);
817
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id')->cache();
818
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
819
820
        $update->bindValues([':id' => 1, ':name' => 'user1'])->execute();
821
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
822
        $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
823
824
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
825
826
        $db->cache(function () use ($command) {
827
            $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
828
            $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
829
        }, 10);
830
    }
831
832
    public function testColumnCase(): void
833
    {
834
        $db = $this->getConnection();
835
836
        $this->assertEquals(PDO::CASE_NATURAL, $db->getSlavePdo()->getAttribute(PDO::ATTR_CASE));
837
838
        $sql = 'SELECT [[customer_id]], [[total]] FROM {{order}}';
839
        $rows = $db->createCommand($sql)->queryAll();
840
        $this->assertTrue(isset($rows[0]));
841
        $this->assertTrue(isset($rows[0]['customer_id']));
842
        $this->assertTrue(isset($rows[0]['total']));
843
844
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
845
        $rows = $db->createCommand($sql)->queryAll();
846
        $this->assertTrue(isset($rows[0]));
847
        $this->assertTrue(isset($rows[0]['customer_id']));
848
        $this->assertTrue(isset($rows[0]['total']));
849
850
        $db->getSlavePdo()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
851
        $rows = $db->createCommand($sql)->queryAll();
852
        $this->assertTrue(isset($rows[0]));
853
        $this->assertTrue(isset($rows[0]['CUSTOMER_ID']));
854
        $this->assertTrue(isset($rows[0]['TOTAL']));
855
    }
856
857
    public function testTransaction(): void
858
    {
859
        $db = $this->getConnection();
860
861
        $this->assertNull($db->getTransaction());
862
863
        $command = $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command transaction')");
864
        $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

864
        $this->/** @scrutinizer ignore-call */ 
865
               invokeMethod($command, 'requireTransaction');
Loading history...
865
        $command->execute();
866
        $this->assertNull($db->getTransaction());
867
        $this->assertEquals(
868
            1,
869
            $db->createCommand(
870
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction'"
871
            )->queryScalar()
872
        );
873
    }
874
875
    public function testRetryHandler(): void
876
    {
877
        $db = $this->getConnection();
878
879
        $this->assertNull($db->getTransaction());
880
881
        $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command retry')")->execute();
882
        $this->assertNull($db->getTransaction());
883
        $this->assertEquals(
884
            1,
885
            $db->createCommand(
886
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command retry'"
887
            )->queryScalar()
888
        );
889
890
        $attempts = null;
891
        $hitHandler = false;
892
        $hitCatch = false;
893
894
        $command = $db->createCommand(
895
            "INSERT INTO {{profile}}([[id]], [[description]]) VALUES(1, 'command retry')"
896
        );
897
        $this->invokeMethod(
898
            $command,
899
            'setRetryHandler',
900
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
901
                $attempts = $attempt;
902
                $hitHandler = true;
903
904
                return $attempt <= 2;
905
            }]
906
        );
907
908
        try {
909
            $command->execute();
910
        } catch (Exception $e) {
911
            $hitCatch = true;
912
            $this->assertInstanceOf(IntegrityException::class, $e);
913
        }
914
915
        $this->assertNull($db->getTransaction());
916
        $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

916
        $this->/** @scrutinizer ignore-call */ 
917
               assertSame(3, $attempts);
Loading history...
917
        $this->assertTrue($hitHandler);
918
        $this->assertTrue($hitCatch);
919
    }
920
921
    public function testCreateView(): void
922
    {
923
        $db = $this->getConnection();
924
925
        $subquery = (new Query($db))
926
            ->select('bar')
927
            ->from('testCreateViewTable')
928
            ->where(['>', 'bar', '5']);
929
930
        if ($db->getSchema()->getTableSchema('testCreateView') !== null) {
931
            $db->createCommand()->dropView('testCreateView')->execute();
932
        }
933
934
        if ($db->getSchema()->getTableSchema('testCreateViewTable')) {
935
            $db->createCommand()->dropTable('testCreateViewTable')->execute();
936
        }
937
938
        $db->createCommand()->createTable('testCreateViewTable', [
939
            'id' => Schema::TYPE_PK,
940
            'bar' => Schema::TYPE_INTEGER,
941
        ])->execute();
942
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 1])->execute();
943
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 6])->execute();
944
        $db->createCommand()->createView('testCreateView', $subquery)->execute();
945
        $records = $db->createCommand('SELECT [[bar]] FROM {{testCreateView}};')->queryAll();
946
        $this->assertEquals([['bar' => 6]], $records);
947
    }
948
949
    public function testDropView(): void
950
    {
951
        $db = $this->getConnection();
952
953
        /* since it already exists in the fixtures */
954
        $viewName = 'animal_view';
955
956
        $this->assertNotNull($db->getSchema()->getTableSchema($viewName));
957
958
        $db->createCommand()->dropView($viewName)->execute();
959
        $this->assertNull($db->getSchema()->getTableSchema($viewName));
960
    }
961
962
    public function batchInsertSqlProviderTrait(): array
963
    {
964
        return [
965
            'issue11242' => [
966
                'type',
967
                ['int_col', 'float_col', 'char_col'],
968
                [['', '', 'Kyiv {{city}}, Ukraine']],
969
                /**
970
                 * {@see https://github.com/yiisoft/yii2/issues/11242}
971
                 *
972
                 * Make sure curly bracelets (`{{..}}`) in values will not be escaped
973
                 */
974
                'expected' => 'INSERT INTO `type` (`int_col`, `float_col`, `char_col`)'
975
                    . " VALUES (NULL, NULL, 'Kyiv {{city}}, Ukraine')",
976
            ],
977
            'wrongBehavior' => [
978
                '{{%type}}',
979
                ['{{%type}}.[[int_col]]', '[[float_col]]', 'char_col'],
980
                [['', '', 'Kyiv {{city}}, Ukraine']],
981
                /**
982
                 * Test covers potentially wrong behavior and marks it as expected!.
983
                 *
984
                 * In case table name or table column is passed with curly or square bracelets, QueryBuilder can not
985
                 * determine the table schema and typecast values properly.
986
                 * TODO: make it work. Impossible without BC breaking for public methods.
987
                 */
988
                'expected' => 'INSERT INTO `type` (`type`.`int_col`, `float_col`, `char_col`)'
989
                    . " VALUES ('', '', 'Kyiv {{city}}, Ukraine')",
990
            ],
991
            'batchInsert binds params from expression' => [
992
                '{{%type}}',
993
                ['int_col'],
994
                /**
995
                 * This example is completely useless. This feature of batchInsert is intended to be used with complex
996
                 * expression objects, such as JsonExpression.
997
                 */
998
                [[new Expression(':qp1', [':qp1' => 42])]],
999
                'expected' => 'INSERT INTO `type` (`int_col`) VALUES (:qp1)',
1000
                'expectedParams' => [':qp1' => 42],
1001
            ],
1002
        ];
1003
    }
1004
1005
    public function bindParamsNonWhereProviderTrait(): array
1006
    {
1007
        return [
1008
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email GROUP BY SUBSTR(name, :len)'],
1009
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email ORDER BY SUBSTR(name, :len)'],
1010
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email'],
1011
        ];
1012
    }
1013
1014
    public function getRawSqlProviderTrait(): array
1015
    {
1016
        return [
1017
            [
1018
                'SELECT * FROM customer WHERE id = :id',
1019
                [':id' => 1],
1020
                'SELECT * FROM customer WHERE id = 1',
1021
            ],
1022
            [
1023
                'SELECT * FROM customer WHERE id = :id',
1024
                ['id' => 1],
1025
                'SELECT * FROM customer WHERE id = 1',
1026
            ],
1027
            [
1028
                'SELECT * FROM customer WHERE id = :id',
1029
                ['id' => null],
1030
                'SELECT * FROM customer WHERE id = NULL',
1031
            ],
1032
            [
1033
                'SELECT * FROM customer WHERE id = :base OR id = :basePrefix',
1034
                [
1035
                    'base' => 1,
1036
                    'basePrefix' => 2,
1037
                ],
1038
                'SELECT * FROM customer WHERE id = 1 OR id = 2',
1039
            ],
1040
            /**
1041
             * {@see https://github.com/yiisoft/yii2/issues/9268}
1042
             */
1043
            [
1044
                'SELECT * FROM customer WHERE active = :active',
1045
                [':active' => false],
1046
                'SELECT * FROM customer WHERE active = FALSE',
1047
            ],
1048
            /**
1049
             * {@see https://github.com/yiisoft/yii2/issues/15122}
1050
             */
1051
            [
1052
                'SELECT * FROM customer WHERE id IN (:ids)',
1053
                [':ids' => new Expression(implode(', ', [1, 2]))],
1054
                'SELECT * FROM customer WHERE id IN (1, 2)',
1055
            ],
1056
        ];
1057
    }
1058
1059
    public function invalidSelectColumnsProviderTrait(): array
1060
    {
1061
        return [
1062
            [[]],
1063
            ['*'],
1064
            [['*']],
1065
        ];
1066
    }
1067
1068
    public function upsertProviderTrait(): array
1069
    {
1070
        return [
1071
            'regular values' => [
1072
                [
1073
                    'params' => [
1074
                        'T_upsert',
1075
                        [
1076
                            'email' => '[email protected]',
1077
                            'address' => 'Earth',
1078
                            'status' => 3,
1079
                        ],
1080
                    ],
1081
                ],
1082
                [
1083
                    'params' => [
1084
                        'T_upsert',
1085
                        [
1086
                            'email' => '[email protected]',
1087
                            'address' => 'Universe',
1088
                            'status' => 1,
1089
                        ],
1090
                    ],
1091
                ],
1092
            ],
1093
            'regular values with update part' => [
1094
                [
1095
                    'params' => [
1096
                        'T_upsert',
1097
                        [
1098
                            'email' => '[email protected]',
1099
                            'address' => 'Earth',
1100
                            'status' => 3,
1101
                        ],
1102
                        [
1103
                            'address' => 'Moon',
1104
                            'status' => 2,
1105
                        ],
1106
                    ],
1107
                ],
1108
                [
1109
                    'params' => [
1110
                        'T_upsert',
1111
                        [
1112
                            'email' => '[email protected]',
1113
                            'address' => 'Universe',
1114
                            'status' => 1,
1115
                        ],
1116
                        [
1117
                            'address' => 'Moon',
1118
                            'status' => 2,
1119
                        ],
1120
                    ],
1121
                    'expected' => [
1122
                        'email' => '[email protected]',
1123
                        'address' => 'Moon',
1124
                        'status' => 2,
1125
                    ],
1126
                ],
1127
            ],
1128
            'regular values without update part' => [
1129
                [
1130
                    'params' => [
1131
                        'T_upsert',
1132
                        [
1133
                            'email' => '[email protected]',
1134
                            'address' => 'Earth',
1135
                            'status' => 3,
1136
                        ],
1137
                        false,
1138
                    ],
1139
                ],
1140
                [
1141
                    'params' => [
1142
                        'T_upsert',
1143
                        [
1144
                            'email' => '[email protected]',
1145
                            'address' => 'Universe',
1146
                            'status' => 1,
1147
                        ],
1148
                        false,
1149
                    ],
1150
                    'expected' => [
1151
                        'email' => '[email protected]',
1152
                        'address' => 'Earth',
1153
                        'status' => 3,
1154
                    ],
1155
                ],
1156
            ],
1157
            'query' => [
1158
                [
1159
                    'params' => [
1160
                        'T_upsert',
1161
                        (new Query($this->getConnection()))
1162
                            ->select([
1163
                                'email',
1164
                                'address',
1165
                                'status' => new Expression('1'),
1166
                            ])
1167
                            ->from('customer')
1168
                            ->where(['name' => 'user1'])
1169
                            ->limit(1),
1170
                    ],
1171
                    'expected' => [
1172
                        'email' => '[email protected]',
1173
                        'address' => 'address1',
1174
                        'status' => 1,
1175
                    ],
1176
                ],
1177
                [
1178
                    'params' => [
1179
                        'T_upsert',
1180
                        (new Query($this->getConnection()))
1181
                            ->select([
1182
                                'email',
1183
                                'address',
1184
                                'status' => new Expression('2'),
1185
                            ])
1186
                            ->from('customer')
1187
                            ->where(['name' => 'user1'])
1188
                            ->limit(1),
1189
                    ],
1190
                    'expected' => [
1191
                        'email' => '[email protected]',
1192
                        'address' => 'address1',
1193
                        'status' => 2,
1194
                    ],
1195
                ],
1196
            ],
1197
            'query with update part' => [
1198
                [
1199
                    'params' => [
1200
                        'T_upsert',
1201
                        (new Query($this->getConnection()))
1202
                            ->select([
1203
                                'email',
1204
                                'address',
1205
                                'status' => new Expression('1'),
1206
                            ])
1207
                            ->from('customer')
1208
                            ->where(['name' => 'user1'])
1209
                            ->limit(1),
1210
                        [
1211
                            'address' => 'Moon',
1212
                            'status' => 2,
1213
                        ],
1214
                    ],
1215
                    'expected' => [
1216
                        'email' => '[email protected]',
1217
                        'address' => 'address1',
1218
                        'status' => 1,
1219
                    ],
1220
                ],
1221
                [
1222
                    'params' => [
1223
                        'T_upsert',
1224
                        (new Query($this->getConnection()))
1225
                            ->select([
1226
                                'email',
1227
                                'address',
1228
                                'status' => new Expression('3'),
1229
                            ])
1230
                            ->from('customer')
1231
                            ->where(['name' => 'user1'])
1232
                            ->limit(1),
1233
                        [
1234
                            'address' => 'Moon',
1235
                            'status' => 2,
1236
                        ],
1237
                    ],
1238
                    'expected' => [
1239
                        'email' => '[email protected]',
1240
                        'address' => 'Moon',
1241
                        'status' => 2,
1242
                    ],
1243
                ],
1244
            ],
1245
            'query without update part' => [
1246
                [
1247
                    'params' => [
1248
                        'T_upsert',
1249
                        (new Query($this->getConnection()))
1250
                            ->select([
1251
                                'email',
1252
                                'address',
1253
                                'status' => new Expression('1'),
1254
                            ])
1255
                            ->from('customer')
1256
                            ->where(['name' => 'user1'])
1257
                            ->limit(1),
1258
                        false,
1259
                    ],
1260
                    'expected' => [
1261
                        'email' => '[email protected]',
1262
                        'address' => 'address1',
1263
                        'status' => 1,
1264
                    ],
1265
                ],
1266
                [
1267
                    'params' => [
1268
                        'T_upsert',
1269
                        (new Query($this->getConnection()))
1270
                            ->select([
1271
                                'email',
1272
                                'address',
1273
                                'status' => new Expression('2'),
1274
                            ])
1275
                            ->from('customer')
1276
                            ->where(['name' => 'user1'])
1277
                            ->limit(1),
1278
                        false,
1279
                    ],
1280
                    'expected' => [
1281
                        'email' => '[email protected]',
1282
                        'address' => 'address1',
1283
                        'status' => 1,
1284
                    ],
1285
                ],
1286
            ],
1287
        ];
1288
    }
1289
1290
    public function testAlterTable(): void
1291
    {
1292
        $db = $this->getConnection();
1293
1294
        if ($db->getDriverName() === 'sqlite') {
1295
            $this->markTestSkipped('Sqlite does not support alterTable');
1296
        }
1297
1298
        if ($db->getSchema()->getTableSchema('testAlterTable', true) !== null) {
1299
            $db->createCommand()->dropTable('testAlterTable')->execute();
1300
        }
1301
1302
        $db->createCommand()->createTable(
1303
            'testAlterTable',
1304
            [
1305
                'id' => Schema::TYPE_PK,
1306
                'bar' => Schema::TYPE_INTEGER,
1307
            ]
1308
        )->execute();
1309
1310
        $db->createCommand()->insert('testAlterTable', ['bar' => 1])->execute();
1311
        $db->createCommand()->alterColumn('testAlterTable', 'bar', Schema::TYPE_STRING)->execute();
1312
        $db->createCommand()->insert('testAlterTable', ['bar' => 'hello'])->execute();
1313
1314
        $records = $db->createCommand('SELECT [[id]], [[bar]] FROM {{testAlterTable}}')->queryAll();
1315
        $this->assertEquals([
1316
            ['id' => 1, 'bar' => 1],
1317
            ['id' => 2, 'bar' => 'hello'],
1318
        ], $records);
1319
    }
1320
}
1321