sql-parameters.ts 3.3 KB

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