Passed
Push — master ( f4eeb8...0d22ee )
by Sergei
11:47 queued 01:39
created

addDropPrimaryKeysProviderTrait()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1.0723

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 0
dl 0
loc 22
ccs 7
cts 12
cp 0.5833
crap 1.0723
rs 9.7666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\TestUtility;
6
7
use Yiisoft\Db\Connection\ConnectionInterface;
8
use Yiisoft\Db\Expression\Expression;
9
use Yiisoft\Db\Query\Conditions\InCondition;
10
use Yiisoft\Db\Query\Conditions\LikeCondition;
11
use Yiisoft\Db\Query\Conditions\BetweenColumnsCondition;
12
use Yiisoft\Db\Query\Query;
13
use Yiisoft\Db\Query\QueryBuilder;
14
use Yiisoft\Db\Schema\Schema;
15
use Yiisoft\Db\Schema\SchemaBuilderTrait;
16
17
use function array_key_exists;
18
use function array_merge;
19
use function array_values;
20
use function is_array;
21
use function preg_match_all;
22
use function str_replace;
23
use function strncmp;
24
use function substr;
25
26
trait TestQueryBuilderTrait
27
{
28
    use SchemaBuilderTrait;
29
30 11
    public function getDb(): ConnectionInterface
31
    {
32 11
        return $this->getConnection();
0 ignored issues
show
Bug introduced by
It seems like getConnection() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

32
        return $this->/** @scrutinizer ignore-call */ getConnection();
Loading history...
33
    }
34
35
    /**
36
     * This is not used as a dataprovider for testGetColumnType to speed up the test when used as dataprovider every
37
     * single line will cause a reconnect with the database which is not needed here.
38
     */
39 10
    public function columnTypes(): array
40
    {
41 10
        $version = $this->getConnection()->getServerVersion();
42
43
        $items = [
44
            [
45 10
                Schema::TYPE_BIGINT,
46 10
                $this->bigInteger(),
47
                [
48
                    'mysql' => 'bigint(20)',
49
                    'pgsql' => 'bigint',
50
                    'sqlite' => 'bigint',
51
                    'oci' => 'NUMBER(20)',
52
                    'sqlsrv' => 'bigint',
53
                ],
54
            ],
55
            [
56
                Schema::TYPE_BIGINT . ' NOT NULL',
57 10
                $this->bigInteger()->notNull(),
58
                [
59
                    'mysql' => 'bigint(20) NOT NULL',
60
                    'pgsql' => 'bigint NOT NULL',
61
                    'sqlite' => 'bigint NOT NULL',
62
                    'oci' => 'NUMBER(20) NOT NULL',
63
                    'sqlsrv' => 'bigint NOT NULL',
64
                ],
65
            ],
66
            [
67
                Schema::TYPE_BIGINT . ' CHECK (value > 5)',
68 10
                $this->bigInteger()->check('value > 5'),
69
                [
70
                    'mysql' => 'bigint(20) CHECK (value > 5)',
71
                    'pgsql' => 'bigint CHECK (value > 5)',
72
                    'sqlite' => 'bigint CHECK (value > 5)',
73
                    'oci' => 'NUMBER(20) CHECK (value > 5)',
74
                    'sqlsrv' => 'bigint CHECK (value > 5)',
75
                ],
76
            ],
77
            [
78
                Schema::TYPE_BIGINT . '(8)',
79 10
                $this->bigInteger(8),
80
                [
81
                    'mysql' => 'bigint(8)',
82
                    'pgsql' => 'bigint',
83
                    'sqlite' => 'bigint',
84
                    'oci' => 'NUMBER(8)',
85
                    'sqlsrv' => 'bigint',
86
                ],
87
            ],
88
            [
89
                Schema::TYPE_BIGINT . '(8) CHECK (value > 5)',
90 10
                $this->bigInteger(8)->check('value > 5'),
91
                [
92
                    'mysql' => 'bigint(8) CHECK (value > 5)',
93
                    'pgsql' => 'bigint CHECK (value > 5)',
94
                    'sqlite' => 'bigint CHECK (value > 5)',
95
                    'oci' => 'NUMBER(8) CHECK (value > 5)',
96
                    'sqlsrv' => 'bigint CHECK (value > 5)',
97
                ],
98
            ],
99
            [
100
                Schema::TYPE_BIGPK,
101 10
                $this->bigPrimaryKey(),
102
                [
103
                    'mysql' => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY',
104
                    'pgsql' => 'bigserial NOT NULL PRIMARY KEY',
105
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
106
                ],
107
            ],
108
            [
109
                Schema::TYPE_BINARY,
110 10
                $this->binary(),
111
                [
112
                    'mysql' => 'blob',
113
                    'pgsql' => 'bytea',
114
                    'sqlite' => 'blob',
115
                    'oci' => 'BLOB',
116
                    'sqlsrv' => 'varbinary(max)',
117
                ],
118
            ],
119
            [
120
                Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1',
121 10
                $this->boolean()->notNull()->defaultValue(1),
122
                [
123
                    'mysql' => 'tinyint(1) NOT NULL DEFAULT 1',
124
                    'sqlite' => 'boolean NOT NULL DEFAULT 1',
125
                    'sqlsrv' => 'bit NOT NULL DEFAULT 1',
126
                ],
127
            ],
128
            [
129
                Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT TRUE',
130 10
                $this->boolean()->notNull()->defaultValue(true),
131
                [
132
                    'pgsql' => 'boolean NOT NULL DEFAULT TRUE',
133
                ],
134
            ],
135
            [
136
                Schema::TYPE_BOOLEAN,
137 10
                $this->boolean(),
138
                [
139
                    'mysql' => 'tinyint(1)',
140
                    'pgsql' => 'boolean',
141
                    'sqlite' => 'boolean',
142
                    'oci' => 'NUMBER(1)',
143
                    'sqlsrv' => 'bit',
144
                ],
145
            ],
146
            [
147
                Schema::TYPE_CHAR . ' CHECK (value LIKE \'test%\')',
148 10
                $this->char()->check('value LIKE \'test%\''),
149
                [
150
                    'pgsql' => 'char(1) CHECK (value LIKE \'test%\')',
151
                ],
152
            ],
153
            [
154
                Schema::TYPE_CHAR . ' CHECK (value LIKE "test%")',
155 10
                $this->char()->check('value LIKE "test%"'),
156
                [
157
                    'mysql' => 'char(1) CHECK (value LIKE "test%")',
158
                    'sqlite' => 'char(1) CHECK (value LIKE "test%")',
159
                ],
160
            ],
161
            [
162
                Schema::TYPE_CHAR . ' NOT NULL',
163 10
                $this->char()->notNull(),
164
                [
165
                    'mysql' => 'char(1) NOT NULL',
166
                    'pgsql' => 'char(1) NOT NULL',
167
                    'sqlite' => 'char(1) NOT NULL',
168
                    'oci' => 'CHAR(1) NOT NULL',
169
                ],
170
            ],
171
            [
172
                Schema::TYPE_CHAR . '(6) CHECK (value LIKE "test%")',
173 10
                $this->char(6)->check('value LIKE "test%"'),
174
                [
175
                    'mysql' => 'char(6) CHECK (value LIKE "test%")',
176
                    'sqlite' => 'char(6) CHECK (value LIKE "test%")',
177
                ],
178
            ],
179
            [
180
                Schema::TYPE_CHAR . '(6)',
181 10
                $this->char(6)->unsigned(),
182
                [
183
                    'pgsql' => 'char(6)',
184
                ],
185
            ],
186
            [
187
                Schema::TYPE_CHAR . '(6)',
188 10
                $this->char(6),
189
                [
190
                    'mysql' => 'char(6)',
191
                    'pgsql' => 'char(6)',
192
                    'sqlite' => 'char(6)',
193
                    'oci' => 'CHAR(6)',
194
                ],
195
            ],
196
            [
197
                Schema::TYPE_CHAR,
198 10
                $this->char(),
199
                [
200
                    'mysql' => 'char(1)',
201
                    'pgsql' => 'char(1)',
202
                    'sqlite' => 'char(1)',
203
                    'oci' => 'CHAR(1)',
204
                ],
205
            ],
206
            [
207
                Schema::TYPE_DATE . ' NOT NULL',
208 10
                $this->date()->notNull(),
209
                [
210
                    'pgsql' => 'date NOT NULL',
211
                    'sqlite' => 'date NOT NULL',
212
                    'oci' => 'DATE NOT NULL',
213
                    'sqlsrv' => 'date NOT NULL',
214
                ],
215
            ],
216
            [
217
                Schema::TYPE_DATE,
218 10
                $this->date(),
219
                [
220
                    'mysql' => 'date',
221
                    'pgsql' => 'date',
222
                    'sqlite' => 'date',
223
                    'oci' => 'DATE',
224
                    'sqlsrv' => 'date',
225
                ],
226
            ],
227
            [
228
                Schema::TYPE_DATETIME . ' NOT NULL',
229 10
                $this->dateTime()->notNull(),
230
                [
231 10
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'datetime(0) NOT NULL' : 'datetime NOT NULL',
232
                    'pgsql' => 'timestamp(0) NOT NULL',
233
                    'sqlite' => 'datetime NOT NULL',
234
                    'oci' => 'TIMESTAMP NOT NULL',
235
                    'sqlsrv' => 'datetime NOT NULL',
236
                ],
237
            ],
238
            [
239
                Schema::TYPE_DATETIME,
240 10
                $this->dateTime(),
241
                [
242 10
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'datetime(0)' : 'datetime',
243
                    'pgsql' => 'timestamp(0)',
244
                    'sqlite' => 'datetime',
245
                    'oci' => 'TIMESTAMP',
246
                    'sqlsrv' => 'datetime',
247
                ],
248
            ],
249
            [
250
                Schema::TYPE_DECIMAL . ' CHECK (value > 5.6)',
251 10
                $this->decimal()->check('value > 5.6'),
252
                [
253
                    'mysql' => 'decimal(10,0) CHECK (value > 5.6)',
254
                    'pgsql' => 'numeric(10,0) CHECK (value > 5.6)',
255
                    'sqlite' => 'decimal(10,0) CHECK (value > 5.6)',
256
                    'oci' => 'NUMBER CHECK (value > 5.6)',
257
                    'sqlsrv' => 'decimal(18,0) CHECK (value > 5.6)',
258
                ],
259
            ],
260
            [
261
                Schema::TYPE_DECIMAL . ' NOT NULL',
262 10
                $this->decimal()->notNull(),
263
                [
264
                    'mysql' => 'decimal(10,0) NOT NULL',
265
                    'pgsql' => 'numeric(10,0) NOT NULL',
266
                    'sqlite' => 'decimal(10,0) NOT NULL',
267
                    'oci' => 'NUMBER NOT NULL',
268
                    'sqlsrv' => 'decimal(18,0) NOT NULL',
269
                ],
270
            ],
271
            [
272
                Schema::TYPE_DECIMAL . '(12,4) CHECK (value > 5.6)',
273 10
                $this->decimal(12, 4)->check('value > 5.6'),
274
                [
275
                    'mysql' => 'decimal(12,4) CHECK (value > 5.6)',
276
                    'pgsql' => 'numeric(12,4) CHECK (value > 5.6)',
277
                    'sqlite' => 'decimal(12,4) CHECK (value > 5.6)',
278
                    'oci' => 'NUMBER CHECK (value > 5.6)',
279
                    'sqlsrv' => 'decimal(12,4) CHECK (value > 5.6)',
280
                ],
281
            ],
282
            [
283
                Schema::TYPE_DECIMAL . '(12,4)',
284 10
                $this->decimal(12, 4),
285
                [
286
                    'mysql' => 'decimal(12,4)',
287
                    'pgsql' => 'numeric(12,4)',
288
                    'sqlite' => 'decimal(12,4)',
289
                    'oci' => 'NUMBER',
290
                    'sqlsrv' => 'decimal(12,4)',
291
                ],
292
            ],
293
            [
294
                Schema::TYPE_DECIMAL,
295 10
                $this->decimal(),
296
                [
297
                    'mysql' => 'decimal(10,0)',
298
                    'pgsql' => 'numeric(10,0)',
299
                    'sqlite' => 'decimal(10,0)',
300
                    'oci' => 'NUMBER',
301
                    'sqlsrv' => 'decimal(18,0)',
302
                ],
303
            ],
304
            [
305
                Schema::TYPE_DOUBLE . ' CHECK (value > 5.6)',
306 10
                $this->double()->check('value > 5.6'),
307
                [
308
                    'mysql' => 'double CHECK (value > 5.6)',
309
                    'pgsql' => 'double precision CHECK (value > 5.6)',
310
                    'sqlite' => 'double CHECK (value > 5.6)',
311
                    'oci' => 'NUMBER CHECK (value > 5.6)',
312
                    'sqlsrv' => 'float CHECK (value > 5.6)',
313
                ],
314
            ],
315
            [
316
                Schema::TYPE_DOUBLE . ' NOT NULL',
317 10
                $this->double()->notNull(),
318
                [
319
                    'mysql' => 'double NOT NULL',
320
                    'pgsql' => 'double precision NOT NULL',
321
                    'sqlite' => 'double NOT NULL',
322
                    'oci' => 'NUMBER NOT NULL',
323
                    'sqlsrv' => 'float NOT NULL',
324
                ],
325
            ],
326
            [
327
                Schema::TYPE_DOUBLE . '(16) CHECK (value > 5.6)',
328 10
                $this->double(16)->check('value > 5.6'),
329
                [
330
                    'mysql' => 'double CHECK (value > 5.6)',
331
                    'pgsql' => 'double precision CHECK (value > 5.6)',
332
                    'sqlite' => 'double CHECK (value > 5.6)',
333
                    'oci' => 'NUMBER CHECK (value > 5.6)',
334
                    'sqlsrv' => 'float CHECK (value > 5.6)',
335
                ],
336
            ],
337
            [
338
                Schema::TYPE_DOUBLE . '(16)',
339 10
                $this->double(16),
340
                [
341
                    'mysql' => 'double',
342
                    'sqlite' => 'double',
343
                    'oci' => 'NUMBER',
344
                    'sqlsrv' => 'float',
345
                ],
346
            ],
347
            [
348
                Schema::TYPE_DOUBLE,
349 10
                $this->double(),
350
                [
351
                    'mysql' => 'double',
352
                    'pgsql' => 'double precision',
353
                    'sqlite' => 'double',
354
                    'oci' => 'NUMBER',
355
                    'sqlsrv' => 'float',
356
                ],
357
            ],
358
            [
359
                Schema::TYPE_FLOAT . ' CHECK (value > 5.6)',
360 10
                $this->float()->check('value > 5.6'),
361
                [
362
                    'mysql' => 'float CHECK (value > 5.6)',
363
                    'pgsql' => 'double precision CHECK (value > 5.6)',
364
                    'sqlite' => 'float CHECK (value > 5.6)',
365
                    'oci' => 'NUMBER CHECK (value > 5.6)',
366
                    'sqlsrv' => 'float CHECK (value > 5.6)',
367
                ],
368
            ],
369
            [
370
                Schema::TYPE_FLOAT . ' NOT NULL',
371 10
                $this->float()->notNull(),
372
                [
373
                    'mysql' => 'float NOT NULL',
374
                    'pgsql' => 'double precision NOT NULL',
375
                    'sqlite' => 'float NOT NULL',
376
                    'oci' => 'NUMBER NOT NULL',
377
                    'sqlsrv' => 'float NOT NULL',
378
                ],
379
            ],
380
            [
381
                Schema::TYPE_FLOAT . '(16) CHECK (value > 5.6)',
382 10
                $this->float(16)->check('value > 5.6'),
383
                [
384
                    'mysql' => 'float CHECK (value > 5.6)',
385
                    'pgsql' => 'double precision CHECK (value > 5.6)',
386
                    'sqlite' => 'float CHECK (value > 5.6)',
387
                    'oci' => 'NUMBER CHECK (value > 5.6)',
388
                    'sqlsrv' => 'float CHECK (value > 5.6)',
389
                ],
390
            ],
391
            [
392
                Schema::TYPE_FLOAT . '(16)',
393 10
                $this->float(16),
394
                [
395
                    'mysql' => 'float',
396
                    'sqlite' => 'float',
397
                    'oci' => 'NUMBER',
398
                    'sqlsrv' => 'float',
399
                ],
400
            ],
401
            [
402
                Schema::TYPE_FLOAT,
403 10
                $this->float(),
404
                [
405
                    'mysql' => 'float',
406
                    'pgsql' => 'double precision',
407
                    'sqlite' => 'float',
408
                    'oci' => 'NUMBER',
409
                    'sqlsrv' => 'float',
410
                ],
411
            ],
412
            [
413
                Schema::TYPE_INTEGER . ' CHECK (value > 5)',
414 10
                $this->integer()->check('value > 5'),
415
                [
416
                    'mysql' => 'int(11) CHECK (value > 5)',
417
                    'pgsql' => 'integer CHECK (value > 5)',
418
                    'sqlite' => 'integer CHECK (value > 5)',
419
                    'oci' => 'NUMBER(10) CHECK (value > 5)',
420
                    'sqlsrv' => 'int CHECK (value > 5)',
421
                ],
422
            ],
423
            [
424
                Schema::TYPE_INTEGER . ' NOT NULL',
425 10
                $this->integer()->notNull(),
426
                [
427
                    'mysql' => 'int(11) NOT NULL',
428
                    'pgsql' => 'integer NOT NULL',
429
                    'sqlite' => 'integer NOT NULL',
430
                    'oci' => 'NUMBER(10) NOT NULL',
431
                    'sqlsrv' => 'int NOT NULL',
432
                ],
433
            ],
434
            [
435
                Schema::TYPE_INTEGER . '(8) CHECK (value > 5)',
436 10
                $this->integer(8)->check('value > 5'),
437
                [
438
                    'mysql' => 'int(8) CHECK (value > 5)',
439
                    'pgsql' => 'integer CHECK (value > 5)',
440
                    'sqlite' => 'integer CHECK (value > 5)',
441
                    'oci' => 'NUMBER(8) CHECK (value > 5)',
442
                    'sqlsrv' => 'int CHECK (value > 5)',
443
                ],
444
            ],
445
            [
446
                Schema::TYPE_INTEGER . '(8)',
447 10
                $this->integer(8)->unsigned(),
448
                [
449
                    'pgsql' => 'integer',
450
                ],
451
            ],
452
            [
453
                Schema::TYPE_INTEGER . '(8)',
454 10
                $this->integer(8),
455
                [
456
                    'mysql' => 'int(8)',
457
                    'pgsql' => 'integer',
458
                    'sqlite' => 'integer',
459
                    'oci' => 'NUMBER(8)',
460
                    'sqlsrv' => 'int',
461
                ],
462
            ],
463
            [
464
                Schema::TYPE_INTEGER,
465 10
                $this->integer(),
466
                [
467
                    'mysql' => 'int(11)',
468
                    'pgsql' => 'integer',
469
                    'sqlite' => 'integer',
470
                    'oci' => 'NUMBER(10)',
471
                    'sqlsrv' => 'int',
472
                ],
473
            ],
474
            [
475
                Schema::TYPE_MONEY . ' CHECK (value > 0.0)',
476 10
                $this->money()->check('value > 0.0'),
477
                [
478
                    'mysql' => 'decimal(19,4) CHECK (value > 0.0)',
479
                    'pgsql' => 'numeric(19,4) CHECK (value > 0.0)',
480
                    'sqlite' => 'decimal(19,4) CHECK (value > 0.0)',
481
                    'oci' => 'NUMBER(19,4) CHECK (value > 0.0)',
482
                    'sqlsrv' => 'decimal(19,4) CHECK (value > 0.0)',
483
                ],
484
            ],
485
            [
486
                Schema::TYPE_MONEY . ' NOT NULL',
487 10
                $this->money()->notNull(),
488
                [
489
                    'mysql' => 'decimal(19,4) NOT NULL',
490
                    'pgsql' => 'numeric(19,4) NOT NULL',
491
                    'sqlite' => 'decimal(19,4) NOT NULL',
492
                    'oci' => 'NUMBER(19,4) NOT NULL',
493
                    'sqlsrv' => 'decimal(19,4) NOT NULL',
494
                ],
495
            ],
496
            [
497
                Schema::TYPE_MONEY . '(16,2) CHECK (value > 0.0)',
498 10
                $this->money(16, 2)->check('value > 0.0'),
499
                [
500
                    'mysql' => 'decimal(16,2) CHECK (value > 0.0)',
501
                    'pgsql' => 'numeric(16,2) CHECK (value > 0.0)',
502
                    'sqlite' => 'decimal(16,2) CHECK (value > 0.0)',
503
                    'oci' => 'NUMBER(16,2) CHECK (value > 0.0)',
504
                    'sqlsrv' => 'decimal(16,2) CHECK (value > 0.0)',
505
                ],
506
            ],
507
            [
508
                Schema::TYPE_MONEY . '(16,2)',
509 10
                $this->money(16, 2),
510
                [
511
                    'mysql' => 'decimal(16,2)',
512
                    'pgsql' => 'numeric(16,2)',
513
                    'sqlite' => 'decimal(16,2)',
514
                    'oci' => 'NUMBER(16,2)',
515
                    'sqlsrv' => 'decimal(16,2)',
516
                ],
517
            ],
518
            [
519
                Schema::TYPE_MONEY,
520 10
                $this->money(),
521
                [
522
                    'mysql' => 'decimal(19,4)',
523
                    'pgsql' => 'numeric(19,4)',
524
                    'sqlite' => 'decimal(19,4)',
525
                    'oci' => 'NUMBER(19,4)',
526
                    'sqlsrv' => 'decimal(19,4)',
527
                ],
528
            ],
529
            [
530
                Schema::TYPE_PK . ' AFTER `col_before`',
531 10
                $this->primaryKey()->after('col_before'),
532
                [
533
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY AFTER `col_before`',
534
                ],
535
            ],
536
            [
537
                Schema::TYPE_PK . ' FIRST',
538 10
                $this->primaryKey()->first(),
539
                [
540
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST',
541
                ],
542
            ],
543
            [
544
                Schema::TYPE_PK . ' FIRST',
545 10
                $this->primaryKey()->first()->after('col_before'),
546
                [
547
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST',
548
                ],
549
                [
550
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
551
                ],
552
            ],
553
            [
554
                Schema::TYPE_PK . '(8) AFTER `col_before`',
555 10
                $this->primaryKey(8)->after('col_before'),
556
                [
557
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY AFTER `col_before`',
558
                ],
559
            ],
560
            [
561
                Schema::TYPE_PK . '(8) FIRST',
562 10
                $this->primaryKey(8)->first(),
563
                [
564
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST',
565
                ],
566
            ],
567
            [
568
                Schema::TYPE_PK . '(8) FIRST',
569 10
                $this->primaryKey(8)->first()->after('col_before'),
570
                [
571
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST',
572
                ],
573
            ],
574
            [
575
                Schema::TYPE_PK . " COMMENT 'test' AFTER `col_before`",
576 10
                $this->primaryKey()->comment('test')->after('col_before'),
577
                [
578
                    'mysql' => "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'test' AFTER `col_before`",
579
                ],
580
            ],
581
            [
582
                Schema::TYPE_PK . " COMMENT 'testing \'quote\'' AFTER `col_before`",
583 10
                $this->primaryKey()->comment('testing \'quote\'')->after('col_before'),
584
                [
585
                    'mysql' => "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'testing \'quote\''"
586
                        . ' AFTER `col_before`',
587
                ],
588
            ],
589
            [
590
                Schema::TYPE_PK . ' CHECK (value > 5)',
591 10
                $this->primaryKey()->check('value > 5'),
592
                [
593
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)',
594
                    'pgsql' => 'serial NOT NULL PRIMARY KEY CHECK (value > 5)',
595
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL CHECK (value > 5)',
596
                    'oci' => 'NUMBER(10) NOT NULL PRIMARY KEY CHECK (value > 5)',
597
                    'sqlsrv' => 'int IDENTITY PRIMARY KEY CHECK (value > 5)',
598
                ],
599
            ],
600
            [
601
                Schema::TYPE_PK . '(8) CHECK (value > 5)',
602 10
                $this->primaryKey(8)->check('value > 5'),
603
                [
604
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)',
605
                    'oci' => 'NUMBER(8) NOT NULL PRIMARY KEY CHECK (value > 5)',
606
                ],
607
            ],
608
            [
609
                Schema::TYPE_PK . '(8)',
610 10
                $this->primaryKey(8),
611
                [
612
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY',
613
                    'oci' => 'NUMBER(8) NOT NULL PRIMARY KEY',
614
                ],
615
            ],
616
            [
617
                Schema::TYPE_PK,
618 10
                $this->primaryKey(),
619
                [
620
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
621
                    'pgsql' => 'serial NOT NULL PRIMARY KEY',
622
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
623
                    'oci' => 'NUMBER(10) NOT NULL PRIMARY KEY',
624
                    'sqlsrv' => 'int IDENTITY PRIMARY KEY',
625
                ],
626
            ],
627
            [
628
                Schema::TYPE_TINYINT . '(2)',
629 10
                $this->tinyInteger(2),
630
                [
631
                    'mysql' => 'tinyint(2)',
632
                    'pgsql' => 'smallint',
633
                    'sqlite' => 'tinyint',
634
                    'oci' => 'NUMBER(2)',
635
                    'sqlsrv' => 'tinyint',
636
                ],
637
            ],
638
            [
639
                Schema::TYPE_TINYINT . ' UNSIGNED',
640 10
                $this->tinyInteger()->unsigned(),
641
                [
642
                    'mysql' => 'tinyint(3) UNSIGNED',
643
                    'sqlite' => 'tinyint UNSIGNED',
644
                ],
645
            ],
646
            [
647
                Schema::TYPE_TINYINT,
648 10
                $this->tinyInteger(),
649
                [
650
                    'mysql' => 'tinyint(3)',
651
                    'pgsql' => 'smallint',
652
                    'sqlite' => 'tinyint',
653
                    'oci' => 'NUMBER(3)',
654
                    'sqlsrv' => 'tinyint',
655
                ],
656
            ],
657
            [
658
                Schema::TYPE_SMALLINT . '(8)',
659 10
                $this->smallInteger(8),
660
                [
661
                    'mysql' => 'smallint(8)',
662
                    'pgsql' => 'smallint',
663
                    'sqlite' => 'smallint',
664
                    'oci' => 'NUMBER(8)',
665
                    'sqlsrv' => 'smallint',
666
                ],
667
            ],
668
            [
669
                Schema::TYPE_SMALLINT,
670 10
                $this->smallInteger(),
671
                [
672
                    'mysql' => 'smallint(6)',
673
                    'pgsql' => 'smallint',
674
                    'sqlite' => 'smallint',
675
                    'oci' => 'NUMBER(5)',
676
                    'sqlsrv' => 'smallint',
677
                ],
678
            ],
679
            [
680
                Schema::TYPE_STRING . " CHECK (value LIKE 'test%')",
681 10
                $this->string()->check("value LIKE 'test%'"),
682
                [
683
                    'mysql' => "varchar(255) CHECK (value LIKE 'test%')",
684
                    'sqlite' => "varchar(255) CHECK (value LIKE 'test%')",
685
                    'sqlsrv' => "nvarchar(255) CHECK (value LIKE 'test%')",
686
                ],
687
            ],
688
            [
689
                Schema::TYPE_STRING . ' CHECK (value LIKE \'test%\')',
690 10
                $this->string()->check('value LIKE \'test%\''),
691
                [
692
                    'pgsql' => 'varchar(255) CHECK (value LIKE \'test%\')',
693
                    'oci' => 'VARCHAR2(255) CHECK (value LIKE \'test%\')',
694
                ],
695
            ],
696
            [
697
                Schema::TYPE_STRING . ' NOT NULL',
698 10
                $this->string()->notNull(),
699
                [
700
                    'mysql' => 'varchar(255) NOT NULL',
701
                    'pgsql' => 'varchar(255) NOT NULL',
702
                    'sqlite' => 'varchar(255) NOT NULL',
703
                    'oci' => 'VARCHAR2(255) NOT NULL',
704
                    'sqlsrv' => 'nvarchar(255) NOT NULL',
705
                ],
706
            ],
707
            [
708
                Schema::TYPE_STRING . "(32) CHECK (value LIKE 'test%')",
709 10
                $this->string(32)->check("value LIKE 'test%'"),
710
                [
711
                    'mysql' => "varchar(32) CHECK (value LIKE 'test%')",
712
                    'sqlite' => "varchar(32) CHECK (value LIKE 'test%')",
713
                    'sqlsrv' => "nvarchar(32) CHECK (value LIKE 'test%')",
714
                ],
715
            ],
716
            [
717
                Schema::TYPE_STRING . '(32) CHECK (value LIKE \'test%\')',
718 10
                $this->string(32)->check('value LIKE \'test%\''),
719
                [
720
                    'pgsql' => 'varchar(32) CHECK (value LIKE \'test%\')',
721
                    'oci' => 'VARCHAR2(32) CHECK (value LIKE \'test%\')',
722
                ],
723
            ],
724
            [
725
                Schema::TYPE_STRING . '(32)',
726 10
                $this->string(32),
727
                [
728
                    'mysql' => 'varchar(32)',
729
                    'pgsql' => 'varchar(32)',
730
                    'sqlite' => 'varchar(32)',
731
                    'oci' => 'VARCHAR2(32)',
732
                    'sqlsrv' => 'nvarchar(32)',
733
                ],
734
            ],
735
            [
736
                Schema::TYPE_STRING,
737 10
                $this->string(),
738
                [
739
                    'mysql' => 'varchar(255)',
740
                    'pgsql' => 'varchar(255)',
741
                    'sqlite' => 'varchar(255)',
742
                    'oci' => 'VARCHAR2(255)',
743
                    'sqlsrv' => 'nvarchar(255)',
744
                ],
745
            ],
746
            [
747
                Schema::TYPE_TEXT . " CHECK (value LIKE 'test%')",
748 10
                $this->text()->check("value LIKE 'test%'"),
749
                [
750
                    'mysql' => "text CHECK (value LIKE 'test%')",
751
                    'sqlite' => "text CHECK (value LIKE 'test%')",
752
                    'sqlsrv' => "nvarchar(max) CHECK (value LIKE 'test%')",
753
                ],
754
            ],
755
            [
756
                Schema::TYPE_TEXT . ' CHECK (value LIKE \'test%\')',
757 10
                $this->text()->check('value LIKE \'test%\''),
758
                [
759
                    'pgsql' => 'text CHECK (value LIKE \'test%\')',
760
                    'oci' => 'CLOB CHECK (value LIKE \'test%\')',
761
                ],
762
            ],
763
            [
764
                Schema::TYPE_TEXT . ' NOT NULL',
765 10
                $this->text()->notNull(),
766
                [
767
                    'mysql' => 'text NOT NULL',
768
                    'pgsql' => 'text NOT NULL',
769
                    'sqlite' => 'text NOT NULL',
770
                    'oci' => 'CLOB NOT NULL',
771
                    'sqlsrv' => 'nvarchar(max) NOT NULL',
772
                ],
773
            ],
774
            [
775
                Schema::TYPE_TEXT . " CHECK (value LIKE 'test%')",
776 10
                $this->text()->check("value LIKE 'test%'"),
777
                [
778
                    'mysql' => "text CHECK (value LIKE 'test%')",
779
                    'sqlite' => "text CHECK (value LIKE 'test%')",
780
                    'sqlsrv' => "nvarchar(max) CHECK (value LIKE 'test%')",
781
                ],
782
                Schema::TYPE_TEXT . " CHECK (value LIKE 'test%')",
783
            ],
784
            [
785
                Schema::TYPE_TEXT . ' CHECK (value LIKE \'test%\')',
786 10
                $this->text()->check('value LIKE \'test%\''),
787
                [
788
                    'pgsql' => 'text CHECK (value LIKE \'test%\')',
789
                    'oci' => 'CLOB CHECK (value LIKE \'test%\')',
790
                ],
791
                Schema::TYPE_TEXT . ' CHECK (value LIKE \'test%\')',
792
            ],
793
            [
794
                Schema::TYPE_TEXT . ' NOT NULL',
795 10
                $this->text()->notNull(),
796
                [
797
                    'mysql' => 'text NOT NULL',
798
                    'pgsql' => 'text NOT NULL',
799
                    'sqlite' => 'text NOT NULL',
800
                    'oci' => 'CLOB NOT NULL',
801
                    'sqlsrv' => 'nvarchar(max) NOT NULL',
802
                ],
803
                Schema::TYPE_TEXT . ' NOT NULL',
804
            ],
805
            [
806
                Schema::TYPE_TEXT,
807 10
                $this->text(),
808
                [
809
                    'mysql' => 'text',
810
                    'pgsql' => 'text',
811
                    'sqlite' => 'text',
812
                    'oci' => 'CLOB',
813
                    'sqlsrv' => 'nvarchar(max)',
814
                ],
815
                Schema::TYPE_TEXT,
816
            ],
817
            [
818
                Schema::TYPE_TEXT,
819 10
                $this->text(),
820
                [
821
                    'mysql' => 'text',
822
                    'pgsql' => 'text',
823
                    'sqlite' => 'text',
824
                    'oci' => 'CLOB',
825
                    'sqlsrv' => 'nvarchar(max)',
826
                ],
827
            ],
828
            [
829
                Schema::TYPE_TIME . ' NOT NULL',
830 10
                $this->time()->notNull(),
831
                [
832 10
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'time(0) NOT NULL' : 'time NOT NULL',
833
                    'pgsql' => 'time(0) NOT NULL',
834
                    'sqlite' => 'time NOT NULL',
835
                    'oci' => 'TIMESTAMP NOT NULL',
836
                    'sqlsrv' => 'time NOT NULL',
837
                ],
838
            ],
839
            [
840
                Schema::TYPE_TIME,
841 10
                $this->time(),
842
                [
843 10
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'time(0)' : 'time',
844
                    'pgsql' => 'time(0)',
845
                    'sqlite' => 'time',
846
                    'oci' => 'TIMESTAMP',
847
                    'sqlsrv' => 'time',
848
                ],
849
            ],
850
            [
851
                Schema::TYPE_TIMESTAMP . ' NOT NULL',
852 10
                $this->timestamp()->notNull(),
853
                [
854 10
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'timestamp(0) NOT NULL'
855 10
                        : 'timestamp NOT NULL',
856
                    'pgsql' => 'timestamp(0) NOT NULL',
857
                    'sqlite' => 'timestamp NOT NULL',
858
                    'oci' => 'TIMESTAMP NOT NULL',
859
                    'sqlsrv' => 'datetime NOT NULL',
860
                ],
861
            ],
862
            [
863
                Schema::TYPE_TIMESTAMP . ' NULL DEFAULT NULL',
864 10
                $this->timestamp()->defaultValue(null),
865
                [
866 10
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'timestamp(0) NULL DEFAULT NULL'
867 10
                        : 'timestamp NULL DEFAULT NULL',
868
                    'pgsql' => 'timestamp(0) NULL DEFAULT NULL',
869
                    'sqlite' => 'timestamp NULL DEFAULT NULL',
870
                    'sqlsrv' => 'datetime NULL DEFAULT NULL',
871
                ],
872
            ],
873
            [
874
                Schema::TYPE_TIMESTAMP . '(4)',
875 10
                $this->timestamp(4),
876
                [
877
                    'pgsql' => 'timestamp(4)',
878
                ],
879
            ],
880
            [
881
                Schema::TYPE_TIMESTAMP,
882 10
                $this->timestamp(),
883
                [
884
                    /**
885
                     * MySQL has its own TIMESTAMP test realization.
886
                     *
887
                     * {@see QueryBuilderTest::columnTypes()}
888
                     */
889
                    'pgsql' => 'timestamp(0)',
890
                    'sqlite' => 'timestamp',
891
                    'oci' => 'TIMESTAMP',
892
                    'sqlsrv' => 'datetime',
893
                ],
894
            ],
895
            [
896
                Schema::TYPE_UPK,
897 10
                $this->primaryKey()->unsigned(),
898
                [
899
                    'mysql' => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
900
                    'pgsql' => 'serial NOT NULL PRIMARY KEY',
901
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
902
                ],
903
            ],
904
            [
905
                Schema::TYPE_UBIGPK,
906 10
                $this->bigPrimaryKey()->unsigned(),
907
                [
908
                    'mysql' => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
909
                    'pgsql' => 'bigserial NOT NULL PRIMARY KEY',
910
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
911
                ],
912
            ],
913
            [
914
                Schema::TYPE_INTEGER . " COMMENT 'test comment'",
915 10
                $this->integer()->comment('test comment'),
916
                [
917
                    'mysql' => "int(11) COMMENT 'test comment'",
918
                    'sqlsrv' => 'int',
919
                ],
920
                [
921
                    'sqlsrv' => 'integer',
922
                ],
923
            ],
924
            [
925
                Schema::TYPE_PK . " COMMENT 'test comment'",
926 10
                $this->primaryKey()->comment('test comment'),
927
                [
928
                    'mysql' => "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'test comment'",
929
                    'sqlsrv' => 'int IDENTITY PRIMARY KEY',
930
                ],
931
                [
932
                    'sqlsrv' => 'pk',
933
                ],
934
            ],
935
            [
936
                Schema::TYPE_PK . ' FIRST',
937 10
                $this->primaryKey()->first(),
938
                [
939
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST',
940
                    'sqlsrv' => 'int IDENTITY PRIMARY KEY',
941
                ],
942
                [
943
                    'oci' => 'NUMBER(10) NOT NULL PRIMARY KEY FIRST',
944
                    'sqlsrv' => 'pk',
945
                ],
946
            ],
947
            [
948
                Schema::TYPE_INTEGER . ' FIRST',
949 10
                $this->integer()->first(),
950
                [
951
                    'mysql' => 'int(11) FIRST',
952
                    'sqlsrv' => 'int',
953
                ],
954
                [
955
                    'oci' => 'NUMBER(10) FIRST',
956
                    'pgsql' => 'integer',
957
                    'sqlsrv' => 'integer',
958
                ],
959
            ],
960
            [
961
                Schema::TYPE_STRING . ' FIRST',
962 10
                $this->string()->first(),
963
                [
964
                    'mysql' => 'varchar(255) FIRST',
965
                    'sqlsrv' => 'nvarchar(255)',
966
                ],
967
                [
968
                    'oci' => 'VARCHAR2(255) FIRST',
969
                    'sqlsrv' => 'string',
970
                ],
971
            ],
972
            [
973
                Schema::TYPE_INTEGER . ' NOT NULL FIRST',
974 10
                $this->integer()->append('NOT NULL')->first(),
975
                [
976
                    'mysql' => 'int(11) NOT NULL FIRST',
977
                    'sqlsrv' => 'int NOT NULL',
978
                ],
979
                [
980
                    'oci' => 'NUMBER(10) NOT NULL FIRST',
981
                    'sqlsrv' => 'integer NOT NULL',
982
                ],
983
            ],
984
            [
985
                Schema::TYPE_STRING . ' NOT NULL FIRST',
986 10
                $this->string()->append('NOT NULL')->first(),
987
                [
988
                    'mysql' => 'varchar(255) NOT NULL FIRST',
989
                    'sqlsrv' => 'nvarchar(255) NOT NULL',
990
                ],
991
                [
992
                    'oci' => 'VARCHAR2(255) NOT NULL FIRST',
993
                    'sqlsrv' => 'string NOT NULL',
994
                ],
995
            ],
996
            [
997
                Schema::TYPE_JSON,
998 10
                $this->json(),
999
                [
1000
                    'pgsql' => 'jsonb',
1001
                ],
1002
            ],
1003
        ];
