SQLParserUtilsTest.php 24.2 KB
Newer Older
1 2 3 4 5
<?php

namespace Doctrine\Tests\DBAL;

use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\ParameterType;
7
use Doctrine\DBAL\SQLParserUtils;
Sergei Morozov's avatar
Sergei Morozov committed
8
use Doctrine\DBAL\SQLParserUtilsException;
Sergei Morozov's avatar
Sergei Morozov committed
9
use Doctrine\Tests\DbalTestCase;
10

Sergei Morozov's avatar
Sergei Morozov committed
11
class SQLParserUtilsTest extends DbalTestCase
12
{
13 14 15
    /**
     * @return mixed[][]
     */
16
    public static function dataGetPlaceholderPositions(): iterable
17
    {
Sergei Morozov's avatar
Sergei Morozov committed
18
        return [
19
            // none
Sergei Morozov's avatar
Sergei Morozov committed
20 21
            ['SELECT * FROM Foo', true, []],
            ['SELECT * FROM Foo', false, []],
22

23
            // Positionals
Sergei Morozov's avatar
Sergei Morozov committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
            ['SELECT ?', true, [7]],
            ['SELECT * FROM Foo WHERE bar IN (?, ?, ?)', true, [32, 35, 38]],
            ['SELECT ? FROM ?', true, [7, 14]],
            ['SELECT "?" FROM foo', true, []],
            ["SELECT '?' FROM foo", true, []],
            ['SELECT `?` FROM foo', true, []], // Ticket DBAL-552
            ['SELECT [?] FROM foo', true, []],
            ["SELECT 'Doctrine\DBAL?' FROM foo", true, []], // Ticket DBAL-558
            ['SELECT "Doctrine\DBAL?" FROM foo', true, []], // Ticket DBAL-558
            ['SELECT `Doctrine\DBAL?` FROM foo', true, []], // Ticket DBAL-558
            ['SELECT [Doctrine\DBAL?] FROM foo', true, []], // Ticket DBAL-558
            ['SELECT "?" FROM foo WHERE bar = ?', true, [32]],
            ["SELECT '?' FROM foo WHERE bar = ?", true, [32]],
            ['SELECT `?` FROM foo WHERE bar = ?', true, [32]], // Ticket DBAL-552
            ['SELECT [?] FROM foo WHERE bar = ?', true, [32]],
            ['SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, ARRAY[?])', true, [56]], // Ticket GH-2295
            ["SELECT 'Doctrine\DBAL?' FROM foo WHERE bar = ?", true, [45]], // Ticket DBAL-558
            ['SELECT "Doctrine\DBAL?" FROM foo WHERE bar = ?', true, [45]], // Ticket DBAL-558
            ['SELECT `Doctrine\DBAL?` FROM foo WHERE bar = ?', true, [45]], // Ticket DBAL-558
            ['SELECT [Doctrine\DBAL?] FROM foo WHERE bar = ?', true, [45]], // Ticket DBAL-558
            ["SELECT * FROM FOO WHERE bar = 'it\\'s a trap? \\\\' OR bar = ?\nAND baz = \"\\\"quote\\\" me on it? \\\\\" OR baz = ?", true, [58, 104]],
            ['SELECT * FROM foo WHERE foo = ? AND bar = ?', true, [1 => 42, 0 => 30]], // explicit keys
46

47
            // named
Sergei Morozov's avatar
Sergei Morozov committed
48 49 50 51 52 53 54 55 56 57 58
            ['SELECT :foo FROM :bar', false, [7 => 'foo', 17 => 'bar']],
            ['SELECT * FROM Foo WHERE bar IN (:name1, :name2)', false, [32 => 'name1', 40 => 'name2']],
            ['SELECT ":foo" FROM Foo WHERE bar IN (:name1, :name2)', false, [37 => 'name1', 45 => 'name2']],
            ["SELECT ':foo' FROM Foo WHERE bar IN (:name1, :name2)", false, [37 => 'name1', 45 => 'name2']],
            ['SELECT :foo_id', false, [7 => 'foo_id']], // Ticket DBAL-231
            ['SELECT @rank := 1', false, []], // Ticket DBAL-398
            ['SELECT @rank := 1 AS rank, :foo AS foo FROM :bar', false, [27 => 'foo', 44 => 'bar']], // Ticket DBAL-398
            ['SELECT * FROM Foo WHERE bar > :start_date AND baz > :start_date', false, [30 => 'start_date', 52 => 'start_date']], // Ticket GH-113
            ['SELECT foo::date as date FROM Foo WHERE bar > :start_date AND baz > :start_date', false, [46 => 'start_date', 68 => 'start_date']], // Ticket GH-259
            ['SELECT `d.ns:col_name` FROM my_table d WHERE `d.date` >= :param1', false, [57 => 'param1']], // Ticket DBAL-552
            ['SELECT [d.ns:col_name] FROM my_table d WHERE [d.date] >= :param1', false, [57 => 'param1']], // Ticket DBAL-552
59 60
            ['SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, ARRAY[:foo])', false, [56 => 'foo']], // Ticket GH-2295
            ['SELECT * FROM foo WHERE jsonb_exists_any(foo.bar, array[:foo])', false, [56 => 'foo']],
61 62 63 64
            ['SELECT table.column1, ARRAY[\'3\'] FROM schema.table table WHERE table.f1 = :foo AND ARRAY[\'3\']', false, [74 => 'foo']],
            ['SELECT table.column1, ARRAY[\'3\']::integer[] FROM schema.table table WHERE table.f1 = :foo AND ARRAY[\'3\']::integer[]', false, [85 => 'foo']],
            ['SELECT table.column1, ARRAY[:foo] FROM schema.table table WHERE table.f1 = :bar AND ARRAY[\'3\']', false, [28 => 'foo', 75 => 'bar']],
            ['SELECT table.column1, ARRAY[:foo]::integer[] FROM schema.table table WHERE table.f1 = :bar AND ARRAY[\'3\']::integer[]', false, [28 => 'foo', 86 => 'bar']],
Sergei Morozov's avatar
Sergei Morozov committed
65 66
            [
                <<<'SQLDATA'
67 68 69 70 71 72
SELECT * FROM foo WHERE 
bar = ':not_a_param1 ''":not_a_param2"'''
OR bar=:a_param1
OR bar=:a_param2||':not_a_param3'
OR bar=':not_a_param4 '':not_a_param5'' :not_a_param6'
OR bar=''
andreas's avatar
andreas committed
73
OR bar=:a_param3
74
SQLDATA
Grégoire Paris's avatar
Grégoire Paris committed
75
,
Sergei Morozov's avatar
Sergei Morozov committed
76 77 78 79 80 81 82 83 84 85 86 87
                false,
                [
                    74 => 'a_param1',
                    91 => 'a_param2',
                    190 => 'a_param3',
                ],
            ],
            ["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", false, [121 => 'condition_0', 174 => 'condition_1']],
            ['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', false, [121 => 'condition_0', 174 => 'condition_1']],
            ['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', false, [121 => 'condition_0', 174 => 'condition_1']],
            ['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', false, [121 => 'condition_0', 174 => 'condition_1']],
            ['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', false, [121 => 'condition_0', 174 => 'condition_1']],
88
            ["SELECT * FROM Foo WHERE (foo.bar LIKE :condition_0 ESCAPE '\') AND (foo.baz = :condition_1) AND (foo.bak LIKE :condition_2 ESCAPE '\')", false, [38 => 'condition_0', 78 => 'condition_1', 110 => 'condition_2']],
Sergei Morozov's avatar
Sergei Morozov committed
89
        ];
90
    }
91

92
    /**
93 94
     * @param int[] $expectedParamPos
     *
95 96
     * @dataProvider dataGetPlaceholderPositions
     */
97
    public function testGetPlaceholderPositions(string $query, bool $isPositional, array $expectedParamPos): void
98 99
    {
        $actualParamPos = SQLParserUtils::getPlaceholderPositions($query, $isPositional);
100
        self::assertEquals($expectedParamPos, $actualParamPos);
101
    }
102

103 104 105
    /**
     * @return mixed[][]
     */
106
    public static function dataExpandListParameters(): iterable
107
    {
Sergei Morozov's avatar
Sergei Morozov committed
108
        return [
109
            'Positional: Very simple with one needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
110 111 112
                'SELECT * FROM Foo WHERE foo IN (?)',
                [[1, 2, 3]],
                [Connection::PARAM_INT_ARRAY],
113
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
114 115 116
                [1, 2, 3],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
            ],
117
            'Positional: One non-list before d one after list-needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
118 119 120
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?)',
                ['string', [1, 2, 3]],
                [ParameterType::STRING, Connection::PARAM_INT_ARRAY],
121
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
122 123 124
                ['string', 1, 2, 3],
                [ParameterType::STRING, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
            ],
125
            'Positional: One non-list after list-needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
126 127 128
                'SELECT * FROM Foo WHERE bar IN (?) AND baz = ?',
                [[1, 2, 3], 'foo'],
                [Connection::PARAM_INT_ARRAY, ParameterType::STRING],
129
                'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?',
Sergei Morozov's avatar
Sergei Morozov committed
130 131 132
                [1, 2, 3, 'foo'],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
            ],
