Failed Conditions
Pull Request — develop (#3348)
by Sergei
60:53
created

SQLParserUtilsTest::testGetPlaceholderPositions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\DBAL;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\DBAL\ParameterType;
9
use Doctrine\DBAL\SQLParserUtils;
10
use Doctrine\DBAL\SQLParserUtilsException;
11
use Doctrine\Tests\DbalTestCase;
12
use ReflectionMethod;
13
14
/**
15
 * @group DBAL-78
16
 * @group DDC-1372
17
 */
18
class SQLParserUtilsTest extends DbalTestCase
19
{
20
    /**
21
     * @return mixed[][]
22
     */
23
    public static function dataGetPlaceholderPositionalPositions() : iterable
24
    {
25
        return [
26
            // none
27
            ['SELECT * FROM Foo', []],
28
29
            // Positionals
30
            ['SELECT ?', [7]],
31
            ['SELECT * FROM Foo WHERE bar IN (?, ?, ?)', [32, 35, 38]],
32
            ['SELECT ? FROM ?', [7, 14]],
33
            ['SELECT "?" FROM foo', []],
34
            ["SELECT '?' FROM foo", []],
35
            ['SELECT `?` FROM foo', []], // Ticket DBAL-552
36
            ['SELECT [?] FROM foo', []],
37
            ["SELECT 'Doctrine\DBAL?' FROM foo", []], // Ticket DBAL-558
38
            ['SELECT "Doctrine\DBAL?" FROM foo', []], // Ticket DBAL-558
39
            ['SELECT `Doctrine\DBAL?` FROM foo', []], // Ticket DBAL-558
40
            ['SELECT [Doctrine\DBAL?] FROM foo', []], // Ticket DBAL-558
41
            ['SELECT "?" FROM foo WHERE bar = ?', [32]],
42
            ["SELECT '?' FROM foo WHERE bar = ?", [32]],
43
            ['SELECT `?` FROM foo WHERE bar = ?', [32]], // Ticket DBAL-552
44
            ['SELECT [?] FROM foo WHERE bar = ?', [32]],
45
            ['SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, ARRAY[?])', [56]], // Ticket GH-2295
46
            ["SELECT 'Doctrine\DBAL?' FROM foo WHERE bar = ?", [45]], // Ticket DBAL-558
47
            ['SELECT "Doctrine\DBAL?" FROM foo WHERE bar = ?', [45]], // Ticket DBAL-558
48
            ['SELECT `Doctrine\DBAL?` FROM foo WHERE bar = ?', [45]], // Ticket DBAL-558
49
            ['SELECT [Doctrine\DBAL?] FROM foo WHERE bar = ?', [45]], // Ticket DBAL-558
50
            ["SELECT * FROM FOO WHERE bar = 'it\\'s a trap? \\\\' OR bar = ?\nAND baz = \"\\\"quote\\\" me on it? \\\\\" OR baz = ?", [58, 104]],
51
            ['SELECT * FROM foo WHERE foo = ? AND bar = ?', [1 => 42, 0 => 30]], // explicit keys
52
53
        ];
54
    }
55
56
    /**
57
     * @return mixed[][]
58
     */
59
    public function dataGetPlaceholderNamedPositions() : iterable
60
    {
61
        return [
62
            // none
63
            ['SELECT * FROM Foo', []],
64
65
            // named
66
            ['SELECT :foo FROM :bar', [7 => 'foo', 17 => 'bar']],
67
            ['SELECT * FROM Foo WHERE bar IN (:name1, :name2)', [32 => 'name1', 40 => 'name2']],
68
            ['SELECT ":foo" FROM Foo WHERE bar IN (:name1, :name2)', [37 => 'name1', 45 => 'name2']],
69
            ["SELECT ':foo' FROM Foo WHERE bar IN (:name1, :name2)", [37 => 'name1', 45 => 'name2']],
70
            ['SELECT :foo_id', [7 => 'foo_id']], // Ticket DBAL-231
71
            ['SELECT @rank := 1', []], // Ticket DBAL-398
72
            ['SELECT @rank := 1 AS rank, :foo AS foo FROM :bar', [27 => 'foo', 44 => 'bar']], // Ticket DBAL-398
73
            ['SELECT * FROM Foo WHERE bar > :start_date AND baz > :start_date', [30 => 'start_date', 52 => 'start_date']], // Ticket GH-113
74
            ['SELECT foo::date as date FROM Foo WHERE bar > :start_date AND baz > :start_date', [46 => 'start_date', 68 => 'start_date']], // Ticket GH-259
75
            ['SELECT `d.ns:col_name` FROM my_table d WHERE `d.date` >= :param1', [57 => 'param1']], // Ticket DBAL-552
76
            ['SELECT [d.ns:col_name] FROM my_table d WHERE [d.date] >= :param1', [57 => 'param1']], // Ticket DBAL-552
77
            ['SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, ARRAY[:foo])', [56 => 'foo']], // Ticket GH-2295
78
            ['SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, array[:foo])', [56 => 'foo']],
79
            ['SELECT table.field1, ARRAY[\'3\'] FROM schema.table table WHERE table.f1 = :foo AND ARRAY[\'3\']', [73 => 'foo']],
80
            ['SELECT table.field1, ARRAY[\'3\']::integer[] FROM schema.table table WHERE table.f1 = :foo AND ARRAY[\'3\']::integer[]', [84 => 'foo']],
81
            ['SELECT table.field1, ARRAY[:foo] FROM schema.table table WHERE table.f1 = :bar AND ARRAY[\'3\']', [27 => 'foo', 74 => 'bar']],
82
            ['SELECT table.field1, ARRAY[:foo]::integer[] FROM schema.table table WHERE table.f1 = :bar AND ARRAY[\'3\']::integer[]', [27 => 'foo', 85 => 'bar']],
83
            [
84
                <<<'SQLDATA'
85
SELECT * FROM foo WHERE
86
bar = ':not_a_param1 ''":not_a_param2"'''
87
OR bar=:a_param1
88
OR bar=:a_param2||':not_a_param3'
89
OR bar=':not_a_param4 '':not_a_param5'' :not_a_param6'
90
OR bar=''
91
OR bar=:a_param3
92
SQLDATA
93
                ,
94
                [
95
                    73 => 'a_param1',
96
                    90 => 'a_param2',
97
                    189 => 'a_param3',
98
                ],
99
            ],
100
            ["SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE :condition_0 ESCAPE '\\\\') AND (data.description LIKE :condition_1 ESCAPE '\\\\') ORDER BY id ASC", [121 => 'condition_0', 174 => 'condition_1']],
101
            ['SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE :condition_0 ESCAPE "\\\\") AND (data.description LIKE :condition_1 ESCAPE "\\\\") ORDER BY id ASC', [121 => 'condition_0', 174 => 'condition_1']],
102
            ['SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE :condition_0 ESCAPE "\\\\") AND (data.description LIKE :condition_1 ESCAPE \'\\\\\') ORDER BY id ASC', [121 => 'condition_0', 174 => 'condition_1']],
103
            ['SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE :condition_0 ESCAPE `\\\\`) AND (data.description LIKE :condition_1 ESCAPE `\\\\`) ORDER BY id ASC', [121 => 'condition_0', 174 => 'condition_1']],
104
            ['SELECT data.age AS age, data.id AS id, data.name AS name, data.id AS id FROM test_data data WHERE (data.description LIKE :condition_0 ESCAPE \'\\\\\') AND (data.description LIKE :condition_1 ESCAPE `\\\\`) ORDER BY id ASC', [121 => 'condition_0', 174 => 'condition_1']],
105
106
        ];
107
    }
108
109
    /**
110
     * @param int[] $expectedParamPos
111
     *
112
     * @dataProvider dataGetPlaceholderPositionalPositions
113
     */
114
    public function testGetPlaceholderPositions(string $query, array $expectedParamPos) : void
115
    {
116
        $reflection = new ReflectionMethod(SQLParserUtils::class, 'getPositionalPlaceholderPositions');
117
        $reflection->setAccessible(true);
118
119
        self::assertEquals($expectedParamPos, $reflection->invoke(null, $query));
120
    }
121
122
    /**
123
     * @param string[] $expectedParamPos
124
     *
125
     * @dataProvider dataGetPlaceholderNamedPositions
126
     */
127
    public function testGetNamedPlaceholderPositions(string $query, array $expectedParamPos) : void
128
    {
129
        $reflection = new ReflectionMethod(SQLParserUtils::class, 'getNamedPlaceholderPositions');
130
        $reflection->setAccessible(true);
131
132
        self::assertEquals($expectedParamPos, $reflection->invoke(null, $query));
133
    }
134
135
    /**
136
     * @return mixed[][]
137
     */
138
    public static function dataExpandListParameters() : iterable
139
    {
140
        return [
141
            // Positional: Very simple with one needle
142
            [
143
                'SELECT * FROM Foo WHERE foo IN (?)',
144
                [[1, 2, 3]],
145
                [Connection::PARAM_INT_ARRAY],
146
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
147
                [1, 2, 3],
148
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
149
            ],
150
            // Positional: One non-list before d one after list-needle
151
            [
152
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?)',
153
                ['string', [1, 2, 3]],
154
                [ParameterType::STRING, Connection::PARAM_INT_ARRAY],
155
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
156
                ['string', 1, 2, 3],
157
                [ParameterType::STRING, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
158
            ],
159
            // Positional: One non-list after list-needle
160
            [
161
                'SELECT * FROM Foo WHERE bar IN (?) AND baz = ?',
162
                [[1, 2, 3], 'foo'],
163
                [Connection::PARAM_INT_ARRAY, ParameterType::STRING],
164
                'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?',
165
                [1, 2, 3, 'foo'],
166
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
167
            ],
168
            // Positional: One non-list before and one after list-needle
169
            [
170
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?) AND baz = ?',
171
                [1, [1, 2, 3], 4],
172
                [ParameterType::INTEGER, Connection::PARAM_INT_ARRAY, ParameterType::INTEGER],
173
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?',
174
                [1, 1, 2, 3, 4],
175
                [
176
                    ParameterType::INTEGER,
177
                    ParameterType::INTEGER,
178
                    ParameterType::INTEGER,
179
                    ParameterType::INTEGER,
180
                    ParameterType::INTEGER,
181
                ],
182
            ],
183
            // Positional: Two lists
184
            [
185
                'SELECT * FROM Foo WHERE foo IN (?, ?)',
186
                [[1, 2, 3], [4, 5]],
187
                [Connection::PARAM_INT_ARRAY, Connection::PARAM_INT_ARRAY],
188
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)',
189
                [1, 2, 3, 4, 5],
190
                [
191
                    ParameterType::INTEGER,
192
                    ParameterType::INTEGER,
193
                    ParameterType::INTEGER,
194
                    ParameterType::INTEGER,
195
                    ParameterType::INTEGER,
196
                ],
197
            ],
198
            // Positional: Empty "integer" array DDC-1978
199
            [
200
                'SELECT * FROM Foo WHERE foo IN (?)',
201
                [[]],
202
                [Connection::PARAM_INT_ARRAY],
203
                'SELECT * FROM Foo WHERE foo IN (NULL)',
204
                [],
205
                [],
206
            ],
207
            // Positional: Empty "str" array DDC-1978
208
            [
209
                'SELECT * FROM Foo WHERE foo IN (?)',
210
                [[]],
211
                [Connection::PARAM_STR_ARRAY],
212
                'SELECT * FROM Foo WHERE foo IN (NULL)',
213
                [],
214
                [],
215
            ],
216
            // Positional: explicit keys for params and types
217
            [
218
                'SELECT * FROM Foo WHERE foo = ? AND bar = ? AND baz = ?',
219
                [1 => 'bar', 2 => 'baz', 0 => 1],
220
                [2 => ParameterType::STRING, 1 => ParameterType::STRING],
221
                'SELECT * FROM Foo WHERE foo = ? AND bar = ? AND baz = ?',
222
                [1 => 'bar', 0 => 1, 2 => 'baz'],
223
                [1 => ParameterType::STRING, 2 => ParameterType::STRING],
224
            ],
225
            // Positional: explicit keys for array params and array types
226
            [
227
                'SELECT * FROM Foo WHERE foo IN (?) AND bar IN (?) AND baz = ?',
228
                [1 => ['bar1', 'bar2'], 2 => true, 0 => [1, 2, 3]],
229
                [2 => ParameterType::BOOLEAN, 1 => Connection::PARAM_STR_ARRAY, 0 => Connection::PARAM_INT_ARRAY],
230
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?) AND bar IN (?, ?) AND baz = ?',
231
                [1, 2, 3, 'bar1', 'bar2', true],
232
                [
233
                    ParameterType::INTEGER,
234
                    ParameterType::INTEGER,
235
                    ParameterType::INTEGER,
236
                    ParameterType::STRING,
237
                    ParameterType::STRING,
238
                    ParameterType::BOOLEAN,
239
                ],
240
            ],
241
            // Positional starts from 1: One non-list before and one after list-needle
242
            [
243
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?) AND baz = ? AND foo IN (?)',
244
                [1 => 1, 2 => [1, 2, 3], 3 => 4, 4 => [5, 6]],
245
                [
246
                    1 => ParameterType::INTEGER,
247
                    2 => Connection::PARAM_INT_ARRAY,
248
                    3 => ParameterType::INTEGER,
249
                    4 => Connection::PARAM_INT_ARRAY,
250
                ],
251
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ? AND foo IN (?, ?)',
252
                [1, 1, 2, 3, 4, 5, 6],
253
                [
254
                    ParameterType::INTEGER,
255
                    ParameterType::INTEGER,
256
                    ParameterType::INTEGER,
257
                    ParameterType::INTEGER,
258
                    ParameterType::INTEGER,
259
                    ParameterType::INTEGER,
260
                    ParameterType::INTEGER,
261
                ],
262
            ],
