Passed
Branch add-more-test-schema (1ebac6)
by Wilmer
02:54
created

AbstractQueryBuilderTest   F

Complexity

Total Complexity 90

Size/Duplication

Total Lines 1919
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1038
dl 0
loc 1919
rs 1.448
c 1
b 0
f 0
wmc 90

How to fix   Complexity   

Complex Class

Complex classes like AbstractQueryBuilderTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractQueryBuilderTest, and based on these observations, apply Extract Interface, too.

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 stdClass;
10
use Yiisoft\Arrays\ArrayHelper;
11
use Yiisoft\Db\Exception\InvalidArgumentException;
12
use Yiisoft\Db\Exception\NotSupportedException;
13
use Yiisoft\Db\Expression\Expression;
14
use Yiisoft\Db\Expression\ExpressionBuilderInterface;
15
use Yiisoft\Db\Expression\ExpressionInterface;
16
use Yiisoft\Db\Query\Query;
17
use Yiisoft\Db\Query\QueryInterface;
18
use Yiisoft\Db\QueryBuilder\Condition\SimpleCondition;
19
use Yiisoft\Db\Schema\QuoterInterface;
20
use Yiisoft\Db\Schema\Schema;
21
use Yiisoft\Db\Tests\Support\Assert;
22
use Yiisoft\Db\Tests\Support\DbHelper;
23
use Yiisoft\Db\Tests\Support\TestTrait;
24
25
abstract class AbstractQueryBuilderTest extends TestCase
26
{
27
    use TestTrait;
28
29
    public function testAddCheck(): void
30
    {
31
        $db = $this->getConnection();
32
33
        $qb = $db->getQueryBuilder();
34
        $sql = $qb->addCheck('T_constraints_1', 'CN_check', '[[C_not_null]] > 100');
35
36
        $this->assertSame(
37
            DbHelper::replaceQuotes(
38
                <<<SQL
39
                ALTER TABLE [[CN_check]] ADD CONSTRAINT [[T_constraints_1]] CHECK ([[C_not_null]] > 100)
40
                SQL,
41
                $db->getName(),
42
            ),
43
            $sql,
44
        );
45
    }
46
47
    public function testAddColumn(): void
48
    {
49
        $db = $this->getConnection();
50
51
        $qb = $db->getQueryBuilder();
52
        $schema = $db->getSchema();
53
        $sql = $qb->addColumn('table', 'column', $schema::TYPE_STRING);
54
55
        $this->assertSame(
56
            DbHelper::replaceQuotes(
57
                <<<SQL
58
                ALTER TABLE [[table]] ADD [[column]]
59
                SQL . ' ' . $qb->getColumnType($schema::TYPE_STRING),
60
                $db->getName(),
61
            ),
62
            $sql,
63
        );
64
    }
65
66
    public function testAddCommentOnColumn(): void
67
    {
68
        $db = $this->getConnection();
69
70
        $qb = $db->getQueryBuilder();
71
        $sql = $qb->addCommentOnColumn('customer', 'id', 'Primary key.');
72
73
        $this->assertSame(
74
            DbHelper::replaceQuotes(
75
                <<<SQL
76
                COMMENT ON COLUMN [[customer]].[[id]] IS 'Primary key.'
77
                SQL,
78
                $db->getName(),
79
            ),
80
            $sql,
81
        );
82
    }
83
84
    public function testAddCommentOnTable(): void
85
    {
86
        $db = $this->getConnection();
87
88
        $qb = $db->getQueryBuilder();
89
        $sql = $qb->addCommentOnTable('customer', 'Customer table.');
90
91
        $this->assertSame(
92
            DbHelper::replaceQuotes(
93
                <<<SQL
94
                COMMENT ON TABLE [[customer]] IS 'Customer table.'
95
                SQL,
96
                $db->getName(),
97
            ),
98
            $sql,
99
        );
100
    }
101
102
    public function testAddDefaultValue(): void
103
    {
104
        $db = $this->getConnection();
105
106
        $qb = $db->getQueryBuilder();
107
        $sql = $qb->addDefaultValue('CN_pk', 'T_constraints_1', 'C_default', 1);
108
109
        $this->assertSame(
110
            DbHelper::replaceQuotes(
111
                <<<SQL
112
                ALTER TABLE [[T_constraints_1]] ALTER COLUMN [[C_default]] SET DEFAULT 1
113
                SQL,
114
                $db->getName(),
115
            ),
116
            $sql,
117
        );
118
    }
119
120
    /**
121
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::addForeignKey()
122
     */
123
    public function testAddForeignKey(
124
        string $name,
125
        string $table,
126
        array|string $columns,
127
        string $refTable,
128
        array|string $refColumns,
129
        string|null $delete,
130
        string|null $update,
131
        string $expected
132
    ): void {
133
        $db = $this->getConnection();
134
135
        $qb = $db->getQueryBuilder();
136
        $sql = $qb->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update);
137
138
        $this->assertSame($expected, $sql);
139
    }
140
141
    /**
142
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::addPrimaryKey()
143
     */
144
    public function testAddPrimaryKey(string $name, string $table, array|string $columns, string $expected): void
145
    {
146
        $db = $this->getConnection();
147
148
        $qb = $db->getQueryBuilder();
149
        $sql = $qb->addPrimaryKey($name, $table, $columns);
150
151
        $this->assertSame($expected, $sql);
152
    }
153
154
    /**
155
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::addUnique()
156
     */
157
    public function testAddUnique(string $name, string $table, array|string $columns, string $expected): void
158
    {
159
        $db = $this->getConnection();
160
161
        $qb = $db->getQueryBuilder();
162
        $sql = $qb->addUnique($name, $table, $columns);
163
164
        $this->assertSame($expected, $sql);
165
    }
166
167
    public function testAlterColumn(): void
168
    {
169
        $db = $this->getConnection();
170
171
        $qb = $db->getQueryBuilder();
172
        $schema = $db->getSchema();
173
        $sql = $qb->alterColumn('customer', 'email', $schema::TYPE_STRING);
174
175
        $this->assertSame(
176
            DbHelper::replaceQuotes(
177
                <<<SQL
178
                ALTER TABLE [[customer]] CHANGE [[email]] [[email]]
179
                SQL . ' ' . $qb->getColumnType($schema::TYPE_STRING),
180
                $db->getName(),
181
            ),
182
            $sql,
183
        );
184
    }
185
186
    /**
187
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::batchInsert()
188
     */
189
    public function testBatchInsert(string $table, array $columns, array $rows, string $expected): void
190
    {
191
        $db = $this->getConnection();
192
193
        $qb = $db->getQueryBuilder();
194
        $sql = $qb->batchInsert($table, $columns, $rows);
195
196
        $this->assertSame($expected, $sql);
197
    }
198
199
    /**
200
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildCondition()
201
     */
202
    public function testBuildCondition(
203
        array|ExpressionInterface|string $condition,
204
        string|null $expected,
205
        array $expectedParams
206
    ): void {
207
        $db = $this->getConnection();
208
209
        $query = (new Query($db))->where($condition);
210
211
        [$sql, $params] = $db->getQueryBuilder()->build($query);
212
213
        $replacedQuotes = DbHelper::replaceQuotes($expected, $db->getName());
214
215
        $this->assertIsString($replacedQuotes);
216
        $this->assertEquals('SELECT *' . (empty($expected) ? '' : ' WHERE ' . $replacedQuotes), $sql);
217
        $this->assertEquals($expectedParams, $params);
218
    }
219
220
    public function testBuildColumnsWithString(): void
221
    {
222
        $db = $this->getConnection();
223
224
        $qb = $db->getQueryBuilder();
225
226
        $this->assertSame('(id)', $qb->buildColumns('(id)'));
227
    }
228
229
    public function testBuildColumnsWithArray(): void
230
    {
231
        $db = $this->getConnection();
232
233
        $qb = $db->getQueryBuilder();
234
235
        $this->assertSame(
236
            DbHelper::replaceQuotes('[[id]], [[name]], [[email]], [[address]], [[status]]', $db->getName()),
237
            $qb->buildColumns(['id', 'name', 'email', 'address', 'status']),
238
        );
239
    }
240
241
    public function testBuildColumnsWithExpression(): void
