Passed
Pull Request — master (#380)
by Wilmer
03:09
created

BaseQueryBuilderProvider::alterColumn()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 128
Code Lines 96

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 96
nc 1
nop 1
dl 0
loc 128
rs 8.0872
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Tests\Provider;
6
7
use Yiisoft\Db\Driver\PDO\ConnectionPDOInterface;
8
use Yiisoft\Db\Expression\Expression;
9
use Yiisoft\Db\Query\Query;
10
use Yiisoft\Db\QueryBuilder\Conditions\BetweenColumnsCondition;
11
use Yiisoft\Db\QueryBuilder\Conditions\InCondition;
12
use Yiisoft\Db\QueryBuilder\Conditions\LikeCondition;
13
use Yiisoft\Db\QueryBuilder\QueryBuilder;
14
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
15
use Yiisoft\Db\Schema\SchemaBuilderTrait;
16
use Yiisoft\Db\Tests\Support\DbHelper;
17
use Yiisoft\Db\Tests\Support\TraversableObject;
18
19
final class BaseQueryBuilderProvider
20
{
21
    use SchemaBuilderTrait;
22
23
    protected ConnectionPDOInterface $db;
24
25
    public function addColumn(): array
26
    {
27
        return [
28
            [
29
                <<<SQL
30
                ALTER TABLE [[table]] ADD [[column]] integer
31
                SQL,
32
            ],
33
        ];
34
    }
35
36
    public function addDropChecks(): array
37
    {
38
        $tableName = 'T_constraints_1';
39
        $name = 'CN_check';
40
41
        return [
42
            'drop' => [
43
                <<<SQL
44
                ALTER TABLE {{{$tableName}}} DROP CONSTRAINT [[$name]]
45
                SQL,
46
                static fn (QueryBuilderInterface $qb) => $qb->dropCheck($name, $tableName),
47
            ],
48
            'add' => [
49
                <<<SQL
50
                ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] CHECK ([[C_not_null]] > 100)
51
                SQL,
52
                static fn (QueryBuilderInterface $qb) => $qb->addCheck($name, $tableName, '[[C_not_null]] > 100'),
53
            ],
54
        ];
55
    }
56
57
    public function addDropForeignKeys(): array
58
    {
59
        $name = 'CN_constraints_3';
60
        $pkTableName = 'T_constraints_2';
61
        $tableName = 'T_constraints_3';
62
63
        return [
64
            'drop' => [
65
                <<<SQL
66
                ALTER TABLE {{{$tableName}}} DROP CONSTRAINT [[$name]]
67
                SQL,
68
                static fn (QueryBuilderInterface $qb) => $qb->dropForeignKey($name, $tableName),
69
            ],
70
            'add' => [
71
                <<<SQL
72
                ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] FOREIGN KEY ([[C_fk_id_1]]) REFERENCES {{{$pkTableName}}} ([[C_id_1]]) ON DELETE CASCADE ON UPDATE CASCADE
73
                SQL,
74
                static fn (QueryBuilderInterface $qb) => $qb->addForeignKey(
75
                    $name,
76
                    $tableName,
77
                    'C_fk_id_1',
78
                    $pkTableName,
79
                    'C_id_1',
80
                    'CASCADE',
81
                    'CASCADE'
82
                ),
83
            ],
84
            'add (2 columns)' => [
85
                <<<SQL
86
                ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] FOREIGN KEY ([[C_fk_id_1]], [[C_fk_id_2]]) REFERENCES {{{$pkTableName}}} ([[C_id_1]], [[C_id_2]]) ON DELETE CASCADE ON UPDATE CASCADE
87
                SQL,
88
                static fn (QueryBuilderInterface $qb) => $qb->addForeignKey(
89
                    $name,
90
                    $tableName,
91
                    'C_fk_id_1, C_fk_id_2',
92
                    $pkTableName,
93
                    'C_id_1, C_id_2',
94
                    'CASCADE',
95
                    'CASCADE'
96
                ),
97
            ],
98
        ];
99
    }
100
101
    public function addDropPrimaryKeys(): array
102
    {
103
        $tableName = 'T_constraints_1';
104
        $name = 'CN_pk';
105
106
        return [
107
            'drop' => [
108
                <<<SQL
109
                ALTER TABLE {{{$tableName}}} DROP CONSTRAINT [[$name]]
110
                SQL,
111
                static fn (QueryBuilderInterface $qb) => $qb->dropPrimaryKey($name, $tableName),
112
            ],
113
            'add' => [
114
                <<<SQL
115
                ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] PRIMARY KEY ([[C_id_1]])
116
                SQL,
117
                static fn (QueryBuilderInterface $qb) => $qb->addPrimaryKey($name, $tableName, 'C_id_1'),
118
            ],
119
            'add (2 columns)' => [
120
                "ALTER TABLE {{{$tableName}}} ADD CONSTRAINT [[$name]] PRIMARY KEY ([[C_id_1]], [[C_id_2]])",
121
                static fn (QueryBuilderInterface $qb) => $qb->addPrimaryKey($name, $tableName, 'C_id_1, C_id_2'),
122
            ],
123
        ];
124
    }
125
126
    public function addDropUniques(): array
127
    {
128
        $tableName1 = 'T_constraints_1';
129
        $name1 = 'CN_unique';
130
        $tableName2 = 'T_constraints_2';
131
        $name2 = 'CN_constraints_2_multi';
132
133
        return [
134
            'drop' => [
135
                <<<SQL
136
                ALTER TABLE {{{$tableName1}}} DROP CONSTRAINT [[$name1]]
137
                SQL,
138
                static fn (QueryBuilderInterface $qb) => $qb->dropUnique($name1, $tableName1),
139
            ],
140
            'add' => [
141
                <<<SQL
142
                ALTER TABLE {{{$tableName1}}} ADD CONSTRAINT [[$name1]] UNIQUE ([[C_unique]])
143
                SQL,
144
                static fn (QueryBuilderInterface $qb) => $qb->addUnique($name1, $tableName1, 'C_unique'),
145
            ],
146
            'add (2 columns)' => [
147
                <<<SQL
148
                ALTER TABLE {{{$tableName2}}} ADD CONSTRAINT [[$name2]] UNIQUE ([[C_index_2_1]], [[C_index_2_2]])
149
                SQL,
150
                static fn (QueryBuilderInterface $qb) => $qb->addUnique(
151
                    $name2,
152
                    $tableName2,
153
                    'C_index_2_1,
154
                    C_index_2_2',
155
                ),
156
            ],
157
        ];
158
    }
159
160
    public function alterColumn(ConnectionPDOInterface $db): array