1004
1005 10
        foreach ($items as $i => $item) {
1006 10
            if (array_key_exists($this->getConnection()->getDriverName(), $item[2])) {
1007 10
                $item[2] = $item[2][$this->getConnection()->getDriverName()];
1008 10
                $items[$i] = $item;
1009
            } else {
1010 10
                unset($items[$i]);
1011
            }
1012
        }
1013
1014 10
        return array_values($items);
1015
    }
1016
1017 5
    public function testGetColumnType(): void
1018
    {
1019 5
        $qb = $this->getQueryBuilder();
0 ignored issues
show
Bug introduced by
It seems like getQueryBuilder() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

1019
        /** @scrutinizer ignore-call */ 
1020
        $qb = $this->getQueryBuilder();
Loading history...
1020
1021 5
        foreach ($this->columnTypes() as $item) {
1022 5
            [$column, $builder, $expected] = $item;
1023
1024 5
            if (isset($item[3][$this->getConnection()->getDriverName()])) {
1025 1
                $expectedColumnSchemaBuilder = $item[3][$this->getConnection()->getDriverName()];
1026 5
            } elseif (isset($item[3]) && !is_array($item[3])) {
1027 5
                $expectedColumnSchemaBuilder = $item[3];
1028
            } else {
1029 5
                $expectedColumnSchemaBuilder = $column;
1030
            }
1031
1032 5
            $this->assertEquals($expected, $qb->getColumnType($column));
0 ignored issues
show
Bug introduced by
It seems like assertEquals() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

1032
            $this->/** @scrutinizer ignore-call */ 
1033
                   assertEquals($expected, $qb->getColumnType($column));
Loading history...
1033 5
            $this->assertEquals($expected, $qb->getColumnType($builder));
1034 5
            $this->assertEquals($expectedColumnSchemaBuilder, $builder->__toString());
1035
        }
1036
    }