242
    {
243
        $db = $this->getConnection();
244
245
        $qb = $db->getQueryBuilder();
246
247
        $this->assertSame(
248
            DbHelper::replaceQuotes(
249
                '[[id]], [[name]], [[email]], [[address]], [[status]], COUNT(*)',
250
                $db->getName(),
251
            ),
252
            $qb->buildColumns(['id', 'name', 'email', 'address', 'status', new Expression('COUNT(*)')]),
253
        );
254
    }
255
256
    /**
257
     * {@see https://github.com/yiisoft/yii2/issues/15653}
258
     */
259
    public function testBuildIssue15653(): void
260
    {
261
        $db = $this->getConnection();
262
263
        $qb = $db->getQueryBuilder();
264
        $query = (new Query($db))->from('admin_user')->where(['is_deleted' => false]);
265
        $query->where([])->andWhere(['in', 'id', ['1', '0']]);
266
267
        [$sql, $params] = $qb->build($query);
268
269
        $this->assertSame(
270
            DbHelper::replaceQuotes(
271
                <<<SQL
272
                SELECT * FROM [[admin_user]] WHERE [[id]] IN (:qp0, :qp1)
273
                SQL,
274
                $db->getName(),
275
            ),
276
            $sql,
277
        );
278
        $this->assertSame([':qp0' => '1', ':qp1' => '0'], $params);
279
    }
280
281
    /**
282
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildFilterCondition()
283
     */
284
    public function testBuildFilterCondition(array $condition, string $expected, array $expectedParams): void
285
    {
286
        $db = $this->getConnection();
287
288
        $qb = $db->getQueryBuilder();
289
        $query = (new Query($db))->filterWhere($condition);
290
291
        [$sql, $params] = $qb->build($query);
292
293
        $this->assertSame(
294
            'SELECT *' . (
295
                empty($expected) ? '' : ' WHERE ' . DbHelper::replaceQuotes($expected, $db->getName())
296
            ),
297
            $sql,
298
        );
299
        $this->assertSame($expectedParams, $params);
300
    }
301
302
    public function testBuildFrom(): void
303
    {
304
        $db = $this->getConnection();
305
306
        $qb = $db->getQueryBuilder();
307
        $query = (new Query($db))->from('admin_user');
308
        $params = [];
309
310
        $this->assertSame(
311
            DbHelper::replaceQuotes(
312
                <<<SQL
313
                FROM [[admin_user]]
314
                SQL,
315
                $db->getName(),
316
            ),
317
            $qb->buildFrom($query->getFrom(), $params),
318
        );
319
    }
320
321
    public function testBuildGroupBy(): void
322
    {
323
        $db = $this->getConnection();
324
325
        $qb = $db->getQueryBuilder();
326
        $query = (new Query($db))->from('admin_user')->groupBy(['id', 'name']);
327
        $params = [];
328
329
        $this->assertSame(
330
            DbHelper::replaceQuotes(
331
                <<<SQL
332
                GROUP BY [[id]], [[name]]
333
                SQL,
334
                $db->getName(),
335
            ),
336
            $qb->buildGroupBy($query->getGroupBy(), $params),
337
        );
338
    }
339
340
    public function testBuildHaving(): void
341
    {
342
        $db = $this->getConnection();
343
344
        $qb = $db->getQueryBuilder();
345
        $query = (new Query($db))->from('admin_user')->having(['id' => 1]);
346
        $params = [];
347
348
        $this->assertSame(
349
            DbHelper::replaceQuotes(
350
                <<<SQL
351
                HAVING [[id]]=:qp0
352
                SQL,
353
                $db->getName(),
354
            ),
355
            $qb->buildHaving($query->getHaving(), $params),
356
        );
357
    }
358
359
    public function testBuildJoin(): void
360
    {
361
        $db = $this->getConnection();
362
363
        $qb = $db->getQueryBuilder();
364
        $query = (new Query($db))
365
            ->from('admin_user')
366
            ->join('INNER JOIN', 'admin_profile', 'admin_user.id = admin_profile.user_id');
367
        $params = [];
368
369
        $this->assertSame(
370
            DbHelper::replaceQuotes(
371
                <<<SQL
372
                INNER JOIN [[admin_profile]] ON admin_user.id = admin_profile.user_id
373
                SQL,
374
                $db->getName(),
375
            ),
376
            $qb->buildJoin($query->getJoin(), $params),
377
        );
378
    }
379
380
    /**
381
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildLikeCondition()
382
     */
383
    public function testBuildLikeCondition(
384
        array|ExpressionInterface $condition,
385
        string $expected,
386
        array $expectedParams
387
    ): void {
388
        $db = $this->getConnection();
389
390
        $query = (new Query($db))->where($condition);
391
392
        [$sql, $params] = $db->getQueryBuilder()->build($query);
393
394
        $replacedQuotes = DbHelper::replaceQuotes($expected, $db->getName());
395
396
        $this->assertIsString($replacedQuotes);
397
        $this->assertSame('SELECT *' . (empty($expected) ? '' : ' WHERE ' . $replacedQuotes), $sql);
398
        $this->assertSame($expectedParams, $params);
399
    }
400
401
    public function testBuildLimit(): void
402
    {
403
        $db = $this->getConnection();
404
405
        $qb = $db->getQueryBuilder();
406
        $query = (new Query($db))->from('admin_user')->limit(10);
407
408
        $this->assertSame('LIMIT 10', $qb->buildLimit($query->getLimit(), 0));
409
    }
410
411
    public function testBuildLimitOffset(): void
412
    {
413
        $db = $this->getConnection();
414
415
        $qb = $db->getQueryBuilder();
416
        $query = (new Query($db))->from('admin_user')->limit(10)->offset(5);
417
418
        $this->assertSame('LIMIT 10 OFFSET 5', $qb->buildLimit($query->getLimit(), $query->getOffset()));
419
    }
420
421
    public function testBuildOrderBy(): void
422
    {
423
        $db = $this->getConnection();
424
425
        $qb = $db->getQueryBuilder();
426
        $query = (new Query($db))->from('admin_user')->orderBy(['id' => SORT_ASC, 'name' => SORT_DESC]);
427
        $params = [];
428
429
        $this->assertSame(
430
            DbHelper::replaceQuotes(
431
                <<<SQL
432
                ORDER BY [[id]], [[name]] DESC
433
                SQL,
434
                $db->getName(),
435
            ),
436
            $qb->buildOrderBy($query->getOrderBy(), $params),
437
        );
438
    }
439
440
    public function testBuildOrderByAndLimit(): void
441
    {
442
        $db = $this->getConnection();
443
444
        $qb = $db->getQueryBuilder();
445
        $query = (new Query($db))
446
            ->from('admin_user')
447
            ->orderBy(['id' => SORT_ASC, 'name' => SORT_DESC])
448
            ->limit(10)
449
            ->offset(5);
450
451
        $this->assertSame(
452
            DbHelper::replaceQuotes(
453
                <<<SQL
454
                SELECT * FROM [[admin_user]] ORDER BY [[id]], [[name]] DESC LIMIT 10 OFFSET 5
455
                SQL,
456
                $db->getName(),
457
            ),
458
            $qb->buildOrderByAndLimit(
459
                DbHelper::replaceQuotes(
460
                    <<<SQL
461
                    SELECT * FROM [[admin_user]]
462
                    SQL,
463
                    $db->getName(),
464
                ),
465
                $query->getOrderBy(),
466
                $query->getLimit(),
467
                $query->getOffset(),
468
            ),
469
        );
470
    }
471
472
    public function testBuildSelect(): void
473
    {
474
        $db = $this->getConnection();
475
476
        $qb = $db->getQueryBuilder();
477
        $query = (new Query($db))->select(['id', 'name', 'email', 'address', 'status']);
478
        $params = [];
479
480
        $this->assertSame(
481
            DbHelper::replaceQuotes(
482
                <<<SQL
483
                SELECT [[id]], [[name]], [[email]], [[address]], [[status]]
484
                SQL,
485
                $db->getName(),
486
            ),
487
            $qb->buildSelect($query->getSelect(), $params),
488
        );
489
    }
490
491
    public function testBuildSelectWithDistinct(): void
492
    {
493
        $db = $this->getConnection();
494
495
        $qb = $db->getQueryBuilder();
496
        $query = (new Query($db))->select(['id', 'name', 'email', 'address', 'status'])->distinct();
497
        $params = [];
498
499
        $this->assertSame(
500
            DbHelper::replaceQuotes(
501
                <<<SQL
502
                SELECT DISTINCT [[id]], [[name]], [[email]], [[address]], [[status]]
503
                SQL,
504
                $db->getName(),
505
            ),
506
            $qb->buildSelect($query->getSelect(), $params, true),
507
        );
508
    }
