Film Inventory Management

L3
ModelContextProtocolPostgresDVD Rental

Manage film inventory through multiple operations including adding films, updating records, and cleaning old data.

Created by Lingxiao Du
2025-08-20
Data MigrationTransactional OperationsSchema Design

Model Ranking

Click on the dots to view the trajectory of each task run
Model
Run Results
Pass@4
Pass^4
Avg Time
Avg Turns
Input Tokens
Output Tokens
Total Tokens
Claude
claude-sonnet-4
4
/4
136.1s
23.8
178,406
4,607
183,013
Claude
claude-sonnet-4-high
4
/4
109.5s
22.0
223,053
4,010
227,063
Claude
claude-sonnet-4-low
4
/4
130.0s
22.0
249,173
4,329
253,502
DeepSeek
deepseek-chat
4
/4
196.8s
19.8
150,390
2,095
152,485
Gemini
gemini-2-5-pro
4
/4
89.7s
9.5
23,575
7,610
31,185
OpenAI
gpt-5-high
4
/4
959.2s
17.8
140,733
36,792
177,525
OpenAI
gpt-5-low
4
/4
393.2s
13.8
62,512
23,318
85,830
OpenAI
gpt-5-medium
4
/4
519.8s
15.3
102,917
25,643
128,560
OpenAI
gpt-5-mini-high
4
/4
198.4s
14.8
281,714
18,512
300,226
OpenAI
gpt-5-mini-medium
4
/4
105.6s
15.5
302,687
10,295
312,982
Grok
grok-4
4
/4
116.1s
16.5
123,744
4,432
128,175
Grok
grok-code-fast-1
4
/4
35.9s
20.3
204,485
3,179
207,664
MoonshotAI
kimi-k2-0711
4
/4
185.3s
22.5
175,619
2,633
178,252
MoonshotAI
kimi-k2-0905
4
/4
225.4s
19.3
159,801
2,596
162,397
Qwen
qwen-3-coder-plus
4
/4
60.5s
25.0
271,699
2,432
274,130
Qwen
qwen-3-max
4
/4
30.9s
13.0
73,724
830
74,553
OpenAI
o3
3
/4
43.1s
6.0
22,277
2,988
25,265
Z.ai
glm-4-5
2
/4
85.6s
21.5
117,567
3,088
120,655
OpenAI
gpt-5-nano-low
2
/4
132.8s
15.3
58,951
27,027
85,978
Claude
claude-opus-4-1
1
/1
--
201.1s
13.0
57,692
3,526
61,218
Gemini
gemini-2-5-flash
1
/4
22.6s
6.5
16,001
2,204
18,205
OpenAI
gpt-5-nano-high
1
/4
215.8s
12.5
71,010
40,282
111,292
OpenAI
gpt-5-nano-medium
1
/4
87.8s
15.8
116,354
15,759
132,113
OpenAI
gpt-4-1
0
/4
16.6s
6.8
12,305
389
12,694
OpenAI
gpt-4-1-mini
0
/4
24.3s
13.5
39,730
543
40,273
OpenAI
gpt-4-1-nano
0
/4
15.1s
4.0
8,899
752
9,650
OpenAI
gpt-5-mini-low
0
/4
12.0s
2.8
2,943
348
3,291
OpenAI
gpt-oss-120b
0
/4
16.3s
6.8
21,412
1,068
22,480
OpenAI
o4-mini
0
/4
70.5s
6.3
17,724
5,422
23,146

Task State