1037
1038 5
    public function testCreateTableColumnTypes(): void
1039
    {
1040 5
        $qb = $this->getQueryBuilder();
1041
1042 5
        if ($qb->getDb()->getTableSchema('column_type_table', true) !== null) {
1043
            $this->getConnection(false)->createCommand($qb->dropTable('column_type_table'))->execute();
1044
        }
1045
1046 5
        $columns = [];
1047 5
        $i = 0;
1048
1049 5
        foreach ($this->columnTypes() as [$column, $builder, $expected]) {
1050
            if (
1051
                !(
1052 5
                    strncmp($column, Schema::TYPE_PK, 2) === 0 ||
1053 5
                    strncmp($column, Schema::TYPE_UPK, 3) === 0 ||
1054 5
                    strncmp($column, Schema::TYPE_BIGPK, 5) === 0 ||
1055 5
                    strncmp($column, Schema::TYPE_UBIGPK, 6) === 0 ||
1056 5
                    strncmp(substr($column, -5), 'FIRST', 5) === 0
1057
                )
1058
            ) {
1059 5
                $columns['col' . ++$i] = str_replace('CHECK (value', 'CHECK ([[col' . $i . ']]', $column);
1060
            }
1061
        }
1062
1063 5
        $this->getConnection(false)->createCommand($qb->createTable('column_type_table', $columns))->execute();
1064
1065 5
        $this->assertNotEmpty($qb->getDb()->getTableSchema('column_type_table', true));
0 ignored issues
show
Bug introduced by
It seems like assertNotEmpty() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

1065
        $this->/** @scrutinizer ignore-call */ 
1066
               assertNotEmpty($qb->getDb()->getTableSchema('column_type_table', true));
Loading history...
1066
    }
