Passed
Pull Request — master (#331)
by Wilmer
12:25 queued 10:18
created

TestCommandTrait::testColumnCase()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 17
nc 1
nop 0
dl 0
loc 23
rs 9.7
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\Command\Param;
10
use Yiisoft\Db\Command\ParamInterface;
11
use Yiisoft\Db\Connection\ConnectionInterface;
12
use Yiisoft\Db\Exception\Exception;
13
use Yiisoft\Db\Exception\IntegrityException;
14
use Yiisoft\Db\Exception\InvalidCallException;
15
use Yiisoft\Db\Exception\InvalidConfigException;
16
use Yiisoft\Db\Exception\InvalidParamException;
17
use Yiisoft\Db\Expression\Expression;
18
use Yiisoft\Db\Query\Data\DataReaderInterface;
19
use Yiisoft\Db\QueryBuilder\QueryBuilder;
20
use Yiisoft\Db\Query\Data\DataReader;
21
use Yiisoft\Db\Query\Query;
22
use Yiisoft\Db\Schema\Schema;
23
24
use function call_user_func_array;
25
use function date;
26
use function is_array;
27
use function range;
28
use function rtrim;
29
use function setlocale;
30
use function time;
31
32
trait TestCommandTrait
33
{
34
    /**
35
     * @throws Exception|InvalidConfigException
36
     */
37
    public function testConstruct(): void
38
    {
39
        $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

39
        /** @scrutinizer ignore-call */ 
40
        $db = $this->getConnection();
Loading history...
40
41
        /* null */
42
        $command = $db->createCommand();
43
        $this->assertEmpty($command->getSql());
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

43
        $this->/** @scrutinizer ignore-call */ 
44
               assertEmpty($command->getSql());
Loading history...
44
45
        /* string */
46
        $sql = 'SELECT * FROM customer';
47
        $command = $db->createCommand($sql);
48
        $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

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

75
        $this->/** @scrutinizer ignore-call */ 
76
               assertNull($command->getPdoStatement());
Loading history...
76
77
        $command->prepare();
78
        $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

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

102
        $this->/** @scrutinizer ignore-call */ 
103
               expectException(Exception::class);
Loading history...
103
        $command->execute();
104
    }
105
106
    public function testDataReaderCreationException(): void
107
    {
108
        $db = $this->getConnection(true);
109
110
        $this->expectException(InvalidParamException::class);
111
        $this->expectExceptionMessage('The PDOStatement cannot be null.');
0 ignored issues
show
Bug introduced by
It seems like expectExceptionMessage() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

111
        $this->/** @scrutinizer ignore-call */ 
112
               expectExceptionMessage('The PDOStatement cannot be null.');
Loading history...
112
113
        $sql = 'SELECT * FROM {{customer}}';
114
        new DataReader($db->createCommand($sql));
115
    }
116
117
    public function testDataReaderRewindException(): void
118
    {
119
        $db = $this->getConnection(true);
120
121
        $this->expectException(InvalidCallException::class);
122
        $this->expectExceptionMessage('DataReader cannot rewind. It is a forward-only reader.');
123
124
        $sql = 'SELECT * FROM {{customer}}';
125
        $reader = $db->createCommand($sql)->query();
126
        $reader->next();
127
        $reader->rewind();
128
    }
129
130
    /**
131
     * @throws Exception|InvalidConfigException|Throwable
132
     */
133
    public function testQuery(): void
134
    {
135
        $db = $this->getConnection(true);
136
137
        $sql = 'SELECT * FROM {{customer}}';
138
        $reader = $db->createCommand($sql)->query();
139
        $this->assertInstanceOf(DataReaderInterface::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

139
        $this->/** @scrutinizer ignore-call */ 
140
               assertInstanceOf(DataReaderInterface::class, $reader);
Loading history...
140
141
        // Next line is commented by reason:: For sqlite & pgsql result may be incorrect
142
        // $this->assertEquals(3, $reader->count());
143
        $this->assertIsInt($reader->count());
0 ignored issues
show
Bug introduced by
It seems like assertIsInt() 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

143
        $this->/** @scrutinizer ignore-call */ 
144
               assertIsInt($reader->count());
Loading history...
144
        foreach ($reader as $row) {
145
            $this->assertIsArray($row);
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

145
            $this->/** @scrutinizer ignore-call */ 
146
                   assertIsArray($row);
Loading history...
146
            $this->assertTrue(count($row) >= 6);
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

146
            $this->/** @scrutinizer ignore-call */ 
147
                   assertTrue(count($row) >= 6);
Loading history...
147
        }
148
149
        $command = $db->createCommand('bad SQL');
150
        $this->expectException(Exception::class);
151
        $command->query();
152
    }
153
154
    public function testQyeryScalar(): void
155
    {
156
        $db = $this->getConnection();
157
158
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
159
        $this->assertEquals($db->createCommand($sql)->queryScalar(), 1);
160
161
        $sql = 'SELECT [[id]] FROM {{customer}} ORDER BY [[id]]';
162
        $command = $db->createCommand($sql);
163
164
        $command->prepare();
165
        $this->assertEquals(1, $command->queryScalar());
166
167
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
168
        $this->assertFalse($command->queryScalar());
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

168
        $this->/** @scrutinizer ignore-call */ 
169
               assertFalse($command->queryScalar());
Loading history...
169
    }
170
171
    public function testQueryOne(): void
172
    {
173
        $db = $this->getConnection();
174
175
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
176
        $row = $db->createCommand($sql)->queryOne();
177
        $this->assertIsArray($row);
178
        $this->assertEquals(1, $row['id']);
179
        $this->assertEquals('user1', $row['name']);
180
181
        $sql = 'SELECT * FROM {{customer}} ORDER BY [[id]]';
182
        $command = $db->createCommand($sql);
183
        $command->prepare();
184
        $row = $command->queryOne();
185
186
        $this->assertIsArray($row);
187
        $this->assertEquals(1, $row['id']);
188
        $this->assertEquals('user1', $row['name']);
189
190
        $sql = 'SELECT * FROM {{customer}} WHERE [[id]] = 10';
191
        $command = $db->createCommand($sql);
192
        $this->assertFalse($command->queryOne());
193
    }
194
195
    public function testQueryColumn(): void
196
    {
197
        $db = $this->getConnection();
198
199
        $sql = 'SELECT * FROM {{customer}}';
200
        $column = $db->createCommand($sql)->queryColumn();
201
        $this->assertEquals(range(1, 3), $column);
202
        $this->assertIsArray($column);
203
204
        $command = $db->createCommand('SELECT [[id]] FROM {{customer}} WHERE [[id]] = 10');
205
        $this->assertFalse($command->queryColumn());
206
    }
207
208
    public function testQueryAll(): void
209
    {
210
        $db = $this->getConnection();
211
212
        $rows = $db->createCommand('SELECT [[id]],[[name]] FROM {{customer}}')->queryAll();
213
        /** @psalm-suppress RedundantCondition */
214
        $this->assertIsArray($rows);
215
        $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

215
        $this->/** @scrutinizer ignore-call */ 
216
               assertCount(3, $rows);
Loading history...
216
217
        $row = $rows[2];
218
        $this->assertEquals(3, $row['id']);
219
        $this->assertEquals('user3', $row['name']);
220
        $this->assertTrue(is_array($rows) && count($rows)>1 && count($rows[0]) === 2);
221
222
        $rows = $db->createCommand('SELECT * FROM {{customer}} WHERE [[id]] = 10')->queryAll();
223
        $this->assertEquals([], $rows);
224
    }
225
226
    /**
227
     * @throws Exception|InvalidConfigException|Throwable
228
     */
229
    public function testBatchInsert(): void
230
    {
231
        $db = $this->getConnection();
232
233
        $command = $db->createCommand();
234
        $command->batchInsert(
235
            '{{customer}}',
236
            ['email', 'name', 'address'],
237
            [
238
                ['[email protected]', 't1', 't1 address'],
239
                ['[email protected]', null, false],
240
            ]
241
        );
242
        $this->assertEquals(2, $command->execute());
243
244
        /**
245
         * @link https://github.com/yiisoft/yii2/issues/11693
246
         */
247
        $command = $db->createCommand();
248
        $command->batchInsert(
249
            '{{customer}}',
250
            ['email', 'name', 'address'],
251
            []
252
        );
253
        $this->assertEquals(0, $command->execute());
254
255
        $command = $db->createCommand();
256
        $command->batchInsert(
257
            '{{customer}}',
258
            ['email', 'name', 'address'],
259
            [
260
                ['[email protected]', 'test_name', 'test_address'],
261
            ]
262
        );
263
        $this->assertEquals(1, $command->execute());
264
    }
265
266
    /**
267
     * @throws Exception|InvalidConfigException|Throwable
268
     */
269
    public function testBatchInsertFails(): void
270
    {
271
        $db = $this->getConnection(true);
272
273
        $command = $db->createCommand();
274
        $command->batchInsert(
275
            '{{customer}}',
276
            ['email', 'name', 'address'],
277
            [
278
                ['[email protected]', 'test_name', 'test_address'],
279
            ]
280
        );
281
        $this->assertEquals(1, $command->execute());
282
283
        $result = (new Query($db))
284
            ->select(['email', 'name', 'address'])
285
            ->from('customer')
286
            ->where(['=', 'email', '[email protected]'])
287
            ->one();
288
289
        $this->assertCount(3, $result);
290
        $this->assertSame(
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

290
        $this->/** @scrutinizer ignore-call */ 
291
               assertSame(
Loading history...
291
            [
292
                'email' => '[email protected]',
293
                'name' => 'test_name',
294
                'address' => 'test_address',
295
            ],
296
            $result,
297
        );
298
    }
299
300
    /**
301
     * @throws Exception|InvalidConfigException|Throwable
302
     */
303
    public function testBatchInsertWithYield(): void
304
    {
305
        $rows = (static function () {
306
            foreach ([['[email protected]', 'test name', 'test address']] as $row) {
307
                yield $row;
308
            }
309
        })();
310
311
        $command = $this->getConnection()->createCommand();
312
        $command->batchInsert(
313
            '{{customer}}',
314
            ['email', 'name', 'address'],
315
            $rows
316
        );
317
        $this->assertEquals(1, $command->execute());
318
    }
319
320
    /**
321
     * Test batch insert with different data types.
322
     *
323
     * Ensure double is inserted with `.` decimal separator.
324
     *
325
     * @link https://github.com/yiisoft/yii2/issues/6526
326
     */
327
    public function testBatchInsertDataTypesLocale(): void
328
    {
329
        $locale = setlocale(LC_NUMERIC, 0);
330
331
        if (false === $locale) {
332
            $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

332
            $this->/** @scrutinizer ignore-call */ 
333
                   markTestSkipped('Your platform does not support locales.');
Loading history...
333
        }
334
335
        $db = $this->getConnection(true);
336
337
        try {
338
            /* This one sets decimal mark to comma sign */
339
            setlocale(LC_NUMERIC, 'ru_RU.utf8');
340
            $cols = ['int_col', 'char_col', 'float_col', 'bool_col'];
341
            $data = [
342
                [1, 'A', 9.735, true],
343
                [2, 'B', -2.123, false],
344
                [3, 'C', 2.123, false],
345
            ];
346
347
            /* clear data in "type" table */
348
            $db->createCommand()->delete('type')->execute();
349
350
            /* change, for point oracle. */
351
            if ($db->getDriver()->getDriverName() === 'oci') {
352
                $db->createCommand("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'")->execute();
353
            }
354
355
            /* batch insert on "type" table */
356
            $db->createCommand()->batchInsert('type', $cols, $data)->execute();
357
            $data = $db->createCommand(
358
                'SELECT [[int_col]], [[char_col]], [[float_col]], [[bool_col]] ' .
359
                'FROM {{type}} WHERE [[int_col]] IN (1,2,3) ORDER BY [[int_col]]'
360
            )->queryAll();
361
            $this->assertCount(3, $data);
362
            $this->assertEquals(1, $data[0]['int_col']);
363
            $this->assertEquals(2, $data[1]['int_col']);
364
            $this->assertEquals(3, $data[2]['int_col']);
365
366
            /* rtrim because Postgres padds the column with whitespace */
367
            $this->assertEquals('A', rtrim($data[0]['char_col']));
368
            $this->assertEquals('B', rtrim($data[1]['char_col']));
369
            $this->assertEquals('C', rtrim($data[2]['char_col']));
370
            $this->assertEquals('9.735', $data[0]['float_col']);
371
            $this->assertEquals('-2.123', $data[1]['float_col']);
372
            $this->assertEquals('2.123', $data[2]['float_col']);
373
            $this->assertEquals('1', $data[0]['bool_col']);
374
            $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

374
            $this->/** @scrutinizer ignore-call */ 
375
                   assertIsOneOf($data[1]['bool_col'], ['0', false]);
Loading history...
375
            $this->assertIsOneOf($data[2]['bool_col'], ['0', false]);
376
        } catch (Exception|Throwable $e) {
377
            setlocale(LC_NUMERIC, $locale);
378
            throw $e;
379
        }
380
381
        setlocale(LC_NUMERIC, $locale);
382
    }
383
384
    /**
385
     * @throws Exception|InvalidConfigException|Throwable
386
     */
387
    public function testInsert(): void
388
    {
389
        $db = $this->getConnection();
390
391
        $db->createCommand('DELETE FROM {{customer}}')->execute();
392
        $command = $db->createCommand();
393
        $command->insert(
394
            '{{customer}}',
395
            [
396
                'email' => '[email protected]',
397
                'name' => 'test',
398
                'address' => 'test address',
399
            ]
400
        )->execute();
401
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{customer}};')->queryScalar());
402
403
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryOne();
404
        $this->assertEquals([
405
            'email' => '[email protected]',
406
            'name' => 'test',
407
            'address' => 'test address',
408
        ], $record);
409
    }
410
411
    public function testInsertEx(): void
412
    {
413
        $db = $this->getConnection();
414
415
        $result = $db->createCommand()->insertEx(
416
            'customer',
417
            [
418
                'name' => 'testParams',
419
                'email' => '[email protected]',
420
                'address' => '1',
421
            ]
422
        );
423
424
        $this->assertIsArray($result);
425
        $this->assertNotNull($result['id']);
426
    }
427
428
    /**
429
     * Verify that {{}} are not going to be replaced in parameters.
430
     */
431
    public function testNoTablenameReplacement(): void
432
    {
433
        $db = $this->getConnection(true);
434
435
        $db->createCommand()->insert(
436
            '{{customer}}',
437
            [
438
                'name' => 'Some {{weird}} name',
439
                'email' => '[email protected]',
440
                'address' => 'Some {{%weird}} address',
441
            ]
442
        )->execute();
443
444
        if ($db->getDriver()->getDriverName() === 'pgsql') {
445
            $customerId = $db->getLastInsertID('public.customer_id_seq');
446
        } else {
447
            $customerId = $db->getLastInsertID();
448
        }
449
450
        $customer = $db->createCommand('SELECT * FROM {{customer}} WHERE id=' . $customerId)->queryOne();
451
        $this->assertIsArray($customer);
452
        $this->assertEquals('Some {{weird}} name', $customer['name']);
453
        $this->assertEquals('Some {{%weird}} address', $customer['address']);
454
455
        $db->createCommand()->update(
456
            '{{customer}}',
457
            [
458
                'name' => 'Some {{updated}} name',
459
                'address' => 'Some {{%updated}} address',
460
            ],
461
            ['id' => $customerId]
462
        )->execute();
463
        $customer = $db->createCommand('SELECT * FROM {{customer}} WHERE id=' . $customerId)->queryOne();
464
        $this->assertIsArray($customer);
465
        $this->assertEquals('Some {{updated}} name', $customer['name']);
466
        $this->assertEquals('Some {{%updated}} address', $customer['address']);
467
    }
468
469
    /**
470
     * Test INSERT INTO ... SELECT SQL statement.
471
     */
472
    public function testInsertSelect(): void
473
    {
474
        $db = $this->getConnection();
475
476
        $db->createCommand('DELETE FROM {{customer}}')->execute();
477
        $command = $db->createCommand();
478
        $command->insert(
479
            '{{customer}}',
480
            [
481
                'email' => '[email protected]',
482
                'name' => 'test',
483
                'address' => 'test address',
484
            ]
485
        )->execute();
486
        $query = new Query($db);
487
        $query->select(
488
            [
489
                '{{customer}}.[[email]] as name',
490
                '[[name]] as email',
491
                '[[address]]',
492
            ]
493
        )
494
            ->from('{{customer}}')
495
            ->where([
496
                'and',
497
                ['<>', 'name', 'foo'],
498
                ['status' => [0, 1, 2, 3]],
499
            ]);
500
        $command = $db->createCommand();
501
        $command->insert(
502
            '{{customer}}',
503
            $query
504
        )->execute();
505
        $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{customer}}')->queryScalar());
506
507
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryAll();
508
        $this->assertEquals([
509
            [
510
                'email' => '[email protected]',
511
                'name' => 'test',
512
                'address' => 'test address',
513
            ],
514
            [
515
                'email' => 'test',
516
                'name' => '[email protected]',
517
                'address' => 'test address',
518
            ],
519
        ], $record);
520
    }
521
522
    /**
523
     * Test INSERT INTO ... SELECT SQL statement with alias syntax.
524
     */
525
    public function testInsertSelectAlias(): void
526
    {
527
        $db = $this->getConnection();
528
529
        $db->createCommand('DELETE FROM {{customer}}')->execute();
530
        $command = $db->createCommand();
531
        $command->insert(
532
            '{{customer}}',
533
            [
534
                'email' => '[email protected]',
535
                'name' => 'test',
536
                'address' => 'test address',
537
            ]
538
        )->execute();
539
        $query = new Query($db);
540
        $query->select(
541
            [
542
                'email' => '{{customer}}.[[email]]',
543
                'address' => 'name',
544
                'name' => 'address',
545
            ]
546
        )
547
            ->from('{{customer}}')
548
            ->where([
549
                'and',
550
                ['<>', 'name', 'foo'],
551
                ['status' => [0, 1, 2, 3]],
552
            ]);
553
        $command = $db->createCommand();
554
        $command->insert(
555
            '{{customer}}',
556
            $query
557
        )->execute();
558
        $this->assertEquals(2, $db->createCommand('SELECT COUNT(*) FROM {{customer}}')->queryScalar());
559
560
        $record = $db->createCommand('SELECT [[email]], [[name]], [[address]] FROM {{customer}}')->queryAll();
561
        $this->assertEquals([
562
            [
563
                'email' => '[email protected]',
564
                'name' => 'test',
565
                'address' => 'test address',
566
            ],
567
            [
568
                'email' => '[email protected]',
569
                'name' => 'test address',
570
                'address' => 'test',
571
            ],
572
        ], $record);
573
    }
574
575
    public function testInsertExpression(): void
576
    {
577
        $expression = '';
578
        $db = $this->getConnection();
579
580
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
581
        switch ($db->getDriver()->getDriverName()) {
582
            case 'pgsql':
583
                $expression = "EXTRACT(YEAR FROM TIMESTAMP 'now')";
584
                break;
585
            case 'mysql':
586
                $expression = 'YEAR(NOW())';
587
                break;
588
            case 'sqlite':
589
                $expression = "strftime('%Y')";
590
                break;
591
            case 'sqlsrv':
592
                $expression = 'YEAR(GETDATE())';
593
        }
594
        $command = $db->createCommand();
595
        $command->insert(
596
            '{{order_with_null_fk}}',
597
            [
598
                'created_at' => new Expression($expression),
599
                'total' => 1,
600
            ]
601
        )->execute();
602
        $this->assertEquals(1, $db->createCommand('SELECT COUNT(*) FROM {{order_with_null_fk}}')->queryScalar());
603
604
        $record = $db->createCommand('SELECT [[created_at]] FROM {{order_with_null_fk}}')->queryOne();
605
        $this->assertEquals(['created_at' => date('Y')], $record);
606
    }
607
608
    public function testsInsertQueryAsColumnValue(): void
609
    {
610
        $time = time();
611
        $db = $this->getConnection(true);
612
613
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
614
        $command = $db->createCommand();
615
        $command->insert('{{order}}', [
616
            'customer_id' => 1,
617
            'created_at' => $time,
618
            'total' => 42,
619
        ])->execute();
620
621
        if ($db->getDriver()->getDriverName() === 'pgsql') {
622
            $orderId = $db->getLastInsertID('public.order_id_seq');
623
        } else {
624
            $orderId = $db->getLastInsertID();
625
        }
626
627
        $columnValueQuery = new Query($db);
628
        $columnValueQuery->select('created_at')->from('{{order}}')->where(['id' => $orderId]);
629
        $command = $db->createCommand();
630
        $command->insert(
631
            '{{order_with_null_fk}}',
632
            [
633
                'customer_id' => $orderId,
634
                'created_at' => $columnValueQuery,
635
                'total' => 42,
636
            ]
637
        )->execute();
638
        $this->assertEquals(
639
            $time,
640
            $db->createCommand(
641
                'SELECT [[created_at]] FROM {{order_with_null_fk}} WHERE [[customer_id]] = ' . $orderId
642
            )->queryScalar()
643
        );
644
645
        $db->createCommand('DELETE FROM {{order_with_null_fk}}')->execute();
646
        $db->createCommand('DELETE FROM {{order}} WHERE [[id]] = ' . $orderId)->execute();
647
    }
648
649
    public function testCreateTable(): void
650
    {
651
        $db = $this->getConnection();
652
653
        if ($db->getSchema()->getTableSchema('testCreateTable') !== null) {
654
            $db->createCommand()->dropTable('testCreateTable')->execute();
655
        }
656
657
        $db->createCommand()->createTable(
658
            'testCreateTable',
659
            ['id' => Schema::TYPE_PK, 'bar' => Schema::TYPE_INTEGER]
660
        )->execute();
661
        $db->createCommand()->insert('testCreateTable', ['bar' => 1])->execute();
662
        $records = $db->createCommand('SELECT [[id]], [[bar]] FROM {{testCreateTable}};')->queryAll();
663
        $this->assertEquals([['id' => 1, 'bar' => 1]], $records);
664
    }
665
666
    public function testDropTable(): void
667
    {
668
        $db = $this->getConnection();
669
670
        $tableName = 'type';
671
        $this->assertNotNull($db->getSchema()->getTableSchema($tableName));
672
673
        $db->createCommand()->dropTable($tableName)->execute();
674
        $this->assertNull($db->getSchema()->getTableSchema($tableName));
675
    }
676
677
    public function testTruncateTable(): void
678
    {
679
        $db = $this->getConnection();
680
681
        $rows = $db->createCommand('SELECT * FROM {{animal}}')->queryAll();
682
        $this->assertCount(2, $rows);
683
684
        $db->createCommand()->truncateTable('animal')->execute();
685
        $rows = $db->createCommand('SELECT * FROM {{animal}}')->queryAll();
686
        $this->assertCount(0, $rows);
687
    }
688
689
    public function testRenameTable(): void
690
    {
691
        $db = $this->getConnection(true);
692
693
        $fromTableName = 'type';
694
        $toTableName = 'new_type';
695
696
        if ($db->getSchema()->getTableSchema($toTableName) !== null) {
697
            $db->createCommand()->dropTable($toTableName)->execute();
698
        }
699
700
        $this->assertNotNull($db->getSchema()->getTableSchema($fromTableName));
701
        $this->assertNull($db->getSchema()->getTableSchema($toTableName));
702
703
        $db->createCommand()->renameTable($fromTableName, $toTableName)->execute();
704
        $this->assertNull($db->getSchema()->getTableSchema($fromTableName, true));
705
        $this->assertNotNull($db->getSchema()->getTableSchema($toTableName, true));
706
    }
707
708
    protected function performAndCompareUpsertResult(ConnectionInterface $db, array $data): void
709
    {
710
        $params = $data['params'];
711
        $expected = $data['expected'] ?? $params[1];
712
713
        $command = $db->createCommand();
714
        call_user_func_array([$command, 'upsert'], $params);
715
        $command->execute();
716
717
        $actual = (new Query($db))
718
            ->select([
719
                'email',
720
                'address' => new Expression($this->upsertTestCharCast),
721
                'status',
722
            ])
723
            ->from('T_upsert')
724
            ->one();
725
        $this->assertEquals($expected, $actual, $this->upsertTestCharCast);
726
    }
727
728
    public function testAddDropForeignKey(): void
729
    {
730
        $db = $this->getConnection();
731
732
        $tableName = 'test_fk';
733
        $name = 'test_fk_constraint';
734
735
        $schema = $db->getSchema();
736
737
        if ($schema->getTableSchema($tableName) !== null) {
738
            $db->createCommand()->dropTable($tableName)->execute();
739
        }
740
741
        $db->createCommand()->createTable($tableName, [
742
            'int1' => 'integer not null unique',
743
            'int2' => 'integer not null unique',
744
            'int3' => 'integer not null unique',
745
            'int4' => 'integer not null unique',
746
            'unique ([[int1]], [[int2]])',
747
            'unique ([[int3]], [[int4]])',
748
        ])->execute();
749
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
750
751
        $db->createCommand()->addForeignKey($name, $tableName, ['int1'], $tableName, ['int3'])->execute();
752
        $this->assertEquals(['int1'], $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames());
753
        $this->assertEquals(['int3'], $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames());
754
755
        $db->createCommand()->dropForeignKey($name, $tableName)->execute();
756
        $this->assertEmpty($schema->getTableForeignKeys($tableName, true));
757
758
        $db->createCommand()->addForeignKey(
759
            $name,
760
            $tableName,
761
            ['int1', 'int2'],
762
            $tableName,
763
            ['int3', 'int4']
764
        )->execute();
765
        $this->assertEquals(
766
            ['int1', 'int2'],
767
            $schema->getTableForeignKeys($tableName, true)[0]->getColumnNames()
768
        );
769
        $this->assertEquals(
770
            ['int3', 'int4'],
771
            $schema->getTableForeignKeys($tableName, true)[0]->getForeignColumnNames()
772
        );
773
    }
774
775
    public function testCreateDropIndex(): void
776
    {
777
        $db = $this->getConnection();
778
779
        $tableName = 'test_idx';
780
        $name = 'test_idx_constraint';
781
782
        $schema = $db->getSchema();
783
784
        if ($schema->getTableSchema($tableName) !== null) {
785
            $db->createCommand()->dropTable($tableName)->execute();
786
        }
787
788
        $db->createCommand()->createTable($tableName, [
789
            'int1' => 'integer not null',
790
            'int2' => 'integer not null',
791
        ])->execute();
792
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
793
794
        $db->createCommand()->createIndex($name, $tableName, ['int1'])->execute();
795
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
796
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
797
798
        $db->createCommand()->dropIndex($name, $tableName)->execute();
799
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
800
801
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'])->execute();
802
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
803
        $this->assertFalse($schema->getTableIndexes($tableName, true)[0]->isUnique());
804
805
        $db->createCommand()->dropIndex($name, $tableName)->execute();
806
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
807
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
808
809
        $db->createCommand()->createIndex($name, $tableName, ['int1'], QueryBuilder::INDEX_UNIQUE)->execute();
810
        $this->assertEquals(['int1'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
811
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
812
813
        $db->createCommand()->dropIndex($name, $tableName)->execute();
814
        $this->assertEmpty($schema->getTableIndexes($tableName, true));
815
816
        $db->createCommand()->createIndex($name, $tableName, ['int1', 'int2'], QueryBuilder::INDEX_UNIQUE)->execute();
817
        $this->assertEquals(['int1', 'int2'], $schema->getTableIndexes($tableName, true)[0]->getColumnNames());
818
        $this->assertTrue($schema->getTableIndexes($tableName, true)[0]->isUnique());
819
    }
820
821
    public function testAddDropUnique(): void
822
    {
823
        $db = $this->getConnection();
824
825
        $tableName = 'test_uq';
826
        $name = 'test_uq_constraint';
827
828
        $schema = $db->getSchema();
829
830
        if ($schema->getTableSchema($tableName) !== null) {
831
            $db->createCommand()->dropTable($tableName)->execute();
832
        }
833
834
        $db->createCommand()->createTable($tableName, [
835
            'int1' => 'integer not null',
836
            'int2' => 'integer not null',
837
        ])->execute();
838
        $this->assertEmpty($schema->getTableUniques($tableName, true));
839
840
        $db->createCommand()->addUnique($name, $tableName, ['int1'])->execute();
841
        $this->assertEquals(['int1'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
842
843
        $db->createCommand()->dropUnique($name, $tableName)->execute();
844
        $this->assertEmpty($schema->getTableUniques($tableName, true));
845
846
        $db->createCommand()->addUnique($name, $tableName, ['int1', 'int2'])->execute();
847
        $this->assertEquals(['int1', 'int2'], $schema->getTableUniques($tableName, true)[0]->getColumnNames());
848
    }
849
850
    public function testBindValues(): void
851
    {
852
        $command = $this->getConnection()->createCommand();
853
854
        $values = [
855
            'int' => 1,
856
            'string' => 'str',
857
        ];
858
        $command->bindValues($values);
859
        $bindedValues = $command->getParams(false);
860
861
        $this->assertIsArray($bindedValues);
862
        $this->assertContainsOnlyInstancesOf(ParamInterface::class, $bindedValues);
0 ignored issues
show
Bug introduced by
It seems like assertContainsOnlyInstancesOf() 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

862
        $this->/** @scrutinizer ignore-call */ 
863
               assertContainsOnlyInstancesOf(ParamInterface::class, $bindedValues);
Loading history...
863
        $this->assertCount(2, $bindedValues);
864
865
        $param = new Param('str', 99);
866
        $command->bindValues(['param' => $param]);
867
        $bindedValues = $command->getParams(false);
868
869
        $this->assertIsArray($bindedValues);
870
        $this->assertContainsOnlyInstancesOf(ParamInterface::class, $bindedValues);
871
        $this->assertCount(3, $bindedValues);
872
        $this->assertEquals($param, $bindedValues['param']);
873
        $this->assertNotEquals($param, $bindedValues['int']);
0 ignored issues
show
Bug introduced by
It seems like assertNotEquals() 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

873
        $this->/** @scrutinizer ignore-call */ 
874
               assertNotEquals($param, $bindedValues['int']);
Loading history...
874
875
        // Replace test
876
        $command->bindValues(['int' => $param]);
877
        $bindedValues = $command->getParams(false);
878
879
        $this->assertIsArray($bindedValues);
880
        $this->assertContainsOnlyInstancesOf(ParamInterface::class, $bindedValues);
881
        $this->assertCount(3, $bindedValues);
882
        $this->assertEquals($param, $bindedValues['int']);
883
    }
884
885
    public function testIntegrityViolation(): void
886
    {
887
        $this->expectException(IntegrityException::class);
888
889
        $db = $this->getConnection();
890
891
        $sql = 'INSERT INTO {{profile}}([[id]], [[description]]) VALUES (123, \'duplicate\')';
892
        $command = $db->createCommand($sql);
893
        $command->execute();
894
        $command->execute();
895
    }
896
897
    public function testLastInsertId(): void
898
    {
899
        $db = $this->getConnection(true);
900
901
        $sql = 'INSERT INTO {{profile}}([[description]]) VALUES (\'non duplicate\')';
902
        $command = $db->createCommand($sql);
903
        $command->execute();
904
        $this->assertEquals(3, $db->getLastInsertID());
905
    }
906
907
    public function testLastInsertIdException(): void
908
    {
909
        $db = $this->getConnection();
910
        $db->close();
911
912
        $this->expectException(InvalidCallException::class);
913
        $db->getLastInsertID();
914
    }
915
916
    public function testQueryCache(): void
917
    {
918
        $db = $this->getConnection(true);
919
920
        /** @psalm-suppress PossiblyNullReference */
921
        $this->queryCache->setEnable(true);
922
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
923
        $this->assertEquals('user1', $command->bindValue(':id', 1)->queryScalar());
924
925
        $update = $db->createCommand('UPDATE {{customer}} SET [[name]] = :name WHERE [[id]] = :id');
926
        $update->bindValues([':id' => 1, ':name' => 'user11'])->execute();
927
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
928
929
        $db->cache(function (ConnectionInterface $db) use ($command, $update) {
930
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
931
932
            $update->bindValues([':id' => 2, ':name' => 'user22'])->execute();
933
934
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
935
936
            $db->noCache(function () use ($command) {
937
                $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
938
            });
939
940
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
941
        }, 10);
942
943
        /** @psalm-suppress PossiblyNullReference */
944
        $this->queryCache->setEnable(false);
945
946
        $db->cache(function () use ($command, $update) {
947
            $this->assertEquals('user22', $command->bindValue(':id', 2)->queryScalar());
948
            $update->bindValues([':id' => 2, ':name' => 'user2'])->execute();
949
            $this->assertEquals('user2', $command->bindValue(':id', 2)->queryScalar());
950
        }, 10);
951
952
        /** @psalm-suppress PossiblyNullReference */
953
        $this->queryCache->setEnable(true);
954
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id')->cache();
955
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
956
957
        $update->bindValues([':id' => 1, ':name' => 'user1'])->execute();
958
        $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
959
        $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
960
961
        $command = $db->createCommand('SELECT [[name]] FROM {{customer}} WHERE [[id]] = :id');
962
963
        $db->cache(function () use ($command) {
964
            $this->assertEquals('user11', $command->bindValue(':id', 1)->queryScalar());
965
            $this->assertEquals('user1', $command->noCache()->bindValue(':id', 1)->queryScalar());
966
        }, 10);
967
    }
968
969
    public function testColumnCase(): void
970
    {
971
        $db = $this->getConnection();
972
973
        $this->assertEquals(PDO::CASE_NATURAL, $db->getActivePDO()->getAttribute(PDO::ATTR_CASE));
974
975
        $sql = 'SELECT [[customer_id]], [[total]] FROM {{order}}';
976
        $rows = $db->createCommand($sql)->queryAll();
977
        $this->assertTrue(isset($rows[0]));
978
        $this->assertTrue(isset($rows[0]['customer_id']));
979
        $this->assertTrue(isset($rows[0]['total']));
980
981
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
982
        $rows = $db->createCommand($sql)->queryAll();
983
        $this->assertTrue(isset($rows[0]));
984
        $this->assertTrue(isset($rows[0]['customer_id']));
985
        $this->assertTrue(isset($rows[0]['total']));
986
987
        $db->getActivePDO()->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
988
        $rows = $db->createCommand($sql)->queryAll();
989
        $this->assertTrue(isset($rows[0]));
990
        $this->assertTrue(isset($rows[0]['CUSTOMER_ID']));
991
        $this->assertTrue(isset($rows[0]['TOTAL']));
992
    }
993
994
    public function testTransaction(): void
995
    {
996
        $db = $this->getConnection();
997
998
        $this->assertNull($db->getTransaction());
999
1000
        $command = $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command transaction')");
1001
        $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

1001
        $this->/** @scrutinizer ignore-call */ 
1002
               invokeMethod($command, 'requireTransaction');
Loading history...
1002
        $command->execute();
1003
        $this->assertNull($db->getTransaction());
1004
        $this->assertEquals(
1005
            1,
1006
            $db->createCommand(
1007
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command transaction'"
1008
            )->queryScalar()
1009
        );
1010
    }
1011
1012
    public function testRetryHandler(): void
1013
    {
1014
        $db = $this->getConnection();
1015
1016
        $this->assertNull($db->getTransaction());
1017
1018
        $db->createCommand("INSERT INTO {{profile}}([[description]]) VALUES('command retry')")->execute();
1019
        $this->assertNull($db->getTransaction());
1020
        $this->assertEquals(
1021
            1,
1022
            $db->createCommand(
1023
                "SELECT COUNT(*) FROM {{profile}} WHERE [[description]] = 'command retry'"
1024
            )->queryScalar()
1025
        );
1026
1027
        $attempts = null;
1028
        $hitHandler = false;
1029
        $hitCatch = false;
1030
1031
        $command = $db->createCommand(
1032
            "INSERT INTO {{profile}}([[id]], [[description]]) VALUES(1, 'command retry')"
1033
        );
1034
        $this->invokeMethod(
1035
            $command,
1036
            'setRetryHandler',
1037
            [static function ($exception, $attempt) use (&$attempts, &$hitHandler) {
1038
                $attempts = $attempt;
1039
                $hitHandler = true;
1040
1041
                return $attempt <= 2;
1042
            }]
1043
        );
1044
1045
        try {
1046
            $command->execute();
1047
        } catch (Exception $e) {
1048
            $hitCatch = true;
1049
            $this->assertInstanceOf(IntegrityException::class, $e);
1050
        }
1051
1052
        $this->assertNull($db->getTransaction());
1053
        $this->assertSame(3, $attempts);
1054
        $this->assertTrue($hitHandler);
1055
        $this->assertTrue($hitCatch);
1056
    }
1057
1058
    public function testCreateView(): void
1059
    {
1060
        $db = $this->getConnection();
1061
1062
        $subquery = (new Query($db))
1063
            ->select('bar')
1064
            ->from('testCreateViewTable')
1065
            ->where(['>', 'bar', '5']);
1066
1067
        if ($db->getSchema()->getTableSchema('testCreateView') !== null) {
1068
            $db->createCommand()->dropView('testCreateView')->execute();
1069
        }
1070
1071
        if ($db->getSchema()->getTableSchema('testCreateViewTable')) {
1072
            $db->createCommand()->dropTable('testCreateViewTable')->execute();
1073
        }
1074
1075
        $db->createCommand()->createTable('testCreateViewTable', [
1076
            'id' => Schema::TYPE_PK,
1077
            'bar' => Schema::TYPE_INTEGER,
1078
        ])->execute();
1079
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 1])->execute();
1080
        $db->createCommand()->insert('testCreateViewTable', ['bar' => 6])->execute();
1081
        $db->createCommand()->createView('testCreateView', $subquery)->execute();
1082
        $records = $db->createCommand('SELECT [[bar]] FROM {{testCreateView}};')->queryAll();
1083
        $this->assertEquals([['bar' => 6]], $records);
1084
    }
1085
1086
    public function testDropView(): void
1087
    {
1088
        $db = $this->getConnection();
1089
1090
        /* since it already exists in the fixtures */
1091
        $viewName = 'animal_view';
1092
1093
        $this->assertNotNull($db->getSchema()->getTableSchema($viewName));
1094
1095
        $db->createCommand()->dropView($viewName)->execute();
1096
        $this->assertNull($db->getSchema()->getTableSchema($viewName));
1097
    }
1098
1099
    public function batchInsertSqlProviderTrait(): array
1100
    {
1101
        return [
1102
            'issue11242' => [
1103
                'type',
1104
                ['int_col', 'float_col', 'char_col'],
1105
                [['', '', 'Kyiv {{city}}, Ukraine']],
1106
                /**
1107
                 * {@see https://github.com/yiisoft/yii2/issues/11242}
1108
                 *
1109
                 * Make sure curly bracelets (`{{..}}`) in values will not be escaped
1110
                 */
1111
                'expected' => 'INSERT INTO `type` (`int_col`, `float_col`, `char_col`)'
1112
                    . " VALUES (NULL, NULL, 'Kyiv {{city}}, Ukraine')",
1113
            ],
1114
            'wrongBehavior' => [
1115
                '{{%type}}',
1116
                ['{{%type}}.[[int_col]]', '[[float_col]]', 'char_col'],
1117
                [['', '', 'Kyiv {{city}}, Ukraine']],
1118
                /**
1119
                 * Test covers potentially wrong behavior and marks it as expected!.
1120
                 *
1121
                 * In case table name or table column is passed with curly or square bracelets, QueryBuilder can not
1122
                 * determine the table schema and typecast values properly.
1123
                 * TODO: make it work. Impossible without BC breaking for public methods.
1124
                 */
1125
                'expected' => 'INSERT INTO `type` (`type`.`int_col`, `float_col`, `char_col`)'
1126
                    . " VALUES ('', '', 'Kyiv {{city}}, Ukraine')",
1127
            ],
1128
            'batchInsert binds params from expression' => [
1129
                '{{%type}}',
1130
                ['int_col'],
1131
                /**
1132
                 * This example is completely useless. This feature of batchInsert is intended to be used with complex
1133
                 * expression objects, such as JsonExpression.
1134
                 */
1135
                [[new Expression(':qp1', [':qp1' => 42])]],
1136
                'expected' => 'INSERT INTO `type` (`int_col`) VALUES (:qp1)',
1137
                'expectedParams' => [':qp1' => 42],
1138
            ],
1139
        ];
1140
    }
1141
1142
    public function bindParamsNonWhereProviderTrait(): array
1143
    {
1144
        return [
1145
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email GROUP BY SUBSTR(name, :len)'],
1146
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email ORDER BY SUBSTR(name, :len)'],
1147
            ['SELECT SUBSTR(name, :len) FROM {{customer}} WHERE [[email]] = :email'],
1148
        ];
1149
    }
1150
1151
    public function getRawSqlProviderTrait(): array
1152
    {
1153
        return [
1154
            [
1155
                'SELECT * FROM customer WHERE id = :id',
1156
                [':id' => 1],
1157
                'SELECT * FROM customer WHERE id = 1',
1158
            ],
1159
            [
1160
                'SELECT * FROM customer WHERE id = :id',
1161
                ['id' => 1],
1162
                'SELECT * FROM customer WHERE id = 1',
1163
            ],
1164
            [
1165
                'SELECT * FROM customer WHERE id = :id',
1166
                ['id' => null],
1167
                'SELECT * FROM customer WHERE id = NULL',
1168
            ],
1169
            [
1170
                'SELECT * FROM customer WHERE id = :base OR id = :basePrefix',
1171
                [
1172
                    'base' => 1,
1173
                    'basePrefix' => 2,
1174
                ],
1175
                'SELECT * FROM customer WHERE id = 1 OR id = 2',
1176
            ],
1177
            /**
1178
             * {@see https://github.com/yiisoft/yii2/issues/9268}
1179
             */
1180
            [
1181
                'SELECT * FROM customer WHERE active = :active',
1182
                [':active' => false],
1183
                'SELECT * FROM customer WHERE active = FALSE',
1184
            ],
1185
            /**
1186
             * {@see https://github.com/yiisoft/yii2/issues/15122}
1187
             */
1188
            [
1189
                'SELECT * FROM customer WHERE id IN (:ids)',
1190
                [':ids' => new Expression(implode(', ', [1, 2]))],
1191
                'SELECT * FROM customer WHERE id IN (1, 2)',
1192
            ],
1193
        ];
1194
    }
1195
1196
    public function invalidSelectColumnsProviderTrait(): array
1197
    {
1198
        return [
1199
            [[]],
1200
            ['*'],
1201
            [['*']],
1202
        ];
1203
    }
1204
1205
    public function upsertProviderTrait(): array
1206
    {
1207
        return [
1208
            'regular values' => [
1209
                [
1210
                    'params' => [
1211
                        'T_upsert',
1212
                        [
1213
                            'email' => '[email protected]',
1214
                            'address' => 'Earth',
1215
                            'status' => 3,
1216
                        ],
1217
                    ],
1218
                ],
1219
                [
1220
                    'params' => [
1221
                        'T_upsert',
1222
                        [
1223
                            'email' => '[email protected]',
1224
                            'address' => 'Universe',
1225
                            'status' => 1,
1226
                        ],
1227
                    ],
1228
                ],
1229
            ],
1230
            'regular values with update part' => [
1231
                [
1232
                    'params' => [
1233
                        'T_upsert',
1234
                        [
1235
                            'email' => '[email protected]',
1236
                            'address' => 'Earth',
1237
                            'status' => 3,
1238
                        ],
1239
                        [
1240
                            'address' => 'Moon',
1241
                            'status' => 2,
1242
                        ],
1243
                    ],
1244
                ],
1245
                [
1246
                    'params' => [
1247
                        'T_upsert',
1248
                        [
1249
                            'email' => '[email protected]',
1250
                            'address' => 'Universe',
1251
                            'status' => 1,
1252
                        ],
1253
                        [
1254
                            'address' => 'Moon',
1255
                            'status' => 2,
1256
                        ],
1257
                    ],
1258
                    'expected' => [
1259
                        'email' => '[email protected]',
1260
                        'address' => 'Moon',
1261
                        'status' => 2,
1262
                    ],
1263
                ],
1264
            ],
1265
            'regular values without update part' => [
1266
                [
1267
                    'params' => [
1268
                        'T_upsert',
1269
                        [
1270
                            'email' => '[email protected]',
1271
                            'address' => 'Earth',
1272
                            'status' => 3,
1273
                        ],
1274
                        false,
1275
                    ],
1276
                ],
1277
                [
1278
                    'params' => [
1279
                        'T_upsert',
1280
                        [
1281
                            'email' => '[email protected]',
1282
                            'address' => 'Universe',
1283
                            'status' => 1,
1284
                        ],
1285
                        false,
1286
                    ],
1287
                    'expected' => [
1288
                        'email' => '[email protected]',
1289
                        'address' => 'Earth',
1290
                        'status' => 3,
1291
                    ],
1292
                ],
1293
            ],
1294
            'query' => [
1295
                [
1296
                    'params' => [
1297
                        'T_upsert',
1298
                        (new Query($this->getConnection()))
1299
                            ->select([
1300
                                'email',
1301
                                'address',
1302
                                'status' => new Expression('1'),
1303
                            ])
1304
                            ->from('customer')
1305
                            ->where(['name' => 'user1'])
1306
                            ->limit(1),
1307
                    ],
1308
                    'expected' => [
1309
                        'email' => '[email protected]',
1310
                        'address' => 'address1',
1311
                        'status' => 1,
1312
                    ],
1313
                ],
1314
                [
1315
                    'params' => [
1316
                        'T_upsert',
1317
                        (new Query($this->getConnection()))
1318
                            ->select([
1319
                                'email',
1320
                                'address',
1321
                                'status' => new Expression('2'),
1322
                            ])
1323
                            ->from('customer')
1324
                            ->where(['name' => 'user1'])
1325
                            ->limit(1),
1326
                    ],
1327
                    'expected' => [
1328
                        'email' => '[email protected]',
1329
                        'address' => 'address1',
1330
                        'status' => 2,
1331
                    ],
1332
                ],
1333
            ],
1334
            'query with update part' => [
1335
                [
1336
                    'params' => [
1337
                        'T_upsert',
1338
                        (new Query($this->getConnection()))
1339
                            ->select([
1340
                                'email',
1341
                                'address',
1342
                                'status' => new Expression('1'),
1343
                            ])
1344
                            ->from('customer')
1345
                            ->where(['name' => 'user1'])
1346
                            ->limit(1),
1347
                        [
1348
                            'address' => 'Moon',
1349
                            'status' => 2,
1350
                        ],
1351
                    ],
1352
                    'expected' => [
1353
                        'email' => '[email protected]',
1354
                        'address' => 'address1',
1355
                        'status' => 1,
1356
                    ],
1357
                ],
1358
                [
1359
                    'params' => [
1360
                        'T_upsert',
1361
                        (new Query($this->getConnection()))
1362
                            ->select([
1363
                                'email',
1364
                                'address',
1365
                                'status' => new Expression('3'),
1366
                            ])
1367
                            ->from('customer')
1368
                            ->where(['name' => 'user1'])
1369
                            ->limit(1),
1370
                        [
1371
                            'address' => 'Moon',
1372
                            'status' => 2,
1373
                        ],
1374
                    ],
1375
                    'expected' => [
1376
                        'email' => '[email protected]',
1377
                        'address' => 'Moon',
1378
                        'status' => 2,
1379
                    ],
1380
                ],
1381
            ],
1382
            'query without update part' => [
1383
                [
1384
                    'params' => [
1385
                        'T_upsert',
1386
                        (new Query($this->getConnection()))
1387
                            ->select([
1388
                                'email',
1389
                                'address',
1390
                                'status' => new Expression('1'),
1391
                            ])
1392
                            ->from('customer')
1393
                            ->where(['name' => 'user1'])
1394
                            ->limit(1),
1395
                        false,
1396
                    ],
1397
                    'expected' => [
1398
                        'email' => '[email protected]',
1399
                        'address' => 'address1',
1400
                        'status' => 1,
1401
                    ],
1402
                ],
1403
                [
1404
                    'params' => [
1405
                        'T_upsert',
1406
                        (new Query($this->getConnection()))
1407
                            ->select([
1408
                                'email',
1409
                                'address',
1410
                                'status' => new Expression('2'),
1411
                            ])
1412
                            ->from('customer')
1413
                            ->where(['name' => 'user1'])
1414
                            ->limit(1),
1415
                        false,
1416
                    ],
1417
                    'expected' => [
1418
                        'email' => '[email protected]',
1419
                        'address' => 'address1',
1420
                        'status' => 1,
1421
                    ],
1422
                ],
1423
            ],
1424
        ];
1425
    }
1426
1427
    public function testAlterTable(): void
1428
    {
1429
        $db = $this->getConnection();
1430
1431
        if ($db->getDriver()->getDriverName() === 'sqlite') {
1432
            $this->markTestSkipped('Sqlite does not support alterTable');
1433
        }
1434
1435
        if ($db->getSchema()->getTableSchema('testAlterTable', true) !== null) {
1436
            $db->createCommand()->dropTable('testAlterTable')->execute();
1437
        }
1438
1439
        $db->createCommand()->createTable(
1440
            'testAlterTable',
1441
            [
1442
                'id' => Schema::TYPE_PK,
1443
                'bar' => Schema::TYPE_INTEGER,
1444
            ]
1445
        )->execute();
1446
1447
        $db->createCommand()->insert('testAlterTable', ['bar' => 1])->execute();
1448
        $db->createCommand()->alterColumn('testAlterTable', 'bar', Schema::TYPE_STRING)->execute();
1449
        $db->createCommand()->insert('testAlterTable', ['bar' => 'hello'])->execute();
1450
1451
        $records = $db->createCommand('SELECT [[id]], [[bar]] FROM {{testAlterTable}}')->queryAll();
1452
        $this->assertEquals([
1453
            ['id' => 1, 'bar' => 1],
1454
            ['id' => 2, 'bar' => 'hello'],
1455
        ], $records);
1456
    }
1457
1458
    public function testInsertToBlob(): void
1459
    {
1460
        $db = $this->getConnection(true);
1461
1462
        $db->createCommand()->delete('type')->execute();
1463
1464
        $columns = [
1465
            'int_col' => 1,
1466
            'char_col' => 'test',
1467
            'float_col' => 3.14,
1468
            'bool_col' => true,
1469
            'blob_col' => serialize(['test' => 'data', 'num' => 222]),
1470
        ];
1471
        $db->createCommand()->insert('type', $columns)->execute();
1472
        $result = $db->createCommand('SELECT [[blob_col]] FROM {{type}}')->queryOne();
1473
1474
        $this->assertIsArray($result);
1475
        $resultBlob = is_resource($result['blob_col']) ? stream_get_contents($result['blob_col']) : $result['blob_col'];
1476
1477
        $this->assertEquals($columns['blob_col'], $resultBlob);
1478
    }
1479
}
1480