263
            //  Named parameters : Very simple with param int
264
            [
265
                'SELECT * FROM Foo WHERE foo = :foo',
266
                ['foo' => 1],
267
                ['foo' => ParameterType::INTEGER],
268
                'SELECT * FROM Foo WHERE foo = ?',
269
                [1],
270
                [ParameterType::INTEGER],
271
            ],
272
273
             //  Named parameters : Very simple with param int and string
274
            [
275
                'SELECT * FROM Foo WHERE foo = :foo AND bar = :bar',
276
                ['bar' => 'Some String','foo' => 1],
277
                ['foo' => ParameterType::INTEGER, 'bar' => ParameterType::STRING],
278
                'SELECT * FROM Foo WHERE foo = ? AND bar = ?',
279
                [1,'Some String'],
280
                [ParameterType::INTEGER, ParameterType::STRING],
281
            ],
282
            //  Named parameters : Very simple with one needle
283
            [
284
                'SELECT * FROM Foo WHERE foo IN (:foo)',
285
                ['foo' => [1, 2, 3]],
286
                ['foo' => Connection::PARAM_INT_ARRAY],
287
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
288
                [1, 2, 3],
289
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
290
            ],
291
            // Named parameters: One non-list before d one after list-needle
292
            [
293
                'SELECT * FROM Foo WHERE foo = :foo AND bar IN (:bar)',
294
                ['foo' => 'string', 'bar' => [1, 2, 3]],
295
                ['foo' => ParameterType::STRING, 'bar' => Connection::PARAM_INT_ARRAY],
296
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
297
                ['string', 1, 2, 3],
298
                [ParameterType::STRING, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
299
            ],
300
            // Named parameters: One non-list after list-needle
301
            [
302
                'SELECT * FROM Foo WHERE bar IN (:bar) AND baz = :baz',
303
                ['bar' => [1, 2, 3], 'baz' => 'foo'],
304
                ['bar' => Connection::PARAM_INT_ARRAY, 'baz' => ParameterType::STRING],
305
                'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?',
306
                [1, 2, 3, 'foo'],
307
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
308
            ],
309
            // Named parameters: One non-list before and one after list-needle
310
            [
311
                'SELECT * FROM Foo WHERE foo = :foo AND bar IN (:bar) AND baz = :baz',
312
                ['bar' => [1, 2, 3],'foo' => 1, 'baz' => 4],
313
                ['bar' => Connection::PARAM_INT_ARRAY, 'foo' => ParameterType::INTEGER, 'baz' => ParameterType::INTEGER],
314
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?',
315
                [1, 1, 2, 3, 4],
316
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
317
            ],
318
            // Named parameters: Two lists
319
            [
320
                'SELECT * FROM Foo WHERE foo IN (:a, :b)',
321
                ['b' => [4, 5],'a' => [1, 2, 3]],
322
                ['a' => Connection::PARAM_INT_ARRAY, 'b' => Connection::PARAM_INT_ARRAY],
323
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)',
324
                [1, 2, 3, 4, 5],
325
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
326
            ],
