Passed
Pull Request — master (#380)
by Alexander
05:14 queued 02:35
created

QueryBuilderTest::testFromSubquery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 52
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 30
nc 1
nop 0
dl 0
loc 52
rs 9.44
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Tests\QueryBuilder;
6
7
use Closure;
8
use PHPUnit\Framework\TestCase;
9
use stdClass;
10
use Yiisoft\Db\Expression\Expression;
11
use Yiisoft\Db\Expression\ExpressionInterface;
12
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
13
use Yiisoft\Db\Tests\Support\Assert;
14
use Yiisoft\Db\Tests\Support\DbHelper;
15
use Yiisoft\Db\Tests\Support\Mock;
16
17
/**
18
 * @group db
19
 */
20
final class QueryBuilderTest extends TestCase
21
{
22
    private QueryBuilderInterface $queryBuilder;
23
    private Mock $mock;
24
25
    public function setUp(): void
26
    {
27
        parent::setUp();
28
29
        $this->mock = new Mock();
30
        $this->queryBuilder = $this->mock->queryBuilder();
31
    }
32
33
    public function tearDown(): void
34
    {
35
        parent::tearDown();
36
37
        unset($this->queryBuilder, $this->mock);
38
    }
39
40
    /**
41
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::addDropChecks()
42
     */
43
    public function testAddDropCheck(string $sql, Closure $builder): void
44
    {
45
        $this->assertSame($this->mock->quoter()->quoteSql($sql), $builder($this->queryBuilder));
46
    }
47
48
    public function testAddColumn(): void
49
    {
50
        $this->assertSame(
51
            <<<SQL
52
            ALTER TABLE `user` ADD `age` integer
53
            SQL,
54
            $this->queryBuilder->addColumn('user', 'age', 'integer')
55
        );
56
    }
57
58
    public function testsAddCommentOnColumn(): void
59
    {
60
        $this->assertSame(
61
            <<<SQL
62
            COMMENT ON COLUMN `user`.`name` IS 'This is a comment'
63
            SQL,
64
            $this->queryBuilder->addCommentOnColumn('user', 'name', 'This is a comment')
65
        );
66
    }
67
68
    public function testsAddCommentOnTable(): void
69
    {
70
        $this->assertSame(
71
            <<<SQL
72
            COMMENT ON TABLE `user` IS 'This is a comment'
73
            SQL,
74
            $this->queryBuilder->addCommentOnTable('user', 'This is a comment')
75
        );
76
    }
77
78
    /**
79
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::batchInsert()
80
     */
81
    public function testBatchInsert(
82
        string $table,
83
        array $columns,
84
        array $value,
85
        string|null $expected,
86
        array $expectedParams = []
87
    ): void {
88
        $params = [];
89
        $sql = $this->queryBuilder->batchInsert($table, $columns, $value, $params);
90
91
        $this->assertSame($expected, $sql);
92
        $this->assertSame($expectedParams, $params);
93
    }
94
95
    public function testBuildColumnsWithString(): void
96
    {
97
        $columns = '(id)';
98
99
        $this->assertSame($columns, $this->queryBuilder->buildColumns($columns));
100
    }
101
102
    public function testBuildColumnsWithArray(): void
103
    {
104
        $columns = [
105
            'id',
106
            'name',
107
            'email',
108
            'address',
109
            'status',
110
        ];
111
112
        $expected = '`id`, `name`, `email`, `address`, `status`';
113
114
        $this->assertSame($expected, $this->queryBuilder->buildColumns($columns));
115
    }
116
117
    public function testBuildColumnsWithExpression(): void
118
    {
119
        $columns = [
120
            'id',
121
            'name',
122
            'email',
123
            'address',
124
            'status',
125
            new Expression('COUNT(*)'),
126
        ];
127
128
        $expected = '`id`, `name`, `email`, `address`, `status`, COUNT(*)';
129
130
        $this->assertSame($expected, $this->queryBuilder->buildColumns($columns));
131
    }
132
133
    /**
134
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildConditions()
135
     */
136
    public function testBuildCondition(
137
        array|ExpressionInterface|string $conditions,
138
        string $expected,
139
        array $expectedParams = []
140
    ): void {
141
        $query = $this->mock->query()->where($conditions);
142
        [$sql, $params] = $this->queryBuilder->build($query);
143
144
        $this->assertSame(
145
            'SELECT *' . (
146
                empty($expected) ? '' : ' WHERE ' . DbHelper::replaceQuotes(
147
                    $expected,
148
                    $this->mock->getDriverName(),
149
                )
150
            ),
151
            $sql,
152
        );
153
        $this->assertSame($expectedParams, $params);
154
    }
155
156
    /**
157
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildFilterCondition()
158
     */
159
    public function testBuildFilterCondition(array $condition, string $expected, array $expectedParams): void
160
    {
161
        $query = $this->mock->query()->filterWhere($condition);
162
        [$sql, $params] = $this->queryBuilder->build($query);
163
164
        $this->assertSame(
165
            'SELECT *' . (
166
                empty($expected) ? '' : ' WHERE ' . DbHelper::replaceQuotes(
167
                    $expected,
168
                    $this->mock->getDriverName(),
169
                )
170
            ),
171
            $sql,
172
        );
173
        $this->assertSame($expectedParams, $params);
174
    }
175
176
    /**
177
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildFrom()
178
     */
179
    public function testBuildFrom(string $table, string $expected): void
180
    {
181
        $params = [];
182
        $sql = $this->queryBuilder->buildFrom([$table], $params);
183
        $replacedQuotes = DbHelper::replaceQuotes($expected, $this->mock->getDriverName());
184
185
        $this->assertIsString($replacedQuotes);
186
        $this->assertSame('FROM ' . $replacedQuotes, $sql);
187
    }
188
189
    public function testBuildLimit(): void
190
    {
191
        $query = $this->mock->query()->limit(10);
192
        [$sql, $params] = $this->queryBuilder->build($query);
193
194
        $this->assertSame('SELECT * LIMIT 10', $sql);
195
        $this->assertSame([], $params);
196
    }
197
198
    public function testBuildOffset(): void
199
    {
200
        $query = $this->mock->query()->offset(10);
201
        [$sql, $params] = $this->queryBuilder->build($query);
202
203
        $this->assertSame('SELECT * OFFSET 10', $sql);
204
        $this->assertSame([], $params);
205
    }
206
207
    public function testBuildSelectColumnWithoutParentheses(): void
208
    {
209
        $params = [];
210
        $sql = $this->queryBuilder->buildSelect(['1'], $params);
211
212
        $this->assertSame('SELECT `1`', $sql);
213
    }
214
215
    public function testBuildSelectOptions(): void
216
    {
217
        $query = $this->mock->query()->selectOption('DISTINCT');
218
        [$sql, $params] = $this->queryBuilder->build($query);
219
220
        $this->assertSame('SELECT DISTINCT *', $sql);
221
        $this->assertSame([], $params);
222
    }
223
224
    /**
225
     * This test contains three select queries connected with UNION and UNION ALL constructions.
226
     * It could be useful to use "phpunit --group=db --filter testBuildUnion" command for run it.
227
     */
228
    public function testBuildUnion(): void
229
    {
230
        $expectedQuerySql = DbHelper::replaceQuotes(
231
            <<<SQL
232
            (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 )
233
            SQL,
234
            $this->mock->getDriverName(),
235
        );
236
237
        $secondQuery = $this->mock
238
            ->query()
239
            ->select('id')
240
            ->from('TotalTotalExample t2')
241
            ->where('w > 5');
242
243
        $thirdQuery = $this->mock
244
            ->query()
245
            ->select('id')
246
            ->from('TotalTotalExample t3')
247
            ->where('w = 3');
248
249
        $query = $this->mock
250
            ->query()
251
            ->select('id')
252
            ->from('TotalExample t1')
253
            ->where(['and', 'w > 0', 'x < 2'])
254
            ->union($secondQuery)
255
            ->union($thirdQuery, true);
256
257
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
258
259
        $this->assertSame($expectedQuerySql, $actualQuerySql);
260
        $this->assertSame([], $queryParams);
261
    }
262
263
    public function testBuildWithQuery(): void
264
    {
265
        $expectedQuerySql = DbHelper::replaceQuotes(
266
            <<<SQL
267
            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]]
268
            SQL,
269
            $this->mock->getDriverName(),
270
        );
271
272
        $with1Query = $this->mock
273
            ->query()
274
            ->select('id')
275
            ->from('t1')
276
            ->where('expr = 1');
277
278
        $with2Query = $this->mock
279
            ->query()
280
            ->select('id')
281
            ->from('t2')
282
            ->innerJoin('a1', 't2.id = a1.id')
283
            ->where('expr = 2');
284
285
        $with3Query = $this->mock
286
            ->query()
287
            ->select('id')
288
            ->from('t3')
289
            ->where('expr = 3');
290
291
        $query = $this->mock
292
            ->query()
293
            ->withQuery($with1Query, 'a1')
294
            ->withQuery($with2Query->union($with3Query), 'a2')
295
            ->from('a2');
296
297
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
298
299
        $this->assertSame($expectedQuerySql, $actualQuerySql);
300
        $this->assertSame([], $queryParams);
301
    }