509
510
    public function testBuildUnion(): void
511
    {
512
        $db = $this->getConnection();
513
514
        $qb = $db->getQueryBuilder();
515
        $query = (new Query($db))->from('admin_user')->union((new Query($db))->from('admin_profile'));
516
        $params = [];
517
518
        $this->assertSame(
519
            DbHelper::replaceQuotes(
520
                <<<SQL
521
                UNION ( SELECT * FROM [[admin_profile]] )
522
                SQL,
523
                $db->getName(),
524
            ),
525
            $qb->buildUnion($query->getUnion(), $params),
526
        );
527
    }
528
529
    public function testBuildWithQueries(): void
530
    {
531
        $db = $this->getConnection();
532
533
        $qb = $db->getQueryBuilder();
534
        $query = (new Query($db))->withQuery((new Query($db))->from('admin_user')->from('admin_profile'), 'cte');
535
        $params = [];
536
537
        $this->assertSame(
538
            DbHelper::replaceQuotes(
539
                <<<SQL
540
                WITH cte AS (SELECT * FROM [[admin_profile]])
541
                SQL,
542
                $db->getName(),
543
            ),
544
            $qb->buildWithQueries($query->getWithQueries(), $params),
545
        );
546
    }
547
548
    public function testBuildWithComplexSelect(): void
549
    {
550
        $db = $this->getConnection();
551
552
        $qb = $db->getQueryBuilder();
553
        $expressionString = DbHelper::replaceQuotes(
554
            <<<SQL
555
            case t.Status_Id when 1 then 'Acknowledge' when 2 then 'No Action' else 'Unknown Action' END as [[Next Action]]
556
            SQL,
557
            $db->getName(),
558
        );
559
560
        $this->assertIsString($expressionString);
561
562
        $query = (new Query($db))
563
            ->select(
564
                [
565
                    'ID' => 't.id',
566
                    'gsm.username as GSM',
567
                    'part.Part',
568
                    'Part Cost' => 't.Part_Cost',
569
                    'st_x(location::geometry) as lon',
570
                    new Expression($expressionString),
571
                ]
572
            )
573
            ->from('tablename');
574
575
        [$sql, $params] = $qb->build($query);
576
577
        $this->assertSame(
578
            DbHelper::replaceQuotes(
579
                <<<SQL
580
                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]]
581
                SQL,
582
                $db->getName(),
583
            ),
584
            $sql,
585
        );
586
        $this->assertEmpty($params);
587
    }
588
589
    /**
590
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildFrom()
591
     */
592
    public function testBuildWithFrom(mixed $table, string $expectedSql, array $expectedParams = []): void
593
    {
594
        $db = $this->getConnection();
595
596
        $query = (new Query($db))->from($table);
597
        $queryBuilder = $db->getQueryBuilder();
598
599
        [$sql, $params] = $queryBuilder->build($query);
600
601
        $this->assertSame($expectedSql, $sql);
602
        $this->assertSame($expectedParams, $params);
603
    }
604
605
    public function testBuildWithFromAliasesNoExist(): void
606
    {
607
        $db = $this->getConnection();
608
609
        $qb = $db->getQueryBuilder();
610
        $query = (new Query($db))->from('no_exist_table');
611
612
        [$sql, $params] = $qb->build($query);
613
614
        $this->assertSame(
615
            DbHelper::replaceQuotes(
616
                <<<SQL
617
                SELECT * FROM [[no_exist_table]]
618
                SQL,
619
                $db->getName(),
620
            ),
621
            $sql,
622
        );
623
624
        $this->assertSame([], $params);
625
    }
626
627
    /**
628
     * {@see https://github.com/yiisoft/yii2/issues/10869}
629
     */
630
    public function testBuildWithFromIndexHint(): void
631
    {
632
        $db = $this->getConnection();
633
634
        $qb = $db->getQueryBuilder();
635
        $query = (new Query($db))->from([new Expression('{{%user}} USE INDEX (primary)')]);
636
637
        [$sql, $params] = $qb->build($query);
638
639
        $this->assertSame(
640
            DbHelper::replaceQuotes(
641
                <<<SQL
642
                SELECT * FROM {{%user}} USE INDEX (primary)
643
                SQL,
644
                $db->getName(),
645
            ),
646
            $sql,
647
        );
648
649
        $this->assertEmpty($params);
650
651
        $query = (new Query($db))
652
            ->from([new Expression('{{user}} {{t}} FORCE INDEX (primary) IGNORE INDEX FOR ORDER BY (i1)')])
653
            ->leftJoin(['p' => 'profile'], 'user.id = profile.user_id USE INDEX (i2)');
654
655
        [$sql, $params] = $qb->build($query);
656
657
        $this->assertSame(
658
            DbHelper::replaceQuotes(
659
                <<<SQL
660
                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)
661
                SQL,
662
                $db->getName(),
663
            ),
664
            $sql,
665
        );
666
667
        $this->assertEmpty($params);
668
    }
669
670
    public function testBuildWithFromSubquery(): void
671
    {
672
        $db = $this->getConnection();
673
674
        $qb = $db->getQueryBuilder();
675
676
        /* subquery */
677
        $subquery = (new Query($db))->from('user')->where('account_id = accounts.id');
678
        $query = (new Query($db))->from(['activeusers' => $subquery]);
679
680
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
681
        [$sql, $params] = $qb->build($query);
682
683
        $this->assertSame(
684
            DbHelper::replaceQuotes(
685
                <<<SQL
686
                SELECT * FROM (SELECT * FROM [[user]] WHERE account_id = accounts.id) [[activeusers]]
687
                SQL,
688
                $db->getName(),
689
            ),
690
            $sql,
691
        );
692
        $this->assertEmpty($params);
693
694
        /* subquery with params */
695
        $subquery = (new Query($db))->from('user')->where('account_id = :id', ['id' => 1]);
696
        $query = (new Query($db))->from(['activeusers' => $subquery])->where('abc = :abc', ['abc' => 'abc']);
697
698
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
699
        [$sql, $params] = $qb->build($query);
700
701
        $this->assertSame(
702
            DbHelper::replaceQuotes(
703
                <<<SQL
704
                SELECT * FROM (SELECT * FROM [[user]] WHERE account_id = :id) [[activeusers]] WHERE abc = :abc
705
                SQL,
706
                $db->getName(),
707
            ),
708
            $sql,
709
        );
710
        $this->assertSame(['abc' => 'abc', 'id' => 1], $params);
711
712
        /* simple subquery */
713
        $subquery = '(SELECT * FROM user WHERE account_id = accounts.id)';
714
        $query = (new Query($db))->from(['activeusers' => $subquery]);
715
716
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
717
        [$sql, $params] = $qb->build($query);
718
719
        $this->assertSame(
720
            DbHelper::replaceQuotes(
721
                <<<SQL
722
                SELECT * FROM (SELECT * FROM user WHERE account_id = accounts.id) [[activeusers]]
723
                SQL,
724
                $db->getName(),
725
            ),
726
            $sql,
727
        );
728
        $this->assertEmpty($params);
729
    }
730
731
    public function testBuildWithGroupBy(): void