327
            //  Named parameters : With the same name arg type string
328
            [
329
                'SELECT * FROM Foo WHERE foo <> :arg AND bar = :arg',
330
                ['arg' => 'Some String'],
331
                ['arg' => ParameterType::STRING],
332
                'SELECT * FROM Foo WHERE foo <> ? AND bar = ?',
333
                ['Some String','Some String'],
334
                [ParameterType::STRING,ParameterType::STRING],
335
            ],
336
             //  Named parameters : With the same name arg
337
            [
338
                'SELECT * FROM Foo WHERE foo IN (:arg) AND NOT bar IN (:arg)',
339
                ['arg' => [1, 2, 3]],
340
                ['arg' => Connection::PARAM_INT_ARRAY],
341
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?) AND NOT bar IN (?, ?, ?)',
342
                [1, 2, 3, 1, 2, 3],
343
                [ParameterType::INTEGER,ParameterType::INTEGER, ParameterType::INTEGER,ParameterType::INTEGER,ParameterType::INTEGER, ParameterType::INTEGER],
344
            ],
345
346
             //  Named parameters : Same name, other name in between DBAL-299
347
            [
348
                'SELECT * FROM Foo WHERE (:foo = 2) AND (:bar = 3) AND (:foo = 2)',
349
                ['foo' => 2,'bar' => 3],
350
                ['foo' => ParameterType::INTEGER,'bar' => ParameterType::INTEGER],
351
                'SELECT * FROM Foo WHERE (? = 2) AND (? = 3) AND (? = 2)',
352
                [2, 3, 2],
353
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
354
            ],