302
303
    public function testBuildWithQueryRecursive(): void
304
    {
305
        $expectedQuerySql = DbHelper::replaceQuotes(
306
            <<<SQL
307
            WITH RECURSIVE a1 AS (SELECT [[id]] FROM [[t1]] WHERE expr = 1) SELECT * FROM [[a1]]
308
            SQL,
309
            $this->mock->getDriverName(),
310
        );
311
312
        $with1Query = $this->mock
313
            ->query()
314
            ->select('id')
315
            ->from('t1')
316
            ->where('expr = 1');
317
318
        $query = $this->mock
319
            ->query()
320
            ->withQuery($with1Query, 'a1', true)
321
            ->from('a1');
322
323
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
324
325
        $this->assertSame($expectedQuerySql, $actualQuerySql);
326
        $this->assertSame([], $queryParams);
327
    }
328
329
    /**
330
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildWhereExists()
331
     */
332
    public function testBuildWhereExists(string $cond, string $expectedQuerySql): void
333
    {
334
        $expectedQueryParams = [];
335
        $subQuery = $this->mock->query()->select('1')->from('Website w');
336
        $query = $this->mock->query()->select('id')->from('TotalExample t')->where([$cond, $subQuery]);
337
338
        [$actualQuerySql, $actualQueryParams] = $this->queryBuilder->build($query);
339
340
        $this->assertSame($expectedQuerySql, $actualQuerySql);
341
        $this->assertSame($expectedQueryParams, $actualQueryParams);
342
    }