732
    {
733
        $db = $this->getConnection();
734
735
        $qb = $db->getQueryBuilder();
736
737
        /* simple string */
738
        $query = (new Query($db))->select('*')->from('operations')->groupBy('name, date');
739
740
        [$sql, $params] = $qb->build($query);
741
742
        $this->assertSame(
743
            DbHelper::replaceQuotes(
744
                <<<SQL
745
                SELECT * FROM [[operations]] GROUP BY [[name]], [[date]]
746
                SQL,
747
                $db->getName(),
748
            ),
749
            $sql,
750
        );
751
        $this->assertEmpty($params);
752
753
        /* array syntax */
754
        $query = (new Query($db))->select('*')->from('operations')->groupBy(['name', 'date']);
755
756
        [$sql, $params] = $qb->build($query);
757
758
        $this->assertSame(
759
            DbHelper::replaceQuotes(
760
                <<<SQL
761
                SELECT * FROM [[operations]] GROUP BY [[name]], [[date]]
762
                SQL,
763
                $db->getName(),
764
            ),
765
            $sql,
766
        );
767
        $this->assertEmpty($params);
768
769
        /* expression */
770
        $query = (new Query($db))
771
            ->select('*')
772
            ->from('operations')
773
            ->where('account_id = accounts.id')
774
            ->groupBy(new Expression('SUBSTR(name, 0, 1), x'));
775
776
        [$sql, $params] = $qb->build($query);
777
778
        $this->assertSame(
779
            DbHelper::replaceQuotes(
780
                <<<SQL
781
                SELECT * FROM [[operations]] WHERE account_id = accounts.id GROUP BY SUBSTR(name, 0, 1), x
782
                SQL,
783
                $db->getName(),
784
            ),
785
            $sql,
786
        );
787
        $this->assertEmpty($params);
788
789
        /* expression with params */
790
        $query = (new Query($db))
791
            ->select('*')
792
            ->from('operations')
793
            ->groupBy(new Expression('SUBSTR(name, 0, :to), x', [':to' => 4]));
794
795
        [$sql, $params] = $qb->build($query);
796
797
        $this->assertSame(
798
            DbHelper::replaceQuotes(
799
                <<<SQL
800
                SELECT * FROM [[operations]] GROUP BY SUBSTR(name, 0, :to), x
801
                SQL,
802
                $db->getName(),
803
            ),
804
            $sql,
805
        );
806
        $this->assertSame([':to' => 4], $params);
807
    }
808
809
    public function testBuildWithLimit(): void
810
    {
811
        $db = $this->getConnection();
812
813
        $qb = $db->getQueryBuilder();
814
        $query = (new Query($db))->limit(10);
815
816
        [$sql, $params] = $qb->build($query);
817
818
        $this->assertSame(
819
            <<<SQL
820
            SELECT * LIMIT 10
821
            SQL,
822
            $sql,
823
        );
824
825
        $this->assertSame([], $params);
826
    }
827
828
    public function testBuildWithOffset(): void
829
    {
830
        $db = $this->getConnection();
831
832
        $qb = $db->getQueryBuilder();
833
        $query = (new Query($db))->offset(10);
834
835
        [$sql, $params] = $qb->build($query);
836
837
        $this->assertSame(
838
            <<<SQL
839
            SELECT * OFFSET 10
840
            SQL,
841
            $sql,
842
        );
843
        $this->assertSame([], $params);
844
    }
845
846
    public function testBuildWithOrderBy(): void
847
    {
848
        $db = $this->getConnection();
849
850
        $qb = $db->getQueryBuilder();
851
852
        /* simple string */
853
        $query = (new Query($db))->select('*')->from('operations')->orderBy('name ASC, date DESC');
854
855
        [$sql, $params] = $qb->build($query);
856
857
        $this->assertSame(
858
            DbHelper::replaceQuotes(
859
                <<<SQL
860
                SELECT * FROM [[operations]] ORDER BY [[name]], [[date]] DESC
861
                SQL,
862
                $db->getName(),
863
            ),
864
            $sql,
865
        );
866
        $this->assertEmpty($params);
867
868
        /* array syntax */
869
        $query = (new Query($db))->select('*')->from('operations')->orderBy(['name' => SORT_ASC, 'date' => SORT_DESC]);
870
871
        [$sql, $params] = $qb->build($query);
872
873
        $this->assertSame(
874
            DbHelper::replaceQuotes(
875
                <<<SQL
876
                SELECT * FROM [[operations]] ORDER BY [[name]], [[date]] DESC
877
                SQL,
878
                $db->getName(),
879
            ),
880
            $sql,
881
        );
882
        $this->assertEmpty($params);
883
884
        /* expression */
885
        $query = (new Query($db))
886
            ->select('*')
887
            ->from('operations')
888
            ->where('account_id = accounts.id')
889
            ->orderBy(new Expression('SUBSTR(name, 3, 4) DESC, x ASC'));
890
891
        [$sql, $params] = $qb->build($query);
892
893
        $this->assertSame(
894
            DbHelper::replaceQuotes(
895
                <<<SQL
896
                SELECT * FROM [[operations]] WHERE account_id = accounts.id ORDER BY SUBSTR(name, 3, 4) DESC, x ASC
897
                SQL,
898
                $db->getName(),
899
            ),
900
            $sql,
901
        );
902
        $this->assertEmpty($params);
903
904
        /* expression with params */
905
        $query = (new Query($db))
906
            ->select('*')
907
            ->from('operations')
908
            ->orderBy(new Expression('SUBSTR(name, 3, :to) DESC, x ASC', [':to' => 4]));
909
910
        [$sql, $params] = $qb->build($query);
911
912
        $this->assertSame(
913
            DbHelper::replaceQuotes(
914
                <<<SQL
915
                SELECT * FROM [[operations]] ORDER BY SUBSTR(name, 3, :to) DESC, x ASC
916
                SQL,
917
                $db->getName(),
918
            ),
919
            $sql,
920
        );
921
        $this->assertSame([':to' => 4], $params);
922
    }
923
924
    public function testBuildWithQuery(): void
925
    {
926
        $db = $this->getConnection();
927
928
        $qb = $db->getQueryBuilder();
929
        $with1Query = (new query($db))->select('id')->from('t1')->where('expr = 1');
930
        $with2Query = (new query($db))->select('id')->from('t2')->innerJoin('a1', 't2.id = a1.id')->where('expr = 2');
931
        $with3Query = (new query($db))->select('id')->from('t3')->where('expr = 3');
932
        $query = (new query($db))
933
            ->withQuery($with1Query, 'a1')
934
            ->withQuery($with2Query->union($with3Query), 'a2')
935
            ->from('a2');
936
937
        [$sql, $params] = $qb->build($query);
938
939
        $this->assertSame(
940
            DbHelper::replaceQuotes(
941
                <<<SQL
942
                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]]
943
                SQL,
944
                $db->getName(),
945
            ),
946
            $sql,
947
        );
948
949
        $this->assertSame([], $params);
950
    }
951
952
    public function testBuildWithQueryRecursive(): void
953
    {
954
        $db = $this->getConnection();
955
956
        $qb = $db->getQueryBuilder();
957
        $with1Query = (new Query($db))->select('id')->from('t1')->where('expr = 1');
958
        $query = (new Query($db))->withQuery($with1Query, 'a1', true)->from('a1');
959
960
        [$sql, $params] = $qb->build($query);
961
962
        $this->assertSame(
963
            DbHelper::replaceQuotes(
964
                <<<SQL
965
                WITH RECURSIVE a1 AS (SELECT [[id]] FROM [[t1]] WHERE expr = 1) SELECT * FROM [[a1]]
966
                SQL,
967
                $db->getName(),
968
            ),
969
            $sql,
970
        );
971
        $this->assertSame([], $params);
972
    }
973
974
    public function testBuildWithSelectExpression(): void
975
    {
976
        $db = $this->getConnection();
977
978
        $qb = $db->getQueryBuilder();
979
        $query = (new Query($db))->select(new Expression('1 AS ab'))->from('tablename');
980
981
        [$sql, $params] = $qb->build($query);
982
983
        $this->assertSame(
984
            DbHelper::replaceQuotes(
985
                <<<SQL
986
                SELECT 1 AS ab FROM [[tablename]]
987
                SQL,
988
                $db->getName(),
989
            ),
990
            $sql,
991
        );
992
        $this->assertEmpty($params);
993
994
        $query = (new Query($db))
995
            ->select(new Expression('1 AS ab'))
996
            ->addSelect(new Expression('2 AS cd'))
997
            ->addSelect(['ef' => new Expression('3')])
998
            ->from('tablename');
999
1000
        [$sql, $params] = $qb->build($query);
1001
1002
        $this->assertSame(
1003
            DbHelper::replaceQuotes(
1004
                <<<SQL
1005
                SELECT 1 AS ab, 2 AS cd, 3 AS [[ef]] FROM [[tablename]]
1006
                SQL,
1007
                $db->getName(),
1008
            ),
1009
            $sql,
1010
        );
1011
        $this->assertEmpty($params);
1012
1013
        $query = (new Query($db))
1014
            ->select(new Expression('SUBSTR(name, 0, :len)', [':len' => 4]))
1015
            ->from('tablename');
1016
1017
        [$sql, $params] = $qb->build($query);
1018
1019
        $this->assertSame(
1020
            DbHelper::replaceQuotes(
1021
                <<<SQL
1022
                SELECT SUBSTR(name, 0, :len) FROM [[tablename]]
1023
                SQL,
1024
                $db->getName(),
1025
            ),
1026
            $sql,
1027
        );
1028
        $this->assertSame([':len' => 4], $params);
1029
    }