133
            'Positional: One non-list before and one after list-needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
134 135 136
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?) AND baz = ?',
                [1, [1, 2, 3], 4],
                [ParameterType::INTEGER, Connection::PARAM_INT_ARRAY, ParameterType::INTEGER],
137
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?',
Sergei Morozov's avatar
Sergei Morozov committed
138 139
                [1, 1, 2, 3, 4],
                [
140 141 142 143 144
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
Sergei Morozov's avatar
Sergei Morozov committed
145 146
                ],
            ],
147
            'Positional: Two lists' => [
Sergei Morozov's avatar
Sergei Morozov committed
148 149 150
                'SELECT * FROM Foo WHERE foo IN (?, ?)',
                [[1, 2, 3], [4, 5]],
                [Connection::PARAM_INT_ARRAY, Connection::PARAM_INT_ARRAY],
151
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
152 153
                [1, 2, 3, 4, 5],
                [
154 155 156 157 158
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
Sergei Morozov's avatar
Sergei Morozov committed
159 160
                ],
            ],
161
            'Positional: Empty "integer" array (DDC-1978)' => [
Sergei Morozov's avatar
Sergei Morozov committed
162 163 164
                'SELECT * FROM Foo WHERE foo IN (?)',
                [[]],
                [Connection::PARAM_INT_ARRAY],
165
                'SELECT * FROM Foo WHERE foo IN (NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
166 167 168
                [],
                [],
            ],
