Passed
Pull Request — master (#380)
by Wilmer
15:15 queued 12:03
created

BaseQueryBuilderProvider::upsert()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 112
Code Lines 91

Duplication

Lines 0
Ratio 0 %

Importance

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