343
344
    public function testBuildWhereExistsWithArrayParameters(): void
345
    {
346
        $expectedQuerySql = DbHelper::replaceQuotes(
347
            <<<SQL
348
            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)
349
            SQL,
350
            $this->mock->getDriverName(),
351
        );
352
353
        $expectedQueryParams = [':qp0' => 6, ':qp1' => 210, ':qp2' => 'asd'];
354
355
        $subQuery = $this->mock
356
            ->query()
357
            ->select('1')
358
            ->from('Website w')
359
            ->where('w.id = t.website_id')
360
            ->andWhere(['w.merchant_id' => 6, 'w.user_id' => 210]);
361
362
        $query = $this->mock
363
            ->query()
364
            ->select('id')
365
            ->from('TotalExample t')
366
            ->where(['exists', $subQuery])
367
            ->andWhere(['t.some_column' => 'asd']);
368
369
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
370
371
        $this->assertSame($expectedQuerySql, $actualQuerySql);
372
        $this->assertSame($expectedQueryParams, $queryParams);
373
    }
374
375
    public function testBuildWhereExistsWithParameters(): void
376
    {
377
        $expectedQuerySql = DbHelper::replaceQuotes(
378
            <<<SQL
379
            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)
380
            SQL,
381
            $this->mock->getDriverName(),
382
        );
383
384
        $expectedQueryParams = [':some_value' => 'asd', ':merchant_id' => 6];
385
386
        $subQuery = $this->mock
387
            ->query()
388
            ->select('1')
389
            ->from('Website w')
390
            ->where('w.id = t.website_id')
391
            ->andWhere('w.merchant_id = :merchant_id', [':merchant_id' => 6]);
392
393
        $query = $this->mock
394
            ->query()
395
            ->select('id')
396
            ->from('TotalExample t')
397
            ->where(['exists', $subQuery])
398
            ->andWhere('t.some_column = :some_value', [':some_value' => 'asd']);
399
400
        [$actualQuerySql, $queryParams] = $this->queryBuilder->build($query);
401
402
        $this->assertSame($expectedQuerySql, $actualQuerySql);
403
        $this->assertSame($expectedQueryParams, $queryParams);