355
             //  Named parameters : Empty "integer" array DDC-1978
356
            [
357
                'SELECT * FROM Foo WHERE foo IN (:foo)',
358
                ['foo' => []],
359
                ['foo' => Connection::PARAM_INT_ARRAY],
360
                'SELECT * FROM Foo WHERE foo IN (NULL)',
361
                [],
362
                [],
363
            ],
364
             //  Named parameters : Two empty "str" array DDC-1978
365
            [
366
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar IN (:bar)',
367
                ['foo' => [], 'bar' => []],
368
                ['foo' => Connection::PARAM_STR_ARRAY, 'bar' => Connection::PARAM_STR_ARRAY],
369
                'SELECT * FROM Foo WHERE foo IN (NULL) OR bar IN (NULL)',
370
                [],
371
                [],
372
            ],
373
            [
374
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar OR baz = :baz',
375
                ['foo' => [1, 2], 'bar' => 'bar', 'baz' => 'baz'],
376
                ['foo' => Connection::PARAM_INT_ARRAY, 'baz' => 'string'],
377
                'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ? OR baz = ?',
378
                [1, 2, 'bar', 'baz'],
379
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING, 'string'],
380
            ],
381
            [
382
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar',
383
                ['foo' => [1, 2], 'bar' => 'bar'],
384
                ['foo' => Connection::PARAM_INT_ARRAY],
385
                'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
386
                [1, 2, 'bar'],
387
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
388
            ],