1067
1068 5
    public function testBuildWhereExistsWithParameters(): void
1069
    {
1070 5
        $db = $this->getConnection();
1071
1072 5
        $expectedQuerySql = $this->replaceQuotes(
0 ignored issues
show
Bug introduced by
It seems like replaceQuotes() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

1072
        /** @scrutinizer ignore-call */ 
1073
        $expectedQuerySql = $this->replaceQuotes(
Loading history...
1073
            'SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE (EXISTS (SELECT [[1]] FROM [[Website]] [[w]]'
1074
            . ' WHERE (w.id = t.website_id) AND (w.merchant_id = :merchant_id))) AND (t.some_column = :some_value)'
1075
        );
1076
1077 5
        $expectedQueryParams = [':some_value' => 'asd', ':merchant_id' => 6];
1078
1079 5
        $subQuery = new Query($db);
1080
1081 5
        $subQuery->select('1')
1082 5
            ->from('Website w')
1083 5
            ->where('w.id = t.website_id')
1084 5
            ->andWhere('w.merchant_id = :merchant_id', [':merchant_id' => 6]);
1085
1086 5
        $query = new Query($db);
1087
1088 5
        $query->select('id')
1089 5
            ->from('TotalExample t')
1090 5
            ->where(['exists', $subQuery])
1091 5
            ->andWhere('t.some_column = :some_value', [':some_value' => 'asd']);
1092
1093 5
        [$actualQuerySql, $queryParams] = $this->getQueryBuilder()->build($query);
1094
1095 5
        $this->assertEquals($expectedQuerySql, $actualQuerySql);
1096 5
        $this->assertEquals($expectedQueryParams, $queryParams);
1097
    }
1098
1099 5
    public function testBuildWhereExistsWithArrayParameters(): void
1100
    {
1101 5
        $db = $this->getConnection();
1102
1103 5
        $expectedQuerySql = $this->replaceQuotes(
1104
            'SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE (EXISTS (SELECT [[1]] FROM [[Website]] [[w]]'
1105
            . ' WHERE (w.id = t.website_id) AND (([[w]].[[merchant_id]]=:qp0) AND ([[w]].[[user_id]]=:qp1))))'
1106
            . ' AND ([[t]].[[some_column]]=:qp2)'
1107
        );
1108
1109 5
        $expectedQueryParams = [':qp0' => 6, ':qp1' => 210, ':qp2' => 'asd'];
1110
1111 5
        $subQuery = new Query($db);
1112
1113 5
        $subQuery->select('1')
1114 5
            ->from('Website w')
1115 5
            ->where('w.id = t.website_id')
1116 5
            ->andWhere(['w.merchant_id' => 6, 'w.user_id' => '210']);
1117
1118 5
        $query = new Query($db);
1119
1120 5
        $query->select('id')
1121 5
            ->from('TotalExample t')
1122 5
            ->where(['exists', $subQuery])
1123 5
            ->andWhere(['t.some_column' => 'asd']);
1124
1125 5
        [$actualQuerySql, $queryParams] = $this->getQueryBuilder()->build($query);
1126
1127 5
        $this->assertEquals($expectedQuerySql, $actualQuerySql);
1128 5
        $this->assertEquals($expectedQueryParams, $queryParams);
1129
    }
1130
1131
    /**
1132
     * This test contains three select queries connected with UNION and UNION ALL constructions.
1133
     * It could be useful to use "phpunit --group=db --filter testBuildUnion" command for run it.
1134
     */
1135 4
    public function testBuildUnion(): void
1136
    {
1137 4
        $db = $this->getConnection();
1138
1139 4
        $expectedQuerySql = $this->replaceQuotes(
1140
            '(SELECT [[id]] FROM [[TotalExample]] [[t1]] WHERE (w > 0) AND (x < 2)) UNION ( SELECT [[id]]'
1141
            . ' FROM [[TotalTotalExample]] [[t2]] WHERE w > 5 ) UNION ALL ( SELECT [[id]] FROM [[TotalTotalExample]]'
1142
            . ' [[t3]] WHERE w = 3 )'
1143
        );
1144
1145 4
        $query = new Query($db);
1146 4
        $secondQuery = new Query($db);
1147
1148 4
        $secondQuery->select('id')
1149 4
              ->from('TotalTotalExample t2')
1150 4
              ->where('w > 5');
1151
1152 4
        $thirdQuery = new Query($db);
1153
1154 4
        $thirdQuery->select('id')
1155 4
              ->from('TotalTotalExample t3')
1156 4
              ->where('w = 3');
1157
1158 4
        $query->select('id')
1159 4
              ->from('TotalExample t1')
1160 4
              ->where(['and', 'w > 0', 'x < 2'])
1161 4
              ->union($secondQuery)
1162 4
              ->union($thirdQuery, true);
1163
1164 4
        [$actualQuerySql, $queryParams] = $this->getQueryBuilder()->build($query);
1165
1166 4
        $this->assertEquals($expectedQuerySql, $actualQuerySql);
1167 4
        $this->assertEquals([], $queryParams);
1168
    }
1169
1170 4
    public function testBuildWithQuery(): void
1171
    {
1172 4
        $db = $this->getConnection();
1173
1174 4
        $expectedQuerySql = $this->replaceQuotes(
1175
            'WITH a1 AS (SELECT [[id]] FROM [[t1]] WHERE expr = 1), a2 AS ((SELECT [[id]] FROM [[t2]]'
1176
            . ' INNER JOIN [[a1]] ON t2.id = a1.id WHERE expr = 2) UNION ( SELECT [[id]] FROM [[t3]] WHERE expr = 3 ))'
1177
            . ' SELECT * FROM [[a2]]'
1178
        );
1179
1180 4
        $with1Query = (new Query($db))
1181 4
            ->select('id')
1182 4
            ->from('t1')
1183 4
            ->where('expr = 1');
1184
1185 4
        $with2Query = (new Query($db))
1186 4
            ->select('id')
1187 4
            ->from('t2')
1188 4
            ->innerJoin('a1', 't2.id = a1.id')
1189 4
            ->where('expr = 2');
1190
1191 4
        $with3Query = (new Query($db))
1192 4
            ->select('id')
1193 4
            ->from('t3')
1194 4
            ->where('expr = 3');
1195
1196 4
        $query = (new Query($db))
1197 4
            ->withQuery($with1Query, 'a1')
1198 4
            ->withQuery($with2Query->union($with3Query), 'a2')
1199 4
            ->from('a2');
1200
1201 4
        [$actualQuerySql, $queryParams] = $this->getQueryBuilder()->build($query);
1202
1203 4
        $this->assertEquals($expectedQuerySql, $actualQuerySql);
1204 4
        $this->assertEquals([], $queryParams);
1205
    }
1206
1207 5
    public function testBuildWithQueryRecursive(): void
1208
    {
1209 5
        $db = $this->getConnection();
1210
1211 5
        $expectedQuerySql = $this->replaceQuotes(
1212
            'WITH RECURSIVE a1 AS (SELECT [[id]] FROM [[t1]] WHERE expr = 1) SELECT * FROM [[a1]]'
1213
        );
1214
1215 5
        $with1Query = (new Query($db))
1216 5
            ->select('id')
1217 5
            ->from('t1')
1218 5
            ->where('expr = 1');
1219
1220 5
        $query = (new Query($db))
1221 5
            ->withQuery($with1Query, 'a1', true)
1222 5
            ->from('a1');
1223
1224 5
        [$actualQuerySql, $queryParams] = $this->getQueryBuilder()->build($query);
1225
1226 5
        $this->assertEquals($expectedQuerySql, $actualQuerySql);
1227 5
        $this->assertEquals([], $queryParams);
1228
    }
1229
1230 5
    public function testSelectSubquery(): void
1231
    {
1232 5
        $db = $this->getConnection();
1233
1234 5
        $subquery = (new Query($db))
1235 5
            ->select('COUNT(*)')
1236 5
            ->from('operations')
1237 5
            ->where('account_id = accounts.id');
1238
1239 5
        $query = (new Query($db))
1240 5
            ->select('*')
1241 5
            ->from('accounts')
1242 5
            ->addSelect(['operations_count' => $subquery]);
1243
1244 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1245
1246 5
        $expected = $this->replaceQuotes(
1247
            'SELECT *, (SELECT COUNT(*) FROM [[operations]] WHERE account_id = accounts.id) AS [[operations_count]]'
1248
            . ' FROM [[accounts]]'
1249
        );
1250
1251 5
        $this->assertEquals($expected, $sql);
1252 5
        $this->assertEmpty($params);
0 ignored issues
show
Bug introduced by
It seems like assertEmpty() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

1252
        $this->/** @scrutinizer ignore-call */ 
1253
               assertEmpty($params);
Loading history...
1253
    }
1254
1255 5
    public function testComplexSelect(): void
1256
    {
1257 5
        $db = $this->getConnection();
1258
1259 5
        $query = (new Query($db))
1260 5
            ->select([
1261
                'ID' => 't.id',
1262
                'gsm.username as GSM',
1263
                'part.Part',
1264
                'Part Cost' => 't.Part_Cost',
1265
                'st_x(location::geometry) as lon',
1266 5
                new Expression(
1267 5
                    $this->replaceQuotes(
1268
                        "case t.Status_Id when 1 then 'Acknowledge' when 2 then 'No Action' else 'Unknown Action'"
1269
                        . ' END as [[Next Action]]'
1270
                    )
1271
                ),
1272
            ])
1273 5
            ->from('tablename');
1274
1275 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1276
1277 5
        $expected = $this->replaceQuotes(
1278
            'SELECT [[t]].[[id]] AS [[ID]], [[gsm]].[[username]] AS [[GSM]], [[part]].[[Part]], [[t]].[[Part_Cost]]'
1279
            . ' AS [[Part Cost]], st_x(location::geometry) AS [[lon]], case t.Status_Id when 1 then \'Acknowledge\''
1280
            . ' when 2 then \'No Action\' else \'Unknown Action\' END as [[Next Action]] FROM [[tablename]]'
1281
        );
1282
1283 5
        $this->assertEquals($expected, $sql);
1284 5
        $this->assertEmpty($params);
1285
    }
1286
1287 5
    public function testSelectExpression(): void
1288
    {
1289 5
        $db = $this->getConnection();
1290
1291 5
        $query = (new Query($db))
1292 5
            ->select(new Expression('1 AS ab'))
1293 5
            ->from('tablename');
1294
1295 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1296
1297 5
        $expected = $this->replaceQuotes('SELECT 1 AS ab FROM [[tablename]]');
1298
1299 5
        $this->assertEquals($expected, $sql);
1300 5
        $this->assertEmpty($params);
1301
1302 5
        $query = (new Query($db))
1303 5
            ->select(new Expression('1 AS ab'))
1304 5
            ->addSelect(new Expression('2 AS cd'))
1305 5
            ->addSelect(['ef' => new Expression('3')])
1306 5
            ->from('tablename');
1307
1308 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1309
1310 5
        $expected = $this->replaceQuotes('SELECT 1 AS ab, 2 AS cd, 3 AS [[ef]] FROM [[tablename]]');
1311
1312 5
        $this->assertEquals($expected, $sql);
1313 5
        $this->assertEmpty($params);
1314
1315 5
        $query = (new Query($db))
1316 5
            ->select(new Expression('SUBSTR(name, 0, :len)', [':len' => 4]))
1317 5
            ->from('tablename');
1318
1319 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1320
1321 5
        $expected = $this->replaceQuotes('SELECT SUBSTR(name, 0, :len) FROM [[tablename]]');
1322
1323 5
        $this->assertEquals($expected, $sql);
1324 5
        $this->assertEquals([':len' => 4], $params);
1325
    }
1326
1327
    /**
1328
     * {@see https://github.com/yiisoft/yii2/issues/10869}
1329
     */
1330 5
    public function testFromIndexHint(): void
1331
    {
1332 5
        $db = $this->getConnection();
1333
1334 5
        $query = (new Query($db))->from([new Expression('{{%user}} USE INDEX (primary)')]);
1335
1336 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1337
1338 5
        $expected = $this->replaceQuotes('SELECT * FROM {{%user}} USE INDEX (primary)');
1339
1340 5
        $this->assertEquals($expected, $sql);
1341 5
        $this->assertEmpty($params);
1342
1343 5
        $query = (new Query($db))
1344 5
            ->from([new Expression('{{user}} {{t}} FORCE INDEX (primary) IGNORE INDEX FOR ORDER BY (i1)')])
1345 5
            ->leftJoin(['p' => 'profile'], 'user.id = profile.user_id USE INDEX (i2)');
1346
1347 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1348
1349 5
        $expected = $this->replaceQuotes(
1350
            'SELECT * FROM {{user}} {{t}} FORCE INDEX (primary) IGNORE INDEX FOR ORDER BY (i1)'
1351
            . ' LEFT JOIN [[profile]] [[p]] ON user.id = profile.user_id USE INDEX (i2)'
1352
        );
1353
1354 5
        $this->assertEquals($expected, $sql);
1355 5
        $this->assertEmpty($params);
1356
    }
1357
1358 5
    public function testFromSubquery(): void
1359
    {
1360 5
        $db = $this->getConnection();
1361
1362
        /* query subquery */
1363 5
        $subquery = (new Query($db))->from('user')->where('account_id = accounts.id');
1364
1365 5
        $query = (new Query($db))->from(['activeusers' => $subquery]);
1366
1367
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
1368 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1369
1370 5
        $expected = $this->replaceQuotes(
1371
            'SELECT * FROM (SELECT * FROM [[user]] WHERE account_id = accounts.id) [[activeusers]]'
1372
        );
1373
1374 5
        $this->assertEquals($expected, $sql);
1375 5
        $this->assertEmpty($params);
1376
1377
        /* query subquery with params */
1378 5
        $subquery = (new Query($db))->from('user')->where('account_id = :id', ['id' => 1]);
1379
1380 5
        $query = (new Query($db))->from(['activeusers' => $subquery])->where('abc = :abc', ['abc' => 'abc']);
1381
1382
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
1383 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1384
1385 5
        $expected = $this->replaceQuotes(
1386
            'SELECT * FROM (SELECT * FROM [[user]] WHERE account_id = :id) [[activeusers]] WHERE abc = :abc'
1387
        );
1388
1389 5
        $this->assertEquals($expected, $sql);
1390 5
        $this->assertEquals(['id' => 1, 'abc' => 'abc'], $params);
1391
1392
        /* simple subquery */
1393 5
        $subquery = '(SELECT * FROM user WHERE account_id = accounts.id)';
1394
1395 5
        $query = (new Query($db))->from(['activeusers' => $subquery]);
1396
1397
        /* SELECT * FROM (SELECT * FROM [[user]] WHERE [[active]] = 1) [[activeusers]]; */
1398 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1399
1400 5
        $expected = $this->replaceQuotes(
1401
            'SELECT * FROM (SELECT * FROM user WHERE account_id = accounts.id) [[activeusers]]'
1402
        );
1403
1404 5
        $this->assertEquals($expected, $sql);
1405 5
        $this->assertEmpty($params);
1406
    }
1407
1408 5
    public function testOrderBy(): void
1409
    {
1410 5
        $db = $this->getConnection();
1411
1412
        /* simple string */
1413 5
        $query = (new Query($db))
1414 5
            ->select('*')
1415 5
            ->from('operations')
1416 5
            ->orderBy('name ASC, date DESC');
1417
1418 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1419
1420 5
        $expected = $this->replaceQuotes('SELECT * FROM [[operations]] ORDER BY [[name]], [[date]] DESC');
1421
1422 5
        $this->assertEquals($expected, $sql);
1423 5
        $this->assertEmpty($params);
1424
1425
        /* array syntax */
1426 5
        $query = (new Query($db))
1427 5
            ->select('*')
1428 5
            ->from('operations')
1429 5
            ->orderBy(['name' => SORT_ASC, 'date' => SORT_DESC]);
1430
1431 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1432
1433 5
        $expected = $this->replaceQuotes('SELECT * FROM [[operations]] ORDER BY [[name]], [[date]] DESC');
1434
1435 5
        $this->assertEquals($expected, $sql);
1436 5
        $this->assertEmpty($params);
1437
1438
        /* expression */
1439 5
        $query = (new Query($db))
1440 5
            ->select('*')
1441 5
            ->from('operations')
1442 5
            ->where('account_id = accounts.id')
1443 5
            ->orderBy(new Expression('SUBSTR(name, 3, 4) DESC, x ASC'));
1444
1445 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1446
1447 5
        $expected = $this->replaceQuotes(
1448
            'SELECT * FROM [[operations]] WHERE account_id = accounts.id ORDER BY SUBSTR(name, 3, 4) DESC, x ASC'
1449
        );
1450
1451 5
        $this->assertEquals($expected, $sql);
1452 5
        $this->assertEmpty($params);
1453
1454
        /* expression with params */
1455 5
        $query = (new Query($db))
1456 5
            ->select('*')
1457 5
            ->from('operations')
1458 5
            ->orderBy(new Expression('SUBSTR(name, 3, :to) DESC, x ASC', [':to' => 4]));
1459
1460 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1461
1462 5
        $expected = $this->replaceQuotes('SELECT * FROM [[operations]] ORDER BY SUBSTR(name, 3, :to) DESC, x ASC');
1463
1464 5
        $this->assertEquals($expected, $sql);
1465 5
        $this->assertEquals([':to' => 4], $params);
1466
    }
1467
1468 5
    public function testGroupBy(): void
1469
    {
1470 5
        $db = $this->getConnection();
1471
1472
        /* simple string */
1473 5
        $query = (new Query($db))
1474 5
            ->select('*')
1475 5
            ->from('operations')
1476 5
            ->groupBy('name, date');
1477
1478 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1479
1480 5
        $expected = $this->replaceQuotes('SELECT * FROM [[operations]] GROUP BY [[name]], [[date]]');
1481
1482 5
        $this->assertEquals($expected, $sql);
1483 5
        $this->assertEmpty($params);
1484
1485
        /* array syntax */
1486 5
        $query = (new Query($db))
1487 5
            ->select('*')
1488 5
            ->from('operations')
1489 5
            ->groupBy(['name', 'date']);
1490
1491 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1492
1493 5
        $expected = $this->replaceQuotes('SELECT * FROM [[operations]] GROUP BY [[name]], [[date]]');
1494
1495 5
        $this->assertEquals($expected, $sql);
1496 5
        $this->assertEmpty($params);
1497
1498
        /* expression */
1499 5
        $query = (new Query($db))
1500 5
            ->select('*')
1501 5
            ->from('operations')
1502 5
            ->where('account_id = accounts.id')
1503 5
            ->groupBy(new Expression('SUBSTR(name, 0, 1), x'));
1504
1505 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1506
1507 5
        $expected = $this->replaceQuotes(
1508
            'SELECT * FROM [[operations]] WHERE account_id = accounts.id GROUP BY SUBSTR(name, 0, 1), x'
1509
        );
1510
1511 5
        $this->assertEquals($expected, $sql);
1512 5
        $this->assertEmpty($params);
1513
1514
        /* expression with params */
1515 5
        $query = (new Query($db))
1516 5
            ->select('*')
1517 5
            ->from('operations')
1518 5
            ->groupBy(new Expression('SUBSTR(name, 0, :to), x', [':to' => 4]));
1519
1520 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1521
1522 5
        $expected = $this->replaceQuotes('SELECT * FROM [[operations]] GROUP BY SUBSTR(name, 0, :to), x');
1523
1524 5
        $this->assertEquals($expected, $sql);
1525 5
        $this->assertEquals([':to' => 4], $params);
1526
    }
1527
1528
    /**
1529
     * Dummy test to speed up QB's tests which rely on DB schema.
1530
     */
1531 5
    public function testInitFixtures(): void
1532
    {
1533 5
        $this->assertInstanceOf(QueryBuilder::class, $this->getQueryBuilder(true, true));
0 ignored issues
show
Bug introduced by
It seems like assertInstanceOf() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

1533
        $this->/** @scrutinizer ignore-call */ 
1534
               assertInstanceOf(QueryBuilder::class, $this->getQueryBuilder(true, true));
Loading history...
1534
    }
1535
1536
    /**
1537
     * {@see https://github.com/yiisoft/yii2/issues/15653}
1538
     */
1539 5
    public function testIssue15653(): void
1540
    {
1541 5
        $db = $this->getConnection();
1542
1543 5
        $query = (new Query($db))
1544 5
            ->from('admin_user')
1545 5
            ->where(['is_deleted' => false]);
1546
1547
        $query
1548 5
            ->where([])
1549 5
            ->andWhere(['in', 'id', ['1', '0']]);
1550
1551 5
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1552
1553 5
        $this->assertSame($this->replaceQuotes('SELECT * FROM [[admin_user]] WHERE [[id]] IN (:qp0, :qp1)'), $sql);
0 ignored issues
show
Bug introduced by
It seems like assertSame() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

1553
        $this->/** @scrutinizer ignore-call */ 
1554
               assertSame($this->replaceQuotes('SELECT * FROM [[admin_user]] WHERE [[id]] IN (:qp0, :qp1)'), $sql);
Loading history...
1554 5
        $this->assertSame([':qp0' => '1', ':qp1' => '0'], $params);
1555
    }
1556
1557 3
    public function addDropChecksProviderTrait(): array
1558
    {
1559
        $tableName = 'T_constraints_1';
1560
        $name = 'CN_check';
1561
1562
        return [
1563
            'drop' => [
1564
                "ALTER TABLE {{{$tableName}}} DROP CONSTRAINT [[$name]]",
1565 3
                static function (QueryBuilder $qb) use ($tableName, $name) {
1566 3
                    return $qb->dropCheck($name, $tableName);
1567
                },
1568
            ],
1569
            'add' => [
1570
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] CHECK ([[C_not_null]] > 100)",
1571 3
                static function (QueryBuilder $qb) use ($tableName, $name) {
1572 3
                    return $qb->addCheck($name, $tableName, '[[C_not_null]] > 100');
1573
                },
1574
            ],
1575
        ];
1576
    }