404
    }
405
406
    /**
407
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::createDropIndex()
408
     */
409
    public function testCreateDropIndex(string $sql, Closure $builder): void
410
    {
411
        $this->assertSame($this->mock->quoter()->quoteSql($sql), $builder($this->queryBuilder));
412
    }
413
414
    public function testComplexSelect(): void
415
    {
416
        $expressionString = DbHelper::replaceQuotes(
417
            <<<SQL
418
            case t.Status_Id when 1 then 'Acknowledge' when 2 then 'No Action' else 'Unknown Action' END as [[Next Action]]
419
            SQL,
420
            $this->mock->getDriverName(),
421
        );
422
423
        $expected = DbHelper::replaceQuotes(
424
            <<<SQL
425
            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]]
426
            SQL,
427
            $this->mock->getDriverName(),
428
        );
429
430
        $this->assertIsString($expressionString);
431
432
        $query = $this->mock
433
            ->query()
434
            ->select(
435
                [
436
                    'ID' => 't.id',
437
                    'gsm.username as GSM',
438
                    'part.Part',
439
                    'Part Cost' => 't.Part_Cost',
440
                    'st_x(location::geometry) as lon',
441
                    new Expression($expressionString),
442
                ]
443
            )
444
            ->from('tablename');
445
446
        [$sql, $params] = $this->queryBuilder->build($query);
447
448
        $this->assertSame($expected, $sql);
449
        $this->assertEmpty($params);
450
    }
451
452
    /**
453
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::delete()
454
     */
455
    public function testDelete(string $table, array|string $condition, string $expectedSQL, array $expectedParams): void
456
    {
457
        $actualParams = [];
458
        $actualSQL = $this->queryBuilder->delete($table, $condition, $actualParams);
459
460
        $this->assertSame($expectedSQL, $actualSQL);
461
        $this->assertSame($expectedParams, $actualParams);
462
    }
463
464
    /**
465
     * {@see https://github.com/yiisoft/yii2/issues/10869}
466
     */
467
    public function testFromIndexHint(): void
468
    {
469
        $query = $this->mock->query()->from([new Expression('{{%user}} USE INDEX (primary)')]);
470
471
        [$sql, $params] = $this->queryBuilder->build($query);
472
473
        $expected = DbHelper::replaceQuotes(
474
            <<<SQL
475
            SELECT * FROM {{%user}} USE INDEX (primary)
476
            SQL,
477
            $this->mock->getDriverName(),
478
        );
479
480
        $this->assertSame($expected, $sql);
481
        $this->assertEmpty($params);
482
483
        $query = $this->mock
484
            ->query()
485
            ->from([new Expression('{{user}} {{t}} FORCE INDEX (primary) IGNORE INDEX FOR ORDER BY (i1)')])
486
            ->leftJoin(['p' => 'profile'], 'user.id = profile.user_id USE INDEX (i2)');
487
488
        [$sql, $params] = $this->queryBuilder->build($query);
489
490
        $expected = DbHelper::replaceQuotes(
491
            <<<SQL
492
            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)
493
            SQL,
494
            $this->mock->getDriverName(),
495
        );
496
497
        $this->assertSame($expected, $sql);
498
        $this->assertEmpty($params);
499
    }
500
501
    public function testFromSubquery(): void