389
            // Params/types with colons
390
            [
391
                'SELECT * FROM Foo WHERE foo = :foo OR bar = :bar',
392
                [':foo' => 'foo', ':bar' => 'bar'],
393
                [':foo' => ParameterType::INTEGER],
394
                'SELECT * FROM Foo WHERE foo = ? OR bar = ?',
395
                ['foo', 'bar'],
396
                [ParameterType::INTEGER, ParameterType::STRING],
397
            ],
398
            [
399
                'SELECT * FROM Foo WHERE foo = :foo OR bar = :bar',
400
                [':foo' => 'foo', ':bar' => 'bar'],
401
                [':foo' => ParameterType::INTEGER, 'bar' => ParameterType::INTEGER],
402
                'SELECT * FROM Foo WHERE foo = ? OR bar = ?',
403
                ['foo', 'bar'],
404
                [ParameterType::INTEGER, ParameterType::INTEGER],
405
            ],
406
            [
407
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar',
408
                [':foo' => [1, 2], ':bar' => 'bar'],
409
                ['foo' => Connection::PARAM_INT_ARRAY],
410
                'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
411
                [1, 2, 'bar'],
412
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
413
            ],
414
            [
415
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar',
416
                ['foo' => [1, 2], 'bar' => 'bar'],
417
                [':foo' => Connection::PARAM_INT_ARRAY],
418
                'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
419
                [1, 2, 'bar'],
420
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
421
            ],