1577
1578 3
    public function addDropForeignKeysProviderTrait(): array
1579
    {
1580
        $tableName = 'T_constraints_3';
1581
        $name = 'CN_constraints_3';
1582
        $pkTableName = 'T_constraints_2';
1583
1584
        return [
1585
            'drop' => [
1586
                "ALTER TABLE {{{$tableName}}} DROP CONSTRAINT [[$name]]",
1587 3
                static function (QueryBuilder $qb) use ($tableName, $name) {
1588 3
                    return $qb->dropForeignKey($name, $tableName);
1589
                },
1590
            ],
1591
            'add' => [
1592
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] FOREIGN KEY ([[C_fk_id_1]])"
1593
                . " REFERENCES {{{$pkTableName}}} ([[C_id_1]]) ON DELETE CASCADE ON UPDATE CASCADE",
1594 3
                static function (QueryBuilder $qb) use ($tableName, $name, $pkTableName) {
1595 3
                    return $qb->addForeignKey(
1596
                        $name,
1597
                        $tableName,
1598
                        'C_fk_id_1',
1599
                        $pkTableName,
1600
                        'C_id_1',
1601
                        'CASCADE',
1602
                        'CASCADE'
1603
                    );
1604
                },
1605
            ],
1606
            'add (2 columns)' => [
1607
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] FOREIGN KEY ([[C_fk_id_1]], [[C_fk_id_2]])"
1608
                . " REFERENCES {{{$pkTableName}}} ([[C_id_1]], [[C_id_2]]) ON DELETE CASCADE ON UPDATE CASCADE",
1609 3
                static function (QueryBuilder $qb) use ($tableName, $name, $pkTableName) {
1610 3
                    return $qb->addForeignKey(
1611
                        $name,
1612
                        $tableName,
1613
                        'C_fk_id_1, C_fk_id_2',
1614
                        $pkTableName,
1615
                        'C_id_1, C_id_2',
1616
                        'CASCADE',
1617
                        'CASCADE'
1618
                    );
1619
                },
1620
            ],
1621
        ];
1622
    }
1623
1624 4
    public function addDropPrimaryKeysProviderTrait(): array
1625
    {
1626
        $tableName = 'T_constraints_1';
1627
        $name = 'CN_pk';
1628
1629
        return [
1630
            'drop' => [
1631
                "ALTER TABLE {{{$tableName}}} DROP CONSTRAINT [[$name]]",
1632 4
                static function (QueryBuilder $qb) use ($tableName, $name) {
1633 4
                    return $qb->dropPrimaryKey($name, $tableName);
1634
                },
1635
            ],
1636
            'add' => [
1637
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] PRIMARY KEY ([[C_id_1]])",
1638 4
                static function (QueryBuilder $qb) use ($tableName, $name) {
1639 4
                    return $qb->addPrimaryKey($name, $tableName, 'C_id_1');
1640
                },
1641
            ],
1642
            'add (2 columns)' => [
1643
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] PRIMARY KEY ([[C_id_1]], [[C_id_2]])",
1644 4
                static function (QueryBuilder $qb) use ($tableName, $name) {
1645 4
                    return $qb->addPrimaryKey($name, $tableName, 'C_id_1, C_id_2');
1646
                },
1647
            ],
1648
        ];
1649
    }
1650
1651 4
    public function addDropUniquesProviderTrait(): array
1652
    {
1653
        $tableName1 = 'T_constraints_1';
1654
        $name1 = 'CN_unique';
1655
        $tableName2 = 'T_constraints_2';
1656
        $name2 = 'CN_constraints_2_multi';
1657
1658
        return [
1659
            'drop' => [
1660
                "ALTER TABLE {{{$tableName1}}} DROP CONSTRAINT [[$name1]]",
1661 4
                static function (QueryBuilder $qb) use ($tableName1, $name1) {
1662 4
                    return $qb->dropUnique($name1, $tableName1);
1663
                },
1664
            ],
1665
            'add' => [
1666
                "ALTER TABLE {{{$tableName1}}} ADD CONSTRAINT [[$name1]] UNIQUE ([[C_unique]])",
1667 4
                static function (QueryBuilder $qb) use ($tableName1, $name1) {
1668 4
                    return $qb->addUnique($name1, $tableName1, 'C_unique');
1669
                },
1670
            ],
1671
            'add (2 columns)' => [
1672
                "ALTER TABLE {{{$tableName2}}} ADD CONSTRAINT [[$name2]] UNIQUE ([[C_index_2_1]], [[C_index_2_2]])",
1673 4
                static function (QueryBuilder $qb) use ($tableName2, $name2) {
1674 4
                    return $qb->addUnique($name2, $tableName2, 'C_index_2_1, C_index_2_2');
1675
                },
1676
            ],
1677
        ];
1678
    }
1679
1680
    public function batchInsertProviderTrait(): array
