| 4 | * Shared utility for applying row limits to SELECT queries only using database-native LIMIT clauses |
| 5 | */ |
| 6 | export class SQLRowLimiter { |
| 7 | /** |
| 8 | * Check if a SQL statement is a SELECT query that can benefit from row limiting |
| 9 | * Only handles SELECT queries |
| 10 | */ |
| 11 | static isSelectQuery(sql: string): boolean { |
| 12 | const trimmed = sql.trim().toLowerCase(); |
| 13 | return trimmed.startsWith('select'); |
| 14 | } |
| 15 | |
| 16 | /** |
| 17 | * Check if a SQL statement already has a LIMIT clause. |
| 18 | * Strips comments and string literals first to avoid false positives. |
| 19 | */ |
| 20 | static hasLimitClause(sql: string): boolean { |
| 21 | // Strip comments and strings to avoid matching LIMIT inside them |
| 22 | const cleanedSQL = stripCommentsAndStrings(sql); |
| 23 | // Detect LIMIT clause - handles literal numbers and parameter placeholders ($1, ?, @p1) |
| 24 | const limitRegex = /\blimit\s+(?:\d+|\$\d+|\?|@p\d+)/i; |
| 25 | return limitRegex.test(cleanedSQL); |
| 26 | } |
| 27 | |
| 28 | /** |
| 29 | * Check if a SQL statement already has a TOP clause (SQL Server). |
| 30 | * Strips comments and string literals first to avoid false positives. |
| 31 | */ |
| 32 | static hasTopClause(sql: string): boolean { |
| 33 | // Strip comments and strings to avoid matching TOP inside them |
| 34 | const cleanedSQL = stripCommentsAndStrings(sql); |
| 35 | // Simple regex to detect TOP clause - handles most common cases |
| 36 | const topRegex = /\bselect\s+top\s+\d+/i; |
| 37 | return topRegex.test(cleanedSQL); |
| 38 | } |
| 39 | |
| 40 | /** |
| 41 | * Extract existing LIMIT value from SQL if present. |
| 42 | * Strips comments and string literals first to avoid false positives. |
| 43 | */ |
| 44 | static extractLimitValue(sql: string): number | null { |
| 45 | // Strip comments and strings to avoid matching LIMIT inside them |
| 46 | const cleanedSQL = stripCommentsAndStrings(sql); |
| 47 | const limitMatch = cleanedSQL.match(/\blimit\s+(\d+)/i); |
| 48 | if (limitMatch) { |
| 49 | return parseInt(limitMatch[1], 10); |
| 50 | } |
| 51 | return null; |
| 52 | } |
| 53 | |
| 54 | /** |
| 55 | * Extract existing TOP value from SQL if present (SQL Server). |
| 56 | * Strips comments and string literals first to avoid false positives. |
| 57 | */ |
| 58 | static extractTopValue(sql: string): number | null { |
| 59 | // Strip comments and strings to avoid matching TOP inside them |
| 60 | const cleanedSQL = stripCommentsAndStrings(sql); |
| 61 | const topMatch = cleanedSQL.match(/\bselect\s+top\s+(\d+)/i); |
| 62 | if (topMatch) { |
| 63 | return parseInt(topMatch[1], 10); |
nothing calls this directly
no outgoing calls
no test coverage detected