1030
1031
    public function testBuildWithSelectSubquery(): void
1032
    {
1033
        $db = $this->getConnection();
1034
1035
        $qb = $db->getQueryBuilder();
1036
        $subquery = (new Query($db))->select('COUNT(*)')->from('operations')->where('account_id = accounts.id');
1037
        $query = (new Query($db))->select('*')->from('accounts')->addSelect(['operations_count' => $subquery]);
1038
1039
        [$sql, $params] = $qb->build($query);
1040
1041
        $this->assertSame(
1042
            DbHelper::replaceQuotes(
1043
                <<<SQL
1044
                SELECT *, (SELECT COUNT(*) FROM [[operations]] WHERE account_id = accounts.id) AS [[operations_count]] FROM [[accounts]]
1045
                SQL,
1046
                $db->getName(),
1047
            ),
1048
            $sql,
1049
        );
1050
        $this->assertEmpty($params);
1051
    }
1052
1053
    public function testBuildWithSelectOption(): void
1054
    {
1055
        $db = $this->getConnection();
1056
1057
        $qb = $db->getQueryBuilder();
1058
        $query = (new Query($db))->selectOption('DISTINCT');
1059
1060
        [$sql, $params] = $qb->build($query);
1061
1062
        $this->assertSame(
1063
            <<<SQL
1064
            SELECT DISTINCT *
1065
            SQL,
1066
            $sql,
1067
        );
1068
        $this->assertSame([], $params);
1069
    }
1070
1071
    public function testBuildWithSetSeparator(): void
1072
    {
1073
        $db = $this->getConnection();
1074
1075
        $qb = $db->getQueryBuilder();
1076
        $qb->setSeparator(' ');
1077
1078
        [$sql, $params] = $qb->build((new Query($db))->select('*')->from('table'));
1079
1080
        $this->assertSame(
1081
            DbHelper::replaceQuotes(
1082
                <<<SQL
1083
                SELECT * FROM [[table]]
1084
                SQL,
1085
                $db->getName(),
1086
            ),
1087
            $sql
1088
        );
1089
        $this->assertEmpty($params);
1090
1091
        $qb->setSeparator("\n");
1092
        [$sql, $params] = $qb->build((new Query($db))->select('*')->from('table'));
1093
1094
        $this->assertSame(
1095
            DbHelper::replaceQuotes(
1096
                <<<SQL
1097
                SELECT *
1098
                FROM [[table]]
1099
                SQL,
1100
                $db->getName(),
1101
            ),
1102
            $sql,
1103
        );
1104
        $this->assertEmpty($params);
1105
    }
1106
1107
    /**
1108
     * This test contains three select queries connected with UNION and UNION ALL constructions.
1109
     */
1110
    public function testBuildWithUnion(): void
1111
    {
1112
        $db = $this->getConnection();
1113
1114
        $qb = $db->getQueryBuilder();
1115
1116
        $secondQuery = (new Query($db))->select('id')->from('TotalTotalExample t2')->where('w > 5');
1117
        $thirdQuery = (new Query($db))->select('id')->from('TotalTotalExample t3')->where('w = 3');
1118
        $firtsQuery = (new Query($db))
1119
            ->select('id')
1120
            ->from('TotalExample t1')
1121
            ->where(['and', 'w > 0', 'x < 2'])
1122
            ->union($secondQuery)
1123
            ->union($thirdQuery, true);
1124
1125
        [$sql, $params] = $qb->build($firtsQuery);
1126
1127
        $this->assertSame(
1128
            DbHelper::replaceQuotes(
1129
                <<<SQL
1130
                (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 )
1131
                SQL,
1132
                $db->getName(),
1133
            ),
1134
            $sql,
1135
        );
1136
        $this->assertSame([], $params);
1137
    }
1138
1139
    /**
1140
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::buildWhereExists()
1141
     */
1142
    public function testBuildWithWhereExists(string $cond, string $expectedQuerySql): void
1143
    {
1144
        $db = $this->getConnection();
1145
1146
        $expectedQueryParams = [];
1147
1148
        $subQuery = new Query($db);
1149
        $subQuery->select('1')->from('Website w');
1150
        $query = new Query($db);
1151
        $query->select('id')->from('TotalExample t')->where([$cond, $subQuery]);
1152
1153
        [$actualQuerySql, $actualQueryParams] = $db->getQueryBuilder()->build($query);
1154
1155
        $this->assertSame($expectedQuerySql, $actualQuerySql);
1156
        $this->assertSame($expectedQueryParams, $actualQueryParams);
1157
    }
1158
1159
    public function testBuildWithWhereExistsArrayParameters(): void
1160
    {
1161
        $db = $this->getConnection();
1162
1163
        $qb = $db->getQueryBuilder();
1164
        $subQuery = (new query($db))
1165
            ->select('1')
1166
            ->from('Website w')
1167
            ->where('w.id = t.website_id')
1168
            ->andWhere(['w.merchant_id' => 6, 'w.user_id' => 210]);
1169
        $query = (new query($db))
1170
            ->select('id')
1171
            ->from('TotalExample t')
1172
            ->where(['exists', $subQuery])
1173
            ->andWhere(['t.some_column' => 'asd']);
1174
1175
        [$sql, $params] = $qb->build($query);
1176
1177
        $this->assertSame(
1178
            DbHelper::replaceQuotes(
1179
                <<<SQL
1180
                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)
1181
                SQL,
1182
                $db->getName(),
1183
            ),
1184
            $sql,
1185
        );
1186
        $this->assertSame([':qp0' => 6, ':qp1' => 210, ':qp2' => 'asd'], $params);
1187
    }
1188
1189
    public function testBuildWithWhereExistsWithParameters(): void
1190
    {
1191
        $db = $this->getConnection();
1192
1193
        $qb = $db->getQueryBuilder();
1194
        $subQuery = (new query($db))
1195
            ->select('1')
1196
            ->from('Website w')
1197
            ->where('w.id = t.website_id')
1198
            ->andWhere('w.merchant_id = :merchant_id', [':merchant_id' => 6]);
1199
        $query = (new query($db))
1200
            ->select('id')
1201
            ->from('TotalExample t')
1202
            ->where(['exists', $subQuery])
1203
            ->andWhere('t.some_column = :some_value', [':some_value' => 'asd']);
1204
1205
        [$sql, $params] = $qb->build($query);
1206
1207
        $this->assertSame(
1208
            DbHelper::replaceQuotes(
1209
                <<<SQL
1210
                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)
1211
                SQL,
1212
                $db->getName(),
1213
            ),
1214
            $sql,
1215
        );
1216
        $this->assertSame([':some_value' => 'asd', ':merchant_id' => 6], $params);
1217
    }
1218
1219
    public function testsCreateConditionFromArray(): void
1220
    {
1221
        $db = $this->getConnection();
1222
1223
        $qb = $db->getQueryBuilder();
1224
1225
        $condition = $qb->createConditionFromArray(['and', 'a = 1', 'b = 2']);
1226
1227
        $this->assertSame('AND', $condition->getOperator());
1228
        $this->assertSame(['a = 1', 'b = 2'], $condition->getExpressions());
1229
1230
        $condition = $qb->createConditionFromArray(['or', 'a = 1', 'b = 2']);
1231
1232
        $this->assertSame('OR', $condition->getOperator());
1233
        $this->assertSame(['a = 1', 'b = 2'], $condition->getExpressions());
1234
1235
        $condition = $qb->createConditionFromArray(['and', 'a = 1', ['or', 'b = 2', 'c = 3']]);
1236
1237
        $this->assertSame('AND', $condition->getOperator());
1238
        $this->assertSame(['a = 1', ['or', 'b = 2', 'c = 3']], $condition->getExpressions());
1239
1240
        $condition = $qb->createConditionFromArray(['or', 'a = 1', ['and', 'b = 2', 'c = 3']]);
1241
1242
        $this->assertSame('OR', $condition->getOperator());
1243
        $this->assertSame(['a = 1', ['and', 'b = 2', 'c = 3']], $condition->getExpressions());
1244
1245
        $condition = $qb->createConditionFromArray(['and', 'a = 1', ['or', 'b = 2', ['and', 'c = 3', 'd = 4']]]);
1246
1247
        $this->assertSame('AND', $condition->getOperator());
1248
        $this->assertSame(['a = 1', ['or', 'b = 2', ['and', 'c = 3', 'd = 4']]], $condition->getExpressions());
1249
1250
        $condition = $qb->createConditionFromArray(['or', 'a = 1', ['and', 'b = 2', ['or', 'c = 3', 'd = 4']]]);
1251
1252
        $this->assertSame('OR', $condition->getOperator());
1253
        $this->assertSame(['a = 1', ['and', 'b = 2', ['or', 'c = 3', 'd = 4']]], $condition->getExpressions());
1254
1255
        $condition = $qb->createConditionFromArray(
1256
            ['and', 'a = 1', ['or', 'b = 2', ['and', 'c = 3', ['or', 'd = 4', 'e = 5']]]]
1257
        );
1258
        $this->assertSame('AND', $condition->getOperator());
1259
        $this->assertSame(
1260
            ['a = 1', ['or', 'b = 2', ['and', 'c = 3', ['or', 'd = 4', 'e = 5']]]],
1261
            $condition->getExpressions(),
1262
        );
1263
    }
