From harness-claude
Reviews SQL queries for performance anti-patterns, N+1 issues, missing indexes, and unsafe operations. Analyzes raw SQL, ORM-generated queries, and migrations with optimization recommendations.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Adversarial review of SQL queries for performance anti-patterns, missing indexes, N+1 queries, and unsafe operations. Analyzes raw SQL, ORM-generated queries, and migration scripts to produce optimization recommendations with estimated impact.
Analyzes SQL queries with EXPLAIN/ANALYZE, detects N+1 issues, missing indexes, inefficient JOINs; suggests rewrites, indexes, caching for PostgreSQL, MySQL, SQLite.
Optimizes slow database queries via EXPLAIN (ANALYZE) analysis, index recommendations, N+1 detection, and ORM fixes for PostgreSQL, MySQL, Prisma, Django.
Optimizes slow SQL queries using systematic patterns, proper indexing, EXPLAIN plan analysis, and N+1 fixes. Useful for debugging performance, schema design, reducing DB load, and improving scalability.
Share bugs, ideas, or general feedback.
Adversarial review of SQL queries for performance anti-patterns, missing indexes, N+1 queries, and unsafe operations. Analyzes raw SQL, ORM-generated queries, and migration scripts to produce optimization recommendations with estimated impact.
Resolve project root. Use provided path or cwd.
Detect SQL dialect. Determine the database from project configuration:
DATABASE_URL containing postgres, Prisma provider = "postgresql", pg packageDATABASE_URL containing mysql, Prisma provider = "mysql", mysql2 package.sqlite or .db files, Prisma provider = "sqlite", better-sqlite3 packageDATABASE_URL containing sqlserver, mssql or tedious package--dialect if auto-detection failsExtract raw SQL queries. Scan source files for:
SELECT, INSERT, UPDATE, DELETE, CREATE.query(), .raw(), .execute(), $queryRaw, $executeRaw*.sql in queries/, src/**/sql/, migration directoriesExtract ORM queries. Identify ORM usage and extract the implied SQL:
findMany, findUnique, include, select with nested relationscreateQueryBuilder, find with relations, @OneToMany eager loadingfindAll with include, raw queries.select(), .where(), .join())Scope to PR changes. If triggered by a PR, focus on queries in changed files. Also scan unchanged files that import changed models (they may be affected by schema changes).
Build query inventory. For each query, record:
Detect N+1 queries. The most common and impactful anti-pattern:
for / forEach / map containing await db.queryfindMany followed by individual findUnique per resultDetect missing indexes. For each WHERE, JOIN, and ORDER BY clause:
CREATE INDEX statements, Prisma @@index)count operations)Detect full table scans. Flag queries that:
SELECT * on large tables without a WHERE clauseLIKE '%pattern%' (leading wildcard prevents index use)WHERE (WHERE LOWER(email) = ...)OR across different columns without index supportNOT IN with large subqueriesDetect unsafe operations. Flag queries that:
UPDATE or DELETE without a WHERE clauseTRUNCATE in application code (should be migration/admin only)ALTER TABLE, DROP) in application codeSELECT ... FOR UPDATE without a transactionSELECT without LIMIT on user-facing endpoints)Detect join inefficiencies. Evaluate:
ON clause or cross join without intent)Detect transaction issues. Check for:
Generate optimized query alternatives. For each finding, provide:
Recommend index additions. For each missing index:
-- Recommended: speeds up user lookup by email in login flow
-- Estimated impact: O(N) full scan -> O(log N) index seek
CREATE INDEX idx_users_email ON users (email);
Include:
Recommend batching strategies for N+1. Provide specific alternatives:
include / relations / with syntaxWHERE IN batching: show the batched query for loop-based N+1JOIN rewrite: show how to combine parent + child in one queryRecommend query rewrites. For complex subqueries and inefficient patterns:
NOT IN -> NOT EXISTS (NULL-safe and often faster)UNION -> single query with CASE WHENDISTINCT on large result sets -> GROUP BY or EXISTSSELECT * -> explicit column listPrioritize by impact. Order recommendations:
Verify semantic equivalence. For every rewritten query:
NOT IN vs NOT EXISTS differs on NULLs)Verify index recommendations. For each proposed index:
Check for regression risks. Flag:
IN lists)Output structured report. Present findings in review format:
SQL Review: [PASS/NEEDS_ATTENTION/FAIL]
Queries analyzed: N
Findings: E errors, W warnings, I info
ERRORS:
[SQL-N1-001] src/repositories/OrderRepository.ts:45
N+1 query in getOrdersWithItems(): queries items per order in loop
Current: 1 + N queries (N = number of orders)
Recommended: Single query with JOIN or Prisma include
Impact: O(N) -> O(1) database round trips
[SQL-UNSAFE-001] src/services/CleanupService.ts:23
DELETE without WHERE clause: db.execute("DELETE FROM temp_records")
Risk: Deletes all records if called outside intended context
Recommended: Add WHERE clause with date filter or use TRUNCATE in migration
WARNINGS:
[SQL-IDX-001] src/repositories/UserRepository.ts:78
Missing index: WHERE email = ? on users table (estimated 500K rows)
Recommended: CREATE INDEX idx_users_email ON users (email);
Verify no new anti-patterns introduced. Check that recommended optimizations do not introduce new problems (e.g., a JOIN recommendation that creates a Cartesian product, or an index recommendation on a column already indexed).
harness skill run harness-sql-review -- Primary command for SQL query analysis.harness validate -- Run after applying query optimizations to verify project health.Glob -- Used to locate SQL files, repository files, DAO classes, and migration scripts.Grep -- Used to extract SQL strings, query builder calls, ORM methods, and index definitions.Read -- Used to read query files, repository implementations, and schema definitions.Write -- Used to generate optimized query files and index migration scripts.Bash -- Used to run EXPLAIN queries when database connection is available and to check migration files.emit_interaction -- Used to present the review report and confirm optimization approach for complex rewrites.CREATE INDEX recommendationsPhase 1: SCAN
Dialect: PostgreSQL (detected from DATABASE_URL)
ORM: Prisma 5.10
Queries found: 34 (28 Prisma, 4 $queryRaw, 2 $executeRaw)
Scope: PR diff (3 changed repository files)
Phase 2: ANALYZE
[SQL-N1-001] src/routes/orders.ts:67
findMany orders then findUnique for each order's customer
Pattern: 1 query for orders + N queries for customers
[SQL-IDX-001] src/routes/products.ts:23
findMany with where: { category: catId, status: "active" }
No composite index on (category_id, status)
[SQL-UNSAFE-001] src/routes/admin.ts:89
$executeRaw DELETE FROM sessions (no WHERE clause)
Phase 3: OPTIMIZE
N+1 fix: Use Prisma include: { customer: true }
Index: CREATE INDEX idx_products_category_status ON products (category_id, status);
Safety: Add WHERE expired_at < NOW() to session cleanup
Phase 4: VALIDATE
All rewrites produce equivalent results: YES
Index does not duplicate existing: YES (checked schema.prisma)
Report: FAIL (1 N+1 error, 1 unsafe operation, 1 missing index)
Phase 1: SCAN
Dialect: PostgreSQL (detected from DATABASES setting)
ORM: Django ORM + 6 raw SQL queries
Queries found: 52 (46 ORM, 6 raw)
Scope: full project audit
Phase 2: ANALYZE
[SQL-N1-001] views/analytics.py:34
for report in reports: report.author.name (lazy loading author per report)
[SQL-SCAN-001] queries/search.sql:12
WHERE description LIKE '%' || search_term || '%' (leading wildcard, full scan)
[SQL-JOIN-001] views/dashboard.py:78
3 sequential queries that could be a single JOIN
[SQL-TXN-001] views/transfer.py:45
Debit + credit operations without @transaction.atomic
Phase 3: OPTIMIZE
N+1: Add select_related('author') to queryset
Search: Recommend PostgreSQL full-text search with GIN index:
CREATE INDEX idx_items_description_gin ON items USING GIN (to_tsvector('english', description));
Join: Combine 3 queries into single query with LEFT JOIN
Transaction: Wrap debit+credit in @transaction.atomic
Phase 4: VALIDATE
All optimizations preserve correctness: YES
Full-text search returns equivalent results: YES (for word-boundary matches)
Report: FAIL (1 N+1, 1 missing transaction) + 2 warnings
Phase 1: SCAN
Dialect: PostgreSQL (detected from connection string)
Query library: sqlx
Queries found: 28 (all raw SQL in repository files)
SQL files: 12 in queries/ directory
Phase 2: ANALYZE
[SQL-N1-001] internal/repo/order_repo.go:56
GetOrdersByUser calls GetItemsByOrderID in loop
[SQL-IDX-001] queries/search_users.sql:3
WHERE created_at > $1 AND status = $2 ORDER BY created_at
Missing composite index on (status, created_at)
[SQL-PERF-001] queries/report_daily.sql:8
Correlated subquery for calculating running total
Recommend: window function SUM() OVER (ORDER BY date)
Phase 3: OPTIMIZE
N+1: Rewrite with single query using LEFT JOIN and GROUP BY
Index: CREATE INDEX idx_users_status_created ON users (status, created_at);
Rewrite: Replace correlated subquery with:
SELECT date, amount, SUM(amount) OVER (ORDER BY date) as running_total
FROM daily_revenue;
Phase 4: VALIDATE
Window function produces identical results: YES
Index column order matches query pattern: YES (equality on status, range on created_at)
Report: NEEDS_ATTENTION (1 N+1 error, 1 missing index, 1 query rewrite)
| Rationalization | Reality |
|---|---|
| "The ORM handles query optimization automatically" | ORMs generate syntactically correct queries but do not detect N+1 patterns, choose optimal join strategies, or add missing indexes. The ORM executes what the code asks for. A findMany followed by per-item findUnique calls in a loop is an N+1 regardless of which ORM executes it. |
| "That endpoint is only called by admins so performance doesn't matter" | Admin endpoints frequently become user-facing as products grow. An N+1 query on a 10-row table becomes a crisis when the table grows to 100,000 rows. Query correctness should not be conditional on current data volume. |
| "We can add indexes later if performance becomes a problem" | Adding indexes to large production tables requires exclusive locks or online rebuild procedures that carry risk. Identifying and adding the correct index during development, before the table grows, costs minutes instead of hours of planned maintenance. |
| "That DELETE without a WHERE clause is wrapped in application logic that only calls it correctly" | Application logic has bugs. A missing WHERE clause is a single misrouted request away from deleting the entire table. Database safety constraints must not depend on application-layer correctness. |
| "The query is fast in development — the test database only has 100 rows" | Development databases do not represent production query plans. Full table scans, missing indexes, and N+1 patterns only manifest at production data volumes. Static analysis catches these issues regardless of local data size. |
TRUNCATE or have a documented justification.customer_name column on orders. This is a schema change that needs harness-database review."--explain with a database connection."$queryRaw for this specific query, (B) accept the suboptimal query with a performance budget note, (C) restructure the data access pattern."