Skip to content

Commit 4841177

Browse files
committed
perf(database): avoid rewriting single SQL spaces
Only replace whitespace when a non-space whitespace character or repeated spaces are present. Keep trimming unconditional and retain the existing replacement semantics for formatted queries. Bun 1.4.1 focused repeat medians: prepared SQL 344 ns to 171 ns, long single- spaced SQL 6.44 us to 5.18 us, formatted SQL 369 ns to 374 ns, Unicode whitespace 379 ns to 377 ns. Developer laptop results; full query/log timings were mixed, so this does not claim an HTTP throughput improvement. Verified 340927 generated cases, 1823 tests, framework types, and lint. Final HTTP validation returned 139250 successful requests with exactly 139250 persisted logs. Add mixed/Unicode whitespace cases and a multiline persisted query assertion.
1 parent e679e1b commit 4841177

3 files changed

Lines changed: 13 additions & 3 deletions

File tree

storage/framework/core/database/src/query-parser.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,11 @@ export function normalizeQuery(sql: string): string {
145145
// Replace boolean and NULL literals in one pass.
146146
normalizedSql = normalizedSql.replace(/\b(?:true|false|null)\b/gi, '?')
147147

148-
// Normalize whitespace
149-
normalizedSql = normalizedSql.replace(/\s+/g, ' ').trim()
148+
// Most generated SQL already uses single spaces. Skip rebuilding it unless
149+
// another whitespace character or repeated spaces need normalization.
150+
if (/[^\S ]| {2}/.test(normalizedSql))
151+
normalizedSql = normalizedSql.replace(/\s+/g, ' ')
152+
normalizedSql = normalizedSql.trim()
150153

151154
return normalizedSql
152155
}

storage/framework/core/database/tests/fixtures/query-logger-dispatch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ try {
106106
await queryWithDiagnostics('query_logger_after_failed_store')
107107

108108
const { logQuery } = await import('../../src/query-logger')
109-
const literalQuery = "SELECT 42 AS total, 'Ada 123' AS name, TRUE AS active, NULL AS missing FROM query_logger_fixture"
109+
const literalQuery = " \tSELECT 42 AS total,\n 'Ada 123' AS name, TRUE AS active, NULL AS missing FROM query_logger_fixture"
110110
await logQuery({ query: { sql: literalQuery }, queryDurationMillis: 1 })
111111
const normalized = await db.unsafe('SELECT normalized_query FROM query_logs WHERE query = ?', [literalQuery]).execute()
112112
if (normalized.length !== 1 || normalized[0]?.normalized_query !== 'SELECT ? AS total, ? AS name, ? AS active, ? AS missing FROM query_logger_fixture')

storage/framework/core/database/tests/query-normalization.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ import { normalizeQuery, parseQuery } from '../src/query-parser'
33

44
describe('query normalization', () => {
55
it.each([
6+
[' SELECT id FROM items ', 'SELECT id FROM items'],
7+
[' SELECT id, name FROM items ', 'SELECT id, name FROM items'],
8+
[' \tSELECT\r\n id,\vname\fFROM items\t WHERE id = 1 ', 'SELECT id, name FROM items WHERE id = ?'],
9+
['SELECT \t id \u00A0 FROM\u2003items', 'SELECT id FROM items'],
10+
['\uFEFFSELECT\u00A0id\u2028FROM\u2029items\u3000', 'SELECT id FROM items'],
11+
[' \u0085SELECT\u180Eid\u200BFROM\u2060items ', '\u0085SELECT\u180Eid\u200BFROM\u2060items'],
12+
[' \t\r\n\v\f\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF', ''],
613
["SELECT '123'456, \"789\"10, a'12'34, _\"56\"78", 'SELECT ??, ??, a??, _??'],
714
["SELECT '12'x34, \"56\"_78, a12'34', _56\"78\"", 'SELECT ?x34, ?_78, a12?, _56?'],
815
["SELECT '12''34, \"56\"\"78", "SELECT ?'?, ?\"?"],

0 commit comments

Comments
 (0)