1264
1265
    /**
1266
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::createIndex()
1267
     */
1268
    public function testCreateIndex(string $sql, Closure $builder): void
1269
    {
1270
        $db = $this->getConnection();
1271
1272
        $qb = $db->getQueryBuilder();
1273
1274
        $this->assertSame($db->getQuoter()->quoteSql($sql), $builder($qb));
1275
    }
1276
1277
    public function testCreateTable(): void
1278
    {
1279
        $db = $this->getConnection();
1280
1281
        $qb = $db->getQueryBuilder();
1282
1283
        $this->assertSame(
1284
            DbHelper::replaceQuotes(
1285
                <<<SQL
1286
                CREATE TABLE [[test]] (
1287
                \t[[id]] pk,
1288
                \t[[name]] string(255) NOT NULL,
1289
                \t[[email]] string(255) NOT NULL,
1290
                \t[[status]] integer NOT NULL,
1291
                \t[[created_at]] datetime NOT NULL
1292
                )
1293
                SQL,
1294
                $db->getName(),
1295
            ),
1296
            $qb->createTable(
1297
                'test',
1298
                [
1299
                    'id' => 'pk',
1300
                    'name' => 'string(255) NOT NULL',
1301
                    'email' => 'string(255) NOT NULL',
1302
                    'status' => 'integer NOT NULL',
1303
                    'created_at' => 'datetime NOT NULL',
1304
                ],
1305
            ),
1306
        );
1307
    }
1308
1309
    public function testCreateView(): void
1310
    {
1311
        $db = $this->getConnection();
1312
1313
        $qb = $db->getQueryBuilder();
1314
1315
        $this->assertSame(
1316
            DbHelper::replaceQuotes(
1317
                <<<SQL
1318
                CREATE VIEW [[animal_view]] AS SELECT [[1]]
1319
                SQL,
1320
                $db->getName(),
1321
            ),
1322
            $qb->createView('animal_view', (new query($db))->select('1')),
1323
        );
1324
    }
1325
1326
    /**
1327
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::delete()
1328
     */
1329
    public function testDelete(string $table, array|string $condition, string $expectedSQL, array $expectedParams): void
1330
    {
1331
        $db = $this->getConnection();
1332
1333
        $qb = $db->getQueryBuilder();
1334
        $actualParams = [];
1335
        $actualSQL = $qb->delete($table, $condition, $actualParams);
1336
1337
        $this->assertSame($expectedSQL, $actualSQL);
1338
        $this->assertSame($expectedParams, $actualParams);
1339
    }
1340
1341
    public function testDropCheck(): void
1342
    {
1343
        $db = $this->getConnection();
1344
1345
        $qb = $db->getQueryBuilder();
1346
1347
        $this->assertSame(
1348
            DbHelper::replaceQuotes(
1349
                <<<SQL
1350
                ALTER TABLE [[T_constraints_1]] DROP CONSTRAINT [[CN_check]]
1351
                SQL,
1352
                $db->getName(),
1353
            ),
1354
            $qb->dropCheck('CN_check', 'T_constraints_1'),
1355
        );
1356
    }
1357
1358
    public function testDropColumn(): void
1359
    {
1360
        $db = $this->getConnection();
1361
1362
        $qb = $db->getQueryBuilder();
1363
1364
        $this->assertSame(
1365
            DbHelper::replaceQuotes(
1366
                <<<SQL
1367
                ALTER TABLE [[customer]] DROP COLUMN [[id]]
1368
                SQL,
1369
                $db->getName(),
1370
            ),
1371
            $qb->dropColumn('customer', 'id'),
1372
        );
1373
    }
1374
1375
    public function testDropCommentFromColumn(): void
1376
    {
1377
        $db = $this->getConnection(true);
1378
1379
        $qb = $db->getQueryBuilder();
1380
1381
        $this->assertSame(
1382
            DbHelper::replaceQuotes(
1383
                <<<SQL
1384
                COMMENT ON COLUMN [customer].[id] IS NULL
1385
                SQL,
1386
                $db->getName(),
1387
            ),
1388
            $qb->dropCommentFromColumn('customer', 'id'),
1389
        );
1390
    }
1391
1392
    public function testDropCommentFromTable(): void
1393
    {
1394
        $db = $this->getConnection();
1395
1396
        $qb = $db->getQueryBuilder();
1397
1398
        $this->assertSame(
1399
            DbHelper::replaceQuotes(
1400
                <<<SQL
1401
                COMMENT ON TABLE [[customer]] IS NULL
1402
                SQL,
1403
                $db->getName(),
1404
            ),
1405
            $qb->dropCommentFromTable('customer'),
1406
        );
1407
    }
1408
1409
    public function testDropDefaultValue(): void
1410
    {
1411
        $db = $this->getConnection(true);
1412
1413
        $qb = $db->getQueryBuilder();
1414
1415
        $this->assertSame(
1416
            DbHelper::replaceQuotes(
1417
                <<<SQL
1418
                ALTER TABLE [[T_constraints_1]] ALTER COLUMN [[C_default]] DROP DEFAULT
1419
                SQL,
1420
                $db->getName(),
1421
            ),
1422
            $qb->dropDefaultValue('CN_pk', 'T_constraints_1'),
1423
        );
1424
    }
1425
1426
    public function testDropForeignKey(): void
1427
    {
1428
        $db = $this->getConnection();
1429
1430
        $qb = $db->getQueryBuilder();
1431
1432
        $this->assertSame(
1433
            DbHelper::replaceQuotes(
1434
                <<<SQL
1435
                ALTER TABLE [[T_constraints_3]] DROP CONSTRAINT [[CN_constraints_3]]
1436
                SQL,
1437
                $db->getName(),
1438
            ),
1439
            $qb->dropForeignKey('CN_constraints_3', 'T_constraints_3'),
1440
        );
1441
    }
1442
1443
    public function testDropIndex(): void
1444
    {
1445
        $db = $this->getConnection();
1446
1447
        $qb = $db->getQueryBuilder();
1448
1449
        $this->assertSame(
1450
            DbHelper::replaceQuotes(
1451
                <<<SQL
1452
                DROP INDEX [[CN_constraints_2_single]] ON [[T_constraints_2]]
1453
                SQL,
1454
                $db->getName(),
1455
            ),
1456
            $qb->dropIndex('CN_constraints_2_single', 'T_constraints_2'),
1457
        );
1458
    }
1459
1460
    public function testDropPrimaryKey(): void
1461
    {
1462
        $db = $this->getConnection();
1463
1464
        $qb = $db->getQueryBuilder();
1465
1466
        $this->assertSame(
1467
            DbHelper::replaceQuotes(
1468
                <<<SQL
1469
                ALTER TABLE [[T_constraints_1]] DROP CONSTRAINT [[CN_pk]]
1470
                SQL,
1471
                $db->getName(),
1472
            ),
1473
            $qb->dropPrimaryKey('CN_pk', 'T_constraints_1'),
1474
        );
1475
    }
1476
1477
    public function testDropTable(): void
1478
    {
1479
        $db = $this->getConnection();
1480
1481
        $qb = $db->getQueryBuilder();
1482
1483
        $this->assertSame(
1484
            DbHelper::replaceQuotes(
1485
                <<<SQL
1486
                DROP TABLE [[customer]]
1487
                SQL,
1488
                $db->getName(),
1489
            ),
1490
            $qb->dropTable('customer'),
1491
        );
1492
    }
1493
1494
    public function testDropUnique(): void