169
            'Positional: Empty "str" array (DDC-1978)' => [
Sergei Morozov's avatar
Sergei Morozov committed
170 171 172
                'SELECT * FROM Foo WHERE foo IN (?)',
                [[]],
                [Connection::PARAM_STR_ARRAY],
173
                'SELECT * FROM Foo WHERE foo IN (NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
174 175 176
                [],
                [],
            ],
177
            'Positional: explicit keys for params and types' => [
178
                'SELECT * FROM Foo WHERE foo = ? AND bar = ? AND baz = ?',
Sergei Morozov's avatar
Sergei Morozov committed
179 180 181 182
                [1 => 'bar', 2 => 'baz', 0 => 1],
                [2 => ParameterType::STRING, 1 => ParameterType::STRING],
                'SELECT * FROM Foo WHERE foo = ? AND bar = ? AND baz = ?',
                [1 => 'bar', 0 => 1, 2 => 'baz'],
183
                [1 => ParameterType::STRING, 2 => ParameterType::STRING, 0 => null],
Sergei Morozov's avatar
Sergei Morozov committed
184
            ],
185
            'Positional: explicit keys for array params and array types' => [
Sergei Morozov's avatar
Sergei Morozov committed
186 187 188
                'SELECT * FROM Foo WHERE foo IN (?) AND bar IN (?) AND baz = ?',
                [1 => ['bar1', 'bar2'], 2 => true, 0 => [1, 2, 3]],
                [2 => ParameterType::BOOLEAN, 1 => Connection::PARAM_STR_ARRAY, 0 => Connection::PARAM_INT_ARRAY],
189
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?) AND bar IN (?, ?) AND baz = ?',
Sergei Morozov's avatar
Sergei Morozov committed
190 191
                [1, 2, 3, 'bar1', 'bar2', true],
                [
192 193 194 195 196 197
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::STRING,
                    ParameterType::STRING,
                    ParameterType::BOOLEAN,
Sergei Morozov's avatar
Sergei Morozov committed
198 199
                ],
            ],
