Passed
Pull Request — master (#405)
by Wilmer
05:12 queued 02:35
created

BaseQueryBuilderProvider::buildFrom()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 60
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

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