Passed
Branch psalm-3 (d5d890)
by Wilmer
02:56
created

TestCommandTrait::testConstruct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 12
rs 10
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
    public function testInsertEx(): void
341
    {
342
        $db = $this->getConnection();
343
344
        $result = $db->createCommand()->insertEx(
345
            'customer',
346
            [
347
                'name' => 'testParams',
348
                'email' => '[email protected]',
349
                'address' => '1',
350
            ]
351
        );
352
353
        $this->assertIsArray($result);
0 ignored issues
show
Bug introduced by
It seems like assertIsArray() 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

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

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

881
        $this->/** @scrutinizer ignore-call */ 
882
               invokeMethod($command, 'requireTransaction');
Loading history...
882
        $command->execute();
883
        $this->assertNull($db->getTransaction());
884
        $this->assertEquals(
885
            1,
886
            $db->createCommand(
887
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction'"
888
            )->queryScalar()
889
        );
890
    }
891
892
    public function testRetryHandler(): void
893
    {
894
        $db = $this->getConnection();
895
896
        $this->assertNull($db->getTransaction());
897
898
        $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command retry')")->execute();
899
        $this->assertNull($db->getTransaction());
900
        $this->assertEquals(
901
            1,
902
            $db->createCommand(
903
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command retry'"
904
            )->queryScalar()
905
        );
906
907
        $attempts = null;
908
        $hitHandler = false;
909
        $hitCatch = false;
910
911
        $command = $db->createCommand(
912
            "INSERT INTO {{profile}}([[id]], [[description]]) VALUES(1, 'command retry')"
913
        );
914
        $this->invokeMethod(
915
            $command,
916
            'setRetryHandler',
917
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
918
                $attempts = $attempt;
919
                $hitHandler = true;
920
921
                return $attempt <= 2;
922
            }]
923
        );
924
925
        try {
926
            $command->execute();
927
        } catch (Exception $e) {
928
            $hitCatch = true;
929
            $this->assertInstanceOf(IntegrityException::class, $e);
930
        }
931
932
        $this->assertNull($db->getTransaction());
933
        $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

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