200
            'Positional starts from 1: One non-list before and one after list-needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
201 202 203
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?) AND baz = ? AND foo IN (?)',
                [1 => 1, 2 => [1, 2, 3], 3 => 4, 4 => [5, 6]],
                [
204 205 206 207
                    1 => ParameterType::INTEGER,
                    2 => Connection::PARAM_INT_ARRAY,
                    3 => ParameterType::INTEGER,
                    4 => Connection::PARAM_INT_ARRAY,
Sergei Morozov's avatar
Sergei Morozov committed
208
                ],
209
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ? AND foo IN (?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
210 211
                [1, 1, 2, 3, 4, 5, 6],
                [
212 213 214 215 216 217 218
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
Sergei Morozov's avatar
Sergei Morozov committed
219 220
                ],
            ],
221
            'Named: Very simple with param int' => [
Sergei Morozov's avatar
Sergei Morozov committed
222 223 224
                'SELECT * FROM Foo WHERE foo = :foo',
                ['foo' => 1],
                ['foo' => ParameterType::INTEGER],
225
                'SELECT * FROM Foo WHERE foo = ?',
Sergei Morozov's avatar
Sergei Morozov committed
226 227 228
                [1],
                [ParameterType::INTEGER],
            ],
229
            'Named: Very simple with param int and string' => [
Sergei Morozov's avatar
Sergei Morozov committed
230 231 232
                'SELECT * FROM Foo WHERE foo = :foo AND bar = :bar',
                ['bar' => 'Some String','foo' => 1],
                ['foo' => ParameterType::INTEGER, 'bar' => ParameterType::STRING],
233
                'SELECT * FROM Foo WHERE foo = ? AND bar = ?',
Sergei Morozov's avatar
Sergei Morozov committed
234 235 236
                [1,'Some String'],
                [ParameterType::INTEGER, ParameterType::STRING],
            ],
237
            'Named: Very simple with one needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
238 239 240
                'SELECT * FROM Foo WHERE foo IN (:foo)',
                ['foo' => [1, 2, 3]],
                ['foo' => Connection::PARAM_INT_ARRAY],
241
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
242 243 244
                [1, 2, 3],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
            ],
245
            'Named: One non-list before d one after list-needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
246 247 248
                'SELECT * FROM Foo WHERE foo = :foo AND bar IN (:bar)',
                ['foo' => 'string', 'bar' => [1, 2, 3]],
                ['foo' => ParameterType::STRING, 'bar' => Connection::PARAM_INT_ARRAY],
249
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
250 251 252
                ['string', 1, 2, 3],
                [ParameterType::STRING, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
            ],
253
            'Named: One non-list after list-needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
254 255 256
                'SELECT * FROM Foo WHERE bar IN (:bar) AND baz = :baz',
                ['bar' => [1, 2, 3], 'baz' => 'foo'],
                ['bar' => Connection::PARAM_INT_ARRAY, 'baz' => ParameterType::STRING],
257
                'SELECT * FROM Foo WHERE bar IN (?, ?, ?) AND baz = ?',
