Passed
Pull Request — master (#163)
by Wilmer
17:48 queued 02:54
created

addDropUniquesProviderTrait()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 17
nc 1
nop 0
dl 0
loc 24
rs 9.7
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\Connection;
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
    public function getDb(): Connection
31
    {
32
        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
    public function columnTypes(): array
40
    {
41
        $version = $this->getConnection()->getServerVersion();
42
43
        $items = [
44
            [
45
                Schema::TYPE_BIGINT,
46
                $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
                $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
                $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
                $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
                $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
                $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
                $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
                $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
                $this->boolean()->notNull()->defaultValue(true),
131
                [
132
                    'pgsql' => 'boolean NOT NULL DEFAULT TRUE'
133
                ]
134
            ],
135
            [
136
                Schema::TYPE_BOOLEAN,
137
                $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
                $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
                $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
                $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
                $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
                $this->char(6)->unsigned(),
182
                [
183
                    'pgsql' => 'char(6)'
184
                ]
185
            ],
186
            [
187
                Schema::TYPE_CHAR . '(6)',
188
                $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
                $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
                $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
                $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
                $this->dateTime()->notNull(),
230
                [
231
                    '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
                $this->dateTime(),
241
                [
242
                    '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
                $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
                $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
                $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
                $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
                $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
                $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
                $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
                $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
                $this->double(16),
340
                [
341
                    'mysql' => 'double',
342
                    'sqlite' => 'double',
343
                    'oci' => 'NUMBER',
344
                    'sqlsrv' => 'float'
345
                ]
346
            ],
347
            [
348
                Schema::TYPE_DOUBLE,
349
                $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
                $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
                $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
                $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
                $this->float(16),
394
                [
395
                    'mysql' => 'float',
396
                    'sqlite' => 'float',
397
                    'oci' => 'NUMBER',
398
                    'sqlsrv' => 'float'
399
                ]
400
            ],
401
            [
402
                Schema::TYPE_FLOAT,
403
                $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
                $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
                $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
                $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
                $this->integer(8)->unsigned(),
448
                [
449
                    'pgsql' => 'integer'
450
                ]
451
            ],
452
            [
453
                Schema::TYPE_INTEGER . '(8)',
454
                $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
                $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
                $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
                $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
                $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
                $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
                $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
                $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
                $this->primaryKey()->first(),
539
                [
540
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST'
541
                ]
542
            ],
543
            [
544
                Schema::TYPE_PK . ' FIRST',
545
                $this->primaryKey()->first()->after('col_before'),
546
                [
547
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST'
548
                ]
549
            ],
550
            [
551
                Schema::TYPE_PK . '(8) AFTER `col_before`',
552
                $this->primaryKey(8)->after('col_before'),
553
                [
554
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY AFTER `col_before`'
555
                ]
556
            ],
557
            [
558
                Schema::TYPE_PK . '(8) FIRST',
559
                $this->primaryKey(8)->first(),
560
                [
561
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST'
562
                ]
563
            ],
564
            [
565
                Schema::TYPE_PK . '(8) FIRST',
566
                $this->primaryKey(8)->first()->after('col_before'),
567
                [
568
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST'
569
                ]
570
            ],
571
            [
572
                Schema::TYPE_PK . " COMMENT 'test' AFTER `col_before`",
573
                $this->primaryKey()->comment('test')->after('col_before'),
574
                [
575
                    'mysql' => "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'test' AFTER `col_before`"
576
                ]
577
            ],
578
            [
579
                Schema::TYPE_PK . " COMMENT 'testing \'quote\'' AFTER `col_before`",
580
                $this->primaryKey()->comment('testing \'quote\'')->after('col_before'),
581
                [
582
                    'mysql' => "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'testing \'quote\''"
583
                        . " AFTER `col_before`"
584
                ]
585
            ],
586
            [
587
                Schema::TYPE_PK . ' CHECK (value > 5)',
588
                $this->primaryKey()->check('value > 5'),
589
                [
590
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)',
591
                    'pgsql' => 'serial NOT NULL PRIMARY KEY CHECK (value > 5)',
592
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL CHECK (value > 5)',
593
                    'oci' => 'NUMBER(10) NOT NULL PRIMARY KEY CHECK (value > 5)',
594
                    'sqlsrv' => 'int IDENTITY PRIMARY KEY CHECK (value > 5)'
595
                ]
596
            ],
597
            [
598
                Schema::TYPE_PK . '(8) CHECK (value > 5)',
599
                $this->primaryKey(8)->check('value > 5'),
600
                [
601
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)',
602
                    'oci' => 'NUMBER(8) NOT NULL PRIMARY KEY CHECK (value > 5)'
603
                ]
604
            ],
605
            [
606
                Schema::TYPE_PK . '(8)',
607
                $this->primaryKey(8),
608
                [
609
                    'mysql' => 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY',
610
                    'oci' => 'NUMBER(8) NOT NULL PRIMARY KEY'
611
                ]
612
            ],
613
            [
614
                Schema::TYPE_PK,
615
                $this->primaryKey(),
616
                [
617
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
618
                    'pgsql' => 'serial NOT NULL PRIMARY KEY',
619
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
620
                    'oci' => 'NUMBER(10) NOT NULL PRIMARY KEY',
621
                    'sqlsrv' => 'int IDENTITY PRIMARY KEY'
622
                ]
623
            ],
624
            [
625
                Schema::TYPE_TINYINT . '(2)',
626
                $this->tinyInteger(2),
627
                [
628
                    'mysql' => 'tinyint(2)',
629
                    'pgsql' => 'smallint',
630
                    'sqlite' => 'tinyint',
631
                    'oci' => 'NUMBER(2)',
632
                    'sqlsrv' => 'tinyint'
633
                ]
634
            ],
635
            [
636
                Schema::TYPE_TINYINT . ' UNSIGNED',
637
                $this->tinyInteger()->unsigned(),
638
                [
639
                    'mysql' => 'tinyint(3) UNSIGNED',
640
                    'sqlite' => 'tinyint UNSIGNED'
641
                ]
642
            ],
643
            [
644
                Schema::TYPE_TINYINT,
645
                $this->tinyInteger(),
646
                [
647
                    'mysql' => 'tinyint(3)',
648
                    'pgsql' => 'smallint',
649
                    'sqlite' => 'tinyint',
650
                    'oci' => 'NUMBER(3)',
651
                    'sqlsrv' => 'tinyint'
652
                ]
653
            ],
654
            [
655
                Schema::TYPE_SMALLINT . '(8)',
656
                $this->smallInteger(8),
657
                [
658
                    'mysql' => 'smallint(8)',
659
                    'pgsql' => 'smallint',
660
                    'sqlite' => 'smallint',
661
                    'oci' => 'NUMBER(8)',
662
                    'sqlsrv' => 'smallint'
663
                ]
664
            ],
665
            [
666
                Schema::TYPE_SMALLINT,
667
                $this->smallInteger(),
668
                [
669
                    'mysql' => 'smallint(6)',
670
                    'pgsql' => 'smallint',
671
                    'sqlite' => 'smallint',
672
                    'oci' => 'NUMBER(5)',
673
                    'sqlsrv' => 'smallint'
674
                ]
675
            ],
676
            [
677
                Schema::TYPE_STRING . " CHECK (value LIKE 'test%')",
678
                $this->string()->check("value LIKE 'test%'"),
679
                [
680
                    'mysql' => "varchar(255) CHECK (value LIKE 'test%')",
681
                    'sqlite' => "varchar(255) CHECK (value LIKE 'test%')",
682
                    'sqlsrv' => "nvarchar(255) CHECK (value LIKE 'test%')"
683
                ]
684
            ],
685
            [
686
                Schema::TYPE_STRING . ' CHECK (value LIKE \'test%\')',
687
                $this->string()->check('value LIKE \'test%\''),
688
                [
689
                    'pgsql' => 'varchar(255) CHECK (value LIKE \'test%\')',
690
                    'oci' => 'VARCHAR2(255) CHECK (value LIKE \'test%\')'
691
                ]
692
            ],
693
            [
694
                Schema::TYPE_STRING . ' NOT NULL',
695
                $this->string()->notNull(),
696
                [
697
                    'mysql' => 'varchar(255) NOT NULL',
698
                    'pgsql' => 'varchar(255) NOT NULL',
699
                    'sqlite' => 'varchar(255) NOT NULL',
700
                    'oci' => 'VARCHAR2(255) NOT NULL',
701
                    'sqlsrv' => 'nvarchar(255) NOT NULL'
702
                ]
703
            ],
704
            [
705
                Schema::TYPE_STRING . "(32) CHECK (value LIKE 'test%')",
706
                $this->string(32)->check("value LIKE 'test%'"),
707
                [
708
                    'mysql' => "varchar(32) CHECK (value LIKE 'test%')",
709
                    'sqlite' => "varchar(32) CHECK (value LIKE 'test%')",
710
                    'sqlsrv' => "nvarchar(32) CHECK (value LIKE 'test%')"
711
                ]
712
            ],
713
            [
714
                Schema::TYPE_STRING . '(32) CHECK (value LIKE \'test%\')',
715
                $this->string(32)->check('value LIKE \'test%\''),
716
                [
717
                    'pgsql' => 'varchar(32) CHECK (value LIKE \'test%\')',
718
                    'oci' => 'VARCHAR2(32) CHECK (value LIKE \'test%\')'
719
                ]
720
            ],
721
            [
722
                Schema::TYPE_STRING . '(32)',
723
                $this->string(32),
724
                [
725
                    'mysql' => 'varchar(32)',
726
                    'pgsql' => 'varchar(32)',
727
                    'sqlite' => 'varchar(32)',
728
                    'oci' => 'VARCHAR2(32)',
729
                    'sqlsrv' => 'nvarchar(32)'
730
                ]
731
            ],
732
            [
733
                Schema::TYPE_STRING,
734
                $this->string(),
735
                [
736
                    'mysql' => 'varchar(255)',
737
                    'pgsql' => 'varchar(255)',
738
                    'sqlite' => 'varchar(255)',
739
                    'oci' => 'VARCHAR2(255)',
740
                    'sqlsrv' => 'nvarchar(255)'
741
                ]
742
            ],
743
            [
744
                Schema::TYPE_TEXT . " CHECK (value LIKE 'test%')",
745
                $this->text()->check("value LIKE 'test%'"),
746
                [
747
                    'mysql' => "text CHECK (value LIKE 'test%')",
748
                    'sqlite' => "text CHECK (value LIKE 'test%')",
749
                    'sqlsrv' => "nvarchar(max) CHECK (value LIKE 'test%')"
750
                ]
751
            ],
752
            [
753
                Schema::TYPE_TEXT . ' CHECK (value LIKE \'test%\')',
754
                $this->text()->check('value LIKE \'test%\''),
755
                [
756
                    'pgsql' => 'text CHECK (value LIKE \'test%\')',
757
                    'oci' => 'CLOB CHECK (value LIKE \'test%\')'
758
                ]
759
            ],
760
            [
761
                Schema::TYPE_TEXT . ' NOT NULL',
762
                $this->text()->notNull(),
763
                [
764
                    'mysql' => 'text NOT NULL',
765
                    'pgsql' => 'text NOT NULL',
766
                    'sqlite' => 'text NOT NULL',
767
                    'oci' => 'CLOB NOT NULL',
768
                    'sqlsrv' => 'nvarchar(max) NOT NULL'
769
                ]
770
            ],
771
            [
772
                Schema::TYPE_TEXT . " CHECK (value LIKE 'test%')",
773
                $this->text()->check("value LIKE 'test%'"),
774
                [
775
                    'mysql' => "text CHECK (value LIKE 'test%')",
776
                    'sqlite' => "text CHECK (value LIKE 'test%')",
777
                    'sqlsrv' => "nvarchar(max) CHECK (value LIKE 'test%')"
778
                ],
779
                Schema::TYPE_TEXT . " CHECK (value LIKE 'test%')"
780
            ],
781
            [
782
                Schema::TYPE_TEXT . ' CHECK (value LIKE \'test%\')',
783
                $this->text()->check('value LIKE \'test%\''),
784
                [
785
                    'pgsql' => 'text CHECK (value LIKE \'test%\')',
786
                    'oci' => 'CLOB CHECK (value LIKE \'test%\')'
787
                ],
788
                Schema::TYPE_TEXT . ' CHECK (value LIKE \'test%\')'
789
            ],
790
            [
791
                Schema::TYPE_TEXT . ' NOT NULL',
792
                $this->text()->notNull(),
793
                [
794
                    'mysql' => 'text NOT NULL',
795
                    'pgsql' => 'text NOT NULL',
796
                    'sqlite' => 'text NOT NULL',
797
                    'oci' => 'CLOB NOT NULL',
798
                    'sqlsrv' => 'nvarchar(max) NOT NULL'
799
                ],
800
                Schema::TYPE_TEXT . ' NOT NULL'
801
            ],
802
            [
803
                Schema::TYPE_TEXT,
804
                $this->text(),
805
                [
806
                    'mysql' => 'text',
807
                    'pgsql' => 'text',
808
                    'sqlite' => 'text',
809
                    'oci' => 'CLOB',
810
                    'sqlsrv' => 'nvarchar(max)'
811
                ],
812
                Schema::TYPE_TEXT
813
            ],
814
            [
815
                Schema::TYPE_TEXT,
816
                $this->text(),
817
                [
818
                    'mysql' => 'text',
819
                    'pgsql' => 'text',
820
                    'sqlite' => 'text',
821
                    'oci' => 'CLOB',
822
                    'sqlsrv' => 'nvarchar(max)'
823
                ]
824
            ],
825
            [
826
                Schema::TYPE_TIME . ' NOT NULL',
827
                $this->time()->notNull(),
828
                [
829
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'time(0) NOT NULL' : 'time NOT NULL',
830
                    'pgsql' => 'time(0) NOT NULL',
831
                    'sqlite' => 'time NOT NULL',
832
                    'oci' => 'TIMESTAMP NOT NULL',
833
                    'sqlsrv' => 'time NOT NULL'
834
                ]
835
            ],
836
            [
837
                Schema::TYPE_TIME,
838
                $this->time(),
839
                [
840
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'time(0)' : 'time',
841
                    'pgsql' => 'time(0)',
842
                    'sqlite' => 'time',
843
                    'oci' => 'TIMESTAMP',
844
                    'sqlsrv' => 'time'
845
                ]
846
            ],
847
            [
848
                Schema::TYPE_TIMESTAMP . ' NOT NULL',
849
                $this->timestamp()->notNull(),
850
                [
851
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'timestamp(0) NOT NULL'
852
                        : 'timestamp NOT NULL',
853
                    'pgsql' => 'timestamp(0) NOT NULL',
854
                    'sqlite' => 'timestamp NOT NULL',
855
                    'oci' => 'TIMESTAMP NOT NULL',
856
                    'sqlsrv' => 'datetime NOT NULL'
857
                ]
858
            ],
859
            [
860
                Schema::TYPE_TIMESTAMP . ' NULL DEFAULT NULL',
861
                $this->timestamp()->defaultValue(null),
862
                [
863
                    'mysql' => version_compare($version, '5.6.4', '>=') ? 'timestamp(0) NULL DEFAULT NULL'
864
                        : 'timestamp NULL DEFAULT NULL',
865
                    'pgsql' => 'timestamp(0) NULL DEFAULT NULL',
866
                    'sqlite' => 'timestamp NULL DEFAULT NULL',
867
                    'sqlsrv' => 'datetime NULL DEFAULT NULL'
868
                ]
869
            ],
870
            [
871
                Schema::TYPE_TIMESTAMP . '(4)',
872
                $this->timestamp(4),
873
                [
874
                    'pgsql' => 'timestamp(4)'
875
                ]
876
            ],
877
            [
878
                Schema::TYPE_TIMESTAMP,
879
                $this->timestamp(),
880
                [
881
                    /**
882
                     * MySQL has its own TIMESTAMP test realization.
883
                     *
884
                     * {@see QueryBuilderTest::columnTypes()}
885
                     */
886
                    'pgsql' => 'timestamp(0)',
887
                    'sqlite' => 'timestamp',
888
                    'oci' => 'TIMESTAMP',
889
                    'sqlsrv' => 'datetime'
890
                ]
891
            ],
892
            [
893
                Schema::TYPE_UPK,
894
                $this->primaryKey()->unsigned(),
895
                [
896
                    'mysql' => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
897
                    'pgsql' => 'serial NOT NULL PRIMARY KEY',
898
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL'
899
                ]
900
            ],
901
            [
902
                Schema::TYPE_UBIGPK,
903
                $this->bigPrimaryKey()->unsigned(),
904
                [
905
                    'mysql' => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
906
                    'pgsql' => 'bigserial NOT NULL PRIMARY KEY',
907
                    'sqlite' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL'
908
                ]
909
            ],
910
            [
911
                Schema::TYPE_INTEGER . " COMMENT 'test comment'",
912
                $this->integer()->comment('test comment'),
913
                [
914
                    'mysql' => "int(11) COMMENT 'test comment'",
915
                    'sqlsrv' => 'int'
916
                ],
917
                [
918
                    'sqlsrv' => 'integer'
919
                ]
920
            ],
921
            [
922
                Schema::TYPE_PK . " COMMENT 'test comment'",
923
                $this->primaryKey()->comment('test comment'),
924
                [
925
                    'mysql' => "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'test comment'",
926
                    'sqlsrv' => 'int IDENTITY PRIMARY KEY'
927
                ],
928
                [
929
                    'sqlsrv' => 'pk'
930
                ]
931
            ],
932
            [
933
                Schema::TYPE_PK . ' FIRST',
934
                $this->primaryKey()->first(),
935
                [
936
                    'mysql' => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST',
937
                    'oci' => 'NUMBER(10) NOT NULL PRIMARY KEY',
938
                    'sqlsrv' => 'int IDENTITY PRIMARY KEY'
939
                ],
940
                [
941
                    'sqlsrv' => 'pk'
942
                ]
943
            ],
944
            [
945
                Schema::TYPE_INTEGER . ' FIRST',
946
                $this->integer()->first(),
947
                [
948
                    'mysql' => 'int(11) FIRST',
949
                    'oci' => 'NUMBER(10)',
950
                    'sqlsrv' => 'int'
951
                ],
952
                [
953
                    'pgsql' => 'integer',
954
                    'sqlsrv' => 'integer'
955
                ]
956
            ],
957
            [
958
                Schema::TYPE_STRING . ' FIRST',
959
                $this->string()->first(),
960
                [
961
                    'mysql' => 'varchar(255) FIRST',
962
                    'oci' => 'VARCHAR2(255)',
963
                    'sqlsrv' => 'nvarchar(255)'
964
                ],
965
                [
966
                    'sqlsrv' => 'string'
967
                ]
968
            ],
969
            [
970
                Schema::TYPE_INTEGER . ' NOT NULL FIRST',
971
                $this->integer()->append('NOT NULL')->first(),
972
                [
973
                    'mysql' => 'int(11) NOT NULL FIRST',
974
                    'oci' => 'NUMBER(10) NOT NULL',
975
                    'sqlsrv' => 'int NOT NULL'
976
                ],
977
                [
978
                    'sqlsrv' => 'integer NOT NULL'
979
                ]
980
            ],
981
            [
982
                Schema::TYPE_STRING . ' NOT NULL FIRST',
983
                $this->string()->append('NOT NULL')->first(),
984
                [
985
                    'mysql' => 'varchar(255) NOT NULL FIRST',
986
                    'oci' => 'VARCHAR2(255) NOT NULL',
987
                    'sqlsrv' => 'nvarchar(255) NOT NULL'
988
                ],
989
                [
990
                    'sqlsrv' => 'string NOT NULL'
991
                ]
992
            ],
993
            [
994
                Schema::TYPE_JSON,
995
                $this->json(),
996
                [
997
                    'pgsql' => 'jsonb'
998
                ]
999
            ],
1000
        ];
1001
1002
        foreach ($items as $i => $item) {
1003
            if (array_key_exists($this->getConnection()->getDriverName(), $item[2])) {
1004
                $item[2] = $item[2][$this->getConnection()->getDriverName()];
1005
                $items[$i] = $item;
1006
            } else {
1007
                unset($items[$i]);
1008
            }
1009
        }
1010
1011
        return array_values($items);
1012
    }
1013
1014
    public function testGetColumnType(): void
1015
    {
1016
        $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

1016
        /** @scrutinizer ignore-call */ 
1017
        $qb = $this->getQueryBuilder();
Loading history...
1017
1018
        foreach ($this->columnTypes() as $item) {
1019
            [$column, $builder, $expected] = $item;
1020
1021
            if (isset($item[3][$this->getConnection()->getDriverName()])) {
1022
                $expectedColumnSchemaBuilder = $item[3][$this->getConnection()->getDriverName()];
1023
            } elseif (isset($item[3]) && !is_array($item[3])) {
1024
                $expectedColumnSchemaBuilder = $item[3];
1025
            } else {
1026
                $expectedColumnSchemaBuilder = $column;
1027
            }
1028
1029
            $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

1029
            $this->/** @scrutinizer ignore-call */ 
1030
                   assertEquals($expected, $qb->getColumnType($column));
Loading history...
1030
            $this->assertEquals($expected, $qb->getColumnType($builder));
1031
            $this->assertEquals($expectedColumnSchemaBuilder, $builder->__toString());
1032
        }
1033
    }
1034
1035
    public function testCreateTableColumnTypes(): void
1036
    {
1037
        $qb = $this->getQueryBuilder();
1038
1039
        if ($qb->getDb()->getTableSchema('column_type_table', true) !== null) {
1040
            $this->getConnection(false)->createCommand($qb->dropTable('column_type_table'))->execute();
1041
        }
1042
1043
        $columns = [];
1044
        $i = 0;
1045
1046
        foreach ($this->columnTypes() as [$column, $builder, $expected]) {
1047
            if (
1048
                !(
1049
                    strncmp($column, Schema::TYPE_PK, 2) === 0 ||
1050
                    strncmp($column, Schema::TYPE_UPK, 3) === 0 ||
1051
                    strncmp($column, Schema::TYPE_BIGPK, 5) === 0 ||
1052
                    strncmp($column, Schema::TYPE_UBIGPK, 6) === 0 ||
1053
                    strncmp(substr($column, -5), 'FIRST', 5) === 0
1054
                )
1055
            ) {
1056
                $columns['col' . ++$i] = str_replace('CHECK (value', 'CHECK ([[col' . $i . ']]', $column);
1057
            }
1058
        }
1059
1060
        $this->getConnection(false)->createCommand($qb->createTable('column_type_table', $columns))->execute();
1061
1062
        $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

1062
        $this->/** @scrutinizer ignore-call */ 
1063
               assertNotEmpty($qb->getDb()->getTableSchema('column_type_table', true));
Loading history...
1063
    }
1064
1065
    public function testBuildWhereExistsWithParameters(): void
1066
    {
1067
        $db = $this->getConnection();
1068
1069
        $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

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

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

1530
        $this->/** @scrutinizer ignore-call */ 
1531
               assertInstanceOf(QueryBuilder::class, $this->getQueryBuilder(true, true));
Loading history...
1531
    }
1532
1533
    /**
1534
     * {@see https://github.com/yiisoft/yii2/issues/15653}
1535
     */
1536
    public function testIssue15653(): void
1537
    {
1538
        $db = $this->getConnection();
1539
1540
        $query = (new Query($db))
1541
            ->from('admin_user')
1542
            ->where(['is_deleted' => false]);
1543
1544
        $query
1545
            ->where([])
1546
            ->andWhere(['in', 'id', ['1', '0']]);
1547
1548
        [$sql, $params] = $this->getQueryBuilder()->build($query);
1549
1550
        $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

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