Passed
Pull Request — master (#380)
by Alexander
04:51 queued 02:23
created

QueryBuilderTest::testIssue15653()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 11
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 17
rs 9.9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Tests;
6
7
use Closure;
8
use PHPUnit\Framework\TestCase;
9
use Yiisoft\Db\Expression\Expression;
10
use Yiisoft\Db\Expression\ExpressionInterface;
11
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
12
use Yiisoft\Db\Tests\Support\DbHelper;
13
use Yiisoft\Db\Tests\Support\Mock;
14
15
/**
16
 * @group db
17
 */
18
final class QueryBuilderTest extends TestCase
19
{
20
    private QueryBuilderInterface $queryBuilder;
21
    private Mock $mock;
22
23
    public function setUp(): void
24
    {
25
        parent::setUp();
26
27
        $this->mock = new Mock();
28
        $this->queryBuilder = $this->mock->queryBuilder('`', '`');
0 ignored issues
show
Unused Code introduced by
The call to Yiisoft\Db\Tests\Support\Mock::queryBuilder() has too many arguments starting with '`'. ( Ignorable by Annotation )

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

28
        /** @scrutinizer ignore-call */ 
29
        $this->queryBuilder = $this->mock->queryBuilder('`', '`');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
29
    }
30
31
    public function tearDown(): void
32
    {
33
        parent::tearDown();
34
35
        unset($this->queryBuilder, $this->mock);
36
    }
37
38
    /**
39
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::batchInsert()
40
     */
41
    public function testBatchInsert(
42
        string $table,
43
        array $columns,
44
        array $value,
45
        string|null $expected,
46
        array $expectedParams = []
47
    ): void {
48
        $params = [];
49
        $sql = $this->queryBuilder->batchInsert($table, $columns, $value, $params);
50
51
        $this->assertSame($expected, $sql);
52
        $this->assertSame($expectedParams, $params);
53
    }
54
55
    /**
56
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildConditions()
57
     */
58
    public function testBuildCondition(
59
        array|ExpressionInterface|string $conditions,
60
        string $expected,
61
        array $expectedParams = []
62
    ): void {
63
        $query = $this->mock->query()->where($conditions);
64
        [$sql, $params] = $this->queryBuilder->build($query);
65
66
        $this->assertSame(
67
            'SELECT *' . (
68
                empty($expected) ? '' : ' WHERE ' . DbHelper::replaceQuotes(
69
                    $expected,
70
                    $this->mock->getDriverName(),
71
                )
72
            ),
73
            $sql,
74
        );
75
        $this->assertSame($expectedParams, $params);
76
    }
77
78
    /**
79
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildFilterCondition()
80
     */
81
    public function testBuildFilterCondition(array $condition, string $expected, array $expectedParams): void
82
    {
83
        $query = $this->mock->query()->filterWhere($condition);
84
        [$sql, $params] = $this->queryBuilder->build($query);
85
86
        $this->assertSame(
87
            'SELECT *' . (
88
                empty($expected) ? '' : ' WHERE ' . DbHelper::replaceQuotes(
89
                    $expected,
90
                    $this->mock->getDriverName(),
91
                )
92
            ),
93
            $sql,
94
        );
95
        $this->assertSame($expectedParams, $params);
96
    }
97
98
    /**
99
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildFrom()
100
     */
101
    public function testBuildFrom(string $table, string $expected): void
102
    {
103
        $params = [];
104
        $sql = $this->queryBuilder->buildFrom([$table], $params);
105
        $replacedQuotes = DbHelper::replaceQuotes($expected, $this->mock->getDriverName());
106
107
        $this->assertIsString($replacedQuotes);
108
        $this->assertSame('FROM ' . $replacedQuotes, $sql);
109
    }
110
111
    /**
112
     * This test contains three select queries connected with UNION and UNION ALL constructions.
113
     * It could be useful to use "phpunit --group=db --filter testBuildUnion" command for run it.
114
     */
115
    public function testBuildUnion(): void
116
    {
117
        $expectedQuerySql = DbHelper::replaceQuotes(
118
            <<<SQL
119
            (SELECT [[id]] FROM [[TotalExample]] [[t1]] WHERE (w > 0) AND (x < 2)) UNION ( SELECT [[id]] FROM [[TotalTotalExample]] [[t2]] WHERE w > 5 ) UNION ALL ( SELECT [[id]] FROM [[TotalTotalExample]] [[t3]] WHERE w = 3 )
120
            SQL,
121
            $this->mock->getDriverName(),
122
        );
123
124
        $secondQuery = $this->mock
125
            ->query()
126
            ->select('id')
127
            ->from('TotalTotalExample t2')
128
            ->where('w > 5');
129
130
        $thirdQuery = $this->mock
131
            ->query()
132
            ->select('id')
133
            ->from('TotalTotalExample t3')
134
            ->where('w = 3');
135
136
        $query = $this->mock
137
            ->query()
138
            ->select('id')
139
            ->from('TotalExample t1')
140
            ->where(['and', 'w > 0', 'x < 2'])
141
            ->union($secondQuery)
142
            ->union($thirdQuery, true);
143
144
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
145
146
        $this->assertSame($expectedQuerySql, $actualQuerySql);
147
        $this->assertSame([], $queryParams);
148
    }
149
150
    public function testBuildWithQuery(): void
151
    {
152
        $expectedQuerySql = DbHelper::replaceQuotes(
153
            <<<SQL
154
            WITH a1 AS (SELECT [[id]] FROM [[t1]] WHERE expr = 1), a2 AS ((SELECT [[id]] FROM [[t2]] INNER JOIN [[a1]] ON t2.id = a1.id WHERE expr = 2) UNION ( SELECT [[id]] FROM [[t3]] WHERE expr = 3 )) SELECT * FROM [[a2]]
155
            SQL,
156
            $this->mock->getDriverName(),
157
        );
158
159
        $with1Query = $this->mock
160
            ->query()
161
            ->select('id')
162
            ->from('t1')
163
            ->where('expr = 1');
164
165
        $with2Query = $this->mock
166
            ->query()
167
            ->select('id')
168
            ->from('t2')
169
            ->innerJoin('a1', 't2.id = a1.id')
170
            ->where('expr = 2');
171
172
        $with3Query = $this->mock
173
            ->query()
174
            ->select('id')
175
            ->from('t3')
176
            ->where('expr = 3');
177
178
        $query = $this->mock
179
            ->query()
180
            ->withQuery($with1Query, 'a1')
181
            ->withQuery($with2Query->union($with3Query), 'a2')
182
            ->from('a2');
183
184
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
185
186
        $this->assertSame($expectedQuerySql, $actualQuerySql);
187
        $this->assertSame([], $queryParams);
188
    }
189
190
    public function testBuildWithQueryRecursive(): void
191
    {
192
        $expectedQuerySql = DbHelper::replaceQuotes(
193
            <<<SQL
194
            WITH RECURSIVE a1 AS (SELECT [[id]] FROM [[t1]] WHERE expr = 1) SELECT * FROM [[a1]]
195
            SQL,
196
            $this->mock->getDriverName(),
197
        );
198
199
        $with1Query = $this->mock
200
            ->query()
201
            ->select('id')
202
            ->from('t1')
203
            ->where('expr = 1');
204
205
        $query = $this->mock
206
            ->query()
207
            ->withQuery($with1Query, 'a1', true)
208
            ->from('a1');
209
210
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
211
212
        $this->assertSame($expectedQuerySql, $actualQuerySql);
213
        $this->assertSame([], $queryParams);
214
    }
215
216
    /**
217
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildWhereExists()
218
     */
219
    public function testBuildWhereExists(string $cond, string $expectedQuerySql): void
220
    {
221
        $expectedQueryParams = [];
222
        $subQuery = $this->mock->query()->select('1')->from('Website w');
223
        $query = $this->mock->query()->select('id')->from('TotalExample t')->where([$cond, $subQuery]);
224
225
        [$actualQuerySql, $actualQueryParams] = $this->queryBuilder->build($query);
226
227
        $this->assertSame($expectedQuerySql, $actualQuerySql);
228
        $this->assertSame($expectedQueryParams, $actualQueryParams);
229
    }
230
231
    public function testBuildWhereExistsWithArrayParameters(): void
232
    {
233
        $expectedQuerySql = DbHelper::replaceQuotes(
234
            <<<SQL
235
            SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE (EXISTS (SELECT [[1]] FROM [[Website]] [[w]] WHERE (w.id = t.website_id) AND (([[w]].[[merchant_id]]=:qp0) AND ([[w]].[[user_id]]=:qp1)))) AND ([[t]].[[some_column]]=:qp2)
236
            SQL,
237
            $this->mock->getDriverName(),
238
        );
239
240
        $expectedQueryParams = [':qp0' => 6, ':qp1' => 210, ':qp2' => 'asd'];
241
242
        $subQuery = $this->mock
243
            ->query()
244
            ->select('1')
245
            ->from('Website w')
246
            ->where('w.id = t.website_id')
247
            ->andWhere(['w.merchant_id' => 6, 'w.user_id' => 210]);
248
249
        $query = $this->mock
250
            ->query()
251
            ->select('id')
252
            ->from('TotalExample t')
253
            ->where(['exists', $subQuery])
254
            ->andWhere(['t.some_column' => 'asd']);
255
256
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
257
258
        $this->assertSame($expectedQuerySql, $actualQuerySql);
259
        $this->assertSame($expectedQueryParams, $queryParams);
260
    }
261
262
    public function testBuildWhereExistsWithParameters(): void
263
    {
264
        $expectedQuerySql = DbHelper::replaceQuotes(
265
            <<<SQL
266
            SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE (EXISTS (SELECT [[1]] FROM [[Website]] [[w]] WHERE (w.id = t.website_id) AND (w.merchant_id = :merchant_id))) AND (t.some_column = :some_value)
267
            SQL,
268
            $this->mock->getDriverName(),
269
        );
270
271
        $expectedQueryParams = [':some_value' => 'asd', ':merchant_id' => 6];
272
273
        $subQuery = $this->mock
274
            ->query()
275
            ->select('1')
276
            ->from('Website w')
277
            ->where('w.id = t.website_id')
278
            ->andWhere('w.merchant_id = :merchant_id', [':merchant_id' => 6]);
279
280
        $query = $this->mock
281
            ->query()
282
            ->select('id')
283
            ->from('TotalExample t')
284
            ->where(['exists', $subQuery])
285
            ->andWhere('t.some_column = :some_value', [':some_value' => 'asd']);
286
287
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
288
289
        $this->assertSame($expectedQuerySql, $actualQuerySql);
290
        $this->assertSame($expectedQueryParams, $queryParams);
291
    }
292
293
    /**
294
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::createDropIndex()
295
     */
296
    public function testCreateDropIndex(string $sql, Closure $builder): void
297
    {
298
        $this->assertSame($this->mock->quoter()->quoteSql($sql), $builder($this->queryBuilder));
299
    }
300
301
    public function testComplexSelect(): void
302
    {
303
        $expressionString = DbHelper::replaceQuotes(
304
            <<<SQL
305
            case t.Status_Id when 1 then 'Acknowledge' when 2 then 'No Action' else 'Unknown Action' END as [[Next Action]]
306
            SQL,
307
            $this->mock->getDriverName(),
308
        );
309
310
        $expected = DbHelper::replaceQuotes(
311
            <<<SQL
312
            SELECT [[t]].[[id]] AS [[ID]], [[gsm]].[[username]] AS [[GSM]], [[part]].[[Part]], [[t]].[[Part_Cost]] AS [[Part Cost]], st_x(location::geometry) AS [[lon]], case t.Status_Id when 1 then 'Acknowledge' when 2 then 'No Action' else 'Unknown Action' END as [[Next Action]] FROM [[tablename]]
313
            SQL,
314
            $this->mock->getDriverName(),
315
        );
316
317
        $this->assertIsString($expressionString);
318
319
        $query = $this->mock
320
            ->query()
321
            ->select(
322
                [
323
                    'ID' => 't.id',
324
                    'gsm.username as GSM',
325
                    'part.Part',
326
                    'Part Cost' => 't.Part_Cost',
327
                    'st_x(location::geometry) as lon',
328
                    new Expression($expressionString),
329
                ]
330
            )
331
            ->from('tablename');
332
333
        [$sql, $params] = $this->queryBuilder->build($query);
334
335
        $this->assertSame($expected, $sql);
336
        $this->assertEmpty($params);
337
    }
338
339
    /**
340
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::delete()
341
     */
342
    public function testDelete(string $table, array|string $condition, string $expectedSQL, array $expectedParams): void
343
    {
344
        $actualParams = [];
345
        $actualSQL = $this->queryBuilder->delete($table, $condition, $actualParams);
346
347
        $this->assertSame($expectedSQL, $actualSQL);
348
        $this->assertSame($expectedParams, $actualParams);
349
    }
350
351
    /**
352
     * {@see https://github.com/yiisoft/yii2/issues/10869}
353
     */
354
    public function testFromIndexHint(): void
355
    {
356
        $query = $this->mock->query()->from([new Expression('{{%user}} USE INDEX (primary)')]);
357
358
        [$sql, $params] = $this->queryBuilder->build($query);
359
360
        $expected = DbHelper::replaceQuotes(
361
            <<<SQL
362
            SELECT * FROM {{%user}} USE INDEX (primary)
363
            SQL,
364
            $this->mock->getDriverName(),
365
        );
366
367
        $this->assertSame($expected, $sql);
368
        $this->assertEmpty($params);
369
370
        $query = $this->mock
371
            ->query()
372
            ->from([new Expression('{{user}} {{t}} FORCE INDEX (primary) IGNORE INDEX FOR ORDER BY (i1)')])
373
            ->leftJoin(['p' => 'profile'], 'user.id = profile.user_id USE INDEX (i2)');
374
375
        [$sql, $params] = $this->queryBuilder->build($query);
376
377
        $expected = DbHelper::replaceQuotes(
378
            <<<SQL
379
            SELECT * FROM {{user}} {{t}} FORCE INDEX (primary) IGNORE INDEX FOR ORDER BY (i1) LEFT JOIN [[profile]] [[p]] ON user.id = profile.user_id USE INDEX (i2)
380
            SQL,
381
            $this->mock->getDriverName(),
382
        );
383
384
        $this->assertSame($expected, $sql);
385
        $this->assertEmpty($params);
386
    }
387
388
    public function testFromSubquery(): void
389
    {
390
        /* subquery */
391
        $subquery = $this->mock->query()->from('user')->where('account_id = accounts.id');
392
        $query = $this->mock->query()->from(['activeusers' => $subquery]);
393
394
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
395
        [$sql, $params] = $this->queryBuilder->build($query);
396
397
        $expected = DbHelper::replaceQuotes(
398
            <<<SQL
399
            SELECT * FROM (SELECT * FROM [[user]] WHERE account_id = accounts.id) [[activeusers]]
400
            SQL,
401
            $this->mock->getDriverName(),
402
        );
403
404
        $this->assertSame($expected, $sql);
405
        $this->assertEmpty($params);
406
407
        /* subquery with params */
408
        $subquery = $this->mock->query()->from('user')->where('account_id = :id', ['id' => 1]);
409
        $query = $this->mock->query()->from(['activeusers' => $subquery])->where('abc = :abc', ['abc' => 'abc']);
410
411
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
412
        [$sql, $params] = $this->queryBuilder->build($query);
413
414
        $expected = DbHelper::replaceQuotes(
415
            <<<SQL
416
            SELECT * FROM (SELECT * FROM [[user]] WHERE account_id = :id) [[activeusers]] WHERE abc = :abc
417
            SQL,
418
            $this->mock->getDriverName(),
419
        );
420
421
        $this->assertSame($expected, $sql);
422
        $this->assertSame(['abc' => 'abc', 'id' => 1], $params);
423
424
        /* simple subquery */
425
        $subquery = '(SELECT * FROM user WHERE account_id = accounts.id)';
426
        $query = $this->mock->query()->from(['activeusers' => $subquery]);
427
428
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
429
        [$sql, $params] = $this->queryBuilder->build($query);
430
431
        $expected = DbHelper::replaceQuotes(
432
            <<<SQL
433
            SELECT * FROM (SELECT * FROM user WHERE account_id = accounts.id) [[activeusers]]
434
            SQL,
435
            $this->mock->getDriverName(),
436
        );
437
438
        $this->assertSame($expected, $sql);
439
        $this->assertEmpty($params);
440
    }
441
442
    public function testGroupBy(): void
443
    {
444
        /* simple string */
445
        $query = $this->mock->query()->select('*')->from('operations')->groupBy('name, date');
446
447
        [$sql, $params] = $this->queryBuilder->build($query);
448
449
        $expected = DbHelper::replaceQuotes(
450
            <<<SQL
451
            SELECT * FROM [[operations]] GROUP BY [[name]], [[date]]
452
            SQL,
453
            $this->mock->getDriverName(),
454
        );
455
456
        $this->assertSame($expected, $sql);
457
        $this->assertEmpty($params);
458
459
        /* array syntax */
460
        $query = $this->mock->query()->select('*')->from('operations')->groupBy(['name', 'date']);
461
462
        [$sql, $params] = $this->queryBuilder->build($query);
463
464
        $expected = DbHelper::replaceQuotes(
465
            <<<SQL
466
            SELECT * FROM [[operations]] GROUP BY [[name]], [[date]]
467
            SQL,
468
            $this->mock->getDriverName(),
469
        );
470
471
        $this->assertSame($expected, $sql);
472
        $this->assertEmpty($params);
473
474
        /* expression */
475
        $query = $this->mock
476
            ->query()
477
            ->select('*')
478
            ->from('operations')
479
            ->where('account_id = accounts.id')
480
            ->groupBy(new Expression('SUBSTR(name, 0, 1), x'));
481
482
        [$sql, $params] = $this->queryBuilder->build($query);
483
484
        $expected = DbHelper::replaceQuotes(
485
            <<<SQL
486
            SELECT * FROM [[operations]] WHERE account_id = accounts.id GROUP BY SUBSTR(name, 0, 1), x
487
            SQL,
488
            $this->mock->getDriverName(),
489
        );
490
491
        $this->assertSame($expected, $sql);
492
        $this->assertEmpty($params);
493
494
        /* expression with params */
495
        $query = $this->mock
496
            ->query()
497
            ->select('*')
498
            ->from('operations')
499
            ->groupBy(new Expression('SUBSTR(name, 0, :to), x', [':to' => 4]));
500
501
        [$sql, $params] = $this->queryBuilder->build($query);
502
503
        $expected = DbHelper::replaceQuotes(
504
            <<<SQL
505
            SELECT * FROM [[operations]] GROUP BY SUBSTR(name, 0, :to), x
506
            SQL,
507
            $this->mock->getDriverName(),
508
        );
509
510
        $this->assertSame($expected, $sql);
511
        $this->assertSame([':to' => 4], $params);
512
    }
513
514
    /**
515
     * {@see https://github.com/yiisoft/yii2/issues/15653}
516
     */
517
    public function testIssue15653(): void
518
    {
519
        $query = $this->mock->query()->from('admin_user')->where(['is_deleted' => false]);
520
        $query->where([])->andWhere(['in', 'id', ['1', '0']]);
521
522
        [$sql, $params] = $this->queryBuilder->build($query);
523
524
        $this->assertSame(
525
            DbHelper::replaceQuotes(
526
                <<<SQL
527
                SELECT * FROM [[admin_user]] WHERE [[id]] IN (:qp0, :qp1)
528
                SQL,
529
                $this->mock->getDriverName(),
530
            ),
531
            $sql,
532
        );
533
        $this->assertSame([':qp0' => '1', ':qp1' => '0'], $params);
534
    }
535
536
    public function testOrderBy(): void
537
    {
538
        /* simple string */
539
        $query = $this->mock->query()->select('*')->from('operations')->orderBy('name ASC, date DESC');
540
541
        [$sql, $params] = $this->queryBuilder->build($query);
542
543
        $expected = DbHelper::replaceQuotes(
544
            <<<SQL
545
            SELECT * FROM [[operations]] ORDER BY [[name]], [[date]] DESC
546
            SQL,
547
            $this->mock->getDriverName(),
548
        );
549
550
        $this->assertSame($expected, $sql);
551
        $this->assertEmpty($params);
552
553
        /* array syntax */
554
        $query = $this->mock->query()->select('*')->from('operations')->orderBy(['name' => SORT_ASC, 'date' => SORT_DESC]);
555
556
        [$sql, $params] = $this->queryBuilder->build($query);
557
558
        $expected = DbHelper::replaceQuotes(
559
            <<<SQL
560
            SELECT * FROM [[operations]] ORDER BY [[name]], [[date]] DESC
561
            SQL,
562
            $this->mock->getDriverName(),
563
        );
564
565
        $this->assertSame($expected, $sql);
566
        $this->assertEmpty($params);
567
568
        /* expression */
569
        $query = $this->mock
570
            ->query()
571
            ->select('*')
572
            ->from('operations')
573
            ->where('account_id = accounts.id')
574
            ->orderBy(new Expression('SUBSTR(name, 3, 4) DESC, x ASC'));
575
576
        [$sql, $params] = $this->queryBuilder->build($query);
577
578
        $expected = DbHelper::replaceQuotes(
579
            <<<SQL
580
            SELECT * FROM [[operations]] WHERE account_id = accounts.id ORDER BY SUBSTR(name, 3, 4) DESC, x ASC
581
            SQL,
582
            $this->mock->getDriverName(),
583
        );
584
585
        $this->assertSame($expected, $sql);
586
        $this->assertEmpty($params);
587
588
        /* expression with params */
589
        $query = $this->mock
590
            ->query()
591
            ->select('*')
592
            ->from('operations')
593
            ->orderBy(new Expression('SUBSTR(name, 3, :to) DESC, x ASC', [':to' => 4]));
594
595
        [$sql, $params] = $this->queryBuilder->build($query);
596
597
        $expected = DbHelper::replaceQuotes(
598
            <<<SQL
599
            SELECT * FROM [[operations]] ORDER BY SUBSTR(name, 3, :to) DESC, x ASC
600
            SQL,
601
            $this->mock->getDriverName(),
602
        );
603
604
        $this->assertSame($expected, $sql);
605
        $this->assertSame([':to' => 4], $params);
606
    }
607
608
    public function testRenameColumn(): void
609
    {
610
        $sql = $this->queryBuilder->renameColumn('alpha', 'string_identifier', 'string_identifier_test');
611
        $this->assertSame(
612
            <<<SQL
613
            ALTER TABLE `alpha` RENAME COLUMN `string_identifier` TO `string_identifier_test`
614
            SQL,
615
            $sql,
616
        );
617
618
        $sql = $this->queryBuilder->renameColumn('alpha', 'string_identifier_test', 'string_identifier');
619
        $this->assertSame(
620
            <<<SQL
621
            ALTER TABLE `alpha` RENAME COLUMN `string_identifier_test` TO `string_identifier`
622
            SQL,
623
            $sql,
624
        );
625
    }
626
627
    public function testRenameTable(): void
628
    {
629
        $sql = $this->queryBuilder->renameTable('table_from', 'table_to');
630
631
        $this->assertSame(
632
            <<<SQL
633
            RENAME TABLE `table_from` TO `table_to`
634
            SQL,
635
            $sql,
636
        );
637
    }
638
639
    public function testSelectExpression(): void
640
    {
641
        $query = $this->mock->query()->select(new Expression('1 AS ab'))->from('tablename');
642
643
        [$sql, $params] = $this->queryBuilder->build($query);
644
645
        $expected = DbHelper::replaceQuotes(
646
            <<<SQL
647
            SELECT 1 AS ab FROM [[tablename]]
648
            SQL,
649
            $this->mock->getDriverName(),
650
        );
651
652
        $this->assertSame($expected, $sql);
653
        $this->assertEmpty($params);
654
655
        $query = $this->mock
656
            ->query()
657
            ->select(new Expression('1 AS ab'))
658
            ->addSelect(new Expression('2 AS cd'))
659
            ->addSelect(['ef' => new Expression('3')])
660
            ->from('tablename');
661
662
        [$sql, $params] = $this->queryBuilder->build($query);
663
664
        $expected = DbHelper::replaceQuotes(
665
            <<<SQL
666
            SELECT 1 AS ab, 2 AS cd, 3 AS [[ef]] FROM [[tablename]]
667
            SQL,
668
            $this->mock->getDriverName(),
669
        );
670
671
        $this->assertSame($expected, $sql);
672
        $this->assertEmpty($params);
673
674
        $query = $this->mock
675
            ->query()
676
            ->select(new Expression('SUBSTR(name, 0, :len)', [':len' => 4]))
677
            ->from('tablename');
678
679
        [$sql, $params] = $this->queryBuilder->build($query);
680
681
        $expected = DbHelper::replaceQuotes(
682
            <<<SQL
683
            SELECT SUBSTR(name, 0, :len) FROM [[tablename]]
684
            SQL,
685
            $this->mock->getDriverName(),
686
        );
687
688
        $this->assertSame($expected, $sql);
689
        $this->assertSame([':len' => 4], $params);
690
    }
691
692
    public function testSelectSubquery(): void
693
    {
694
        $expected = DbHelper::replaceQuotes(
695
            <<<SQL
696
            SELECT *, (SELECT COUNT(*) FROM [[operations]] WHERE account_id = accounts.id) AS [[operations_count]] FROM [[accounts]]
697
            SQL,
698
            $this->mock->getDriverName(),
699
        );
700
701
        $subquery = $this->mock
702
            ->query()
703
            ->select('COUNT(*)')
704
            ->from('operations')
705
            ->where('account_id = accounts.id');
706
707
        $query = $this->mock
708
            ->query()
709
            ->select('*')
710
            ->from('accounts')
711
            ->addSelect(['operations_count' => $subquery]);
712
713
        [$sql, $params] = $this->queryBuilder->build($query);
714
715
        $this->assertSame($expected, $sql);
716
        $this->assertEmpty($params);
717
    }
718
}
719