Sergei Morozov's avatar
Sergei Morozov committed
258 259 260
                [1, 2, 3, 'foo'],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
            ],
261
            'Named: One non-list before and one after list-needle' => [
Sergei Morozov's avatar
Sergei Morozov committed
262 263
                'SELECT * FROM Foo WHERE foo = :foo AND bar IN (:bar) AND baz = :baz',
                ['bar' => [1, 2, 3],'foo' => 1, 'baz' => 4],
264 265 266 267 268
                [
                    'bar' => Connection::PARAM_INT_ARRAY,
                    'foo' => ParameterType::INTEGER,
                    'baz' => ParameterType::INTEGER,
                ],
269
                'SELECT * FROM Foo WHERE foo = ? AND bar IN (?, ?, ?) AND baz = ?',
Sergei Morozov's avatar
Sergei Morozov committed
270
                [1, 1, 2, 3, 4],
271 272 273 274 275 276 277
                [
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                ],
Sergei Morozov's avatar
Sergei Morozov committed
278
            ],
279
            'Named: Two lists' => [
Sergei Morozov's avatar
Sergei Morozov committed
280 281 282
                'SELECT * FROM Foo WHERE foo IN (:a, :b)',
                ['b' => [4, 5],'a' => [1, 2, 3]],
                ['a' => Connection::PARAM_INT_ARRAY, 'b' => Connection::PARAM_INT_ARRAY],
283
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?, ?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
284
                [1, 2, 3, 4, 5],
285 286 287 288 289 290 291
                [
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                ],
Sergei Morozov's avatar
Sergei Morozov committed
292
            ],
293
            'Named: With the same name arg type string' => [
Sergei Morozov's avatar
Sergei Morozov committed
294 295 296
                'SELECT * FROM Foo WHERE foo <> :arg AND bar = :arg',
                ['arg' => 'Some String'],
                ['arg' => ParameterType::STRING],
297
                'SELECT * FROM Foo WHERE foo <> ? AND bar = ?',
Sergei Morozov's avatar
Sergei Morozov committed
298 299 300
                ['Some String','Some String'],
                [ParameterType::STRING,ParameterType::STRING],
            ],
301
            'Named: With the same name arg' => [
Sergei Morozov's avatar
Sergei Morozov committed
302 303 304
                'SELECT * FROM Foo WHERE foo IN (:arg) AND NOT bar IN (:arg)',
                ['arg' => [1, 2, 3]],
                ['arg' => Connection::PARAM_INT_ARRAY],
305
                'SELECT * FROM Foo WHERE foo IN (?, ?, ?) AND NOT bar IN (?, ?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
306
                [1, 2, 3, 1, 2, 3],
307 308 309 310 311 312 313 314
                [
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                    ParameterType::INTEGER,
                ],
Sergei Morozov's avatar
Sergei Morozov committed
315
            ],
316
            'Named: Same name, other name in between (DBAL-299)' => [
Sergei Morozov's avatar
Sergei Morozov committed
317 318 319
                'SELECT * FROM Foo WHERE (:foo = 2) AND (:bar = 3) AND (:foo = 2)',
                ['foo' => 2,'bar' => 3],
                ['foo' => ParameterType::INTEGER,'bar' => ParameterType::INTEGER],
320
                'SELECT * FROM Foo WHERE (? = 2) AND (? = 3) AND (? = 2)',
Sergei Morozov's avatar
Sergei Morozov committed
321 322 323
                [2, 3, 2],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::INTEGER],
            ],
324
            'Named: Empty "integer" array (DDC-1978)' => [
Sergei Morozov's avatar
Sergei Morozov committed
325 326 327
                'SELECT * FROM Foo WHERE foo IN (:foo)',
                ['foo' => []],
                ['foo' => Connection::PARAM_INT_ARRAY],
328
                'SELECT * FROM Foo WHERE foo IN (NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
329 330 331
                [],
                [],
            ],
