Performance
The parser is a pure-PHP port with a flat token store, cached dispatch, and a fast path through the operator levels. Against pure-Python sqlglot it is 2.5–4.4x faster on average (geometric mean) and faster on every benchmark query. With JIT it is level with the compiled sqlglot[c] build.
Benchmarks
Best of three rounds on sqlglot's own benchmark queries (PHP 8.4, opcache on; sqlglot 30.21.0 on Python 3.12). Run them yourself with composer bench.
Parse — selected rows (see README for the full table):
| Query |
sqlglot |
sqlglot (compiled) |
this package |
+ JIT |
| tpch |
2.5 ms |
676 µs |
793 µs |
3.3 ms |
| short |
175 µs |
47 µs |
79 µs |
112 µs |
| large_in |
488 ms |
116 ms |
156 ms |
90.8 ms |
| many_joins |
10.1 ms |
2.9 ms |
4.0 ms |
2.6 ms |
With JIT enabled this package is faster than the compiled build on most large queries.
Production checklist
- Turn on OPcache (and
opcache.enable_cli=1 for queue workers and Octane).
- JIT (
opcache.jit=tracing, opcache.jit_buffer_size=128M) roughly halves time for large statements. Small statements gain nothing — it only pays off for long SQL or long-running processes.
- Preload the parser core to skip loading about 1,100 classes per request (first transpile dropped from ~0.9 ms to ~0.45 ms):
opcache.preload=/path/to/app/vendor/kwasii/laravel-sql-parser/preload.php
opcache.preload_user=www-data
Preloading is unavailable on Windows; the package works the same without it.
- Optimize the autoloader:
composer dump-autoload --classmap-authoritative.
- Cache repeated work:
memo for Octane/queue workers, cache for long statements.
Memo and cache
// config/laravel-sql-parser.php
'memo' => ['enabled' => true, 'size' => 1000],
'cache' => [
'enabled' => true,
'store' => 'redis',
'ttl' => 86400,
'prefix' => 'sql-parser:',
'min_length' => 2000,
],
memo keeps recent results in memory for the life of the process. Ideal for Octane and queue workers; useless for classic PHP-FPM short requests.
cache stores results in a Laravel cache store. A cache round trip can cost more than transpiling short SQL, so statements shorter than min_length bytes skip it.
- Cache keys include the package version, so upgrades never serve stale output.
Benchmark it
composer bench
composer bench -- --subject=transpile