161
    {
162
        $this->db = $db;
163
164
        return [
165
            [
166
                'foo1',
167
                'bar',
168
                'varchar(255)',
169
                DbHelper::replaceQuotes(
170
                    <<<SQL
171
                    ALTER TABLE [[foo1]] CHANGE [[bar]] [[bar]] varchar(255)
172
                    SQL,
173
                    $db->getName(),
174
                ),
175
            ],
176
            [
177
                'foo1',
178
                'bar',
179
                'SET NOT null',
180
                DbHelper::replaceQuotes(
181
                    <<<SQL
182
                    ALTER TABLE [[foo1]] CHANGE [[bar]] [[bar]] SET NOT null
183
                    SQL,
184
                    $db->getName(),
185
                ),
186
            ],
187
            [
188
                'foo1',
189
                'bar',
190
                'drop default',
191
                <<<SQL
192
                ALTER TABLE `foo1` CHANGE `bar` `bar` drop default
193
                SQL,
194
            ],
195
            [
196
                'foo1',
197
                'bar',
198
                'reset xyz',
199
                <<<SQL
200
                ALTER TABLE `foo1` CHANGE `bar` `bar` reset xyz
201
                SQL,
202
            ],
203
            [
204
                'foo1',
205
                'bar',
206
                $this->string(255),
207
                <<<SQL
208
                ALTER TABLE `foo1` CHANGE `bar` `bar` string(255)
209
                SQL,
210
            ],
211
            [
212
                'foo1',
213
                'bar',
214
                'varchar(255) USING bar::varchar',
215
                <<<SQL
216
                ALTER TABLE `foo1` CHANGE `bar` `bar` varchar(255) USING bar::varchar
217
                SQL,
218
            ],
219
            [
220
                'foo1',
221
                'bar',
222
                'varchar(255) using cast("bar" as varchar)',
223
                <<<SQL
224
                ALTER TABLE `foo1` CHANGE `bar` `bar` varchar(255) using cast("bar" as varchar)
225
                SQL,
226
            ],
227
            [
228
                'foo1',
229
                'bar',
230
                $this->string(255)->notNull(),
231
                <<<SQL
232
                ALTER TABLE `foo1` CHANGE `bar` `bar` string(255) NOT NULL
233
                SQL,
234
            ],
235
            [
236
                'foo1',
237
                'bar',
238
                $this->string(255)->null(),
239
                <<<SQL
240
                ALTER TABLE `foo1` CHANGE `bar` `bar` string(255) NULL DEFAULT NULL
241
                SQL,
242
            ],
243
            [
244
                'foo1',
245
                'bar',
246
                $this->string(255)->null()->defaultValue('xxx'),
247
                <<<SQL
248
                ALTER TABLE `foo1` CHANGE `bar` `bar` string(255) NULL DEFAULT 'xxx'
249
                SQL,
250
            ],
251
            [
252
                'foo1',
253
                'bar',
254
                $this->string(255)->check('char_length(bar) > 5'),
255
                <<<SQL
256
                ALTER TABLE `foo1` CHANGE `bar` `bar` string(255) CHECK (char_length(bar) > 5)
257
                SQL,
258
            ],
259
            [
260
                'foo1',
261
                'bar',
262
                $this->string(255)->defaultValue(''),
263
                <<<SQL
264
                ALTER TABLE `foo1` CHANGE `bar` `bar` string(255) DEFAULT ''
265
                SQL,
266
            ],
267
            [
268
                'foo1',
269
                'bar',
270
                $this->string(255)->defaultValue('AbCdE'),
271
                <<<SQL
272
                ALTER TABLE `foo1` CHANGE `bar` `bar` string(255) DEFAULT 'AbCdE'
273
                SQL,
274
            ],
275
            [
276
                'foo1',
277
                'bar',
278
                $this->timestamp()->defaultExpression('CURRENT_TIMESTAMP'),
279
                <<<SQL
280
                ALTER TABLE `foo1` CHANGE `bar` `bar` timestamp DEFAULT CURRENT_TIMESTAMP
281
                SQL,
282
            ],
283
            [
284
                'foo1',
285
                'bar',
286
                $this->string(30)->unique(),
287
                <<<SQL
288
                ALTER TABLE `foo1` CHANGE `bar` `bar` string(30) UNIQUE
289
                SQL,
290
            ],
291
        ];
292
    }
293
294
    public function batchInsert(ConnectionPDOInterface $db): array
295
    {
296
        return [
297
            'simple' => [
298
                'customer',
299
                ['email', 'name', 'address'],
300
                [['[email protected]', 'silverfire', 'Kyiv {{city}}, Ukraine']],
301
                'expected' => DbHelper::replaceQuotes(
302
                    <<<SQL
303
                    INSERT INTO [[customer]] ([[email]], [[name]], [[address]]) VALUES (:qp0, :qp1, :qp2)
304
                    SQL,
305
                    $db->getName(),
306
                ),
307
                [':qp0' => '[email protected]', ':qp1' => 'silverfire', ':qp2' => 'Kyiv {{city}}, Ukraine'],
308
            ],
309
            'escape-danger-chars' => [
310
                'customer',
311
                ['address'],
312
                [["SQL-danger chars are escaped: '); --"]],
313
                'expected' => DbHelper::replaceQuotes(
314
                    <<<SQL
315
                    INSERT INTO [[customer]] ([[address]]) VALUES (:qp0)
316
                    SQL,
317
                    $db->getName(),
318
                ),
319
                [
320
                    ':qp0' => "SQL-danger chars are escaped: '); --",
321
                ],
322
            ],
323
            'customer2' => ['customer', ['address'], [], ''],
324
            'customer3' => [
325
                'customer',
326
                [],
327
                [['no columns passed']],
328
                'expected' => DbHelper::replaceQuotes(
329
                    <<<SQL
330
                    INSERT INTO [[customer]] () VALUES (:qp0)
331
                    SQL,
332
                    $db->getName(),
333
                ),
334
                [':qp0' => 'no columns passed'],
335
            ],
336
            'bool-false, bool2-null' => [
337
                'type',
338
                ['bool_col', 'bool_col2'],
339
                [[false, null]],
340
                'expected' => DbHelper::replaceQuotes(
341
                    <<<SQL
342
                    INSERT INTO [[type]] ([[bool_col]], [[bool_col2]]) VALUES (:qp0, :qp1)
343
                    SQL,
344
                    $db->getName(),
345
                ),
346
                [':qp0' => false, ':qp1' => null],
347
            ],
348
            'wrong' => [
349
                '{{%type}}',
350
                ['{{%type}}.[[float_col]]', '[[time]]'],
351
                [[null, new Expression('now()')], [null, new Expression('now()')]],
352
                'expected' => 'INSERT INTO {{%type}} ({{%type}}.[[float_col]], [[time]]) VALUES (:qp0, now()), (:qp1, now())',
353
                [':qp0' => null, ':qp1' => null],
354
            ],
355
            'bool-false, time-now()' => [
356
                '{{%type}}',
357
                ['{{%type}}.[[bool_col]]', '[[time]]'],
358
                [[false, new Expression('now()')]],
359
                'expected' => 'INSERT INTO {{%type}} ({{%type}}.[[bool_col]], [[time]]) VALUES (:qp0, now())',
360
                [':qp0' => false],
361
            ],
362
        ];
363
    }
364
365
    public function buildConditions(ConnectionPDOInterface $db): array