1495
    {
1496
        $db = $this->getConnection();
1497
1498
        $qb = $db->getQueryBuilder();
1499
1500
        $this->assertSame(
1501
            DbHelper::replaceQuotes(
1502
                <<<SQL
1503
                ALTER TABLE [[test_uq]] DROP CONSTRAINT [[test_uq_constraint]]
1504
                SQL,
1505
                $db->getName(),
1506
            ),
1507
            $qb->dropUnique('test_uq_constraint', 'test_uq', ['int1']),
1508
        );
1509
    }
1510
1511
    public function testDropView(): void
1512
    {
1513
        $db = $this->getConnection(true);
1514
1515
        $qb = $db->getQueryBuilder();
1516
1517
        $this->assertSame(
1518
            DbHelper::replaceQuotes(
1519
                <<<SQL
1520
                DROP VIEW [[animal_view]]
1521
                SQL,
1522
                $db->getName(),
1523
            ),
1524
            $qb->dropview('animal_view'),
1525
        );
1526
    }
1527
1528
    public function testGetColumnType(): void
1529
    {
1530
        $db = $this->getConnection();
1531
1532
        $qb = $db->getQueryBuilder();
1533
1534
        $this->assertSame('pk', $qb->getColumnType(Schema::TYPE_PK));
1535
        $this->assertSame('upk', $qb->getColumnType(Schema::TYPE_UPK));
1536
        $this->assertSame('bigpk', $qb->getColumnType(Schema::TYPE_BIGPK));
1537
        $this->assertSame('ubigpk', $qb->getColumnType(Schema::TYPE_UBIGPK));
1538
        $this->assertSame('char', $qb->getColumnType(Schema::TYPE_CHAR));
1539
        $this->assertSame('string', $qb->getColumnType(Schema::TYPE_STRING));
1540
        $this->assertSame('text', $qb->getColumnType(Schema::TYPE_TEXT));
1541
        $this->assertSame('tinyint', $qb->getColumnType(Schema::TYPE_TINYINT));
1542
        $this->assertSame('smallint', $qb->getColumnType(Schema::TYPE_SMALLINT));
1543
        $this->assertSame('integer', $qb->getColumnType(Schema::TYPE_INTEGER));
1544
        $this->assertSame('bigint', $qb->getColumnType(Schema::TYPE_BIGINT));
1545
        $this->assertSame('float', $qb->getColumnType(Schema::TYPE_FLOAT));
1546
        $this->assertSame('double', $qb->getColumnType(Schema::TYPE_DOUBLE));
1547
        $this->assertSame('decimal', $qb->getColumnType(Schema::TYPE_DECIMAL));
1548
        $this->assertSame('datetime', $qb->getColumnType(Schema::TYPE_DATETIME));
1549
        $this->assertSame('timestamp', $qb->getColumnType(Schema::TYPE_TIMESTAMP));
1550
        $this->assertSame('time', $qb->getColumnType(Schema::TYPE_TIME));
1551
        $this->assertSame('date', $qb->getColumnType(Schema::TYPE_DATE));
1552
        $this->assertSame('binary', $qb->getColumnType(Schema::TYPE_BINARY));
1553
        $this->assertSame('boolean', $qb->getColumnType(Schema::TYPE_BOOLEAN));
1554
        $this->assertSame('money', $qb->getColumnType(Schema::TYPE_MONEY));
1555
        $this->assertSame('json', $qb->getColumnType(Schema::TYPE_JSON));
1556
    }
1557
1558
    public function testGetExpressionBuilder(): void
1559
    {
1560
        $db = $this->getConnection();
1561
1562
        $qb = $db->getQueryBuilder();
1563
1564
        $simpleCondition = new SimpleCondition('a', '=', 1);
1565
1566
        $this->assertInstanceOf(
1567
            ExpressionBuilderInterface::class,
1568
            $qb->getExpressionBuilder($simpleCondition),
1569
        );
1570
    }
1571
1572
    /**
1573
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::insert()
1574
     */
1575
    public function testInsert(
1576
        string $table,
1577
        array|QueryInterface $columns,
1578
        array $params,
1579
        string $expectedSQL,
1580
        array $expectedParams
1581
    ): void {
1582
        $db = $this->getConnection();
1583
1584
        $qb = $db->getQueryBuilder();
1585
1586
        $this->assertSame($expectedSQL, $qb->insert($table, $columns, $params));
1587
        $this->assertSame($expectedParams, $params);
1588
    }
1589
1590
    /**
1591
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::insertEx()
1592
     */
1593
    public function testInsertEx(
1594
        string $table,
1595
        array|QueryInterface $columns,
1596
        array $params,
1597
        string $expectedSQL,
1598
        array $expectedParams
1599
    ): void {
1600
        $db = $this->getConnection(true);
1601
1602
        $qb = $db->getQueryBuilder();
1603
1604
        $this->assertSame($expectedSQL, $qb->insertEx($table, $columns, $params));
1605
        $this->assertSame($expectedParams, $params);
1606
    }
1607
1608
    public function testQuoter(): void
1609
    {
1610
        $db = $this->getConnection();
1611
1612
        $qb = $db->getQueryBuilder();
1613
1614
        $this->assertInstanceOf(QuoterInterface::class, $qb->quoter());
1615
    }
1616
1617
    public function testRenameColumn(): void
1618
    {
1619
        $db = $this->getConnection();
1620
1621
        $qb = $db->getQueryBuilder();
1622
        $sql = $qb->renameColumn('alpha', 'string_identifier', 'string_identifier_test');
1623
1624
        $this->assertSame(
1625
            DbHelper::replaceQuotes(
1626
                <<<SQL
1627
                ALTER TABLE [[alpha]] RENAME COLUMN [[string_identifier]] TO [[string_identifier_test]]
1628
                SQL,
1629
                $db->getName(),
1630
            ),
1631
            $sql,
1632
        );
1633
    }
1634
1635
    public function testRenameTable(): void
1636
    {
1637
        $db = $this->getConnection();
1638
1639
        $qb = $db->getQueryBuilder();
1640
        $sql = $qb->renameTable('alpha', 'alpha-test');
1641
1642
        $this->assertSame(
1643
            DbHelper::replaceQuotes(
1644
                <<<SQL
1645
                RENAME TABLE [[alpha]] TO [[alpha-test]]
1646
                SQL,
1647
                $db->getName(),
1648
            ),
1649
            $sql,
1650
        );
1651
    }
1652
1653
    public function testResetSequence(): void
1654
    {
1655
        $db = $this->getConnection(true);
1656
1657
        $qb = $db->getQueryBuilder();
1658
1659
        $this->assertSame(
1660
            <<<SQL
1661
            SELECT SETVAL('"item_id_seq"',(SELECT COALESCE(MAX("id"),0) FROM "item")+1,false)
1662
            SQL,
1663
            $qb->resetSequence('item'),
1664
        );
1665
1666
        $this->assertSame(
1667
            <<<SQL
1668
            SELECT SETVAL('"item_id_seq"',3,false)
1669
            SQL,
1670
            $qb->resetSequence('item', 3),
1671
        );
1672
    }
1673
1674
    /**
1675
     * @throws Exception
1676
     * @throws InvalidConfigException
1677
     * @throws NotSupportedException
1678
     */
1679
    public function testResetSequenceNoAssociatedException(): void
1680
    {
1681
        $db = $this->getConnection(true);
1682
1683
        $qb = $db->getQueryBuilder();
1684
1685
        if ($db->getName() === 'db') {
1686
            $this->expectException(NotSupportedException::class);
1687
            $this->expectExceptionMessage(
1688
                'Yiisoft\Db\QueryBuilder\DMLQueryBuilder::resetSequence() is not supported by this DBMS.'
1689
            );
1690
        } else {
1691
            $this->expectException(InvalidArgumentException::class);
1692
            $this->expectExceptionMessage(
1693
                "There is not sequence associated with table 'type'."
1694
            );
1695
        }
1696
1697
        $qb->resetSequence('type');
1698
    }
1699
1700
    /**
1701
     * @throws Exception
1702
     * @throws InvalidConfigException
1703
     * @throws NotSupportedException
1704
     */
1705
    public function testResetSequenceTableNoExistException(): void
