sql-parameters.ts114 lines · main
| 1 | export interface Parameter { |
| 2 | name: string |
| 3 | value: string |
| 4 | defaultValue?: string |
| 5 | type?: string |
| 6 | possibleValues?: string[] |
| 7 | occurrences: number |
| 8 | } |
| 9 | |
| 10 | // [Joshen TODO] We'll want tests for this to ensure that this runs properly |
| 11 | |
| 12 | export const parseParameters = (sql: string | undefined) => { |
| 13 | if (!sql) return [] |
| 14 | |
| 15 | // Parse @set parameter defaults with optional type information |
| 16 | const setParamRegex = /@set\s+(\w+)(?::([^=]+))?\s*=\s*([^;\n]+)/g |
| 17 | const paramDefaults: Record<string, { value: string; type?: string; possibleValues?: string[] }> = |
| 18 | {} |
| 19 | let match |
| 20 | |
| 21 | while ((match = setParamRegex.exec(sql)) !== null) { |
| 22 | const [_, paramName, paramType, paramValue] = match |
| 23 | if (!paramName || !paramValue?.trim()) continue |
| 24 | |
| 25 | const typeInfo = paramType?.trim() |
| 26 | |
| 27 | let type: string | undefined |
| 28 | let possibleValues: string[] | undefined |
| 29 | |
| 30 | if (typeInfo) { |
| 31 | // Handle union types (value1 | value2 | value3) |
| 32 | if (typeInfo.includes('|')) { |
| 33 | possibleValues = typeInfo.split('|').map((v) => v.trim()) |
| 34 | type = 'enum' |
| 35 | } else { |
| 36 | type = typeInfo.trim() |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | paramDefaults[paramName] = { |
| 41 | value: paramValue.trim(), |
| 42 | type, |
| 43 | possibleValues, |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | // Find all :parameter occurrences and count them |
| 48 | const paramRegex = /:(\w+)/g |
| 49 | const paramOccurrences: Record<string, number> = {} |
| 50 | const uniqueParams = new Set<string>() |
| 51 | |
| 52 | while ((match = paramRegex.exec(sql)) !== null) { |
| 53 | const [_, paramName] = match |
| 54 | paramOccurrences[paramName] = (paramOccurrences[paramName] || 0) + 1 |
| 55 | uniqueParams.add(paramName) |
| 56 | } |
| 57 | |
| 58 | // Create parameter objects for unique parameters |
| 59 | return Array.from(uniqueParams).map((paramName) => ({ |
| 60 | name: paramName, |
| 61 | value: paramDefaults[paramName]?.value || '', |
| 62 | defaultValue: paramDefaults[paramName]?.value, |
| 63 | type: paramDefaults[paramName]?.type, |
| 64 | possibleValues: paramDefaults[paramName]?.possibleValues, |
| 65 | occurrences: paramOccurrences[paramName], |
| 66 | })) |
| 67 | } |
| 68 | |
| 69 | export const processParameterizedSql = (sql: string, parameters: Record<string, string>) => { |
| 70 | // Parse @set parameter defaults with type information from SQL |
| 71 | const setParamRegex = /@set\s+(\w+)(?::([^=]+))?\s*=\s*([^;\n]+)/g |
| 72 | const paramDefaults: Record<string, { value: string; type?: string; possibleValues?: string[] }> = |
| 73 | {} |
| 74 | let match |
| 75 | |
| 76 | while ((match = setParamRegex.exec(sql)) !== null) { |
| 77 | const [_, paramName, paramType, paramValue] = match |
| 78 | if (!paramName || !paramValue?.trim()) continue |
| 79 | |
| 80 | const typeInfo = paramType?.trim() |
| 81 | let type: string | undefined |
| 82 | let possibleValues: string[] | undefined |
| 83 | |
| 84 | if (typeInfo) { |
| 85 | if (typeInfo.includes('|')) { |
| 86 | possibleValues = typeInfo.split('|').map((v) => v.trim()) |
| 87 | type = 'enum' |
| 88 | } else { |
| 89 | type = typeInfo.trim() |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | paramDefaults[paramName] = { |
| 94 | value: paramValue.trim(), |
| 95 | type, |
| 96 | possibleValues, |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | // Remove @set lines from SQL |
| 101 | let processedSql = sql.replace(/@set\s+\w+(?:\s*:\s*[^=]+)?\s*=\s*[^;\n]+[\n;]*/g, '') |
| 102 | |
| 103 | // Replace :parameters with values |
| 104 | const paramRegex = /:(\w+)/g |
| 105 | processedSql = processedSql.replace(paramRegex, (_match, paramName) => { |
| 106 | const value = parameters[paramName] ?? paramDefaults[paramName]?.value |
| 107 | if (value === undefined) { |
| 108 | throw new Error(`Missing value for parameter: ${paramName}`) |
| 109 | } |
| 110 | return value |
| 111 | }) |
| 112 | |
| 113 | return processedSql |
| 114 | } |