Team Ai
Apppublic

Mahathi4554/sql-query-debugging

sourceHugging Faceupdated 7mo agoView on Hugging Face
0likes
Tasks.py509 linesDownload Raw Back to root
1"""2Task definitions for the SQL Query Debugging Environment.3Each task defines:4  - A real-world scenario5  - A database schema (populated with seed data)6  - A broken/suboptimal SQL query for the agent to fix7  - Expected correct result rows8  - A programmatic grader9  - Progressive hints (revealed after 2 and 4 failed attempts)10"""11 12from __future__ import annotations13import sqlite314import tempfile15from dataclasses import dataclass, field16from typing import Optional17 18 19@dataclass20class Task:21    task_id: str22    difficulty: str          # easy | medium | hard | expert23    description: str24    schema_sql: str          # CREATE TABLE + INSERT statements25    broken_query: str        # What the agent receives26    solution_query: str      # Reference solution (not shown to agent)27    expected_rows: list[dict]28    hint: str                # Hint after 2 failed attempts29    hint_advanced: str       # Stronger hint after 4 failed attempts30    max_attempts: int = 531    check_optimization: bool = False32    optimization_target_cost: int = 233 34    def setup_db(self) -> str:35        """Create a fresh SQLite DB for this episode, return path."""36        tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False)37        tmp.close()38        conn = sqlite3.connect(tmp.name)39        conn.executescript(self.schema_sql)40        conn.commit()41        conn.close()42        return tmp.name43 44 45# ─── Task 1: Syntax Error Fix (Easy) ─────────────────────────────────────────46 47TASK1_SCHEMA = """48CREATE TABLE employees (49    id INTEGER PRIMARY KEY,50    name TEXT NOT NULL,51    department TEXT NOT NULL,52    salary REAL NOT NULL,53    hire_date TEXT NOT NULL54);55INSERT INTO employees VALUES56  (1, 'Alice Chen',    'Engineering', 95000, '2019-03-15'),57  (2, 'Bob Martinez',  'Engineering', 88000, '2020-07-01'),58  (3, 'Carol White',   'Marketing',   72000, '2018-11-20'),59  (4, 'David Kim',     'Engineering', 102000,'2017-05-10'),60  (5, 'Eve Johnson',   'Marketing',   68000, '2021-02-28'),61  (6, 'Frank Lee',     'HR',          61000, '2022-01-15'),62  (7, 'Grace Park',    'Engineering', 91000, '2020-09-08'),63  (8, 'Henry Brown',   'HR',          58000, '2021-06-30');64"""65 66TASK1_BROKEN = """SELECT name, salary67FORM employees68WHERE department = 'Engineering'69ORDER BY salary DESC;"""70 71TASK1_SOLUTION = """SELECT name, salary72FROM employees73WHERE department = 'Engineering'74ORDER BY salary DESC;"""75 76TASK1_EXPECTED = [77    {"name": "David Kim",    "salary": 102000.0},78    {"name": "Alice Chen",   "salary": 95000.0},79    {"name": "Grace Park",   "salary": 91000.0},80    {"name": "Bob Martinez", "salary": 88000.0},81]82 83TASK1 = Task(84    task_id="task_syntax_fix",85    difficulty="easy",86    description=(87        "A data analyst needs to list all Engineering department employees "88        "by salary (highest first). The query has a typo — fix it so it runs "89        "and returns the correct result."90    ),91    schema_sql=TASK1_SCHEMA,92    broken_query=TASK1_BROKEN,93    solution_query=TASK1_SOLUTION,94    expected_rows=TASK1_EXPECTED,95    hint="Check the FROM keyword — it appears to be misspelled as FORM.",96    hint_advanced="Replace 'FORM' with 'FROM' on the second line. That's the only change needed.",97    max_attempts=5,98)99 100 101# ─── Task 2: Logic Bug Fix (Medium) ──────────────────────────────────────────102 103TASK2_SCHEMA = """104CREATE TABLE customers (105    id INTEGER PRIMARY KEY,106    name TEXT NOT NULL,107    email TEXT NOT NULL,108    region TEXT NOT NULL109);110CREATE TABLE orders (111    id INTEGER PRIMARY KEY,112    customer_id INTEGER NOT NULL,113    amount REAL NOT NULL,114    order_date TEXT NOT NULL,115    status TEXT NOT NULL116);117INSERT INTO customers VALUES118  (1, 'Acme Corp',      'acme@example.com',     'North'),119  (2, 'Beta LLC',       'beta@example.com',      'South'),120  (3, 'Gamma Inc',      'gamma@example.com',     'North'),121  (4, 'Delta Co',       'delta@example.com',     'West'),122  (5, 'Echo Ltd',       'echo@example.com',      'South');123INSERT INTO orders VALUES124  (1,  1, 750.00,  '2024-11-15', 'completed'),125  (2,  1, 200.00,  '2024-11-20', 'completed'),126  (3,  2, 1200.00, '2024-12-01', 'completed'),127  (4,  3, 450.00,  '2024-12-10', 'completed'),128  (5,  3, 600.00,  '2024-10-05', 'completed'),129  (6,  4, 850.00,  '2024-12-20', 'completed'),130  (7,  5, 300.00,  '2024-12-22', 'completed'),131  (8,  2, 550.00,  '2024-09-01', 'completed'),132  (9,  1, 900.00,  '2024-12-18', 'completed'),133  (10, 4, 125.00,  '2024-12-19', 'pending');134"""135 136TASK2_BROKEN = """SELECT DISTINCT c.name, c.email137FROM customers c138LEFT JOIN orders o ON c.id = o.customer_id139WHERE o.amount > 50140  AND o.order_date < '2024-10-01'141  AND o.status = 'completed'142ORDER BY c.name;"""143 144TASK2_SOLUTION = """SELECT DISTINCT c.name, c.email145FROM customers c146INNER JOIN orders o ON c.id = o.customer_id147WHERE o.amount > 500148  AND o.order_date >= '2024-10-01'149  AND o.status = 'completed'150ORDER BY c.name;"""151 152TASK2_EXPECTED = [153    {"name": "Acme Corp",  "email": "acme@example.com"},154    {"name": "Beta LLC",   "email": "beta@example.com"},155    {"name": "Delta Co",   "email": "delta@example.com"},156    {"name": "Gamma Inc",  "email": "gamma@example.com"},157]158 159TASK2 = Task(160    task_id="task_logic_bug",161    difficulty="medium",162    description=(163        "Find all customers who placed at least one completed order over $500 "164        "on or after 2024-10-01. The query has multiple logic bugs — "165        "the wrong JOIN type, a wrong amount threshold, and a wrong date comparison. "166        "Fix all issues to return the correct 4 customers."167    ),168    schema_sql=TASK2_SCHEMA,169    broken_query=TASK2_BROKEN,170    solution_query=TASK2_SOLUTION,171    expected_rows=TASK2_EXPECTED,172    hint="There are 3 bugs: check the JOIN type, the amount threshold value, and the direction of the date comparison.",173    hint_advanced=(174        "Fix all three: (1) LEFT JOIN → INNER JOIN, "175        "(2) amount > 50 → amount > 500, "176        "(3) order_date < '2024-10-01' → order_date >= '2024-10-01'."177    ),178    max_attempts=7,179)180 181 182# ─── Task 3: Query Optimization (Hard) ───────────────────────────────────────183 184TASK3_SCHEMA = """185CREATE TABLE products (186    id INTEGER PRIMARY KEY,187    name TEXT NOT NULL,188    category TEXT NOT NULL,189    price REAL NOT NULL190);191CREATE TABLE sales (192    id INTEGER PRIMARY KEY,193    product_id INTEGER NOT NULL,194    quantity INTEGER NOT NULL,195    sale_date TEXT NOT NULL,196    region TEXT NOT NULL197);198INSERT INTO products VALUES199  (1,  'Widget A',   'Widgets',   29.99),200  (2,  'Widget B',   'Widgets',   49.99),201  (3,  'Gadget X',   'Gadgets',   99.99),202  (4,  'Gadget Y',   'Gadgets',   149.99),203  (5,  'Doohickey',  'Other',     19.99),204  (6,  'Thingamajig','Other',     39.99),205  (7,  'Widget C',   'Widgets',   34.99),206  (8,  'Gadget Z',   'Gadgets',   199.99);207INSERT INTO sales VALUES208  (1,  1, 10, '2024-Q4', 'North'),209  (2,  1, 15, '2024-Q4', 'South'),210  (3,  2,  8, '2024-Q4', 'North'),211  (4,  2, 12, '2024-Q4', 'West'),212  (5,  3,  5, '2024-Q4', 'North'),213  (6,  3,  7, '2024-Q4', 'South'),214  (7,  4,  3, '2024-Q4', 'North'),215  (8,  4,  4, '2024-Q4', 'West'),216  (9,  5, 20, '2024-Q4', 'South'),217  (10, 6,  9, '2024-Q4', 'North'),218  (11, 7, 11, '2024-Q4', 'West'),219  (12, 8,  2, '2024-Q4', 'North'),220  (13, 1, 18, '2024-Q3', 'North'),221  (14, 3,  6, '2024-Q3', 'South'),222  (15, 5, 25, '2024-Q3', 'West');223"""224 225TASK3_BROKEN = """SELECT226    p.name,227    p.category,228    (SELECT SUM(s.quantity)229     FROM sales s230     WHERE s.product_id = p.id231       AND s.sale_date = '2024-Q4') AS total_units_sold232FROM products p233WHERE (SELECT SUM(s.quantity)234       FROM sales s235       WHERE s.product_id = p.id236         AND s.sale_date = '2024-Q4') > 10237ORDER BY total_units_sold DESC;"""238 239TASK3_SOLUTION = """SELECT240    p.name,241    p.category,242    SUM(s.quantity) AS total_units_sold243FROM products p244INNER JOIN sales s ON p.id = s.product_id245WHERE s.sale_date = '2024-Q4'246GROUP BY p.id, p.name, p.category247HAVING SUM(s.quantity) > 10248ORDER BY total_units_sold DESC;"""249 250TASK3_EXPECTED = [251    {"name": "Widget A",  "category": "Widgets", "total_units_sold": 25},252    {"name": "Widget B",  "category": "Widgets", "total_units_sold": 20},253    {"name": "Doohickey", "category": "Other",   "total_units_sold": 20},254    {"name": "Widget C",  "category": "Widgets", "total_units_sold": 11},255    {"name": "Gadget X",  "category": "Gadgets", "total_units_sold": 12},256]257 258TASK3 = Task(259    task_id="task_optimization",260    difficulty="hard",261    description=(262        "This production query lists products with more than 10 units sold in Q4-2024, "263        "but it uses correlated subqueries that make it extremely slow at scale. "264        "Rewrite it to use a JOIN + GROUP BY + HAVING approach for efficiency. "265        "The result must match the original output exactly."266    ),267    schema_sql=TASK3_SCHEMA,268    broken_query=TASK3_BROKEN,269    solution_query=TASK3_SOLUTION,270    expected_rows=TASK3_EXPECTED,271    hint="Replace the correlated subqueries with a single INNER JOIN, GROUP BY the product columns, and use HAVING to filter.",272    hint_advanced=(273        "Structure: SELECT p.name, p.category, SUM(s.quantity) AS total_units_sold "274        "FROM products p INNER JOIN sales s ON p.id = s.product_id "275        "WHERE s.sale_date = '2024-Q4' GROUP BY p.id, p.name, p.category "276        "HAVING SUM(s.quantity) > 10 ORDER BY total_units_sold DESC;"277    ),278    max_attempts=7,279    check_optimization=True,280    optimization_target_cost=2,281)282 283 284# ─── Task 4: SQL Injection Fix (Medium-Hard) ─────────────────────────────────285# NEW TASK: A query is vulnerable to SQL injection via string concatenation.286# Agent must rewrite it to use parameterized query style (safe string escaping).287# In SQLite context: agent must use proper quoting / escaping or restructure.288 289TASK4_SCHEMA = """290CREATE TABLE users (291    id INTEGER PRIMARY KEY,292    username TEXT NOT NULL UNIQUE,293    email TEXT NOT NULL,294    role TEXT NOT NULL DEFAULT 'user',295    active INTEGER NOT NULL DEFAULT 1296);297CREATE TABLE audit_log (298    id INTEGER PRIMARY KEY,299    user_id INTEGER,300    action TEXT NOT NULL,301    performed_at TEXT NOT NULL302);303INSERT INTO users VALUES304  (1, 'alice',   'alice@example.com',   'admin', 1),305  (2, 'bob',     'bob@example.com',     'user',  1),306  (3, 'charlie', 'charlie@example.com', 'user',  1),307  (4, 'diana',   'diana@example.com',   'mod',   1),308  (5, 'eve',     'eve@example.com',     'user',  0);309INSERT INTO audit_log VALUES310  (1, 1, 'login',  '2024-12-01 09:00'),311  (2, 2, 'login',  '2024-12-01 09:05'),312  (3, 1, 'update', '2024-12-01 10:00'),313  (4, 3, 'login',  '2024-12-02 08:30'),314  (5, 2, 'logout', '2024-12-02 09:00');315"""316 317# Broken: uses string concatenation — vulnerable to SQL injection.318# Also has a logic bug: should only return ACTIVE users (active = 1).319# Agent must fix both the injection pattern AND the missing active filter.320TASK4_BROKEN = """SELECT u.id, u.username, u.email, u.role,321       COUNT(a.id) AS action_count322FROM users u323LEFT JOIN audit_log a ON u.id = a.user_id324WHERE u.username = '' OR '1'='1'325GROUP BY u.id, u.username, u.email, u.role326ORDER BY u.username;"""327 328TASK4_SOLUTION = """SELECT u.id, u.username, u.email, u.role,329       COUNT(a.id) AS action_count330FROM users u331LEFT JOIN audit_log a ON u.id = a.user_id332WHERE u.active = 1333GROUP BY u.id, u.username, u.email, u.role334ORDER BY u.username;"""335 336TASK4_EXPECTED = [337    {"id": 1, "username": "alice",   "email": "alice@example.com",   "role": "admin", "action_count": 2},338    {"id": 2, "username": "bob",     "email": "bob@example.com",     "role": "user",  "action_count": 2},339    {"id": 3, "username": "charlie", "email": "charlie@example.com", "role": "user",  "action_count": 1},340    {"id": 4, "username": "diana",   "email": "diana@example.com",   "role": "mod",   "action_count": 0},341]342 343TASK4 = Task(344    task_id="task_injection_fix",345    difficulty="medium",346    description=(347        "A login audit report query contains a classic SQL injection payload in the WHERE clause "348        "('OR 1=1') that bypasses all filtering and returns every user including inactive ones. "349        "Fix the query to: (1) remove the injection payload, and (2) correctly filter to only "350        "active users (active = 1). Return all 4 active users with their audit action counts."351    ),352    schema_sql=TASK4_SCHEMA,353    broken_query=TASK4_BROKEN,354    solution_query=TASK4_SOLUTION,355    expected_rows=TASK4_EXPECTED,356    hint="The WHERE clause has a SQL injection pattern (OR '1'='1'). Replace the entire WHERE clause to filter on u.active = 1 instead.",357    hint_advanced=(358        "Replace the WHERE clause entirely: WHERE u.active = 1. "359        "Remove the username injection pattern completely. Keep the LEFT JOIN and GROUP BY as-is."360    ),361    max_attempts=6,362)363 364 365# ─── Task 5: Missing Index & Rewrite (Expert) ─────────────────────────────────366# NEW TASK: A reporting query does multiple full-table scans because it lacks367# proper use of indexed columns and uses inefficient LIKE patterns + OR chains.368# Agent must rewrite using indexed column lookups and CTEs for clarity.369 370TASK5_SCHEMA = """371CREATE TABLE transactions (372    id INTEGER PRIMARY KEY,373    account_id INTEGER NOT NULL,374    transaction_type TEXT NOT NULL,375    amount REAL NOT NULL,376    currency TEXT NOT NULL,377    transaction_date TEXT NOT NULL,378    status TEXT NOT NULL,379    merchant TEXT380);381CREATE TABLE accounts (382    id INTEGER PRIMARY KEY,383    owner_name TEXT NOT NULL,384    account_type TEXT NOT NULL,385    country TEXT NOT NULL,386    opened_date TEXT NOT NULL387);388INSERT INTO accounts VALUES389  (1, 'Sarah Connor',  'checking', 'US', '2020-01-10'),390  (2, 'John Wick',     'savings',  'US', '2019-06-15'),391  (3, 'Ellen Ripley',  'checking', 'UK', '2021-03-20'),392  (4, 'Rick Deckard',  'savings',  'US', '2018-11-05'),393  (5, 'Dana Scully',   'checking', 'CA', '2022-07-01');394INSERT INTO transactions VALUES395  (1,  1, 'debit',  250.00, 'USD', '2024-11-01', 'completed', 'Amazon'),396  (2,  1, 'credit', 3000.00,'USD', '2024-11-05', 'completed', NULL),397  (3,  2, 'debit',  1500.00,'USD', '2024-11-10', 'completed', 'Rent'),398  (4,  2, 'credit', 5000.00,'USD', '2024-11-15', 'completed', NULL),399  (5,  3, 'debit',  89.99,  'GBP', '2024-11-03', 'completed', 'Tesco'),400  (6,  3, 'debit',  450.00, 'GBP', '2024-11-20', 'pending',   'British Gas'),401  (7,  4, 'debit',  2200.00,'USD', '2024-11-12', 'completed', 'Mortgage'),402  (8,  4, 'credit', 4500.00,'USD', '2024-11-28', 'completed', NULL),403  (9,  1, 'debit',  75.50,  'USD', '2024-12-01', 'completed', 'Starbucks'),404  (10, 5, 'credit', 2800.00,'CAD', '2024-12-05', 'completed', NULL),405  (11, 5, 'debit',  120.00, 'CAD', '2024-12-08', 'failed',    'Rogers'),406  (12, 2, 'debit',  350.00, 'USD', '2024-12-10', 'completed', 'Gym');407"""408 409# Broken: uses LIKE '%completed%' (no index use), OR chain for transaction types,410# and does not aggregate correctly — double-counts due to missing DISTINCT on join.411TASK5_BROKEN = """SELECT a.owner_name,412       a.account_type,413       a.country,414       SUM(t.amount) AS total_spent,415       COUNT(t.id) AS transaction_count416FROM accounts a417LEFT JOIN transactions t ON a.id = t.account_id418WHERE t.status LIKE '%completed%'419   OR t.status LIKE '%done%'420   OR t.status LIKE '%processed%'421GROUP BY a.id422ORDER BY total_spent DESC;"""423 424TASK5_SOLUTION = """WITH completed_txns AS (425    SELECT account_id,426           SUM(CASE WHEN transaction_type = 'debit' THEN amount ELSE 0 END) AS total_spent,427           COUNT(*) AS transaction_count428    FROM transactions429    WHERE status = 'completed'430      AND transaction_type = 'debit'431    GROUP BY account_id432)433SELECT a.owner_name,434       a.account_type,435       a.country,436       COALESCE(c.total_spent, 0) AS total_spent,437       COALESCE(c.transaction_count, 0) AS transaction_count438FROM accounts a439LEFT JOIN completed_txns c ON a.id = c.account_id440ORDER BY total_spent DESC;"""441 442TASK5_EXPECTED = [443    {"owner_name": "Rick Deckard", "account_type": "savings",  "country": "US", "total_spent": 2200.0, "transaction_count": 1},444    {"owner_name": "John Wick",    "account_type": "savings",  "country": "US", "total_spent": 1850.0, "transaction_count": 2},445    {"owner_name": "Sarah Connor", "account_type": "checking", "country": "US", "total_spent": 325.5,  "transaction_count": 2},446    {"owner_name": "Dana Scully",  "account_type": "checking", "country": "CA", "total_spent": 0.0,    "transaction_count": 0},447    {"owner_name": "Ellen Ripley", "account_type": "checking", "country": "UK", "total_spent": 89.99,  "transaction_count": 1},448]449 450TASK5 = Task(451    task_id="task_reporting_rewrite",452    difficulty="expert",453    description=(454        "A financial reporting query uses LIKE '%completed%' pattern matching (preventing index use), "455        "an inefficient OR chain for status values, and incorrectly sums ALL transaction amounts "456        "instead of only debit transactions. Rewrite it using a CTE to pre-aggregate only completed "457        "debit transactions per account, then LEFT JOIN to accounts. "458        "Return all 5 accounts ordered by total_spent descending — accounts with no completed debits should show 0."459    ),460    schema_sql=TASK5_SCHEMA,461    broken_query=TASK5_BROKEN,462    solution_query=TASK5_SOLUTION,463    expected_rows=TASK5_EXPECTED,464    hint=(465        "Three issues: (1) Use status = 'completed' exact match instead of LIKE. "466        "(2) Filter to transaction_type = 'debit' only. "467        "(3) Use a CTE or subquery to pre-aggregate before joining to accounts."468    ),469    hint_advanced=(470        "Use: WITH completed_txns AS (SELECT account_id, SUM(amount) AS total_spent, COUNT(*) AS transaction_count "471        "FROM transactions WHERE status = 'completed' AND transaction_type = 'debit' GROUP BY account_id) "472        "then LEFT JOIN this CTE to accounts and COALESCE nulls to 0."473    ),474    max_attempts=8,475    check_optimization=True,476    optimization_target_cost=3,477)478 479 480# ─── Task Registry ────────────────────────────────────────────────────────────481 482TASKS: dict[str, Task] = {483    TASK1.task_id: TASK1,484    TASK2.task_id: TASK2,485    TASK3.task_id: TASK3,486    TASK4.task_id: TASK4,487    TASK5.task_id: TASK5,488}489 490TASK_LIST = [491    {492        "task_id": t.task_id,493        "difficulty": t.difficulty,494        "description": t.description,495        "max_attempts": t.max_attempts,496        "check_optimization": t.check_optimization,497        "action_schema": {498            "type": "object",499            "properties": {500                "sql_query": {501                    "type": "string",502                    "description": "The SQL query to execute as the agent's attempt",503                }504            },505            "required": ["sql_query"],506        },507    }508    for t in [TASK1, TASK2, TASK3, TASK4, TASK5]509]