1681
    {
1682
        return [
1683
            [
1684
                'customer',
1685
                ['email', 'name', 'address'],
1686
                [['[email protected]', 'silverfire', 'Kyiv {{city}}, Ukraine']],
1687
                $this->replaceQuotes(
1688
                    'INSERT INTO [[customer]] ([[email]], [[name]], [[address]])'
1689
                    . " VALUES ('[email protected]', 'silverfire', 'Kyiv {{city}}, Ukraine')"
1690
                ),
1691
            ],
1692
            'escape-danger-chars' => [
1693
                'customer',
1694
                ['address'],
1695
                [["SQL-danger chars are escaped: '); --"]],
1696
                'expected' => $this->replaceQuotes(
1697
                    "INSERT INTO [[customer]] ([[address]]) VALUES ('SQL-danger chars are escaped: \'); --')"
1698
                ),
1699
            ],
1700
            [
1701
                'customer',
1702
                ['address'],
1703
                [],
1704
                '',
1705
            ],
1706
            [
1707
                'customer',
1708
                [],
1709
                [['no columns passed']],
1710
                $this->replaceQuotes("INSERT INTO [[customer]] () VALUES ('no columns passed')"),
1711
            ],
1712
            'bool-false, bool2-null' => [
1713
                'type',
1714
                ['bool_col', 'bool_col2'],
1715
                [[false, null]],
1716
                'expected' => $this->replaceQuotes(
1717
                    'INSERT INTO [[type]] ([[bool_col]], [[bool_col2]]) VALUES (0, NULL)'
1718
                ),
1719
            ],
1720
            [
1721
                '{{%type}}',
1722
                ['{{%type}}.[[float_col]]', '[[time]]'],
1723
                [[null, new Expression('now()')]],
1724
                'INSERT INTO {{%type}} ({{%type}}.[[float_col]], [[time]]) VALUES (NULL, now())',
1725
            ],
1726
            'bool-false, time-now()' => [
1727
                '{{%type}}',
1728
                ['{{%type}}.[[bool_col]]', '[[time]]'],
1729
                [[false, new Expression('now()')]],
1730
                'expected' => 'INSERT INTO {{%type}} ({{%type}}.[[bool_col]], [[time]]) VALUES (0, now())',
1731
            ],
1732
        ];
1733
    }
1734
1735
    public function buildConditionsProviderTrait(): array