Enum "mpaa_rating" { "G" "PG" "PG-13" "R" "NC-17" } Table "customer" { "customer_id" int4 [pk, not null, increment] "store_id" int2 [not null] "first_name" varchar(45) [not null] "last_name" varchar(45) [not null] "email" varchar(50) "address_id" int2 [not null] "activebool" bool [not null, default: true] "create_date" date [not null, default: `('now'::text)::date`] "last_update" timestamp [default: `now()`] "active" int4 Indexes { address_id [type: btree, name: "idx_fk_address_id"] store_id [type: btree, name: "idx_fk_store_id"] last_name [type: btree, name: "idx_last_name"] } } Table "actor" { "actor_id" int4 [pk, not null, increment] "first_name" varchar(45) [not null] "last_name" varchar(45) [not null] "last_update" timestamp [not null, default: `now()`] Indexes { last_name [type: btree, name: "idx_actor_last_name"] } } Table "category" { "category_id" int4 [pk, not null, increment] "name" varchar(25) [not null] "last_update" timestamp [not null, default: `now()`] } Table "film" { "film_id" int4 [pk, not null, increment] "title" varchar(255) [not null] "description" text "release_year" int4 "language_id" int2 [not null] "rental_duration" int2 [not null, default: 3] "rental_rate" numeric(4,2) [not null, default: 4.99] "length" int2 "replacement_cost" numeric(5,2) [not null, default: 19.99] "rating" mpaa_rating [default: 'G'] "last_update" timestamp [not null, default: `now()`] "special_features" "text[]" "fulltext" tsvector [not null] Indexes { fulltext [type: gist, name: "film_fulltext_idx"] language_id [type: btree, name: "idx_fk_language_id"] title [type: btree, name: "idx_title"] } } Table "film_actor" { "actor_id" int2 [not null] "film_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { (actor_id, film_id) [type: btree, name: "film_actor_pkey"] film_id [type: btree, name: "idx_fk_film_id"] } } Table "film_category" { "film_id" int2 [not null] "category_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { (film_id, category_id) [type: btree, name: "film_category_pkey"] } } Table "address" { "address_id" int4 [pk, not null, increment] "address" varchar(50) [not null] "address2" varchar(50) "district" varchar(20) [not null] "city_id" int2 [not null] "postal_code" varchar(10) "phone" varchar(20) [not null] "last_update" timestamp [not null, default: `now()`] Indexes { city_id [type: btree, name: "idx_fk_city_id"] } } Table "city" { "city_id" int4 [pk, not null, increment] "city" varchar(50) [not null] "country_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { country_id [type: btree, name: "idx_fk_country_id"] } } Table "country" { "country_id" int4 [pk, not null, increment] "country" varchar(50) [not null] "last_update" timestamp [not null, default: `now()`] } Table "inventory" { "inventory_id" int4 [pk, not null, increment] "film_id" int2 [not null] "store_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { (store_id, film_id) [type: btree, name: "idx_store_id_film_id"] } } Table "language" { "language_id" int4 [pk, not null, increment] "name" bpchar(20) [not null] "last_update" timestamp [not null, default: `now()`] } Table "payment" { "payment_id" int4 [pk, not null, increment] "customer_id" int2 [not null] "staff_id" int2 [not null] "rental_id" int4 [not null] "amount" numeric(5,2) [not null] "payment_date" timestamp [not null] Indexes { rental_id [type: btree, name: "idx_fk_rental_id"] staff_id [type: btree, name: "idx_fk_staff_id"] } } Table "rental" { "rental_id" int4 [pk, not null, increment] "rental_date" timestamp [not null] "inventory_id" int4 [not null] "customer_id" int2 [not null] "return_date" timestamp "staff_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] Indexes { (rental_date, inventory_id, customer_id) [type: btree, name: "idx_unq_rental_rental_date_inventory_id_customer_id"] inventory_id [type: btree, name: "idx_fk_inventory_id"] } } Table "staff" { "staff_id" int4 [pk, not null, increment] "first_name" varchar(45) [not null] "last_name" varchar(45) [not null] "address_id" int2 [not null] "email" varchar(50) "store_id" int2 [not null] "active" bool [not null, default: true] "username" varchar(16) [not null] "password" varchar(40) "last_update" timestamp [not null, default: `now()`] "picture" bytea } Table "store" { "store_id" int4 [pk, not null, increment] "manager_staff_id" int2 [unique, not null] "address_id" int2 [not null] "last_update" timestamp [not null, default: `now()`] } Ref "fk_address_city":"city"."city_id" < "address"."city_id" Ref "fk_city":"country"."country_id" < "city"."country_id" Ref "customer_address_id_fkey":"address"."address_id" < "customer"."address_id" [update: cascade, delete: restrict] Ref "film_language_id_fkey":"language"."language_id" < "film"."language_id" [update: cascade, delete: restrict] Ref "film_actor_actor_id_fkey":"actor"."actor_id" < "film_actor"."actor_id" [update: cascade, delete: restrict] Ref "film_actor_film_id_fkey":"film"."film_id" < "film_actor"."film_id" [update: cascade, delete: restrict] Ref "film_category_category_id_fkey":"category"."category_id" < "film_category"."category_id" [update: cascade, delete: restrict] Ref "film_category_film_id_fkey":"film"."film_id" < "film_category"."film_id" [update: cascade, delete: restrict] Ref "inventory_film_id_fkey":"film"."film_id" < "inventory"."film_id" [update: cascade, delete: restrict] Ref "payment_customer_id_fkey":"customer"."customer_id" < "payment"."customer_id" [update: cascade, delete: restrict] Ref "payment_rental_id_fkey":"rental"."rental_id" < "payment"."rental_id" [update: cascade, delete: set null] Ref "payment_staff_id_fkey":"staff"."staff_id" < "payment"."staff_id" [update: cascade, delete: restrict] Ref "rental_customer_id_fkey":"customer"."customer_id" < "rental"."customer_id" [update: cascade, delete: restrict] Ref "rental_inventory_id_fkey":"inventory"."inventory_id" < "rental"."inventory_id" [update: cascade, delete: restrict] Ref "rental_staff_id_key":"staff"."staff_id" < "rental"."staff_id" Ref "staff_address_id_fkey":"address"."address_id" < "staff"."address_id" [update: cascade, delete: restrict] Ref "store_address_id_fkey":"address"."address_id" < "store"."address_id" [update: cascade, delete: restrict] Ref "store_manager_staff_id_fkey":"staff"."staff_id" < "store"."manager_staff_id" [update: cascade, delete: restrict]

Instruction

Manage film inventory operations in the DVD rental database.

Background

You are the database administrator for the DVD rental store. The store manager has requested several database operations to manage the film inventory. You need to perform multiple operations including adding new films, updating inventory, querying available films, and cleaning up old records.

Your Task

Complete the following database operations in sequence:

1. Add New Films

Add these two new films to the database:

  • Film 1: Title "Data Science Adventures", Description "A thrilling journey through machine learning algorithms", Release Year 2024, Language ID 1, Rental Duration 5 days, Rental Rate 3.99,Length120minutes,ReplacementCost3.99, Length 120 minutes, Replacement Cost 15.99, Rating 'PG-13'
  • Film 2: Title "Cloud Computing Chronicles", Description "Exploring the world of distributed systems", Release Year 2024, Language ID 1, Rental Duration 7 days, Rental Rate 4.99,Length135minutes,ReplacementCost4.99, Length 135 minutes, Replacement Cost 18.99, Rating 'PG'

2. Add Inventory Records

For each new film, add 3 inventory records for store_id = 1 and 2 inventory records for store_id = 2.

3. Update Film Information

Update the rental_rate of all films with rating 'PG-13' to increase by 10% (multiply by 1.1).

4. Create Available Films Table

Create a table called available_films with the following structure:

  • film_id (INTEGER, PRIMARY KEY)
  • title (VARCHAR(255), NOT NULL)
  • rental_rate (NUMERIC(4,2), NOT NULL)
  • length (SMALLINT)

Populate this table with films that meet these criteria:

  • Have rental_rate between 3.00and3.00 and 5.00
  • Have length greater than 100 minutes
  • Are available in store_id = 1 (have at least 1 inventory record)

5. Clean Up Inventory

Delete inventory records for films that meet ALL of the following criteria:

  • Have a replacement_cost greater than $25.00
  • AND have rental_rate less than $1.00
  • AND have no rental history (no records in the rental table)

6. Create Summary Report Table

Create a table called film_inventory_summary with the following structure:

  • title (VARCHAR(255), NOT NULL)
  • rental_rate (NUMERIC(4,2), NOT NULL)
  • total_inventory (INTEGER, NOT NULL)
  • store1_count (INTEGER, NOT NULL)
  • store2_count (INTEGER, NOT NULL)

Populate this table with a summary query that shows:

  • Film title
  • Current rental rate (after any updates from step 3)
  • Total count of inventory records across all stores
  • Count of inventory records in store_id = 1
  • Count of inventory records in store_id = 2

Requirements for the summary report:

  • Include only films that currently have at least one inventory record
  • Insert the results sorted by inventory count from highest to lowest, and then alphabetically by film title
  • Ensure all counts reflect the state after completing the previous operations

Requirements

  • Complete all operations in the specified sequence
  • Ensure data integrity throughout all operations
  • Verify that your operations affect the expected number of records
  • Handle any constraint violations appropriately


Verify

*.py
Python
"""
Verification script for PostgreSQL Task 4: Film Inventory Management
"""

import os
import sys
import psycopg2
from decimal import Decimal

def rows_match(actual_row, expected_row):
    """Compare two rows with appropriate tolerance for decimals and floats."""
    if len(actual_row) != len(expected_row):
        return False
    
    for actual, expected in zip(actual_row, expected_row):
        if isinstance(actual, (Decimal, float)) and isinstance(expected, (Decimal, float)):
            # Use higher tolerance for floating point comparisons
            if abs(float(actual) - float(expected)) > 0.01:
                return False
        elif actual != expected:
            return False
    
    return True

def get_connection_params() -> dict:
    """Get database connection parameters."""
    return {
        "host": os.getenv("POSTGRES_HOST", "localhost"),
        "port": int(os.getenv("POSTGRES_PORT", 5432)),
        "database": os.getenv("POSTGRES_DATABASE"),
        "user": os.getenv("POSTGRES_USERNAME"),
        "password": os.getenv("POSTGRES_PASSWORD")
    }

def check_new_films(conn) -> bool:
    """Check if the two new films were added correctly."""
    with conn.cursor() as cur:
        cur.execute("""
            SELECT title, description, release_year, language_id, 
                   rental_duration, rental_rate, length, replacement_cost, 
                   rating
            FROM film 
            WHERE title IN ('Data Science Adventures', 'Cloud Computing Chronicles')
            ORDER BY title
        """)
        actual_films = cur.fetchall()
        
        expected_films = [
            ('Cloud Computing Chronicles', 'Exploring the world of distributed systems', 2024, 1, 7, Decimal('4.99'), 135, Decimal('18.99'), 'PG'),
            ('Data Science Adventures', 'A thrilling journey through machine learning algorithms', 2024, 1, 5, Decimal('4.389'), 120, Decimal('15.99'), 'PG-13')
        ]
        
        if len(actual_films) != len(expected_films):
            print(f"❌ Expected {len(expected_films)} new films, found {len(actual_films)}")
            return False
            
        mismatches = 0
        for i, (actual, expected) in enumerate(zip(actual_films, expected_films)):
            if not rows_match(actual, expected):
                print(f"❌ Film {i+1} mismatch: expected {expected}, got {actual}")
                mismatches += 1
                
        if mismatches > 0:
            print(f"❌ Total film mismatches: {mismatches}")
            return False
            
        print("✅ Both new films added correctly")
        return True

def check_inventory_records(conn) -> bool:
    """Check if inventory records were added for new films."""
    with conn.cursor() as cur:
        cur.execute("""
            SELECT f.title, i.store_id, COUNT(*) as count
            FROM film f
            JOIN inventory i ON f.film_id = i.film_id
            WHERE f.title IN ('Data Science Adventures', 'Cloud Computing Chronicles')
            GROUP BY f.title, i.store_id
            ORDER BY f.title, i.store_id
        """)
        actual_inventory = cur.fetchall()
        
        expected_inventory = [
            ('Cloud Computing Chronicles', 1, 3),
            ('Cloud Computing Chronicles', 2, 2), 
            ('Data Science Adventures', 1, 3),
            ('Data Science Adventures', 2, 2)
        ]
        
        if len(actual_inventory) != len(expected_inventory):
            print(f"❌ Expected {len(expected_inventory)} inventory groups, found {len(actual_inventory)}")
            return False
            
        mismatches = 0
        for i, (actual, expected) in enumerate(zip(actual_inventory, expected_inventory)):
            if not rows_match(actual, expected):
                print(f"❌ Inventory group {i+1} mismatch: expected {expected}, got {actual}")
                mismatches += 1
                
        if mismatches > 0:
            print(f"❌ Total inventory mismatches: {mismatches}")
            return False
                
        print("✅ Inventory records added correctly")
        return True

def check_available_films_table(conn) -> bool:
    """Check if available_films table was created and populated correctly."""
    with conn.cursor() as cur:
        # Get actual results from the created table
        cur.execute("""
            SELECT film_id, title, rental_rate, length
            FROM available_films
            ORDER BY rental_rate DESC, length DESC, title ASC
        """)
        actual_results = cur.fetchall()
        
        # Execute ground truth query
        cur.execute("""
            SELECT DISTINCT f.film_id, f.title, f.rental_rate, f.length
            FROM film f
            JOIN inventory i ON f.film_id = i.film_id
            WHERE f.rental_rate >= 3.00 AND f.rental_rate <= 5.00
            AND f.length > 100
            AND i.store_id = 1
            ORDER BY f.rental_rate DESC, f.length DESC, f.title ASC
        """)
        expected_results = cur.fetchall()
        
        if len(actual_results) != len(expected_results):
            print(f"❌ available_films table has {len(actual_results)} records, expected {len(expected_results)}")
            return False
            
        mismatches = 0
        for i, (actual, expected) in enumerate(zip(actual_results, expected_results)):
            if not rows_match(actual, expected):
                if mismatches < 5:  # Only show first 5 mismatches
                    print(f"❌ available_films row {i+1} mismatch: expected {expected}, got {actual}")
                mismatches += 1
                
        if mismatches > 0:
            print(f"❌ Total available_films mismatches: {mismatches}")
            return False
            
        print(f"✅ available_films table created and populated correctly ({len(actual_results)} records)")
        return True

def check_inventory_cleanup(conn) -> bool:
    """Check if inventory cleanup was performed correctly."""
    with conn.cursor() as cur:
        # Check that no inventory exists for films with replacement_cost > 25 AND rental_rate < 1
        # that also don't have rental records (safe to delete)
        cur.execute("""
            SELECT COUNT(*)
            FROM inventory i
            JOIN film f ON i.film_id = f.film_id
            WHERE f.replacement_cost > 25.00 AND f.rental_rate < 1.00
            AND NOT EXISTS (SELECT 1 FROM rental r WHERE r.inventory_id = i.inventory_id)
        """)
        
        remaining_count = cur.fetchone()[0]
        
        if remaining_count > 0:
            print(f"❌ Found {remaining_count} inventory records that should have been deleted (no rental history)")
            return False
            
        print("✅ Inventory cleanup completed correctly")
        return True

def check_summary_table(conn) -> bool:
    """Check if film_inventory_summary table was created and populated correctly."""
    with conn.cursor() as cur:
            
        # Get actual results from the created table
        cur.execute("""
            SELECT title, rental_rate, total_inventory, store1_count, store2_count
            FROM film_inventory_summary
        """)
        actual_results = cur.fetchall()
        
        # Execute ground truth query
        cur.execute("""
            SELECT f.title, f.rental_rate,
                   COUNT(i.inventory_id) as total_inventory,
                   COUNT(CASE WHEN i.store_id = 1 THEN 1 END) as store1_count,
                   COUNT(CASE WHEN i.store_id = 2 THEN 1 END) as store2_count
            FROM film f
            JOIN inventory i ON f.film_id = i.film_id
            GROUP BY f.film_id, f.title, f.rental_rate
            ORDER BY total_inventory DESC, f.title ASC
        """)
        expected_results = cur.fetchall()
        
        if len(actual_results) != len(expected_results):
            print(f"❌ film_inventory_summary table has {len(actual_results)} records, expected {len(expected_results)}")
            return False
            
        mismatches = 0
        for i, (actual, expected) in enumerate(zip(actual_results, expected_results)):
            if not rows_match(actual, expected):
                if mismatches < 5:  # Only show first 5 mismatches
                    print(f"❌ Summary row {i+1} mismatch: expected {expected}, got {actual}")
                mismatches += 1
                
        if mismatches > 0:
            print(f"❌ Total summary table mismatches: {mismatches}")
            return False
                
        print(f"✅ film_inventory_summary table created and populated correctly ({len(actual_results)} records)")
        return True

def main():
    """Main verification function."""
    print("=" * 70)
    print("PostgreSQL Task 4 Verification: Film Inventory Management")
    print("=" * 70)
    
    # Get connection parameters
    conn_params = get_connection_params()
    
    if not conn_params["database"]:
        print("❌ No database specified")
        sys.exit(1)
    
    try:
        # Connect to database
        conn = psycopg2.connect(**conn_params)
        
        # Verify all operations with short-circuit evaluation
        success = (
            check_new_films(conn) and 
            check_inventory_records(conn) and
            check_available_films_table(conn) and 
            check_inventory_cleanup(conn) and
            check_summary_table(conn)
        )
        
        conn.close()
        
        if success:
            print(f"\n🎉 Task verification: PASS")
            sys.exit(0)
        else:
            print(f"\n❌ Task verification: FAIL")
            sys.exit(1)
            
    except psycopg2.Error as e:
        print(f"❌ Database error: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"❌ Verification error: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()