Passed
Pull Request — master (#380)
by Wilmer
04:47 queued 01:51
created

BaseQueryBuilderProvider::insertEx()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 92
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

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