366
    {
367
        $conditions = [
368
            /* empty values */
369
            [['like', 'name', []], '0=1', []],
370
            [['not like', 'name', []], '', []],
371
            [['or like', 'name', []], '0=1', []],
372
            [['or not like', 'name', []], '', []],
373
374
            /* not */
375
            [['not', ''], '', []],
376
            [['not', 'name'], 'NOT (name)', []],
377
            [
378
                [
379
                    'not',
380
                    (new query($db))->select('exists')->from('some_table'),
381
                ],
382
                'NOT ((SELECT [[exists]] FROM [[some_table]]))', [],
383
            ],
384
385
            /* and */
386
            [['and', '', ''], '', []],
387
            [['and', '', 'id=2'], 'id=2', []],
388
            [['and', 'id=1', 'id=2'], '(id=1) AND (id=2)', []],
389
            [['and', 'type=1', ['or', 'id=1', 'id=2']], '(type=1) AND ((id=1) OR (id=2))', []],
390
            [['and', 'id=1', new Expression('id=:qp0', [':qp0' => 2])], '(id=1) AND (id=:qp0)', [':qp0' => 2]],
391
            [
392
                [
393
                    'and',
394
                    ['expired' => false],
395
                    (new query($db))->select('count(*) > 1')->from('queue'),
396
                ],
397
                '([[expired]]=:qp0) AND ((SELECT count(*) > 1 FROM [[queue]]))',
398
                [':qp0' => false],
399
            ],
400
401
            /* or */
402
            [['or', 'id=1', 'id=2'], '(id=1) OR (id=2)', []],
403
            [['or', 'type=1', ['or', 'id=1', 'id=2']], '(type=1) OR ((id=1) OR (id=2))', []],
404
            [['or', 'type=1', new Expression('id=:qp0', [':qp0' => 1])], '(type=1) OR (id=:qp0)', [':qp0' => 1]],
405
406
            /* between */
407
            [['between', 'id', 1, 10], '[[id]] BETWEEN :qp0 AND :qp1', [':qp0' => 1, ':qp1' => 10]],
408
            [['not between', 'id', 1, 10], '[[id]] NOT BETWEEN :qp0 AND :qp1', [':qp0' => 1, ':qp1' => 10]],
409
            [
410
                ['between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), new Expression('NOW()')],
411
                '[[date]] BETWEEN (NOW() - INTERVAL 1 MONTH) AND NOW()',
412
                [],
413
            ],
414
            [
415
                ['between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), 123],
416
                '[[date]] BETWEEN (NOW() - INTERVAL 1 MONTH) AND :qp0',
417
                [':qp0' => 123],
418
            ],
419
            [
420
                ['not between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), new Expression('NOW()')],
421
                '[[date]] NOT BETWEEN (NOW() - INTERVAL 1 MONTH) AND NOW()',
422
                [],
423
            ],
424
            [
425
                ['not between', 'date', new Expression('(NOW() - INTERVAL 1 MONTH)'), 123],
426
                '[[date]] NOT BETWEEN (NOW() - INTERVAL 1 MONTH) AND :qp0',
427
                [':qp0' => 123],
428
            ],
429
            [
430
                new BetweenColumnsCondition('2018-02-11', 'BETWEEN', 'create_time', 'update_time'),
431
                ':qp0 BETWEEN [[create_time]] AND [[update_time]]',
432
                [':qp0' => '2018-02-11'],
433
            ],
434
            [
435
                new BetweenColumnsCondition('2018-02-11', 'NOT BETWEEN', 'NOW()', 'update_time'),
436
                ':qp0 NOT BETWEEN NOW() AND [[update_time]]',
437
                [':qp0' => '2018-02-11'],
438
            ],
439
            [
440
                new BetweenColumnsCondition(new Expression('NOW()'), 'BETWEEN', 'create_time', 'update_time'),
441
                'NOW() BETWEEN [[create_time]] AND [[update_time]]',
442
                [],
443
            ],
444
            [
445
                new BetweenColumnsCondition(new Expression('NOW()'), 'NOT BETWEEN', 'create_time', 'update_time'),
446
                'NOW() NOT BETWEEN [[create_time]] AND [[update_time]]',
447
                [],
448
            ],
449
            [
450
                new BetweenColumnsCondition(
451
                    new Expression('NOW()'),
452
                    'NOT BETWEEN',
453
                    (new query($db))->select('min_date')->from('some_table'),
454
                    'max_date'
455
                ),
456
                'NOW() NOT BETWEEN (SELECT [[min_date]] FROM [[some_table]]) AND [[max_date]]',
457
                [],
458
            ],
459
            [
460
                new BetweenColumnsCondition(
461
                    new Expression('NOW()'),
462
                    'NOT BETWEEN',
463
                    new Expression('min_date'),
464
                    (new query($db))->select('max_date')->from('some_table'),
465
                ),
466
                'NOW() NOT BETWEEN min_date AND (SELECT [[max_date]] FROM [[some_table]])',
467
                [],
468
            ],
469
470
            /* in */
471
            [
472
                ['in', 'id', [1, 2, (new query($db))->select('three')->from('digits')]],
473
                '[[id]] IN (:qp0, :qp1, (SELECT [[three]] FROM [[digits]]))',
474
                [':qp0' => 1, ':qp1' => 2],
475
            ],
476
            [
477
                ['not in', 'id', [1, 2, 3]],
478
                '[[id]] NOT IN (:qp0, :qp1, :qp2)',
479
                [':qp0' => 1, ':qp1' => 2, ':qp2' => 3],
480
            ],
481
            [
482
                ['in', 'id', (new query($db))->select('id')->from('users')->where(['active' => 1])],
483
                '[[id]] IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
484
                [':qp0' => 1],
485
            ],
486
            [
487
                ['not in', 'id', (new query($db))->select('id')->from('users')->where(['active' => 1])],
488
                '[[id]] NOT IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
489
                [':qp0' => 1],
490
            ],
491
            [['in', 'id', 1], '[[id]]=:qp0', [':qp0' => 1]],
492
            [['in', 'id', [1]], '[[id]]=:qp0', [':qp0' => 1]],
493
            [['in', 'id', new TraversableObject([1])], '[[id]]=:qp0', [':qp0' => 1]],
494
            'composite in' => [
495
                ['in', ['id', 'name'], [['id' => 1, 'name' => 'oy']]],
496
                '([[id]], [[name]]) IN ((:qp0, :qp1))',
497
                [':qp0' => 1, ':qp1' => 'oy'],
498
            ],
499
            'composite in (just one column)' => [
500
                ['in', ['id'], [['id' => 1, 'name' => 'Name1'], ['id' => 2, 'name' => 'Name2']]],
501
                '[[id]] IN (:qp0, :qp1)',
502
                [':qp0' => 1, ':qp1' => 2],
503
            ],
504
            'composite in using array objects (just one column)' => [
505
                [
506
                    'in',
507
                    new TraversableObject(['id']),
508
                    new TraversableObject([['id' => 1, 'name' => 'Name1'], ['id' => 2, 'name' => 'Name2']]),
509
                ],
510
                '[[id]] IN (:qp0, :qp1)',
511
                [':qp0' => 1, ':qp1' => 2],
512
            ],
513
514
            /* in using array objects. */
515
            [['id' => new TraversableObject([1, 2])], '[[id]] IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
516
            [
517
                ['in', 'id', new TraversableObject([1, 2, 3])],
518
                '[[id]] IN (:qp0, :qp1, :qp2)',
519
                [':qp0' => 1, ':qp1' => 2, ':qp2' => 3],
520
            ],
521
522
            /* in using array objects containing null value */
523
            [['in', 'id', new TraversableObject([1, null])], '[[id]]=:qp0 OR [[id]] IS NULL', [':qp0' => 1]],
524
            [
525
                ['in', 'id', new TraversableObject([1, 2, null])],
526
                '[[id]] IN (:qp0, :qp1) OR [[id]] IS NULL', [':qp0' => 1, ':qp1' => 2],
527
            ],
528
529
            /* not in using array object containing null value */
530
            [
531
                ['not in', 'id', new TraversableObject([1, null])],
532
                '[[id]]<>:qp0 AND [[id]] IS NOT NULL', [':qp0' => 1],
533
            ],
534
            [
535
                ['not in', 'id', new TraversableObject([1, 2, null])],
536
                '[[id]] NOT IN (:qp0, :qp1) AND [[id]] IS NOT NULL',
537
                [':qp0' => 1, ':qp1' => 2],
538
            ],
539
540
            /* in using array object containing only null value */
541
            [['in', 'id', new TraversableObject([null])], '[[id]] IS NULL', []],
542
            [['not in', 'id', new TraversableObject([null])], '[[id]] IS NOT NULL', []],
543
            'composite in using array objects' => [
544
                [
545
                    'in',
546
                    new TraversableObject(['id', 'name']),
547
                    new TraversableObject([['id' => 1, 'name' => 'oy'], ['id' => 2, 'name' => 'yo']]),
548
                ],
549
                '([[id]], [[name]]) IN ((:qp0, :qp1), (:qp2, :qp3))',
550
                [':qp0' => 1, ':qp1' => 'oy', ':qp2' => 2, ':qp3' => 'yo'],
551
            ],
552
553
            /* in object conditions */
554
            [new InCondition('id', 'in', 1), '[[id]]=:qp0', [':qp0' => 1]],
555
            [new InCondition('id', 'in', [1]), '[[id]]=:qp0', [':qp0' => 1]],
556
            [new InCondition('id', 'not in', 1), '[[id]]<>:qp0', [':qp0' => 1]],
557
            [new InCondition('id', 'not in', [1]), '[[id]]<>:qp0', [':qp0' => 1]],
558
            [new InCondition('id', 'in', [1, 2]), '[[id]] IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
559
            [new InCondition('id', 'not in', [1, 2]), '[[id]] NOT IN (:qp0, :qp1)', [':qp0' => 1, ':qp1' => 2]],
560
            [new InCondition([], 'in', 1), '0=1', []],
561
            [new InCondition([], 'in', [1]), '0=1', []],
562
            'inCondition-custom-1' => [new InCondition(['id', 'name'], 'in', []), '0=1', []],
563
            'inCondition-custom-2' => [
564
                new InCondition(
565
                    ['id'],
566
                    'in',
567
                    (new query($db))->select('id')->from('users')->where(['active' => 1]),
568
                ),
569
                '([[id]]) IN (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
570
                [':qp0' => 1],
571
            ],
572
            'inCondition-custom-3' => [
573
                new InCondition(['id', 'name'], 'in', [['id' => 1]]),
574
                '([[id]], [[name]]) IN ((:qp0, NULL))',
575
                [':qp0' => 1],
576
            ],
577
            'inCondition-custom-4' => [
578
                new InCondition(['id', 'name'], 'in', [['name' => 'oy']]),
579
                '([[id]], [[name]]) IN ((NULL, :qp0))',
580
                [':qp0' => 'oy'],
581
            ],
582
            'inCondition-custom-5' => [
583
                new InCondition(['id', 'name'], 'in', [['id' => 1, 'name' => 'oy']]),
584
                '([[id]], [[name]]) IN ((:qp0, :qp1))',
585
                [':qp0' => 1, ':qp1' => 'oy'],
586
            ],
587
588
            /* exists */
589
            [
590
                [
591
                    'exists',
592
                    (new query($db))->select('id')->from('users')->where(['active' => 1]),
593
                ],
594
                'EXISTS (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)',
595
                [':qp0' => 1],
596
            ],
597
            [
598
                ['not exists', (new query($db))->select('id')->from('users')->where(['active' => 1])],
599
                'NOT EXISTS (SELECT [[id]] FROM [[users]] WHERE [[active]]=:qp0)', [':qp0' => 1],
600
            ],
601
602
            /* simple conditions */
603
            [['=', 'a', 'b'], '[[a]] = :qp0', [':qp0' => 'b']],
604
            [['>', 'a', 1], '[[a]] > :qp0', [':qp0' => 1]],
605
            [['>=', 'a', 'b'], '[[a]] >= :qp0', [':qp0' => 'b']],
606
            [['<', 'a', 2], '[[a]] < :qp0', [':qp0' => 2]],
607
            [['<=', 'a', 'b'], '[[a]] <= :qp0', [':qp0' => 'b']],
608
            [['<>', 'a', 3], '[[a]] <> :qp0', [':qp0' => 3]],
609
            [['!=', 'a', 'b'], '[[a]] != :qp0', [':qp0' => 'b']],
610
            [
611
                ['>=', 'date', new Expression('DATE_SUB(NOW(), INTERVAL 1 MONTH)')],
612
                '[[date]] >= DATE_SUB(NOW(), INTERVAL 1 MONTH)',
613
                [],
614
            ],
615
            [
616
                ['>=', 'date', new Expression('DATE_SUB(NOW(), INTERVAL :month MONTH)', [':month' => 2])],
617
                '[[date]] >= DATE_SUB(NOW(), INTERVAL :month MONTH)',
618
                [':month' => 2],
619
            ],
620
            [
621
                ['=', 'date', (new query($db))->select('max(date)')->from('test')->where(['id' => 5])],
622
                '[[date]] = (SELECT max(date) FROM [[test]] WHERE [[id]]=:qp0)',
623
                [':qp0' => 5],
624
            ],
625
            [['=', 'a', null], '[[a]] = NULL', []],
626
627
            /* operand1 is Expression */
628
            [
629
                ['=', new Expression('date'), '2019-08-01'],
630
                'date = :qp0',
631
                [':qp0' => '2019-08-01'],
632
            ],
633
            [
634
                ['=', (new query($db))->select('COUNT(*)')->from('test')->where(['id' => 6]), 0],
635
                '(SELECT COUNT(*) FROM [[test]] WHERE [[id]]=:qp0) = :qp1',
636
                [':qp0' => 6, ':qp1' => 0],
637
            ],
638
639
            /* hash condition */
640
            [['a' => 1, 'b' => 2], '([[a]]=:qp0) AND ([[b]]=:qp1)', [':qp0' => 1, ':qp1' => 2]],
641
            [
642
                ['a' => new Expression('CONCAT(col1, col2)'), 'b' => 2],
643
                '([[a]]=CONCAT(col1, col2)) AND ([[b]]=:qp0)',
644
                [':qp0' => 2],
645
            ],
646
            [['a' => null], '[[a]] IS NULL', []],
647
648
            /* direct conditions */
649
            ['a = CONCAT(col1, col2)', 'a = CONCAT(col1, col2)', []],
650
            [
651
                new Expression('a = CONCAT(col1, :param1)', ['param1' => 'value1']),
652
                'a = CONCAT(col1, :param1)',
653
                ['param1' => 'value1'],
654
            ],
655
656
            /* Expression with params as operand of 'not' */
657
            [
658
                ['not', new Expression('any_expression(:a)', [':a' => 1])],
659
                'NOT (any_expression(:a))', [':a' => 1],
660
            ],
661
            [new Expression('NOT (any_expression(:a))', [':a' => 1]), 'NOT (any_expression(:a))', [':a' => 1]],
662
663
            /* like */
664
            'like-custom-1' => [['like', 'a', 'b'], '[[a]] LIKE :qp0', [':qp0' => '%b%']],
665
            'like-custom-2' => [
666
                ['like', 'a', new Expression(':qp0', [':qp0' => '%b%'])],
667
                '[[a]] LIKE :qp0',
668
                [':qp0' => '%b%'],
669
            ],
670
            'like-custom-3' => [
671
                ['like', new Expression('CONCAT(col1, col2)'), 'b'], 'CONCAT(col1, col2) LIKE :qp0', [':qp0' => '%b%'],
672
            ],
673
        ];
674
675
        /* adjust dbms specific escaping */
676
        foreach ($conditions as $i => $condition) {
677
            $conditions[$i][1] = DbHelper::replaceQuotes($condition[1], $db->getName());
678
        }
679
680
        return $conditions;
681
    }
682
683
    public function buildExistsParams(ConnectionPDOInterface $db): array
684
    {
685
        return [
686
            [
687
                'exists',
688
                DbHelper::replaceQuotes(
689
                    <<<SQL
690
                    SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE EXISTS (SELECT [[1]] FROM [[Website]] [[w]])
691
                    SQL,
692
                    $db->getName(),
693
                ),
694
            ],
695
            [
696
                'not exists',
697
                DbHelper::replaceQuotes(
698
                    <<<SQL
699
                    SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE NOT EXISTS (SELECT [[1]] FROM [[Website]] [[w]])
700
                    SQL,
701
                    $db->getName(),
702
                ),
703
            ],
704
        ];
705
    }
706
707
    public function buildFilterConditions(ConnectionPDOInterface $db): array
708
    {
709
        $conditions = [
710
            /* like */
711
            [['like', 'name', []], '', []],
712
            [['not like', 'name', []], '', []],
713
            [['or like', 'name', []], '', []],
714
            [['or not like', 'name', []], '', []],
715
716
            /* not */
717
            [['not', ''], '', []],
718
719
            /* and */
720
            [['and', '', ''], '', []],
721
            [['and', '', 'id=2'], 'id=2', []],
722
            [['and', 'id=1', ''], 'id=1', []],
723
            [['and', 'type=1', ['or', '', 'id=2']], '(type=1) AND (id=2)', []],
724
725
            /* or */
726
            [['or', 'id=1', ''], 'id=1', []],
727
            [['or', 'type=1', ['or', '', 'id=2']], '(type=1) OR (id=2)', []],
728
729
            /* between */
730
            [['between', 'id', 1, null], '', []],
731
            [['not between', 'id', null, 10], '', []],
732
733
            /* in */
734
            [['in', 'id', []], '', []],
735
            [['not in', 'id', []], '', []],
736
737
            /* simple conditions */
738
            [['=', 'a', ''], '', []],
739
            [['>', 'a', ''], '', []],
740
            [['>=', 'a', ''], '', []],
741
            [['<', 'a', ''], '', []],
742
            [['<=', 'a', ''], '', []],
743
            [['<>', 'a', ''], '', []],
744
            [['!=', 'a', ''], '', []],
745
        ];
746
747
        /* adjust dbms specific escaping */
748
        foreach ($conditions as $i => $condition) {
749
            $conditions[$i][1] = DbHelper::replaceQuotes($condition[1], $db->getName());
750
        }
751
752
        return $conditions;
753
    }
754
755
    public function buildFrom(): array
756
    {
757
        return [
758
            ['test t1', '[[test]] [[t1]]'],
759
            ['test as t1', '[[test]] [[t1]]'],
760
            ['test AS t1', '[[test]] [[t1]]'],
761
            ['test', '[[test]]'],
762
        ];
763
    }
764
765
    public function buildLikeConditions(
766
        ConnectionPDOInterface $db,
767
        string $likeEscapeCharSql = '',
768
        array $likeParameterReplacements = []
769
    ): array {
770
        $conditions = [
771
            /* simple like */
772
            [['like', 'name', 'foo%'], '[[name]] LIKE :qp0', [':qp0' => '%foo\%%']],
773
            [['not like', 'name', 'foo%'], '[[name]] NOT LIKE :qp0', [':qp0' => '%foo\%%']],
774
            [['or like', 'name', 'foo%'], '[[name]] LIKE :qp0', [':qp0' => '%foo\%%']],
775
            [['or not like', 'name', 'foo%'], '[[name]] NOT LIKE :qp0', [':qp0' => '%foo\%%']],
776
777
            /* like for many values */
778
            [
779
                ['like', 'name', ['foo%', '[abc]']],
780
                '[[name]] LIKE :qp0 AND [[name]] LIKE :qp1',
781
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
782
            ],
783
            [
784
                ['not like', 'name', ['foo%', '[abc]']],
785
                '[[name]] NOT LIKE :qp0 AND [[name]] NOT LIKE :qp1',
786
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
787
            ],
788
            [
789
                ['or like', 'name', ['foo%', '[abc]']],
790
                '[[name]] LIKE :qp0 OR [[name]] LIKE :qp1',
791
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
792
            ],
793
            [
794
                ['or not like', 'name', ['foo%', '[abc]']],
795
                '[[name]] NOT LIKE :qp0 OR [[name]] NOT LIKE :qp1',
796
                [':qp0' => '%foo\%%', ':qp1' => '%[abc]%'],
797
            ],
798
799
            /* like with Expression */
800
            [
801
                ['like', 'name', new Expression('CONCAT("test", name, "%")')],
802
                '[[name]] LIKE CONCAT("test", name, "%")',
803
                [],
804
            ],
805
            [
806
                ['not like', 'name', new Expression('CONCAT("test", name, "%")')],
807
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
808
                [],
809
            ],
810
            [
811
                ['or like', 'name', new Expression('CONCAT("test", name, "%")')],
812
                '[[name]] LIKE CONCAT("test", name, "%")',
813
                [],
814
            ],
815
            [
816
                ['or not like', 'name', new Expression('CONCAT("test", name, "%")')],
817
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
818
                [],
819
            ],
820
            [
821
                ['like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
822
                '[[name]] LIKE CONCAT("test", name, "%") AND [[name]] LIKE :qp0',
823
                [':qp0' => '%\\\ab\_c%'],
824
            ],
825
            [
826
                ['not like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
827
                '[[name]] NOT LIKE CONCAT("test", name, "%") AND [[name]] NOT LIKE :qp0',
828
                [':qp0' => '%\\\ab\_c%'],
829
            ],
830
            [
831
                ['or like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
832
                '[[name]] LIKE CONCAT("test", name, "%") OR [[name]] LIKE :qp0',
833
                [':qp0' => '%\\\ab\_c%'],
834
            ],
835
            [
836
                ['or not like', 'name', [new Expression('CONCAT("test", name, "%")'), '\ab_c']],
837
                '[[name]] NOT LIKE CONCAT("test", name, "%") OR [[name]] NOT LIKE :qp0',
838
                [':qp0' => '%\\\ab\_c%'],
839
            ],
840
841
            /**
842
             * {@see https://github.com/yiisoft/yii2/issues/15630}
843
             */
844
            [['like', 'location.title_ru', 'vi%', null], '[[location]].[[title_ru]] LIKE :qp0', [':qp0' => 'vi%']],
845
846
            /* like object conditions */
847
            [
848
                new LikeCondition('name', 'like', new Expression('CONCAT("test", name, "%")')),
849
                '[[name]] LIKE CONCAT("test", name, "%")',
850
                [],
851
            ],
852
            [
853
                new LikeCondition('name', 'not like', new Expression('CONCAT("test", name, "%")')),
854
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
855
                [],
856
            ],
857
            [
858
                new LikeCondition('name', 'or like', new Expression('CONCAT("test", name, "%")')),
859
                '[[name]] LIKE CONCAT("test", name, "%")',
860
                [],
861
            ],
862
            [
863
                new LikeCondition('name', 'or not like', new Expression('CONCAT("test", name, "%")')),
864
                '[[name]] NOT LIKE CONCAT("test", name, "%")',
865
                [],
866
            ],
867
            [
868
                new LikeCondition('name', 'like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
869
                '[[name]] LIKE CONCAT("test", name, "%") AND [[name]] LIKE :qp0',
870
                [':qp0' => '%\\\ab\_c%'],
871
            ],
872
            [
873
                new LikeCondition('name', 'not like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
874
                '[[name]] NOT LIKE CONCAT("test", name, "%") AND [[name]] NOT LIKE :qp0',
875
                [':qp0' => '%\\\ab\_c%'],
876
            ],
877
            [
878
                new LikeCondition('name', 'or like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
879
                '[[name]] LIKE CONCAT("test", name, "%") OR [[name]] LIKE :qp0', [':qp0' => '%\\\ab\_c%'],
880
            ],
881
            [
882
                new LikeCondition('name', 'or not like', [new Expression('CONCAT("test", name, "%")'), '\ab_c']),
883
                '[[name]] NOT LIKE CONCAT("test", name, "%") OR [[name]] NOT LIKE :qp0', [':qp0' => '%\\\ab\_c%'],
884
            ],
885
886
            /* like with expression as columnName */
887
            [['like', new Expression('name'), 'teststring'], 'name LIKE :qp0', [':qp0' => '%teststring%']],
888
        ];
889
890
        /* adjust dbms specific escaping */
891
        foreach ($conditions as $i => $condition) {
892
            $conditions[$i][1] = DbHelper::replaceQuotes($condition[1], $db->getName());
893
894
            if ($likeEscapeCharSql !== '') {
895
                preg_match_all('/(?P<condition>LIKE.+?)( AND| OR|$)/', $conditions[$i][1], $matches, PREG_SET_ORDER);
896
897
                foreach ($matches as $match) {
898
                    $conditions[$i][1] = str_replace(
899
                        $match['condition'],
900
                        $match['condition'] . $likeEscapeCharSql,
901
                        $conditions[$i][1]
902
                    );
903
                }
904
            }
905
906
            foreach ($conditions[$i][2] as $name => $value) {
907
                $conditions[$i][2][$name] = strtr($conditions[$i][2][$name], $likeParameterReplacements);
908
            }
909
        }
910
911
        return $conditions;
912
    }
913
914
    public function buildWhereExists(ConnectionPDOInterface $db): array
915
    {
916
        return [
917
            [
918
                'exists',
919
                DbHelper::replaceQuotes(
920
                    <<<SQL
921
                    SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE EXISTS (SELECT [[1]] FROM [[Website]] [[w]])
922
                    SQL,
923
                    $db->getName(),
924
                ),
925
            ],
926
            [
927
                'not exists',
928
                DbHelper::replaceQuotes(
929
                    <<<SQL
930
                    SELECT [[id]] FROM [[TotalExample]] [[t]] WHERE NOT EXISTS (SELECT [[1]] FROM [[Website]] [[w]])
931
                    SQL,
932
                    $db->getName(),
933
                ),
934
            ],
935
        ];
936
    }
937
938
    public function createDropIndex(): array
939
    {
940
        $tableName = 'T_constraints_2';
941
        $name1 = 'CN_constraints_2_single';
942
        $name2 = 'CN_constraints_2_multi';
943
944
        return [
945
            'drop' => [
946
                <<<SQL
947
                DROP INDEX [[$name1]] ON {{{$tableName}}}
948
                SQL,
949
                static fn (QueryBuilderInterface $qb) => $qb->dropIndex($name1, $tableName),
950
            ],
951
            'create' => [
952
                <<<SQL
953
                CREATE INDEX [[$name1]] ON {{{$tableName}}} ([[C_index_1]])
954
                SQL,
955
                static fn (QueryBuilderInterface $qb) => $qb->createIndex($name1, $tableName, 'C_index_1'),
956
            ],
957
            'create (2 columns)' => [
958
                <<<SQL
959
                CREATE INDEX [[$name2]] ON {{{$tableName}}} ([[C_index_2_1]], [[C_index_2_2]])
960
                SQL,
961
                static fn (QueryBuilderInterface $qb) => $qb->createIndex(
962
                    $name2,
963
                    $tableName,
964
                    'C_index_2_1,
965
                    C_index_2_2',
966
                ),
967
            ],
968
            'create unique' => [
969
                <<<SQL
970
                CREATE UNIQUE INDEX [[$name1]] ON {{{$tableName}}} ([[C_index_1]])
971
                SQL,
972
                static fn (QueryBuilderInterface $qb) => $qb->createIndex(
973
                    $name1,
974
                    $tableName,
975
                    'C_index_1',
976
                    QueryBuilder::INDEX_UNIQUE,
977
                ),
978
            ],
979
            'create unique (2 columns)' => [
980
                <<<SQL
981
                CREATE UNIQUE INDEX [[$name2]] ON {{{$tableName}}} ([[C_index_2_1]], [[C_index_2_2]])
982
                SQL,
983
                static fn (QueryBuilderInterface $qb) => $qb->createIndex(
984
                    $name2,
985
                    $tableName,
986
                    'C_index_2_1,
987
                    C_index_2_2',
988
                    QueryBuilder::INDEX_UNIQUE,
989
                ),
990
            ],
991
        ];
992
    }
993
994
    public function delete(ConnectionPDOInterface $db): array
995
    {
996
        return [
997
            [
998
                'user',
999
                ['is_enabled' => false, 'power' => new Expression('WRONG_POWER()')],
1000
                DbHelper::replaceQuotes(
1001
                    <<<SQL
1002
                    DELETE FROM [[user]] WHERE ([[is_enabled]]=:qp0) AND ([[power]]=WRONG_POWER())
1003
                    SQL,
1004
                    $db->getName(),
1005
                ),
1006
                [':qp0' => false],
1007
            ],
1008
        ];
1009
    }
1010
1011
    public function fromCases(): array
1012
    {
1013
        return [
1014
            ['table1', 'SELECT * FROM `table1`'],
1015
            [['table1'], 'SELECT * FROM `table1`'],
1016
            [new Expression('table2'), 'SELECT * FROM table2'],
1017
            [[new Expression('table2')], 'SELECT * FROM table2'],
1018
            [['alias' => 'table3'], 'SELECT * FROM `table3` `alias`'],
1019
            [['alias' => new Expression('table4')], 'SELECT * FROM table4 `alias`'],
1020
            [
1021
                ['alias' => new Expression('func(:param1, :param2)', ['param1' => 'A', 'param2' => 'B'])],
1022
                'SELECT * FROM func(:param1, :param2) `alias`',
1023
                ['param1' => 'A', 'param2' => 'B'],
1024
            ],
1025
        ];
1026
    }
1027
1028
    public function insert(ConnectionPDOInterface $db): array
1029
    {
1030
        return [
1031
            'regular-values' => [
1032
                'customer',
1033
                [
1034
                    'email' => '[email protected]',
1035
                    'name' => 'silverfire',
1036
                    'address' => 'Kyiv {{city}}, Ukraine',
1037
                    'is_active' => false,
1038
                    'related_id' => null,
1039
                ],
1040
                [],
1041
                <<<SQL
1042
                INSERT INTO `customer` (`email`, `name`, `address`, `is_active`, `related_id`) VALUES (:qp0, :qp1, :qp2, :qp3, :qp4)
1043
                SQL,
1044
                [
1045
                    ':qp0' => '[email protected]',
1046
                    ':qp1' => 'silverfire',
1047
                    ':qp2' => 'Kyiv {{city}}, Ukraine',
1048
                    ':qp3' => false,
1049
                    ':qp4' => null,
1050
                ],
1051
            ],
1052
            'params-and-expressions' => [
1053
                '{{%type}}',
1054
                ['{{%type}}.[[related_id]]' => null, '[[time]]' => new Expression('now()')],
1055
                [],
1056
                <<<SQL
1057
                INSERT INTO {{%type}} ({{%type}}.[[related_id]], [[time]]) VALUES (:qp0, now())
1058
                SQL,
1059
                [':qp0' => null],
1060
            ],
1061
            'carry passed params' => [
1062
                'customer',
1063
                [
1064
                    'email' => '[email protected]',
1065
                    'name' => 'sergeymakinen',
1066
                    'address' => '{{city}}',
1067
                    'is_active' => false,
1068
                    'related_id' => null,
1069
                    'col' => new Expression('CONCAT(:phFoo, :phBar)', [':phFoo' => 'foo']),
1070
                ],
1071
                [':phBar' => 'bar'],
1072
                <<<SQL
1073
                INSERT INTO `customer` (`email`, `name`, `address`, `is_active`, `related_id`, `col`) VALUES (:qp1, :qp2, :qp3, :qp4, :qp5, CONCAT(:phFoo, :phBar))
1074
                SQL,
1075
                [
1076
                    ':phBar' => 'bar',
1077
                    ':qp1' => '[email protected]',
1078
                    ':qp2' => 'sergeymakinen',
1079
                    ':qp3' => '{{city}}',
1080
                    ':qp4' => false,
1081
                    ':qp5' => null,
1082
                    ':phFoo' => 'foo',
1083
                ],
1084
            ],
1085
            'carry passed params (query)' => [
1086
                'customer',
1087
                (new Query($db))
1088
                    ->select(['email', 'name', 'address', 'is_active', 'related_id'])
1089
                    ->from('customer')
1090
                    ->where(
1091
                        [
1092
                            'email' => '[email protected]',
1093
                            'name' => 'sergeymakinen',
1094
                            'address' => '{{city}}',
1095
                            'is_active' => false,
1096
                            'related_id' => null,
1097
                            'col' => new Expression('CONCAT(:phFoo, :phBar)', [':phFoo' => 'foo']),
1098
                        ],
1099
                    ),
1100
                [':phBar' => 'bar'],
1101
                <<<SQL
1102
                INSERT INTO `customer` (`email`, `name`, `address`, `is_active`, `related_id`) SELECT `email`, `name`, `address`, `is_active`, `related_id` FROM `customer` WHERE (`email`=:qp1) AND (`name`=:qp2) AND (`address`=:qp3) AND (`is_active`=:qp4) AND (`related_id` IS NULL) AND (`col`=CONCAT(:phFoo, :phBar))
1103
                SQL,
1104
                [
1105
                    ':phBar' => 'bar',
1106
                    ':qp1' => '[email protected]',
1107
                    ':qp2' => 'sergeymakinen',
1108
                    ':qp3' => '{{city}}',
1109
                    ':qp4' => false,
1110
                    ':phFoo' => 'foo',
1111
                ],
1112
            ],
1113
        ];
1114
    }
1115
1116
    public function insertEx(ConnectionPDOInterface $db): array
1117
    {
1118
        return [
1119
            'regular-values' => [
1120
                'customer',
1121
                [
1122
                    'email' => '[email protected]',
1123
                    'name' => 'silverfire',
1124
                    'address' => 'Kyiv {{city}}, Ukraine',
1125
                    'is_active' => false,
1126
                    'related_id' => null,
1127
                ],
1128
                [],
1129
                DbHelper::replaceQuotes(
1130
                    <<<SQL
1131
                    INSERT INTO `customer` (`email`, `name`, `address`, `is_active`, `related_id`) VALUES (:qp0, :qp1, :qp2, :qp3, :qp4)
1132
                    SQL,
1133
                    $db->getName(),
1134
                ),
1135
                [
1136
                    ':qp0' => '[email protected]',
1137
                    ':qp1' => 'silverfire',
1138
                    ':qp2' => 'Kyiv {{city}}, Ukraine',
1139
                    ':qp3' => false,
1140
                    ':qp4' => null,
1141
                ],
1142
            ],
1143
            'params-and-expressions' => [
1144
                '{{%type}}',
1145
                ['{{%type}}.[[related_id]]' => null, '[[time]]' => new Expression('now()')],
1146
                [],
1147
                <<<SQL
1148
                INSERT INTO {{%type}} ({{%type}}.[[related_id]], [[time]]) VALUES (:qp0, now())
1149
                SQL,
1150
                [':qp0' => null],
1151
            ],
1152
            'carry passed params' => [
1153
                'customer',
1154
                [
1155
                    'email' => '[email protected]',
1156
                    'name' => 'sergeymakinen',
1157
                    'address' => '{{city}}',
1158
                    'is_active' => false,
1159
                    'related_id' => null,
1160
                    'col' => new Expression('CONCAT(:phFoo, :phBar)', [':phFoo' => 'foo']),
1161
                ],
1162
                [':phBar' => 'bar'],
1163
                DbHelper::replaceQuotes(
1164
                    <<<SQL
1165
                    INSERT INTO `customer` (`email`, `name`, `address`, `is_active`, `related_id`, `col`) VALUES (:qp1, :qp2, :qp3, :qp4, :qp5, CONCAT(:phFoo, :phBar))
1166
                    SQL,
1167
                    $db->getName(),
1168
                ),
1169
                [
1170
                    ':phBar' => 'bar',
1171
                    ':qp1' => '[email protected]',
1172
                    ':qp2' => 'sergeymakinen',
1173
                    ':qp3' => '{{city}}',
1174
                    ':qp4' => false,
1175
                    ':qp5' => null,
1176
                    ':phFoo' => 'foo',
1177
                ],
1178
            ],
1179
            'carry passed params (query)' => [
1180
                'customer',
1181
                (new Query($db))
1182
                    ->select(['email', 'name', 'address', 'is_active', 'related_id'])
1183
                    ->from('customer')
1184
                    ->where(
1185
                        [
1186
                            'email' => '[email protected]',
1187
                            'name' => 'sergeymakinen',
1188
                            'address' => '{{city}}',
1189
                            'is_active' => false,
1190
                            'related_id' => null,
1191
                            'col' => new Expression('CONCAT(:phFoo, :phBar)', [':phFoo' => 'foo']),
1192
                        ],
1193
                    ),
1194
                [':phBar' => 'bar'],
1195
                DbHelper::replaceQuotes(
1196
                    <<<SQL
1197
                    INSERT INTO `customer` (`email`, `name`, `address`, `is_active`, `related_id`) SELECT `email`, `name`, `address`, `is_active`, `related_id` FROM `customer` WHERE (`email`=:qp1) AND (`name`=:qp2) AND (`address`=:qp3) AND (`is_active`=:qp4) AND (`related_id` IS NULL) AND (`col`=CONCAT(:phFoo, :phBar))
1198
                    SQL,
1199
                    $db->getName(),
1200
                ),
1201
                [
1202
                    ':phBar' => 'bar',
1203
                    ':qp1' => '[email protected]',
1204
                    ':qp2' => 'sergeymakinen',
1205
                    ':qp3' => '{{city}}',
1206
                    ':qp4' => false,
1207
                    ':phFoo' => 'foo',
1208
                ],
1209
            ],
1210
        ];
1211
    }
1212
1213
    public function selectExist(): array
1214
    {
1215
        return [
1216
            [
1217
                <<<SQL
1218
                SELECT 1 FROM `table` WHERE `id` = 1
1219
                SQL,
1220
                <<<SQL
1221
                SELECT EXISTS(SELECT 1 FROM `table` WHERE `id` = 1)
1222
                SQL,
1223
            ],
1224
        ];
1225
    }
1226
1227
    public function update(ConnectionPDOInterface $db): array
1228
    {
1229
        return [
1230
            [
1231
                'customer',
1232
                ['status' => 1, 'updated_at' => new Expression('now()')],
1233
                ['id' => 100],
1234
                DbHelper::replaceQuotes(
1235
                    <<<SQL
1236
                    UPDATE [[customer]] SET [[status]]=:qp0, [[updated_at]]=now() WHERE [[id]]=:qp1
1237
                    SQL,
1238
                    $db->getName(),
1239
                ),
1240
                [':qp0' => 1, ':qp1' => 100],
1241
            ],
1242
        ];
1243
    }
1244
1245
    public function upsert(ConnectionPDOInterface $db): array
1246
    {
1247
        return [
1248
            'regular values' => [
1249
                'T_upsert',
1250
                ['email' => '[email protected]', 'address' => 'bar {{city}}', 'status' => 1, 'profile_id' => null],
1251
                true,
1252
                null,
1253
                [':qp0' => '[email protected]', ':qp1' => 'bar {{city}}', ':qp2' => 1, ':qp3' => null],
1254
            ],
1255
            'regular values with update part' => [
1256
                'T_upsert',
1257
                ['email' => '[email protected]', 'address' => 'bar {{city}}', 'status' => 1, 'profile_id' => null],
1258
                ['address' => 'foo {{city}}', 'status' => 2, 'orders' => new Expression('T_upsert.orders + 1')],
1259
                null,
1260
                [
1261
                    ':qp0' => '[email protected]',
1262
                    ':qp1' => 'bar {{city}}',
1263
                    ':qp2' => 1,
1264
                    ':qp3' => null,
1265
                    ':qp4' => 'foo {{city}}',
1266
                    ':qp5' => 2,
1267
                ],
1268
            ],
1269
            'regular values without update part' => [
1270
                'T_upsert',
1271
                ['email' => '[email protected]', 'address' => 'bar {{city}}', 'status' => 1, 'profile_id' => null],
1272
                false,
1273
                null,
1274
                [':qp0' => '[email protected]', ':qp1' => 'bar {{city}}', ':qp2' => 1, ':qp3' => null],
1275
            ],
1276
            'query' => [
1277
                'T_upsert',
1278
                (new Query($db))
1279
                    ->select(['email', 'status' => new Expression('2')])
1280
                    ->from('customer')
1281
                    ->where(['name' => 'user1'])
1282
                    ->limit(1),
1283
                true,
1284
                null,
1285
                [':qp0' => 'user1'],
1286
            ],
1287
            'query with update part' => [
1288
                'T_upsert',
1289
                (new Query($db))
1290
                    ->select(['email', 'status' => new Expression('2')])
1291
                    ->from('customer')
1292
                    ->where(['name' => 'user1'])
1293
                    ->limit(1),
1294
                ['address' => 'foo {{city}}', 'status' => 2, 'orders' => new Expression('T_upsert.orders + 1')],
1295
                null,
1296
                [':qp0' => 'user1', ':qp1' => 'foo {{city}}', ':qp2' => 2],
1297
            ],
1298
            'query without update part' => [
1299
                'T_upsert',
1300
                (new Query($db))
1301
                    ->select(['email', 'status' => new Expression('2')])
1302
                    ->from('customer')
1303
                    ->where(['name' => 'user1'])
1304
                    ->limit(1),
1305
                false,
1306
                null,
1307
                [':qp0' => 'user1'],
1308
            ],
1309
            'values and expressions' => [
1310
                '{{%T_upsert}}',
1311
                ['{{%T_upsert}}.[[email]]' => '[email protected]', '[[ts]]' => new Expression('now()')],
1312
                true,
1313
                null,
1314
                [':qp0' => '[email protected]'],
1315
            ],
1316
            'values and expressions with update part' => [
1317
                '{{%T_upsert}}',
1318
                ['{{%T_upsert}}.[[email]]' => '[email protected]', '[[ts]]' => new Expression('now()')],
1319
                ['[[orders]]' => new Expression('T_upsert.orders + 1')],
1320
                null,
1321
                [':qp0' => '[email protected]'],
1322
            ],
1323
            'values and expressions without update part' => [
1324
                '{{%T_upsert}}',
1325
                ['{{%T_upsert}}.[[email]]' => '[email protected]', '[[ts]]' => new Expression('now()')],
1326
                false,
1327
                null,
1328
                [':qp0' => '[email protected]'],
1329
            ],
1330
            'query, values and expressions with update part' => [
1331
                '{{%T_upsert}}',
1332
                (new Query($db))
1333
                    ->select(
1334
                        [
1335
                            'email' => new Expression(':phEmail', [':phEmail' => '[email protected]']),
1336
                            '[[time]]' => new Expression('now()'),
1337
                        ],
1338
                    ),
1339
                ['ts' => 0, '[[orders]]' => new Expression('T_upsert.orders + 1')],
1340
                null,
1341
                [':phEmail' => '[email protected]', ':qp1' => 0],
1342
            ],
1343
            'query, values and expressions without update part' => [
1344
                '{{%T_upsert}}',
1345
                (new Query($db))
1346
                    ->select(
1347
                        [
1348
                            'email' => new Expression(':phEmail', [':phEmail' => '[email protected]']),
1349
                            '[[time]]' => new Expression('now()'),
1350
                        ],
1351
                    ),
1352
                ['ts' => 0, '[[orders]]' => new Expression('T_upsert.orders + 1')],
1353
                null,
1354
                [':phEmail' => '[email protected]', ':qp1' => 0],
1355
            ],
1356
            'no columns to update' => ['T_upsert_1', ['a' => 1], false, null, [':qp0' => 1]],
1357
        ];
1358
    }
1359
}
1360