* Identify queries that expose schema/column metadata. Combines a textual * pre-screen (catches statements the parser doesn't understand, like * `SHOW FULL COLUMNS FROM users`) with an AST walk (catches `db.table` * references to `information_schema` or `mysql` anywhere in the query, * including
(sql: string)
| 212 | * downstream executor will reject or surface the parse error normally. |
| 213 | */ |
| 214 | function isIntrospectionQuery(sql: string): IntrospectionResult { |
| 215 | const showMatch = sql.match(SHOW_INTROSPECTION_RE); |
| 216 | if (showMatch) { |
| 217 | const keyword = showMatch[1].toUpperCase().replace(/\s+/g, " "); |
| 218 | if (keyword.startsWith("COLUMNS") || keyword.startsWith("FIELDS")) { |
| 219 | return { kind: "show_columns" }; |
| 220 | } |
| 221 | if (keyword.startsWith("CREATE")) { |
| 222 | return { kind: "show_create" }; |
| 223 | } |
| 224 | if ( |
| 225 | keyword.startsWith("INDEX") || |
| 226 | keyword.startsWith("KEYS") |
| 227 | ) { |
| 228 | return { kind: "show_index" }; |
| 229 | } |
| 230 | // Table-/database-level listings: schema topology only, no column data. |
| 231 | if ( |
| 232 | keyword === "TABLES" || |
| 233 | keyword.startsWith("TABLE STATUS") || |
| 234 | keyword === "DATABASES" || |
| 235 | keyword === "SCHEMAS" || |
| 236 | keyword.startsWith("CHARACTER") || |
| 237 | keyword === "CHARSET" || |
| 238 | keyword === "COLLATION" |
| 239 | ) { |
| 240 | return { kind: "show_passthrough" }; |
| 241 | } |
| 242 | return { kind: "show_other" }; |
| 243 | } |
| 244 | if (DESCRIBE_RE.test(sql) || EXPLAIN_TABLE_RE.test(sql)) { |
| 245 | return { kind: "describe" }; |
| 246 | } |
| 247 | |
| 248 | let astOrArray: AST | AST[]; |
| 249 | try { |
| 250 | astOrArray = parser.astify(sql, { database: "mysql" }); |
| 251 | } catch { |
| 252 | return { kind: null }; |
| 253 | } |
| 254 | const statements = Array.isArray(astOrArray) ? astOrArray : [astOrArray]; |
| 255 | for (const stmt of statements) { |
| 256 | const kind = findIntrospectionKind(stmt); |
| 257 | if (kind) return { kind }; |
| 258 | } |
| 259 | return { kind: null }; |
| 260 | } |
| 261 | |
| 262 | function findIntrospectionKind(node: unknown): IntrospectionKind | null { |
| 263 | if (node == null || typeof node !== "object") return null; |
no test coverage detected