502
    {
503
        /* subquery */
504
        $subquery = $this->mock->query()->from('user')->where('account_id = accounts.id');
505
        $query = $this->mock->query()->from(['activeusers' => $subquery]);
506
507
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
508
        [$sql, $params] = $this->queryBuilder->build($query);
509
510
        $expected = DbHelper::replaceQuotes(
511
            <<<SQL
512
            SELECT * FROM (SELECT * FROM [[user]] WHERE account_id = accounts.id) [[activeusers]]
513
            SQL,
514
            $this->mock->getDriverName(),
515
        );
516
517
        $this->assertSame($expected, $sql);
518
        $this->assertEmpty($params);
519
520
        /* subquery with params */
521
        $subquery = $this->mock->query()->from('user')->where('account_id = :id', ['id' => 1]);
522
        $query = $this->mock->query()->from(['activeusers' => $subquery])->where('abc = :abc', ['abc' => 'abc']);
523
524
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
525
        [$sql, $params] = $this->queryBuilder->build($query);
526
527
        $expected = DbHelper::replaceQuotes(
528
            <<<SQL
529
            SELECT * FROM (SELECT * FROM [[user]] WHERE account_id = :id) [[activeusers]] WHERE abc = :abc
530
            SQL,
531
            $this->mock->getDriverName(),
532
        );
533
534
        $this->assertSame($expected, $sql);
535
        $this->assertSame(['abc' => 'abc', 'id' => 1], $params);
536
537
        /* simple subquery */
538
        $subquery = '(SELECT * FROM user WHERE account_id = accounts.id)';
539
        $query = $this->mock->query()->from(['activeusers' => $subquery]);
540
541
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
542
        [$sql, $params] = $this->queryBuilder->build($query);
543
544
        $expected = DbHelper::replaceQuotes(
545
            <<<SQL
546
            SELECT * FROM (SELECT * FROM user WHERE account_id = accounts.id) [[activeusers]]
547
            SQL,
548
            $this->mock->getDriverName(),
549
        );
550
551
        $this->assertSame($expected, $sql);
552
        $this->assertEmpty($params);
553
    }
554
555
    public function testGroupBy(): void
556
    {
557
        /* simple string */
558
        $query = $this->mock->query()->select('*')->from('operations')->groupBy('name, date');
559
560
        [$sql, $params] = $this->queryBuilder->build($query);
561
562
        $expected = DbHelper::replaceQuotes(
563
            <<<SQL
564
            SELECT * FROM [[operations]] GROUP BY [[name]], [[date]]
565
            SQL,
566
            $this->mock->getDriverName(),
567
        );
568
569
        $this->assertSame($expected, $sql);
570
        $this->assertEmpty($params);
571
572
        /* array syntax */
573
        $query = $this->mock->query()->select('*')->from('operations')->groupBy(['name', 'date']);
574
575
        [$sql, $params] = $this->queryBuilder->build($query);
576
577
        $expected = DbHelper::replaceQuotes(
578
            <<<SQL
579
            SELECT * FROM [[operations]] GROUP BY [[name]], [[date]]
580
            SQL,
581
            $this->mock->getDriverName(),
582
        );
583
584
        $this->assertSame($expected, $sql);
585
        $this->assertEmpty($params);
586
587
        /* expression */
588
        $query = $this->mock
589
            ->query()
590
            ->select('*')
591
            ->from('operations')
592
            ->where('account_id = accounts.id')
593
            ->groupBy(new Expression('SUBSTR(name, 0, 1), x'));
594
595
        [$sql, $params] = $this->queryBuilder->build($query);
596
597
        $expected = DbHelper::replaceQuotes(
598
            <<<SQL
599
            SELECT * FROM [[operations]] WHERE account_id = accounts.id GROUP BY SUBSTR(name, 0, 1), x
600
            SQL,
601
            $this->mock->getDriverName(),
602
        );
603
604
        $this->assertSame($expected, $sql);
605
        $this->assertEmpty($params);
606
607
        /* expression with params */
608
        $query = $this->mock
609
            ->query()
610
            ->select('*')
611
            ->from('operations')
612
            ->groupBy(new Expression('SUBSTR(name, 0, :to), x', [':to' => 4]));
613
614
        [$sql, $params] = $this->queryBuilder->build($query);
615
616
        $expected = DbHelper::replaceQuotes(
617
            <<<SQL
618
            SELECT * FROM [[operations]] GROUP BY SUBSTR(name, 0, :to), x
619
            SQL,
620
            $this->mock->getDriverName(),
621
        );
622
623
        $this->assertSame($expected, $sql);
624
        $this->assertSame([':to' => 4], $params);
625
    }
626
627
    /**
628
     * {@see https://github.com/yiisoft/yii2/issues/15653}
629
     */
630
    public function testIssue15653(): void
631
    {
632
        $query = $this->mock->query()->from('admin_user')->where(['is_deleted' => false]);
633
        $query->where([])->andWhere(['in', 'id', ['1', '0']]);
634
635
        [$sql, $params] = $this->queryBuilder->build($query);
636
637
        $this->assertSame(
638
            DbHelper::replaceQuotes(
639
                <<<SQL
640
                SELECT * FROM [[admin_user]] WHERE [[id]] IN (:qp0, :qp1)
641
                SQL,
642
                $this->mock->getDriverName(),
643
            ),
644
            $sql,
645
        );
646
        $this->assertSame([':qp0' => '1', ':qp1' => '0'], $params);
647
    }
