UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

130 lines (109 loc) 3.96 kB
/** * @file N+1 query detector * @description Detects potential N+1 query patterns with context awareness */ const { detectExecutionContext } = require('./contextDetector'); /** * Detects potential N+1 query patterns * @param {string} content - File content * @param {string} filePath - Path to the file * @returns {Array} Array of N+1 query issues */ function detectNPlusOne(content, filePath) { const issues = []; // Get execution context to avoid false positives const context = detectExecutionContext(content, filePath); // Skip CLI tools - they rarely have database operations if (context.type === 'cli' || context.isCLI) { return issues; } // Skip test files unless they have clear database setup if (context.type === 'test' && !hasTestDatabaseSetup(content)) { return issues; } const loops = [ /for\s*\(\s*(let|const|var)\s+.+\)/, /\.forEach\s*\(/, /\.map\s*\(/, /while\s*\(/ ]; // More specific database patterns to reduce false positives const dbPatterns = [ // Direct database calls /db\.query\s*\(/, /database\.query\s*\(/, /connection\.query\s*\(/, // ORM patterns with context /Model\.(find|findOne|findAll)\s*\(/, /\.find\s*\(\s*{.*}\s*\)/, // Object-based find (not array.find()) /\.findOne\s*\(\s*{.*}\s*\)/, /\.findById\s*\(/, /\.findWhere\s*\(/, // SQL patterns /SELECT\s+.*FROM.*WHERE/i, /INSERT\s+INTO.*VALUES/i, /UPDATE\s+.*SET.*WHERE/i, // Async database operations /await\s+.*\.(query|find|findOne|get|fetch)\s*\([^)]*{/, /\.then\s*\(\s*.*\.(query|find|findOne)/ ]; const hasLoop = loops.some(pattern => pattern.test(content)); const hasDbCall = dbPatterns.some(pattern => pattern.test(content)); // Additional validation: must have database-related imports or setup const hasDatabaseContext = hasDatabaseImportsOrSetup(content); if (hasLoop && hasDbCall && hasDatabaseContext) { const severity = context.type === 'server' ? 'HIGH' : 'MEDIUM'; issues.push({ type: 'n_plus_one', severity, category: 'Database', location: filePath, pattern: 'Loop + Database Query', summary: `Possible N+1 query pattern in ${context.type} context: database calls inside loops`, recommendation: context.type === 'server' ? 'CRITICAL: Refactor with batched queries, joins, or eager loading to prevent server overload' : 'Consider batching database operations to improve performance', impact: context.type === 'server' ? 'Can cause exponential database load growth affecting all users' : 'May cause performance issues with large datasets', context: context.type }); } return issues; } /** * Check if content has database-related imports or setup * @param {string} content - File content * @returns {boolean} True if database context is present */ function hasDatabaseImportsOrSetup(content) { const dbContextPatterns = [ // Database libraries /require\s*\(\s*['"`]mysql['"`]\s*\)/, /require\s*\(\s*['"`]pg['"`]\s*\)/, /require\s*\(\s*['"`]mongodb['"`]\s*\)/, /require\s*\(\s*['"`]mongoose['"`]\s*\)/, /require\s*\(\s*['"`]sequelize['"`]\s*\)/, /import.*from\s+['"`](mysql|pg|mongodb|mongoose|sequelize)['"`]/, // Database variables /\b(db|database|connection|pool|client)\s*=/, /\b(DB_|DATABASE_)/, // Common ORM/database setup /mongoose\.connect/, /createConnection/, /new.*Client/, /\.authenticate\s*\(\)/ ]; return dbContextPatterns.some(pattern => pattern.test(content)); } /** * Check if test file has database setup * @param {string} content - File content * @returns {boolean} True if test has database setup */ function hasTestDatabaseSetup(content) { return /beforeEach.*db|beforeAll.*db|setupDatabase|createTestDb/i.test(content); } module.exports = { detectNPlusOne };