332
            'Named: Two empty "str" array (DDC-1978)' => [
Sergei Morozov's avatar
Sergei Morozov committed
333 334 335
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar IN (:bar)',
                ['foo' => [], 'bar' => []],
                ['foo' => Connection::PARAM_STR_ARRAY, 'bar' => Connection::PARAM_STR_ARRAY],
336
                'SELECT * FROM Foo WHERE foo IN (NULL) OR bar IN (NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
337 338 339 340 341 342 343
                [],
                [],
            ],
            [
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar OR baz = :baz',
                ['foo' => [1, 2], 'bar' => 'bar', 'baz' => 'baz'],
                ['foo' => Connection::PARAM_INT_ARRAY, 'baz' => 'string'],
344
                'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ? OR baz = ?',
Sergei Morozov's avatar
Sergei Morozov committed
345 346 347 348 349 350 351
                [1, 2, 'bar', 'baz'],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING, 'string'],
            ],
            [
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar',
                ['foo' => [1, 2], 'bar' => 'bar'],
                ['foo' => Connection::PARAM_INT_ARRAY],
352
                'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
Sergei Morozov's avatar
Sergei Morozov committed
353 354 355
                [1, 2, 'bar'],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
            ],
356
            'Params/types with colons' => [
Sergei Morozov's avatar
Sergei Morozov committed
357 358 359
                'SELECT * FROM Foo WHERE foo = :foo OR bar = :bar',
                [':foo' => 'foo', ':bar' => 'bar'],
                [':foo' => ParameterType::INTEGER],
360
                'SELECT * FROM Foo WHERE foo = ? OR bar = ?',
Sergei Morozov's avatar
Sergei Morozov committed
361 362 363 364 365 366 367
                ['foo', 'bar'],
                [ParameterType::INTEGER, ParameterType::STRING],
            ],
            [
                'SELECT * FROM Foo WHERE foo = :foo OR bar = :bar',
                [':foo' => 'foo', ':bar' => 'bar'],
                [':foo' => ParameterType::INTEGER, 'bar' => ParameterType::INTEGER],
368
                'SELECT * FROM Foo WHERE foo = ? OR bar = ?',
Sergei Morozov's avatar
Sergei Morozov committed
369 370 371 372 373 374 375
                ['foo', 'bar'],
                [ParameterType::INTEGER, ParameterType::INTEGER],
            ],
            [
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar',
                [':foo' => [1, 2], ':bar' => 'bar'],
                ['foo' => Connection::PARAM_INT_ARRAY],
376
                'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
Sergei Morozov's avatar
Sergei Morozov committed
377 378 379 380 381 382 383
                [1, 2, 'bar'],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
            ],
            [
                'SELECT * FROM Foo WHERE foo IN (:foo) OR bar = :bar',
                ['foo' => [1, 2], 'bar' => 'bar'],
                [':foo' => Connection::PARAM_INT_ARRAY],
384
                'SELECT * FROM Foo WHERE foo IN (?, ?) OR bar = ?',
Sergei Morozov's avatar
Sergei Morozov committed
385 386 387
                [1, 2, 'bar'],
                [ParameterType::INTEGER, ParameterType::INTEGER, ParameterType::STRING],
            ],
388
            'Null valued parameters (DBAL-522)' => [
389
                'INSERT INTO Foo (foo, bar) values (:foo, :bar)',
Sergei Morozov's avatar
Sergei Morozov committed
390 391
                ['foo' => 1, 'bar' => null],
                [':foo' => ParameterType::INTEGER, ':bar' => ParameterType::NULL],
392
                'INSERT INTO Foo (foo, bar) values (?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
393 394 395 396
                [1, null],
                [ParameterType::INTEGER, ParameterType::NULL],
            ],
            [
397
                'INSERT INTO Foo (foo, bar) values (?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
398 399
                [1, null],
                [ParameterType::INTEGER, ParameterType::NULL],
400
                'INSERT INTO Foo (foo, bar) values (?, ?)',
Sergei Morozov's avatar
Sergei Morozov committed
401 402 403
                [1, null],
                [ParameterType::INTEGER, ParameterType::NULL],
            ],