648
649
    public function testOrderBy(): void
650
    {
651
        /* simple string */
652
        $query = $this->mock->query()->select('*')->from('operations')->orderBy('name ASC, date DESC');
653
654
        [$sql, $params] = $this->queryBuilder->build($query);
655
656
        $expected = DbHelper::replaceQuotes(
657
            <<<SQL
658
            SELECT * FROM [[operations]] ORDER BY [[name]], [[date]] DESC
659
            SQL,
660
            $this->mock->getDriverName(),
661
        );
662
663
        $this->assertSame($expected, $sql);
664
        $this->assertEmpty($params);
665
666
        /* array syntax */
667
        $query = $this->mock->query()->select('*')->from('operations')->orderBy(['name' => SORT_ASC, 'date' => SORT_DESC]);
668
669
        [$sql, $params] = $this->queryBuilder->build($query);
670
671
        $expected = DbHelper::replaceQuotes(
672
            <<<SQL
673
            SELECT * FROM [[operations]] ORDER BY [[name]], [[date]] DESC
674
            SQL,
675
            $this->mock->getDriverName(),
676
        );
677
678
        $this->assertSame($expected, $sql);
679
        $this->assertEmpty($params);
680
681
        /* expression */
682
        $query = $this->mock
683
            ->query()
684
            ->select('*')
685
            ->from('operations')
686
            ->where('account_id = accounts.id')
687
            ->orderBy(new Expression('SUBSTR(name, 3, 4) DESC, x ASC'));
688
689
        [$sql, $params] = $this->queryBuilder->build($query);
690
691
        $expected = DbHelper::replaceQuotes(
692
            <<<SQL
693
            SELECT * FROM [[operations]] WHERE account_id = accounts.id ORDER BY SUBSTR(name, 3, 4) DESC, x ASC
694
            SQL,
695
            $this->mock->getDriverName(),
696
        );
697
698
        $this->assertSame($expected, $sql);
699
        $this->assertEmpty($params);
700
701
        /* expression with params */
702
        $query = $this->mock
703
            ->query()
704
            ->select('*')
705
            ->from('operations')
706
            ->orderBy(new Expression('SUBSTR(name, 3, :to) DESC, x ASC', [':to' => 4]));
707
708
        [$sql, $params] = $this->queryBuilder->build($query);
709
710
        $expected = DbHelper::replaceQuotes(
711
            <<<SQL
712
            SELECT * FROM [[operations]] ORDER BY SUBSTR(name, 3, :to) DESC, x ASC
713
            SQL,
714
            $this->mock->getDriverName(),
715
        );
716
717
        $this->assertSame($expected, $sql);
718
        $this->assertSame([':to' => 4], $params);
719
    }
720
721
    public function testRenameColumn(): void
722
    {
723
        $sql = $this->queryBuilder->renameColumn('alpha', 'string_identifier', 'string_identifier_test');
724
        $this->assertSame(
725
            <<<SQL
726
            ALTER TABLE `alpha` RENAME COLUMN `string_identifier` TO `string_identifier_test`
727
            SQL,
728
            $sql,
729
        );
730
731
        $sql = $this->queryBuilder->renameColumn('alpha', 'string_identifier_test', 'string_identifier');
732
        $this->assertSame(
733
            <<<SQL
734
            ALTER TABLE `alpha` RENAME COLUMN `string_identifier_test` TO `string_identifier`
735
            SQL,
736
            $sql,
737
        );
738
    }
739
740
    public function testRenameTable(): void
741
    {
742
        $sql = $this->queryBuilder->renameTable('table_from', 'table_to');
743
744
        $this->assertSame(
745
            <<<SQL
746
            RENAME TABLE `table_from` TO `table_to`
747
            SQL,
748
            $sql,
749
        );
750
    }
751
752
    public function testSelectExpression(): void
