Skip to content

Commit d6194b5

Browse files
committed
Improved query splitter to handle semicolons in quoted blocks
1 parent e55d9c1 commit d6194b5

File tree

3 files changed

+40
-3
lines changed

3 files changed

+40
-3
lines changed

src/Instrumentation/MySqli/src/MySqliTracker.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,22 @@ private function splitQueries(string $sql)
171171
$blockDepth = 0;
172172
$tokens = preg_split('/(;)/', $sql, -1, PREG_SPLIT_DELIM_CAPTURE); // Keep semicolons as separate tokens
173173

174+
$singleQuotes = 0;
175+
$doubleQuotes = 0;
176+
174177
foreach ($tokens as $token) {
175178
if ($token === '') {
176179
continue;
177180
}
178181

179-
if ($blockDepth === 0) {
180-
$token = trim($token);
182+
$tokenLen = strlen($token);
183+
for ($i = 0; $i < $tokenLen; $i++) {
184+
if ($token[$i] == "'" && ($token[$i - 1] ?? false) !== '\\') {
185+
$singleQuotes++;
186+
}
187+
if ($token[$i] == '"' && ($token[$i - 1] ?? false) !== '\\') {
188+
$doubleQuotes++;
189+
}
181190
}
182191

183192
$buffer .= $token;
@@ -192,13 +201,20 @@ private function splitQueries(string $sql)
192201
$blockDepth--;
193202
}
194203

204+
// we're somewhere inside qoutes
205+
if (($singleQuotes % 2) != 0 || ($doubleQuotes % 2) != 0) {
206+
continue;
207+
}
208+
195209
// If we are outside a block and encounter a semicolon, split the query
196210
if ($blockDepth === 0 && $token === ';') {
197211
$trimmedQuery = trim($buffer);
198212
if ($trimmedQuery !== ';') { // Ignore empty queries
199213
$queries[] = $trimmedQuery;
200214
}
201215
$buffer = '';
216+
$singleQuotes = 0;
217+
$doubleQuotes = 0;
202218
}
203219
}
204220

src/Instrumentation/MySqli/tests/Integration/MySqliInstrumentationTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1374,7 +1374,6 @@ public function test_mysqli_select_db(): void
13741374
]);
13751375
}
13761376

1377-
13781377
mysqli_select_db($mysqli, $this->database);
13791378

13801379
$res = $mysqli->query('SELECT * FROM users;');

src/Instrumentation/MySqli/tests/Integration/MySqliTrackerTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,28 @@ public function test_split_queries_whitespaces(): void
5555
$this->assertEquals($expected, $result);
5656
}
5757

58+
public function test_split_queries_with_binding(): void
59+
{
60+
$query = '
61+
INSERT INTO `tableA` (columnA) VALUES (\'hello; world\'); SELECT LAST_INSERT_ID();
62+
INSERT INTO `tableA` (columnA) VALUES (?); SELECT LAST_INSERT_ID();
63+
INSERT INTO `tableA` (columnA) VALUES (\'hel\\\'lo; \\"world\'); SELECT LAST_INSERT_ID();
64+
';
65+
66+
$result = $this->splitQueries->invoke($this->tracker, $query);
67+
68+
$expected = [
69+
"INSERT INTO `tableA` (columnA) VALUES ('hello; world');",
70+
'SELECT LAST_INSERT_ID();',
71+
'INSERT INTO `tableA` (columnA) VALUES (?);',
72+
'SELECT LAST_INSERT_ID();',
73+
"INSERT INTO `tableA` (columnA) VALUES ('hel\'lo; \\\"world');",
74+
'SELECT LAST_INSERT_ID();',
75+
];
76+
77+
$this->assertEquals($expected, $result);
78+
}
79+
5880
public function test_split_queries_with_begin_end(): void
5981
{
6082
$query = "

0 commit comments

Comments
 (0)