1736
    {
1737
        $conditions = [
1738
            /* empty values */
1739
            [['like', 'name', []], '0=1', []],
1740
            [['not like', 'name', []], '', []],
1741
            [['or like', 'name', []], '0=1', []],
1742
            [['or not like', 'name', []], '', []],
1743
1744
            /* not */
1745
            [['not', 'name'], 'NOT (name)', []],
1746
            [
1747
                [
1748
                    'not',
1749
                    (new Query($this->getConnection()))
1750
                        ->select('exists')
1751
                        ->from('some_table'),
1752
                ],
1753
                'NOT ((SELECT [[exists]] FROM [[some_table]]))', [],
1754
            ],
1755
1756
            /* and */
1757
            [['and', 'id=1', 'id=2'], '(id=1) AND (id=2)', []],
1758
            [['and', 'type=1', ['or', 'id=1', 'id=2']], '(type=1) AND ((id=1) OR (id=2))', []],
1759
            [['and', 'id=1', new Expression('id=:qp0', [':qp0' => 2])], '(id=1) AND (id=:qp0)', [':qp0' => 2]],
1760
            [
1761
                [
1762
                    'and',
1763
                    ['expired' => false],
1764
                    (new Query($this->getConnection()))
1765
                        ->select('count(*) > 1')
1766
                        ->from('queue'),
1767
                ],
1768
                '([[expired]]=:qp0) AND ((SELECT count(*) > 1 FROM [[queue]]))',
1769
                [':qp0' => false],
1770
            ],
1771
1772
            /* or */
1773
            [['or', 'id=1', 'id=2'], '(id=1) OR (id=2)', []],
1774
            [['or', 'type=1', ['or', 'id=1', 'id=2']], '(type=1) OR ((id=1) OR (id=2))', []],
1775
            [['or', 'type=1', new Expression('id=:qp0', [':qp0' => 1])], '(type=1) OR (id=:qp0)', [':qp0' => 1]],
1776
1777
            /* between */
1778
            [['between', 'id', 1, 10], '[[id]] BETWEEN :qp0 AND :qp1', [':qp0' => 1, ':qp1' => 10]],
1779
            [['not between', 'id', 1, 10], '[[id]] NOT BETWEEN :qp0 AND :qp1', [':qp0' => 1, ':qp1' => 10]],
1780
            [
1781
                ['between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), new Expression('NOW()')],
1782
                '[[date]] BETWEEN (NOW() - INTERVAL 1 MONTH) AND NOW()',
1783
                [],
1784
            ],
1785
            [
1786
                ['between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), 123],
1787
                '[[date]] BETWEEN (NOW() - INTERVAL 1 MONTH) AND :qp0',
1788
                [':qp0' => 123],
1789
            ],
1790
            [
1791
                ['not between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), new Expression('NOW()')],
1792
                '[[date]] NOT BETWEEN (NOW() - INTERVAL 1 MONTH) AND NOW()',
1793
                [],
1794
            ],
1795
            [
1796
                ['not between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), 123],
1797
                '[[date]] NOT BETWEEN (NOW() - INTERVAL 1 MONTH) AND :qp0',
1798
                [':qp0' => 123],
1799
            ],
1800
            [
1801
                new BetweenColumnsCondition('2018-02-11', 'BETWEEN', 'create_time', 'update_time'),
1802
                ':qp0 BETWEEN [[create_time]] AND [[update_time]]',
1803
                [':qp0' => '2018-02-11'],
1804
            ],
1805
            [
1806
                new BetweenColumnsCondition('2018-02-11', 'NOT BETWEEN', 'NOW()', 'update_time'),
1807
                ':qp0 NOT BETWEEN NOW() AND [[update_time]]',
1808
                [':qp0' => '2018-02-11'],
1809
            ],
1810
            [
1811
                new BetweenColumnsCondition(new Expression('NOW()'), 'BETWEEN', 'create_time', 'update_time'),
1812
                'NOW() BETWEEN [[create_time]] AND [[update_time]]',
1813
                [],
1814
            ],
1815
            [
1816
                new BetweenColumnsCondition(new Expression('NOW()'), 'NOT BETWEEN', 'create_time', 'update_time'),
1817
                'NOW() NOT BETWEEN [[create_time]] AND [[update_time]]',
1818
                [],
1819
            ],
1820
            [
1821
                new BetweenColumnsCondition(
1822
                    new Expression('NOW()'),
1823
                    'NOT BETWEEN',
1824
                    (new Query($this->getConnection()))
1825
                        ->select('min_date')
1826
                        ->from('some_table'),
1827
                    'max_date'
1828
                ),
1829
                'NOW() NOT BETWEEN (SELECT [[min_date]] FROM [[some_table]]) AND [[max_date]]',
1830
                [],
1831
            ],
1832
1833
            /* in */
1834
            [
1835
                [
1836
                    'in',
1837
                    'id',
1838
                    [
1839
                        1,
1840
                        2,
1841
                        (new Query($this->getConnection()))
1842
                            ->select('three')
1843
                            ->from('digits'),
1844
                    ],
1845
                ],
1846
                '[[id]] IN (:qp0, :qp1, (SELECT [[three]] FROM [[digits]]))',
1847
                [':qp0' => 1, ':qp1' => 2],
1848
            ],
1849
            [
1850
                ['not in', 'id', [1, 2, 3]],
1851
                '[[id]] NOT IN (:qp0, :qp1, :qp2)',
1852
                [':qp0' => 1, ':qp1' => 2, ':qp2' => 3],
1853
            ],
1854
            [
1855
                [
1856
                    'in',
1857
                    'id',
1858
                    (new Query($this->getConnection()))
1859
                        ->select('id')
1860
                        ->from('users')
1861
                        ->where(['active' => 1]),
1862
                ],
1863
                '[[id]] IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
1864
                [':qp0' => 1],
1865
            ],
1866
            [
1867
                [
1868
                    'not in',
1869
                    'id',
1870
                    (new Query($this->getConnection()))
1871
                        ->select('id')
1872
                        ->from('users')
1873
                        ->where(['active' => 1]),
1874
                ],
1875
                '[[id]] NOT IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
1876
                [':qp0' => 1],
1877
            ],
1878
            [['in', 'id', 1], '[[id]]=:qp0', [':qp0' => 1]],
1879
            [['in', 'id', [1]], '[[id]]=:qp0', [':qp0' => 1]],
1880
            [['in', 'id', new TraversableObject([1])], '[[id]]=:qp0', [':qp0' => 1]],
1881
            'composite in' => [
1882
                ['in', ['id', 'name'], [['id' => 1, 'name' => 'oy']]],
1883
                '([[id]], [[name]]) IN ((:qp0, :qp1))',
1884
                [':qp0' => 1, ':qp1' => 'oy'],
1885
            ],
1886
            'composite in (just one column)' => [
1887
                ['in', ['id'], [['id' => 1, 'name' => 'Name1'], ['id' => 2, 'name' => 'Name2']]],
1888
                '[[id]] IN (:qp0, :qp1)',
1889
                [':qp0' => 1, ':qp1' => 2],
1890
            ],
1891
            'composite in using array objects (just one column)' => [
1892
                ['in', new TraversableObject(['id']), new TraversableObject([
1893
                    ['id' => 1, 'name' => 'Name1'],
1894
                    ['id' => 2, 'name' => 'Name2'],
1895
                ])],
1896
                '[[id]] IN (:qp0, :qp1)',
1897
                [':qp0' => 1, ':qp1' => 2],
1898
            ],
1899
1900
            /* in using array objects. */
1901
            [['id' => new TraversableObject([1, 2])], '[[id]] IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
1902
            [
1903
                ['in', 'id', new TraversableObject([1, 2, 3])],
1904
                '[[id]] IN (:qp0, :qp1, :qp2)',
1905
                [':qp0' => 1, ':qp1' => 2, ':qp2' => 3],
1906
            ],
1907
1908
            /* in using array objects containing null value */
1909
            [['in', 'id', new TraversableObject([1, null])], '[[id]]=:qp0 OR [[id]] IS NULL', [':qp0' => 1]],
1910
            [
1911
                ['in', 'id', new TraversableObject([1, 2, null])],
1912
                '[[id]] IN (:qp0, :qp1) OR [[id]] IS NULL', [':qp0' => 1, ':qp1' => 2],
1913
            ],
1914
1915
            /* not in using array object containing null value */
1916
            [
1917
                ['not in', 'id', new TraversableObject([1, null])],
1918
                '[[id]]<>:qp0 AND [[id]] IS NOT NULL', [':qp0' => 1],
1919
            ],
1920
            [
1921
                ['not in', 'id', new TraversableObject([1, 2, null])],
1922
                '[[id]] NOT IN (:qp0, :qp1) AND [[id]] IS NOT NULL',
1923
                [':qp0' => 1, ':qp1' => 2],
1924
            ],
1925
1926
            /* in using array object containing only null value */
1927
            [['in', 'id', new TraversableObject([null])], '[[id]] IS NULL', []],
1928
            [['not in', 'id', new TraversableObject([null])], '[[id]] IS NOT NULL', []],
1929
            'composite in using array objects' => [
1930
                ['in', new TraversableObject(['id', 'name']), new TraversableObject([
1931
                    ['id' => 1, 'name' => 'oy'],
1932
                    ['id' => 2, 'name' => 'yo'],
1933
                ])],
1934
                '([[id]], [[name]]) IN ((:qp0, :qp1), (:qp2, :qp3))',
1935
                [':qp0' => 1, ':qp1' => 'oy', ':qp2' => 2, ':qp3' => 'yo'],
1936
            ],
1937
1938
            /* in object conditions */
1939
            [new InCondition('id', 'in', 1), '[[id]]=:qp0', [':qp0' => 1]],
1940
            [new InCondition('id', 'in', [1]), '[[id]]=:qp0', [':qp0' => 1]],
1941
            [new InCondition('id', 'not in', 1), '[[id]]<>:qp0', [':qp0' => 1]],
1942
            [new InCondition('id', 'not in', [1]), '[[id]]<>:qp0', [':qp0' => 1]],
1943
            [new InCondition('id', 'in', [1, 2]), '[[id]] IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
1944
            [new InCondition('id', 'not in', [1, 2]), '[[id]] NOT IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
1945
1946
            /* exists */
1947
            [
1948
                [
1949
                    'exists',
1950
                    (new Query($this->getConnection()))
1951
                        ->select('id')
1952
                        ->from('users')
1953
                        ->where(['active' => 1]),
1954
                ],
1955
                'EXISTS (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
1956
                [':qp0' => 1],
1957
            ],
1958
            [
1959
                [
1960
                    'not exists',
1961
                    (new Query($this->getConnection()))
1962
                        ->select('id')
1963
                        ->from('users')
1964
                        ->where(['active' => 1]),
1965
                ],
1966
                'NOT EXISTS (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)', [':qp0' => 1],
1967
            ],
1968
1969
            /* simple conditions */
1970
            [['=', 'a', 'b'], '[[a]] = :qp0', [':qp0' => 'b']],
1971
            [['>', 'a', 1], '[[a]] > :qp0', [':qp0' => 1]],
1972
            [['>=', 'a', 'b'], '[[a]] >= :qp0', [':qp0' => 'b']],
1973
            [['<', 'a', 2], '[[a]] < :qp0', [':qp0' => 2]],
1974
            [['<=', 'a', 'b'], '[[a]] <= :qp0', [':qp0' => 'b']],
1975
            [['<>', 'a', 3], '[[a]] <> :qp0', [':qp0' => 3]],
1976
            [['!=', 'a', 'b'], '[[a]] != :qp0', [':qp0' => 'b']],
1977
            [
1978
                ['>=', 'date', new Expression('DATE_SUB(NOW(), INTERVAL 1 MONTH)')],
1979
                '[[date]] >= DATE_SUB(NOW(), INTERVAL 1 MONTH)',
1980
                [],
1981
            ],
1982
            [
1983
                ['>=', 'date', new Expression('DATE_SUB(NOW(), INTERVAL :month MONTH)', [':month' => 2])],
1984
                '[[date]] >= DATE_SUB(NOW(), INTERVAL :month MONTH)',
1985
                [':month' => 2],
1986
            ],
1987
            [
1988
                [
1989
                    '=',
1990
                    'date',
1991
                    (new Query($this->getConnection()))
1992
                        ->select('max(date)')
1993
                        ->from('test')
1994
                        ->where(['id' => 5]),
1995
                ],
1996
                '[[date]] = (SELECT max(date) FROM [[test]] WHERE [[id]]=:qp0)',
1997
                [':qp0' => 5],
1998
            ],
1999
2000
            /* operand1 is Expression */
2001
            [
2002
                ['=', new Expression('date'), '2019-08-01'],
2003
                'date = :qp0',
2004
                [':qp0' => '2019-08-01'],
2005
            ],
2006
            [
2007
                [
2008
                    '=',
2009
                    (new Query($this->getConnection()))
2010
                        ->select('COUNT(*)')
2011
                        ->from('test')
2012
                        ->where(['id' => 6]),
2013
                    0,
2014
                ],
2015
                '(SELECT COUNT(*) FROM [[test]] WHERE [[id]]=:qp0) = :qp1',
2016
                [':qp0' => 6, ':qp1' => 0],
2017
            ],
2018
2019
            /* hash condition */
2020
            [['a' => 1, 'b' => 2], '([[a]]=:qp0) AND ([[b]]=:qp1)', [':qp0' => 1, ':qp1' => 2]],
2021
            [
2022
                ['a' => new Expression('CONCAT(col1, col2)'), 'b' => 2],
2023
                '([[a]]=CONCAT(col1, col2)) AND ([[b]]=:qp0)',
2024
                [':qp0' => 2],
2025
            ],
2026
2027
            /* direct conditions */
2028
            ['a = CONCAT(col1, col2)', 'a = CONCAT(col1, col2)', []],
2029
            [
2030
                new Expression('a = CONCAT(col1, :param1)', ['param1' => 'value1']),
2031
                'a = CONCAT(col1, :param1)',
2032
                ['param1' => 'value1'],
2033
            ],
2034
2035
            /* Expression with params as operand of 'not' */
2036
            [
2037
                ['not', new Expression('any_expression(:a)', [':a' => 1])],
2038
                'NOT (any_expression(:a))', [':a' => 1],
2039
            ],
2040
            [new Expression('NOT (any_expression(:a))', [':a' => 1]), 'NOT (any_expression(:a))', [':a' => 1]],
2041
        ];
2042
2043
        switch ($this->getConnection()->getDriverName()) {
2044
            case 'sqlsrv':
2045
            case 'sqlite':
2046
                $conditions = array_merge($conditions, [
2047
                    [
2048
                        ['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]],
2049
                        '(([[id]] = :qp0 AND [[name]] = :qp1) OR ([[id]] = :qp2 AND [[name]] = :qp3))',
2050
                        [':qp0' => 1,
2051
                            ':qp1' => 'foo',
2052
                            ':qp2' => 2,
2053
                            ':qp3' => 'bar', ],
2054
                    ],
2055
                    [
2056
                        ['not in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]],
2057
                        '(([[id]] != :qp0 OR [[name]] != :qp1) AND ([[id]] != :qp2 OR [[name]] != :qp3))',
2058
                        [':qp0' => 1,
2059
                            ':qp1' => 'foo',
2060
                            ':qp2' => 2,
2061
                            ':qp3' => 'bar', ],
2062
                    ],
2063
                    //[['in', ['id', 'name'], (new Query())->select(['id', 'name'])->from('users')->where(['active' => 1])], 'EXISTS (SELECT 1 FROM (SELECT [[id]], [[name]] FROM [[users]] WHERE [[active]]=:qp0) AS a WHERE a.[[id]] = [[id AND a.]]name[[ = ]]name`)', [':qp0' => 1] ],
2064
                    //[ ['not in', ['id', 'name'], (new Query())->select(['id', 'name'])->from('users')->where(['active' => 1])], 'NOT EXISTS (SELECT 1 FROM (SELECT [[id]], [[name]] FROM [[users]] WHERE [[active]]=:qp0) AS a WHERE a.[[id]] = [[id]] AND a.[[name = ]]name`)', [':qp0' => 1] ],
2065
                ]);
2066
2067
                break;
2068
            default:
2069
                $conditions = array_merge($conditions, [
2070
                    [
2071
                        ['in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]],
2072
                        '([[id]], [[name]]) IN ((:qp0, :qp1), (:qp2, :qp3))',
2073
                        [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'],
2074
                    ],
2075
                    [
2076
                        ['not in', ['id', 'name'], [['id' => 1, 'name' => 'foo'], ['id' => 2, 'name' => 'bar']]],
2077
                        '([[id]], [[name]]) NOT IN ((:qp0, :qp1), (:qp2, :qp3))',
2078
                        [':qp0' => 1, ':qp1' => 'foo', ':qp2' => 2, ':qp3' => 'bar'],
2079
                    ],
2080
                    [
2081
                        [
2082
                            'in',
2083
                            ['id', 'name'],
2084
                            (new Query($this->getConnection()))
2085
                                ->select(['id', 'name'])
2086
                                ->from('users')
2087
                                ->where(['active' => 1]),
2088
                        ],
2089
                        '([[id]], [[name]]) IN (SELECT [[id]], [[name]] FROM [[users]] WHERE [[active]]=:qp0)',
2090
                        [':qp0' => 1],
2091
                    ],
2092
                    [
2093
                        [
2094
                            'not in',
2095
                            ['id', 'name'],
2096
                            (new Query($this->getConnection()))
2097
                                ->select(['id', 'name'])
2098
                                ->from('users')
2099
                                ->where(['active' => 1]),
2100
                        ],
2101
                        '([[id]], [[name]]) NOT IN (SELECT [[id]], [[name]] FROM [[users]] WHERE [[active]]=:qp0)',
2102
                        [':qp0' => 1],
2103
                    ],
2104
                ]);
2105
2106
                break;
2107
        }
2108
2109
        /* adjust dbms specific escaping */
2110
        foreach ($conditions as $i => $condition) {
2111
            $conditions[$i][1] = $this->replaceQuotes($condition[1]);
2112
        }
2113
2114
        return $conditions;
2115
    }
2116
2117
    public function buildFilterConditionProviderTrait(): array
2118
    {
2119
        $conditions = [
2120
            /* like */
2121
            [['like', 'name', []], '', []],
2122
            [['not like', 'name', []], '', []],
2123
            [['or like', 'name', []], '', []],
2124
            [['or not like', 'name', []], '', []],
2125
2126
            /* not */
2127
            [['not', ''], '', []],
2128
2129
            /* and */
2130
            [['and', '', ''], '', []],
2131
            [['and', '', 'id=2'], 'id=2', []],
2132
            [['and', 'id=1', ''], 'id=1', []],
2133
            [['and', 'type=1', ['or', '', 'id=2']], '(type=1) AND (id=2)', []],
2134
2135
            /* or */
2136
            [['or', 'id=1', ''], 'id=1', []],
2137
            [['or', 'type=1', ['or', '', 'id=2']], '(type=1) OR (id=2)', []],
2138
2139
            /* between */
2140
            [['between', 'id', 1, null], '', []],
2141
            [['not between', 'id', null, 10], '', []],
2142
2143
            /* in */
2144
            [['in', 'id', []], '', []],
2145
            [['not in', 'id', []], '', []],
2146
2147
            /* simple conditions */
2148
            [['=', 'a', ''], '', []],
2149
            [['>', 'a', ''], '', []],
2150
            [['>=', 'a', ''], '', []],
2151
            [['<', 'a', ''], '', []],
2152
            [['<=', 'a', ''], '', []],
2153
            [['<>', 'a', ''], '', []],
2154
            [['!=', 'a', ''], '', []],
2155
        ];
2156
2157
        /* adjust dbms specific escaping */
2158
        foreach ($conditions as $i => $condition) {
2159
            $conditions[$i][1] = $this->replaceQuotes($condition[1]);
2160
        }
2161
2162
        return $conditions;
2163
    }
2164
2165
    public function buildFromDataProviderTrait(): array
2166
    {
2167
        return [
2168
            ['test t1', '[[test]] [[t1]]'],
2169
            ['test as t1', '[[test]] [[t1]]'],
2170
            ['test AS t1', '[[test]] [[t1]]'],
2171
            ['test', '[[test]]'],
2172
        ];
2173
    }
2174
2175
    public function buildLikeConditionsProviderTrait(): array
2176
    {
2177
        $conditions = [
2178
            /* simple like */
2179
            [['like', 'name', 'foo%'], '[[name]] LIKE :qp0', [':qp0' => '%foo\%%']],
2180
            [['not like', 'name', 'foo%'], '[[name]] NOT LIKE :qp0', [':qp0' => '%foo\%%']],
2181
            [['or like', 'name', 'foo%'], '[[name]] LIKE :qp0', [':qp0' => '%foo\%%']],
2182
            [['or not like', 'name', 'foo%'], '[[name]] NOT LIKE :qp0', [':qp0' => '%foo\%%']],
2183
2184
            /* like for many values */
2185
            [
2186
                ['like', 'name', ['foo%', '[abc]']],
2187
                '[[name]] LIKE :qp0 AND [[name]] LIKE :qp1',
2188
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
2189
            ],
2190
            [
2191
                ['not like', 'name', ['foo%', '[abc]']],
2192
                '[[name]] NOT LIKE :qp0 AND [[name]] NOT LIKE :qp1',
2193
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
2194
            ],
2195
            [
2196
                ['or like', 'name', ['foo%', '[abc]']],
2197
                '[[name]] LIKE :qp0 OR [[name]] LIKE :qp1',
2198
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
2199
            ],
2200
            [
2201
                ['or not like', 'name', ['foo%', '[abc]']],
2202
                '[[name]] NOT LIKE :qp0 OR [[name]] NOT LIKE :qp1',
2203
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
2204
            ],
2205
2206
            /* like with Expression */
2207
            [
2208
                ['like', 'name', new Expression('CONCAT("test", name, "%")')],
2209
                '[[name]] LIKE CONCAT("test", name, "%")',
2210
                [],
2211
            ],
2212
            [
2213
                ['not like', 'name', new Expression('CONCAT("test", name, "%")')],
2214
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
2215
                [],
2216
            ],
2217
            [
2218
                ['or like', 'name', new Expression('CONCAT("test", name, "%")')],
2219
                '[[name]] LIKE CONCAT("test", name, "%")',
2220
                [],
2221
            ],
2222
            [
2223
                ['or not like', 'name', new Expression('CONCAT("test", name, "%")')],
2224
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
2225
                [],
2226
            ],
2227
            [
2228
                ['like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
2229
                '[[name]] LIKE CONCAT("test", name, "%") AND [[name]] LIKE :qp0',
2230
                [':qp0' => '%\\\ab\_c%'],
2231
            ],
2232
            [
2233
                ['not like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
2234
                '[[name]] NOT LIKE CONCAT("test", name, "%") AND [[name]] NOT LIKE :qp0',
2235
                [':qp0' => '%\\\ab\_c%'],
2236
            ],
2237
            [
2238
                ['or like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
2239
                '[[name]] LIKE CONCAT("test", name, "%") OR [[name]] LIKE :qp0',
2240
                [':qp0' => '%\\\ab\_c%'],
2241
            ],
2242
            [
2243
                ['or not like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
2244
                '[[name]] NOT LIKE CONCAT("test", name, "%") OR [[name]] NOT LIKE :qp0',
2245
                [':qp0' => '%\\\ab\_c%'],
2246
            ],
2247
2248
            /**
2249
             * {@see https://github.com/yiisoft/yii2/issues/15630}
2250
             */
2251
            [
2252
                ['like', 'location.title_ru', 'vi%', false],
2253
                '[[location]].[[title_ru]] LIKE :qp0',
2254
                [':qp0' => 'vi%'],
2255
            ],
2256
2257
            /* like object conditions */
2258
            [
2259
                new LikeCondition('name', 'like', new Expression('CONCAT("test", name, "%")')),
2260
                '[[name]] LIKE CONCAT("test", name, "%")',
2261
                [],
2262
            ],
2263
            [
2264
                new LikeCondition('name', 'not like', new Expression('CONCAT("test", name, "%")')),
2265
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
2266
                [],
2267
            ],
2268
            [
2269
                new LikeCondition('name', 'or like', new Expression('CONCAT("test", name, "%")')),
2270
                '[[name]] LIKE CONCAT("test", name, "%")',
2271
                [],
2272
            ],
2273
            [
2274
                new LikeCondition('name', 'or not like', new Expression('CONCAT("test", name, "%")')),
2275
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
2276
                [],
2277
            ],
2278
            [
2279
                new LikeCondition('name', 'like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
2280
                '[[name]] LIKE CONCAT("test", name, "%") AND [[name]] LIKE :qp0',
2281
                [':qp0' => '%\\\ab\_c%'],
2282
            ],
2283
            [
2284
                new LikeCondition('name', 'not like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
2285
                '[[name]] NOT LIKE CONCAT("test", name, "%") AND [[name]] NOT LIKE :qp0',
2286
                [':qp0' => '%\\\ab\_c%'],
2287
            ],
2288
            [
2289
                new LikeCondition('name', 'or like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
2290
                '[[name]] LIKE CONCAT("test", name, "%") OR [[name]] LIKE :qp0', [':qp0' => '%\\\ab\_c%'],
2291
            ],
2292
            [
2293
                new LikeCondition('name', 'or not like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
2294
                '[[name]] NOT LIKE CONCAT("test", name, "%") OR [[name]] NOT LIKE :qp0', [':qp0' => '%\\\ab\_c%'],
2295
            ],
2296
2297
            /* like with expression as columnName */
2298
            [['like', new Expression('name'), 'teststring'], 'name LIKE :qp0', [':qp0' => '%teststring%']],
2299
        ];
2300
2301
        /* adjust dbms specific escaping */
2302
        foreach ($conditions as $i => $condition) {
2303
            $conditions[$i][1] = $this->replaceQuotes($condition[1]);
2304
            if (!empty($this->likeEscapeCharSql)) {
2305
                preg_match_all('/(?P<condition>LIKE.+?)( AND| OR|$)/', $conditions[$i][1], $matches, PREG_SET_ORDER);
2306
2307
                foreach ($matches as $match) {
2308
                    $conditions[$i][1] = str_replace(
2309
                        $match['condition'],
2310
                        $match['condition'] . $this->likeEscapeCharSql,
2311
                        $conditions[$i][1]
2312
                    );
2313
                }
2314
            }
2315
2316
            foreach ($conditions[$i][2] as $name => $value) {
2317
                $conditions[$i][2][$name] = strtr($conditions[$i][2][$name], $this->likeParameterReplacements);
2318
            }
2319
        }
2320
2321
        return $conditions;
2322
    }
2323
2324
    public function buildExistsParamsProviderTrait(): array
2325
    {
2326
        return [
2327
            [
2328
                'exists',
2329
                $this->replaceQuotes(
2330
                    'SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE EXISTS (SELECT [[1]] FROM [[Website]] [[w]])'
2331
                ),
2332
            ],
2333
            [
2334
                'not exists',
2335
                $this->replaceQuotes(
2336
                    'SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE NOT EXISTS (SELECT [[1]] FROM [[Website]] [[w]])'
2337
                ),
2338
            ],
2339
        ];
2340
    }
2341
2342 5
    public function createDropIndexesProviderTrait(): array
2343
    {
2344
        $tableName = 'T_constraints_2';
2345
        $name1 = 'CN_constraints_2_single';
2346
        $name2 = 'CN_constraints_2_multi';
2347
2348
        return [
2349
            'drop' => [
2350
                "DROP INDEX [[$name1]] ON {{{$tableName}}}",
2351 5
                static function (QueryBuilder $qb) use ($tableName, $name1) {
2352 5
                    return $qb->dropIndex($name1, $tableName);
2353
                },
2354
            ],
2355
            'create' => [
2356
                "CREATE INDEX [[$name1]] ON {{{$tableName}}} ([[C_index_1]])",
2357 5
                static function (QueryBuilder $qb) use ($tableName, $name1) {
2358 5
                    return $qb->createIndex($name1, $tableName, 'C_index_1');
2359
                },
2360
            ],
2361
            'create (2 columns)' => [
2362
                "CREATE INDEX [[$name2]] ON {{{$tableName}}} ([[C_index_2_1]], [[C_index_2_2]])",
2363 5
                static function (QueryBuilder $qb) use ($tableName, $name2) {
2364 5
                    return $qb->createIndex($name2, $tableName, 'C_index_2_1, C_index_2_2');
2365
                },
2366
            ],
2367
            'create unique' => [
2368
                "CREATE UNIQUE INDEX [[$name1]] ON {{{$tableName}}} ([[C_index_1]])",
2369 5
                static function (QueryBuilder $qb) use ($tableName, $name1) {
2370 5
                    return $qb->createIndex($name1, $tableName, 'C_index_1', true);
2371
                },
2372
            ],
2373
            'create unique (2 columns)' => [
2374
                "CREATE UNIQUE INDEX [[$name2]] ON {{{$tableName}}} ([[C_index_2_1]], [[C_index_2_2]])",
2375 5
                static function (QueryBuilder $qb) use ($tableName, $name2) {
2376 5
                    return $qb->createIndex($name2, $tableName, 'C_index_2_1, C_index_2_2', true);
2377
                },
2378
            ],
2379
        ];
2380
    }
2381
2382
    public function deleteProviderTrait(): array
2383
    {
2384
        return [
2385
            [
2386
                'user',
2387
                [
2388
                    'is_enabled' => false,
2389
                    'power' => new Expression('WRONG_POWER()'),
2390
                ],
2391
                $this->replaceQuotes('DELETE FROM [[user]] WHERE ([[is_enabled]]=:qp0) AND ([[power]]=WRONG_POWER())'),
2392
                [
2393
                    ':qp0' => false,
2394
                ],
2395
            ],
2396
        ];
2397
    }
2398
2399
    public function insertProviderTrait(): array
2400
    {
2401
        return [
2402
            'regular-values' => [
2403
                'customer',
2404
                [
2405
                    'email' => '[email protected]',
2406
                    'name' => 'silverfire',
2407
                    'address' => 'Kyiv {{city}}, Ukraine',
2408
                    'is_active' => false,
2409
                    'related_id' => null,
2410
                ],
2411
                [],
2412
                $this->replaceQuotes(
2413
                    'INSERT INTO [[customer]] ([[email]], [[name]], [[address]], [[is_active]], [[related_id]])'
2414
                    . ' VALUES (:qp0, :qp1, :qp2, :qp3, :qp4)'
2415
                ),
2416
                [
2417
                    ':qp0' => '[email protected]',
2418
                    ':qp1' => 'silverfire',
2419
                    ':qp2' => 'Kyiv {{city}}, Ukraine',
2420
                    ':qp3' => false,
2421
                    ':qp4' => null,
2422
                ],
2423
            ],
2424
            'params-and-expressions' => [
2425
                '{{%type}}',
2426
                [
2427
                    '{{%type}}.[[related_id]]' => null,
2428
                    '[[time]]' => new Expression('now()'),
2429
                ],
2430
                [],
2431
                'INSERT INTO {{%type}} ({{%type}}.[[related_id]], [[time]]) VALUES (:qp0, now())',
2432
                [
2433
                    ':qp0' => null,
2434
                ],
2435
            ],
2436
            'carry passed params' => [
2437
                'customer',
2438
                [
2439
                    'email' => '[email protected]',
2440
                    'name' => 'sergeymakinen',
2441
                    'address' => '{{city}}',
2442
                    'is_active' => false,
2443
                    'related_id' => null,
2444
                    'col' => new Expression('CONCAT(:phFoo, :phBar)', [':phFoo' => 'foo']),
2445
                ],
2446
                [':phBar' => 'bar'],
2447
                $this->replaceQuotes(
2448
                    'INSERT INTO [[customer]] ([[email]], [[name]], [[address]], [[is_active]], [[related_id]], [[col]])'
2449
                    . ' VALUES (:qp1, :qp2, :qp3, :qp4, :qp5, CONCAT(:phFoo, :phBar))'
2450
                ),
2451
                [
2452
                    ':phBar' => 'bar',
2453
                    ':qp1' => '[email protected]',
2454
                    ':qp2' => 'sergeymakinen',
2455
                    ':qp3' => '{{city}}',
2456
                    ':qp4' => false,
2457
                    ':qp5' => null,
2458
                    ':phFoo' => 'foo',
2459
                ],
2460
            ],
2461
            'carry passed params (query)' => [
2462
                'customer',
2463
                (new Query($this->getConnection()))
2464
                    ->select([
2465
                        'email',
2466
                        'name',
2467
                        'address',
2468
                        'is_active',
2469
                        'related_id',
2470
                    ])
2471
                    ->from('customer')
2472
                    ->where([
2473
                        'email' => '[email protected]',
2474
                        'name' => 'sergeymakinen',
2475
                        'address' => '{{city}}',
2476
                        'is_active' => false,
2477
                        'related_id' => null,
2478
                        'col' => new Expression('CONCAT(:phFoo, :phBar)', [':phFoo' => 'foo']),
2479
                    ]),
2480
                [':phBar' => 'bar'],
2481
                $this->replaceQuotes(
2482
                    'INSERT INTO [[customer]] ([[email]], [[name]], [[address]], [[is_active]], [[related_id]])'
2483
                    . ' SELECT [[email]], [[name]], [[address]], [[is_active]], [[related_id]] FROM [[customer]]'
2484
                    . ' WHERE ([[email]]=:qp1) AND ([[name]]=:qp2) AND ([[address]]=:qp3) AND ([[is_active]]=:qp4)'
2485
                    . ' AND ([[related_id]] IS NULL) AND ([[col]]=CONCAT(:phFoo, :phBar))'
2486
                ),
2487
                [
2488
                    ':phBar' => 'bar',
2489
                    ':qp1' => '[email protected]',
2490
                    ':qp2' => 'sergeymakinen',
2491
                    ':qp3' => '{{city}}',
2492
                    ':qp4' => false,
2493
                    ':phFoo' => 'foo',
2494
                ],
2495
            ],
2496
        ];
2497
    }
2498
2499
    public function updateProviderTrait(): array
2500
    {
2501
        return [
2502
            [
2503
                'customer',
2504
                [
2505
                    'status' => 1,
2506
                    'updated_at' => new Expression('now()'),
2507
                ],
2508
                [
2509
                    'id' => 100,
2510
                ],
2511
                $this->replaceQuotes(
2512
                    'UPDATE [[customer]] SET [[status]]=:qp0, [[updated_at]]=now() WHERE [[id]]=:qp1'
2513
                ),
2514
                [
2515
                    ':qp0' => 1,
2516
                    ':qp1' => 100,
2517
                ],
2518
            ],
2519
        ];
2520
    }
2521
2522
    public function upsertProviderTrait(): array
2523
    {
2524
        return [
2525
            'regular values' => [
2526
                'T_upsert',
2527
                [
2528
                    'email' => '[email protected]',
2529
                    'address' => 'bar {{city}}',
2530
                    'status' => 1,
2531
                    'profile_id' => null,
2532
                ],
2533
                true,
2534
                null,
2535
                [
2536
                    ':qp0' => '[email protected]',
2537
                    ':qp1' => 'bar {{city}}',
2538
                    ':qp2' => 1,
2539
                    ':qp3' => null,
2540
                ],
2541
            ],
2542
            'regular values with update part' => [
2543
                'T_upsert',
2544
                [
2545
                    'email' => '[email protected]',
2546
                    'address' => 'bar {{city}}',
2547
                    'status' => 1,
2548
                    'profile_id' => null,
2549
                ],
2550
                [
2551
                    'address' => 'foo {{city}}',
2552
                    'status' => 2,
2553
                    'orders' => new Expression('T_upsert.orders + 1'),
2554
                ],
2555
                null,
2556
                [
2557
                    ':qp0' => '[email protected]',
2558
                    ':qp1' => 'bar {{city}}',
2559
                    ':qp2' => 1,
2560
                    ':qp3' => null,
2561
                    ':qp4' => 'foo {{city}}',
2562
                    ':qp5' => 2,
2563
                ],
2564
            ],
2565
            'regular values without update part' => [
2566
                'T_upsert',
2567
                [
2568
                    'email' => '[email protected]',
2569
                    'address' => 'bar {{city}}',
2570
                    'status' => 1,
2571
                    'profile_id' => null,
2572
                ],
2573
                false,
2574
                null,
2575
                [
2576
                    ':qp0' => '[email protected]',
2577
                    ':qp1' => 'bar {{city}}',
2578
                    ':qp2' => 1,
2579
                    ':qp3' => null,
2580
                ],
2581
            ],
2582
            'query' => [
2583
                'T_upsert',
2584
                (new Query($this->getConnection()))
2585
                    ->select([
2586
                        'email',
2587
                        'status' => new Expression('2'),
2588
                    ])
2589
                    ->from('customer')
2590
                    ->where(['name' => 'user1'])
2591
                    ->limit(1),
2592
                true,
2593
                null,
2594
                [
2595
                    ':qp0' => 'user1',
2596
                ],
2597
            ],
2598
            'query with update part' => [
2599
                'T_upsert',
2600
                (new Query($this->getConnection()))
2601
                    ->select([
2602
                        'email',
2603
                        'status' => new Expression('2'),
2604
                    ])
2605
                    ->from('customer')
2606
                    ->where(['name' => 'user1'])
2607
                    ->limit(1),
2608
                [
2609
                    'address' => 'foo {{city}}',
2610
                    'status' => 2,
2611
                    'orders' => new Expression('T_upsert.orders + 1'),
2612
                ],
2613
                null,
2614
                [
2615
                    ':qp0' => 'user1',
2616
                    ':qp1' => 'foo {{city}}',
2617
                    ':qp2' => 2,
2618
                ],
2619
            ],
2620
            'query without update part' => [
2621
                'T_upsert',
2622
                (new Query($this->getConnection()))
2623
                    ->select([
2624
                        'email',
2625
                        'status' => new Expression('2'),
2626
                    ])
2627
                    ->from('customer')
2628
                    ->where(['name' => 'user1'])
2629
                    ->limit(1),
2630
                false,
2631
                null,
2632
                [
2633
                    ':qp0' => 'user1',
2634
                ],
2635
            ],
2636
            'values and expressions' => [
2637
                '{{%T_upsert}}',
2638
                [
2639
                    '{{%T_upsert}}.[[email]]' => '[email protected]',
2640
                    '[[ts]]' => new Expression('now()'),
2641
                ],
2642
                true,
2643
                null,
2644
                [
2645
                    ':qp0' => '[email protected]',
2646
                ],
2647
            ],
2648
            'values and expressions with update part' => [
2649
                '{{%T_upsert}}',
2650
                [
2651
                    '{{%T_upsert}}.[[email]]' => '[email protected]',
2652
                    '[[ts]]' => new Expression('now()'),
2653
                ],
2654
                [
2655
                    '[[orders]]' => new Expression('T_upsert.orders + 1'),
2656
                ],
2657
                null,
2658
                [
2659
                    ':qp0' => '[email protected]',
2660
                ],
2661
            ],
2662
            'values and expressions without update part' => [
2663
                '{{%T_upsert}}',
2664
                [
2665
                    '{{%T_upsert}}.[[email]]' => '[email protected]',
2666
                    '[[ts]]' => new Expression('now()'),
2667
                ],
2668
                false,
2669
                null,
2670
                [
2671
                    ':qp0' => '[email protected]',
2672
                ],
2673
            ],
2674
            'query, values and expressions with update part' => [
2675
                '{{%T_upsert}}',
2676
                (new Query($this->getConnection()))
2677
                    ->select([
2678
                        'email' => new Expression(':phEmail', [':phEmail' => '[email protected]']),
2679
                        '[[time]]' => new Expression('now()'),
2680
                    ]),
2681
                [
2682
                    'ts' => 0,
2683
                    '[[orders]]' => new Expression('T_upsert.orders + 1'),
2684
                ],
2685
                null,
2686
                [
2687
                    ':phEmail' => '[email protected]',
2688
                    ':qp1' => 0,
2689
                ],
2690
            ],
2691
            'query, values and expressions without update part' => [
2692
                '{{%T_upsert}}',
2693
                (new Query($this->getConnection()))
2694
                    ->select([
2695
                        'email' => new Expression(':phEmail', [':phEmail' => '[email protected]']),
2696
                        '[[time]]' => new Expression('now()'),
2697
                    ]),
2698
                [
2699
                    'ts' => 0,
2700
                    '[[orders]]' => new Expression('T_upsert.orders + 1'),
2701
                ],
2702
                null,
2703
                [
2704
                    ':phEmail' => '[email protected]',
2705
                    ':qp1' => 0,
2706
                ],
2707
            ],
2708
            'no columns to update' => [
2709
                'T_upsert_1',
2710
                [
2711
                    'a' => 1,
2712
                ],
2713
                false,
2714
                null,
2715
                [
2716
                    ':qp0' => 1,
2717
                ],
2718
            ],
2719
        ];
2720
    }
2721
}
2722