404
            'Escaped single quotes SQL- and C-Style (DBAL-1205)' => [
405
                "SELECT * FROM Foo WHERE foo = :foo||''':not_a_param''\\'' OR bar = ''':not_a_param''\\'':bar",
Sergei Morozov's avatar
Sergei Morozov committed
406 407
                [':foo' => 1, ':bar' => 2],
                [':foo' => ParameterType::INTEGER, 'bar' => ParameterType::INTEGER],
408
                'SELECT * FROM Foo WHERE foo = ?||\'\'\':not_a_param\'\'\\\'\' OR bar = \'\'\':not_a_param\'\'\\\'\'?',
Sergei Morozov's avatar
Sergei Morozov committed
409 410 411
                [1, 2],
                [ParameterType::INTEGER, ParameterType::INTEGER],
            ],
412 413 414 415 416 417 418 419
            [
                'SELECT NULL FROM dummy WHERE ? IN (?)',
                ['foo', ['bar', 'baz']],
                [1 => Connection::PARAM_STR_ARRAY],
                'SELECT NULL FROM dummy WHERE ? IN (?, ?)',
                ['foo', 'bar', 'baz'],
                [null, ParameterType::STRING, ParameterType::STRING],
            ],
Sergei Morozov's avatar
Sergei Morozov committed
420
        ];
421
    }
422

423
    /**
424 425 426 427 428
     * @param mixed[] $params
     * @param mixed[] $types
     * @param mixed[] $expectedParams
     * @param mixed[] $expectedTypes
     *
429 430
     * @dataProvider dataExpandListParameters
     */
431 432 433 434 435 436 437
    public function testExpandListParameters(
        string $query,
        array $params,
        array $types,
        string $expectedQuery,
        array $expectedParams,
        array $expectedTypes
438
    ): void {
439
        [$query, $params, $types] = SQLParserUtils::expandListParameters($query, $params, $types);
440

Sergei Morozov's avatar
Sergei Morozov committed
441 442 443
        self::assertEquals($expectedQuery, $query, 'Query was not rewritten correctly.');
        self::assertEquals($expectedParams, $params, 'Params dont match');
        self::assertEquals($expectedTypes, $types, 'Types dont match');
444
    }
445

446 447 448
    /**
     * @return mixed[][]
     */
449
    public static function dataQueryWithMissingParameters(): iterable
450
    {
Sergei Morozov's avatar
Sergei Morozov committed
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
        return [
            [
                'SELECT * FROM foo WHERE bar = :param',
                ['other' => 'val'],
                [],
            ],
            [
                'SELECT * FROM foo WHERE bar = :param',
                [],
                [],
            ],
            [
                'SELECT * FROM foo WHERE bar = :param',
                [],
                ['param' => Connection::PARAM_INT_ARRAY],
            ],
            [
                'SELECT * FROM foo WHERE bar = :param',
                [],
                [':param' => Connection::PARAM_INT_ARRAY],
            ],
            [
                'SELECT * FROM foo WHERE bar = :param',
                [],
                ['bar' => Connection::PARAM_INT_ARRAY],
            ],
            [
                'SELECT * FROM foo WHERE bar = :param',
                ['bar' => 'value'],
                ['bar' => Connection::PARAM_INT_ARRAY],
            ],
        ];
483 484 485
    }

    /**
486 487 488
     * @param mixed[] $params
     * @param mixed[] $types
     *
489 490
     * @dataProvider dataQueryWithMissingParameters
     */
491
    public function testExceptionIsThrownForMissingParam(string $query, array $params, array $types = []): void
492
    {
Sergei Morozov's avatar
Sergei Morozov committed
493
        $this->expectException(SQLParserUtilsException::class);
494
        $this->expectExceptionMessage('Value for :param not found in params array. Params array key should be "param"');
495 496 497

        SQLParserUtils::expandListParameters($query, $params, $types);
    }
498
}