1706
    {
1707
        $db = $this->getConnection();
1708
1709
        $qb = $db->getQueryBuilder();
1710
1711
        if ($db->getName() === 'db') {
1712
            $this->expectException(NotSupportedException::class);
1713
            $this->expectExceptionMessage(
1714
                'Yiisoft\Db\QueryBuilder\DMLQueryBuilder::resetSequence() is not supported by this DBMS.'
1715
            );
1716
        } else {
1717
            $this->expectException(InvalidArgumentException::class);
1718
            $this->expectExceptionMessage("Table not found: 'noExist'.");
1719
        }
1720
1721
        $qb->resetSequence('noExist', 1);
1722
    }
1723
1724
    /**
1725
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::selectExist()
1726
     */
1727
    public function testSelectExists(string $sql, string $expected): void
1728
    {
1729
        $db = $this->getConnection();
1730
1731
        $qb = $db->getQueryBuilder();
1732
        $sqlSelectExist = $qb->selectExists($sql);
1733
1734
        $this->assertSame($expected, $sqlSelectExist);
1735
    }
1736
1737
    public function testSelectExpression(): void
1738
    {
1739
        $db = $this->getConnection();
1740
1741
        $qb = $db->getQueryBuilder();
1742
        $query = (new Query($db))->select(new Expression('1 AS ab'))->from('tablename');
1743
1744
        [$sql, $params] = $qb->build($query);
1745
1746
        $expected = DbHelper::replaceQuotes(
1747
            <<<SQL
1748
            SELECT 1 AS ab FROM [[tablename]]
1749
            SQL,
1750
            $db->getName(),
1751
        );
1752
1753
        $this->assertSame($expected, $sql);
1754
        $this->assertEmpty($params);
1755
1756
        $query = (new Query($db))
1757
            ->select(new Expression('1 AS ab'))
1758
            ->addSelect(new Expression('2 AS cd'))
1759
            ->addSelect(['ef' => new Expression('3')])
1760
            ->from('tablename');
1761
1762
        [$sql, $params] = $qb->build($query);
1763
1764
        $expected = DbHelper::replaceQuotes(
1765
            <<<SQL
1766
            SELECT 1 AS ab, 2 AS cd, 3 AS [[ef]] FROM [[tablename]]
1767
            SQL,
1768
            $db->getName(),
1769
        );
1770
1771
        $this->assertSame($expected, $sql);
1772
        $this->assertEmpty($params);
1773
1774
        $query = (new Query($db))
1775
            ->select(new Expression('SUBSTR(name, 0, :len)', [':len' => 4]))
1776
            ->from('tablename');
1777
1778
        [$sql, $params] = $qb->build($query);
1779
1780
        $expected = DbHelper::replaceQuotes(
1781
            <<<SQL
1782
            SELECT SUBSTR(name, 0, :len) FROM [[tablename]]
1783
            SQL,
1784
            $db->getName(),
1785
        );
1786
1787
        $this->assertSame($expected, $sql);
1788
        $this->assertSame([':len' => 4], $params);
1789
    }
1790
1791
    public function testSelectSubquery(): void
1792
    {
1793
        $db = $this->getConnection();
1794
1795
        $qb = $db->getQueryBuilder();
1796
        $expected = DbHelper::replaceQuotes(
1797
            <<<SQL
1798
            SELECT *, (SELECT COUNT(*) FROM [[operations]] WHERE account_id = accounts.id) AS [[operations_count]] FROM [[accounts]]
1799
            SQL,
1800
            $db->getName(),
1801
        );
1802
        $subquery = (new Query($db))->select('COUNT(*)')->from('operations')->where('account_id = accounts.id');
1803
        $query = (new Query($db))->select('*')->from('accounts')->addSelect(['operations_count' => $subquery]);
1804
1805
        [$sql, $params] = $qb->build($query);
1806
1807
        $this->assertSame($expected, $sql);
1808
        $this->assertEmpty($params);
1809
    }
1810
1811
    public function testSetConditionClasses(): void
1812
    {
1813
        $db = $this->getConnection();
1814
1815
        $qb = $db->getQueryBuilder();
1816
        $qb->setConditionClasses(['stdClass' => stdClass::class]);
1817
        $dqlBuilder = Assert::getInaccessibleProperty($qb, 'dqlBuilder');
1818
        $conditionClasses = Assert::getInaccessibleProperty($dqlBuilder, 'conditionClasses');
1819
1820
        $this->assertSame(stdClass::class, $conditionClasses['stdClass']);
1821
    }
1822
1823
    public function testSetExpressionBuilder(): void
1824
    {
1825
        $db = $this->getConnection();
1826
1827
        $qb = $db->getQueryBuilder();
1828
        $qb->setExpressionBuilders(['stdClass' => stdClass::class]);
1829
        $dqlBuilder = Assert::getInaccessibleProperty($qb, 'dqlBuilder');
1830
        $expressionBuilders = Assert::getInaccessibleProperty($dqlBuilder, 'expressionBuilders');
1831
1832
        $this->assertSame(stdClass::class, $expressionBuilders['stdClass']);
1833
    }
1834
1835
    public function testSetSeparator(): void
1836
    {
1837
        $db = $this->getConnection();
1838
1839
        $qb = $db->getQueryBuilder();
1840
        $qb->setSeparator(' ');
1841
        [$sql, $params] = $qb->build((new Query($db))->select('*')->from('table'));
1842
1843
        $this->assertSame(
1844
            DbHelper::replaceQuotes(
1845
                <<<SQL
1846
                SELECT * FROM [[table]]
1847
                SQL,
1848
                $db->getName(),
1849
            ),
1850
            $sql
1851
        );
1852
        $this->assertEmpty($params);
1853
1854
        $qb->setSeparator("\n");
1855
        [$sql, $params] = $qb->build((new Query($db))->select('*')->from('table'));
1856
1857
        $this->assertSame(
1858
            DbHelper::replaceQuotes(
1859
                <<<SQL
1860
                SELECT *
1861
                FROM [[table]]
1862
                SQL,
1863
                $db->getName(),
1864
            ),
1865
            $sql,
1866
        );
1867
        $this->assertEmpty($params);
1868
    }
1869
1870
    public function testTruncateTable(): void
1871
    {
1872
        $db = $this->getConnection();
1873
1874
        $qb = $db->getQueryBuilder();
1875
        $sql = $qb->truncateTable('customer');
1876
1877
        $this->assertSame(
1878
            DbHelper::replaceQuotes(
1879
                <<<SQL
1880
                TRUNCATE TABLE [[customer]]
1881
                SQL,
1882
                $db->getName(),
1883
            ),
1884
            $sql,
1885
        );
1886
1887
        $sql = $qb->truncateTable('T_constraints_1');
1888
1889
        $this->assertSame(
1890
            DbHelper::replaceQuotes(
1891
                <<<SQL
1892
                TRUNCATE TABLE [[T_constraints_1]]
1893
                SQL,
1894
                $db->getName(),
1895
            ),
1896
            $sql,
1897
        );
1898
    }
1899
1900
    /**
1901
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::update()
1902
     */
1903
    public function testUpdate(
1904
        string $table,
1905
        array $columns,
1906
        array|string $condition,
1907
        string $expectedSQL,
1908
        array $expectedParams
1909
    ): void {
1910
        $db = $this->getConnection();
1911
1912
        $qb = $db->getQueryBuilder();
1913
        $actualParams = [];
1914
1915
        $this->assertSame($expectedSQL, $qb->update($table, $columns, $condition, $actualParams));
1916
        $this->assertSame($expectedParams, $actualParams);
1917
    }
1918
1919
    /**
1920
     * @dataProvider \Yiisoft\Db\Tests\Provider\QueryBuilderProvider::upsert()
1921
     */
1922
    public function testUpsert(
1923
        string $table,
1924
        array|QueryInterface $insertColumns,
1925
        array|bool $updateColumns,
1926
        string|array $expectedSQL,
1927
        array $expectedParams
1928
    ): void {
1929
        $db = $this->getConnection();
1930
1931
        $actualParams = [];
1932
        $actualSQL = $db->getQueryBuilder()->upsert($table, $insertColumns, $updateColumns, $actualParams);
1933
1934
        if (is_string($expectedSQL)) {
1935
            $this->assertSame($expectedSQL, $actualSQL);
1936
        } else {
1937
            $this->assertContains($actualSQL, $expectedSQL);
1938
        }
1939
1940
        if (ArrayHelper::isAssociative($expectedParams)) {
1941
            $this->assertSame($expectedParams, $actualParams);
1942
        } else {
1943
            Assert::isOneOf($actualParams, $expectedParams);
1944
        }
1945
    }
1946
}
1947