QueryInsightsTable.utils.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { formatDuration as formatDurationLong } from '../../QueryPerformance/QueryPerformance.utils'
  2. export const formatDuration = (ms: number) => {
  3. if (ms < 1000) {
  4. return `${Math.round(ms)}ms`
  5. }
  6. return formatDurationLong(ms)
  7. }
  8. export const getQueryType = (query: string | undefined | null): string | null => {
  9. if (!query) return null
  10. const trimmed = query.trim()
  11. const firstWord = trimmed.split(/\s+/)[0]?.toUpperCase()
  12. const sqlTypes = [
  13. 'SELECT',
  14. 'INSERT',
  15. 'UPDATE',
  16. 'DELETE',
  17. 'CREATE',
  18. 'DROP',
  19. 'ALTER',
  20. 'TRUNCATE',
  21. 'WITH',
  22. ]
  23. if (firstWord && sqlTypes.includes(firstWord)) {
  24. return firstWord
  25. }
  26. return null
  27. }
  28. const cleanIdentifier = (identifier?: string): string | null => {
  29. if (!identifier) return null
  30. return (
  31. identifier
  32. .replace(/["`']/g, '')
  33. .replace(/^[\w]+\./, '')
  34. .trim() || null
  35. )
  36. }
  37. export const getTableName = (query: string | undefined | null): string | null => {
  38. if (!query) return null
  39. const trimmed = query.trim()
  40. let match = trimmed.match(
  41. /FROM\s+(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i
  42. )
  43. if (match?.groups?.table) {
  44. return cleanIdentifier(match.groups.table)
  45. }
  46. match = trimmed.match(
  47. /INSERT\s+INTO\s+(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i
  48. )
  49. if (match?.groups?.table) {
  50. return cleanIdentifier(match.groups.table)
  51. }
  52. match = trimmed.match(/UPDATE\s+(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i)
  53. if (match?.groups?.table) {
  54. return cleanIdentifier(match.groups.table)
  55. }
  56. match = trimmed.match(
  57. /DELETE\s+FROM\s+(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i
  58. )
  59. if (match?.groups?.table) {
  60. return cleanIdentifier(match.groups.table)
  61. }
  62. match = trimmed.match(
  63. /CREATE\s+(?:TEMPORARY\s+|TEMP\s+|UNLOGGED\s+)?TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i
  64. )
  65. if (match?.groups?.table) {
  66. return cleanIdentifier(match.groups.table)
  67. }
  68. match = trimmed.match(
  69. /ALTER\s+TABLE\s+(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i
  70. )
  71. if (match?.groups?.table) {
  72. return cleanIdentifier(match.groups.table)
  73. }
  74. match = trimmed.match(
  75. /DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i
  76. )
  77. if (match?.groups?.table) {
  78. return cleanIdentifier(match.groups.table)
  79. }
  80. match = trimmed.match(
  81. /TRUNCATE\s+(?:TABLE\s+)?(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i
  82. )
  83. if (match?.groups?.table) {
  84. return cleanIdentifier(match.groups.table)
  85. }
  86. if (trimmed.toUpperCase().startsWith('WITH')) {
  87. match = trimmed.match(
  88. /WITH\s+[\s\S]*?\s+FROM\s+(?:(?<schema>(?:"[^"]+"|[\w]+)\.)?(?<table>(?:"[^"]+"|[\w]+)))/i
  89. )
  90. if (match?.groups?.table) {
  91. return cleanIdentifier(match.groups.table)
  92. }
  93. }
  94. return null
  95. }
  96. export const getColumnName = (query: string | undefined | null): string | null => {
  97. if (!query) return null
  98. const trimmed = query.trim()
  99. let match = trimmed.match(
  100. /WHERE\s+(?:(?<table>(?:"[^"]+"|[\w]+)\.)?(?<column>(?:"[^"]+"|[\w]+)))/i
  101. )
  102. if (match?.groups?.column) {
  103. return cleanIdentifier(match?.groups?.column)
  104. }
  105. match = trimmed.match(
  106. /SELECT\s+(?:\*\s+FROM|(?:(?<table>(?:"[^"]+"|[\w]+)\.)?(?<column>(?:"[^"]+"|[\w]+))(?:\s*,\s*[\w.]+)*)\s+FROM)/i
  107. )
  108. if (match?.groups?.column && match.groups.column.toUpperCase() !== '*') {
  109. return cleanIdentifier(match.groups.column)
  110. }
  111. match = trimmed.match(
  112. /ORDER\s+BY\s+(?:(?<table>(?:"[^"]+"|[\w]+)\.)?(?<column>(?:"[^"]+"|[\w]+)))/i
  113. )
  114. if (match?.groups?.column) {
  115. return cleanIdentifier(match.groups.column)
  116. }
  117. match = trimmed.match(
  118. /GROUP\s+BY\s+(?:(?<table>(?:"[^"]+"|[\w]+)\.)?(?<column>(?:"[^"]+"|[\w]+)))/i
  119. )
  120. if (match?.groups?.column) {
  121. return cleanIdentifier(match.groups.column)
  122. }
  123. match = trimmed.match(
  124. /UPDATE\s+[\w.]+\s+SET\s+(?:(?<table>(?:"[^"]+"|[\w]+)\.)?(?<column>(?:"[^"]+"|[\w]+)))/i
  125. )
  126. if (match?.groups?.column) {
  127. return cleanIdentifier(match.groups.column)
  128. }
  129. match = trimmed.match(/INSERT\s+INTO\s+[\w.]+\s*\((?<column>(?:"[^"]+"|[\w]+))/i)
  130. if (match?.groups?.column) {
  131. return cleanIdentifier(match.groups.column)
  132. }
  133. return null
  134. }
  135. export const formatQueryDisplay = (
  136. queryType: string | null,
  137. tableName: string | null,
  138. columnName: string | null
  139. ): string => {
  140. const type = queryType ?? '–'
  141. const table = tableName ?? '–'
  142. const column = columnName ?? '–'
  143. return `${type} in ${table}, ${column}`
  144. }