Skip to content

Commit eb772ff

Browse files
committed
Merge branch '3.10.x' into 4.3.x
* 3.10.x: feat: improve performance of sql parser
2 parents cbd5af3 + 6aca517 commit eb772ff

File tree

2 files changed

+24
-33
lines changed

2 files changed

+24
-33
lines changed

.github/workflows/phpunit-sqlite.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ jobs:
2424
- name: Checkout
2525
uses: actions/checkout@v4
2626

27+
- name: Remove optional dependencies
28+
run: composer remove --no-update --dev doctrine/cache
29+
if: ${{ matrix.dependency-versions == 'minimal' }}
30+
2731
- name: Install PHP
2832
uses: shivammathur/setup-php@v2
2933
with:

src/SQL/Parser.php

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,9 @@
99
use Doctrine\DBAL\SQL\Parser\Visitor;
1010

1111
use function array_merge;
12-
use function assert;
13-
use function current;
1412
use function implode;
15-
use function key;
16-
use function next;
1713
use function preg_last_error;
1814
use function preg_match;
19-
use function reset;
2015
use function sprintf;
2116
use function strlen;
2217

@@ -47,6 +42,7 @@ final class Parser
4742
private const OTHER = '[^' . self::SPECIAL_CHARS . ']+';
4843

4944
private readonly string $sqlPattern;
45+
private readonly string $tokenPattern;
5046

5147
public function __construct(bool $mySQLStringEscaping)
5248
{
@@ -71,7 +67,12 @@ public function __construct(bool $mySQLStringEscaping)
7167
self::OTHER,
7268
]);
7369

74-
$this->sqlPattern = sprintf('(%s)', implode('|', $patterns));
70+
$this->sqlPattern = sprintf('(%s)', implode('|', $patterns));
71+
$this->tokenPattern = '~\\G'
72+
. '(?P<named>' . self::NAMED_PARAMETER . ')'
73+
. '|(?P<positional>' . self::POSITIONAL_PARAMETER . ')'
74+
. '|(?P<other>' . $this->sqlPattern . '|' . self::SPECIAL . ')'
75+
. '~s';
7576
}
7677

7778
/**
@@ -81,40 +82,26 @@ public function __construct(bool $mySQLStringEscaping)
8182
*/
8283
public function parse(string $sql, Visitor $visitor): void
8384
{
84-
/** @var array<string,callable> $patterns */
85-
$patterns = [
86-
self::NAMED_PARAMETER => static function (string $sql) use ($visitor): void {
87-
$visitor->acceptNamedParameter($sql);
88-
},
89-
self::POSITIONAL_PARAMETER => static function (string $sql) use ($visitor): void {
90-
$visitor->acceptPositionalParameter($sql);
91-
},
92-
$this->sqlPattern => static function (string $sql) use ($visitor): void {
93-
$visitor->acceptOther($sql);
94-
},
95-
self::SPECIAL => static function (string $sql) use ($visitor): void {
96-
$visitor->acceptOther($sql);
97-
},
98-
];
99-
10085
$offset = 0;
101-
102-
while (($handler = current($patterns)) !== false) {
103-
if (preg_match('~\G' . key($patterns) . '~s', $sql, $matches, 0, $offset) === 1) {
104-
$handler($matches[0]);
105-
reset($patterns);
106-
107-
$offset += strlen($matches[0]);
86+
$length = strlen($sql);
87+
while ($offset < $length) {
88+
if (preg_match($this->tokenPattern, $sql, $matches, 0, $offset) === 1) {
89+
$match = $matches[0];
90+
if ($matches['named'] !== '') {
91+
$visitor->acceptNamedParameter($match);
92+
} elseif ($matches['positional'] !== '') {
93+
$visitor->acceptPositionalParameter($match);
94+
} else {
95+
$visitor->acceptOther($match);
96+
}
97+
98+
$offset += strlen($match);
10899
} elseif (preg_last_error() !== PREG_NO_ERROR) {
109100
// @codeCoverageIgnoreStart
110101
throw RegularExpressionError::new();
111102
// @codeCoverageIgnoreEnd
112-
} else {
113-
next($patterns);
114103
}
115104
}
116-
117-
assert($offset === strlen($sql));
118105
}
119106

120107
private function getMySQLStringLiteralPattern(string $delimiter): string

0 commit comments

Comments
 (0)