422
            // DBAL-522 - null valued parameters are not considered
423
            [
424
                'INSERT INTO Foo (foo, bar) values (:foo, :bar)',
425
                ['foo' => 1, 'bar' => null],
426
                [':foo' => ParameterType::INTEGER, ':bar' => ParameterType::NULL],
427
                'INSERT INTO Foo (foo, bar) values (?, ?)',
428
                [1, null],
429
                [ParameterType::INTEGER, ParameterType::NULL],
430
            ],
431
            [
432
                'INSERT INTO Foo (foo, bar) values (?, ?)',
433
                [1, null],
434
                [ParameterType::INTEGER, ParameterType::NULL],
435
                'INSERT INTO Foo (foo, bar) values (?, ?)',
436
                [1, null],
437
                [ParameterType::INTEGER, ParameterType::NULL],
438
            ],
439
            // DBAL-1205 - Escaped single quotes SQL- and C-Style
440
            [
441
                "SELECT * FROM Foo WHERE foo = :foo||''':not_a_param''\\'' OR bar = ''':not_a_param''\\'':bar",
442
                [':foo' => 1, ':bar' => 2],
443
                [':foo' => ParameterType::INTEGER, 'bar' => ParameterType::INTEGER],
444
                'SELECT * FROM Foo WHERE foo = ?||\'\'\':not_a_param\'\'\\\'\' OR bar = \'\'\':not_a_param\'\'\\\'\'?',
445
                [1, 2],
446
                [ParameterType::INTEGER, ParameterType::INTEGER],
447
            ],
448
        ];
449
    }
450
451
    /**
452
     * @param mixed[] $params
453
     * @param mixed[] $types
454
     * @param mixed[] $expectedParams
455
     * @param mixed[] $expectedTypes
456
     *
457
     * @dataProvider dataExpandListParameters
458
     */
459
    public function testExpandListParameters(
460
        string $query,
461
        array $params,
462
        array $types,
463
        string $expectedQuery,
464
        array $expectedParams,
465
        array $expectedTypes
466
    ) : void {
467
        [$query, $params, $types] = SQLParserUtils::expandListParameters($query, $params, $types);
468
469
        self::assertEquals($expectedQuery, $query, 'Query was not rewritten correctly.');
470
        self::assertEquals($expectedParams, $params, 'Params dont match');
471
        self::assertEquals($expectedTypes, $types, 'Types dont match');
472
    }
473
474
    /**
475
     * @return mixed[][]
476
     */
477
    public static function dataQueryWithMissingParameters() : iterable
478
    {
479
        return [
480
            [
481
                'SELECT * FROM foo WHERE bar = :param',
482
                ['other' => 'val'],
483
                [],
484
            ],
485
            [
486
                'SELECT * FROM foo WHERE bar = :param',
487
                [],
488
                [],
489
            ],
490
            [
491
                'SELECT * FROM foo WHERE bar = :param',
492
                [],
493
                ['param' => Connection::PARAM_INT_ARRAY],
494
            ],
495
            [
496
                'SELECT * FROM foo WHERE bar = :param',
497
                [],
498
                [':param' => Connection::PARAM_INT_ARRAY],
499
            ],
500
            [
501
                'SELECT * FROM foo WHERE bar = :param',
502
                [],
503
                ['bar' => Connection::PARAM_INT_ARRAY],
504
            ],
505
            [
506
                'SELECT * FROM foo WHERE bar = :param',
507
                ['bar' => 'value'],
508
                ['bar' => Connection::PARAM_INT_ARRAY],
509
            ],
510
        ];
511
    }
512
513
    /**
514
     * @param mixed[] $params
515
     * @param mixed[] $types
516
     *
517
     * @dataProvider dataQueryWithMissingParameters
518
     */
519
    public function testExceptionIsThrownForMissingParam(string $query, array $params, array $types = []) : void
520
    {
521
        $this->expectException(SQLParserUtilsException::class);
522
        $this->expectExceptionMessage('Parameter "param" is missing.');
523
524
        SQLParserUtils::expandListParameters($query, $params, $types);
525
    }
526
}
527