UNPKG

cyber-mysql-openai

Version:

Intelligent natural language to SQL translator with self-correction capabilities using OpenAI and MySQL

120 lines 4.65 kB
"use strict"; // src/utils/queryValidator.ts Object.defineProperty(exports, "__esModule", { value: true }); exports.validateQuery = validateQuery; /** * Valida una consulta SQL generada contra el esquema de la base de datos * para detectar posibles problemas antes de la ejecución. */ function validateQuery(sql, schema) { const warnings = []; const errors = []; const suggestions = []; const normalizedSQL = sql.toUpperCase().trim(); const schemaTableNames = Object.keys(schema).map((t) => t.toLowerCase()); // 1. Verificar que es una consulta SELECT if (!normalizedSQL.startsWith("SELECT")) { errors.push("Solo se permiten consultas SELECT"); return { valid: false, warnings, errors, suggestions }; } // 2. Bloquear operaciones peligrosas const dangerousKeywords = [ "INSERT", "UPDATE", "DELETE", "DROP", "CREATE", "ALTER", "TRUNCATE", "EXEC", "EXECUTE", ]; for (const keyword of dangerousKeywords) { // Buscar keyword como palabra completa (no como parte de un nombre de columna) const regex = new RegExp(`\\b${keyword}\\b`, "i"); if (regex.test(sql)) { errors.push(`Operación peligrosa detectada: ${keyword}`); } } // 3. Extraer tablas referenciadas del SQL const referencedTables = extractTablesFromSQL(sql); // 4. Validar que las tablas existan en el esquema for (const table of referencedTables) { if (!schemaTableNames.includes(table.toLowerCase())) { errors.push(`Tabla '${table}' no encontrada en el esquema. Disponibles: ${schemaTableNames.join(", ")}`); } } // 5. Validar que las columnas existan (mejor esfuerzo para queries simples) const referencedColumns = extractColumnsFromSQL(sql); for (const { table, column } of referencedColumns) { if (table && schema[table]) { const tableColumns = schema[table].columns.map((c) => (c.COLUMN_NAME || c.column_name || c.Field || "").toLowerCase()); if (tableColumns.length > 0 && !tableColumns.includes(column.toLowerCase())) { warnings.push(`La columna '${column}' podría no existir en la tabla '${table}'`); } } } // 6. Advertir si falta LIMIT if (!normalizedSQL.includes("LIMIT") && !normalizedSQL.includes("COUNT(") && !normalizedSQL.includes("SUM(") && !normalizedSQL.includes("AVG(") && !normalizedSQL.includes("MAX(") && !normalizedSQL.includes("MIN(")) { warnings.push("La consulta no tiene cláusula LIMIT — podría devolver muchas filas"); suggestions.push("Considera agregar LIMIT para evitar conjuntos de resultados grandes"); } // 7. Advertir sobre posibles productos cartesianos const joinCount = (normalizedSQL.match(/\bJOIN\b/g) || []).length; const onCount = (normalizedSQL.match(/\bON\b/g) || []).length; if (joinCount > 0 && onCount < joinCount) { warnings.push("Posible producto cartesiano: JOINs detectados sin cláusulas ON correspondientes"); } // 8. Advertir sobre SELECT * if (normalizedSQL.includes("SELECT *")) { suggestions.push("Considera especificar columnas en lugar de SELECT * para mejor rendimiento"); } return { valid: errors.length === 0, warnings, errors, suggestions, }; } /** * Extrae nombres de tablas de una consulta SQL (análisis de mejor esfuerzo) */ function extractTablesFromSQL(sql) { const tables = new Set(); // Buscar FROM nombre_tabla const fromRegex = /\bFROM\s+`?(\w+)`?/gi; let match; while ((match = fromRegex.exec(sql)) !== null) { tables.add(match[1]); } // Buscar JOIN nombre_tabla const joinRegex = /\bJOIN\s+`?(\w+)`?/gi; while ((match = joinRegex.exec(sql)) !== null) { tables.add(match[1]); } return Array.from(tables); } /** * Extrae referencias de columnas con sus aliases de tabla (análisis de mejor esfuerzo) */ function extractColumnsFromSQL(sql) { const columns = []; // Buscar patrones tabla.columna const qualifiedRegex = /\b(\w+)\.`?(\w+)`?/g; let match; while ((match = qualifiedRegex.exec(sql)) !== null) { // Omitir palabras clave SQL y funciones const keyword = match[1].toUpperCase(); if (!["DATE", "YEAR", "MONTH", "DAY", "CURRENT_DATE", "NOW"].includes(keyword)) { columns.push({ table: match[1], column: match[2] }); } } return columns; } //# sourceMappingURL=queryValidator.js.map