753
    {
754
        $query = $this->mock->query()->select(new Expression('1 AS ab'))->from('tablename');
755
756
        [$sql, $params] = $this->queryBuilder->build($query);
757
758
        $expected = DbHelper::replaceQuotes(
759
            <<<SQL
760
            SELECT 1 AS ab FROM [[tablename]]
761
            SQL,
762
            $this->mock->getDriverName(),
763
        );
764
765
        $this->assertSame($expected, $sql);
766
        $this->assertEmpty($params);
767
768
        $query = $this->mock
769
            ->query()
770
            ->select(new Expression('1 AS ab'))
771
            ->addSelect(new Expression('2 AS cd'))
772
            ->addSelect(['ef' => new Expression('3')])
773
            ->from('tablename');
774
775
        [$sql, $params] = $this->queryBuilder->build($query);
776
777
        $expected = DbHelper::replaceQuotes(
778
            <<<SQL
779
            SELECT 1 AS ab, 2 AS cd, 3 AS [[ef]] FROM [[tablename]]
780
            SQL,
781
            $this->mock->getDriverName(),
782
        );
783
784
        $this->assertSame($expected, $sql);
785
        $this->assertEmpty($params);
786
787
        $query = $this->mock
788
            ->query()
789
            ->select(new Expression('SUBSTR(name, 0, :len)', [':len' => 4]))
790
            ->from('tablename');
791
792
        [$sql, $params] = $this->queryBuilder->build($query);
793
794
        $expected = DbHelper::replaceQuotes(
795
            <<<SQL
796
            SELECT SUBSTR(name, 0, :len) FROM [[tablename]]
797
            SQL,
798
            $this->mock->getDriverName(),
799
        );
800
801
        $this->assertSame($expected, $sql);
802
        $this->assertSame([':len' => 4], $params);
803
    }
804
805
    public function testSelectExists(): void
806
    {
807
        $sql = $this->queryBuilder->selectExists('SELECT 1 FROM `table` WHERE `id` = 1');
808
809
        $this->assertSame('SELECT EXISTS(SELECT 1 FROM `table` WHERE `id` = 1)', $sql);
810
    }
811
812
    public function testSelectSubquery(): void
813
    {
814
        $expected = DbHelper::replaceQuotes(
815
            <<<SQL
816
            SELECT *, (SELECT COUNT(*) FROM [[operations]] WHERE account_id = accounts.id) AS [[operations_count]] FROM [[accounts]]
817
            SQL,
818
            $this->mock->getDriverName(),
819
        );
820
821
        $subquery = $this->mock
822
            ->query()
823
            ->select('COUNT(*)')
824
            ->from('operations')
825
            ->where('account_id = accounts.id');
826
827
        $query = $this->mock
828
            ->query()
829
            ->select('*')
830
            ->from('accounts')
831
            ->addSelect(['operations_count' => $subquery]);
832
833
        [$sql, $params] = $this->queryBuilder->build($query);
834
835
        $this->assertSame($expected, $sql);
836
        $this->assertEmpty($params);
837
    }
838
839
    public function testSetConditionClasses(): void
840
    {
841
        $this->queryBuilder->setConditionClasses(['stdClass' => stdClass::class]);
842
        $dqlBuilder = Assert::getInaccessibleProperty($this->queryBuilder, 'dqlBuilder');
843
        $conditionClasses = Assert::getInaccessibleProperty($dqlBuilder, 'conditionClasses');
844
845
        $this->assertSame(stdClass::class, $conditionClasses['stdClass']);
846
    }
847
848
    public function testSelectExpressionBuilder(): void
849
    {
850
        $this->queryBuilder->setExpressionBuilders(['stdClass' => stdClass::class]);
851
        $dqlBuilder = Assert::getInaccessibleProperty($this->queryBuilder, 'dqlBuilder');
852
        $expressionBuilders = Assert::getInaccessibleProperty($dqlBuilder, 'expressionBuilders');
853
854
        $this->assertSame(stdClass::class, $expressionBuilders['stdClass']);
855
    }
856
857
    public function testSetSeparator(): void
858
    {
859
        $this->queryBuilder->setSeparator(' ');
860
        [$sql, $params] = $this->queryBuilder->build($this->mock->query()->select('*')->from('table'));
861
862
        $this->assertSame('SELECT * FROM `table`', $sql);
863
        $this->assertEmpty($params);
864
865
        $this->queryBuilder->setSeparator("\n");
866
        [$sql, $params] = $this->queryBuilder->build($this->mock->query()->select('*')->from('table'));
867
868
        $this->assertSame(
869
            <<<SQL
870
            SELECT *
871
            FROM `table`
872
            SQL,
873
            $sql,
874
        );
875
        $this->assertEmpty($params);
876
    }
877
}
878