Passed
Pull Request — master (#380)
by Wilmer
05:23 queued 02:13
created

QueryBuilderTest::testBuildColumnsWithArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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