Passed
Push — master ( d3f1dd...e0d474 )
by Def
10:50 queued 08:16
created

AbstractQueryBuilderProvider::insertEx()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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