import sqlite3 from typing import Any, Iterable, List, Tuple, Optional, Dict class SQLiteDB: """Thin wrapper around sqlite3 that exposes CRUD helpers.""" def __init__(self, db_path: str = ":memory:") -> None: """ Open a connection to the SQLite database. Parameters ---------- db_path : str Path to the database file. Use ':memory:' for an in‑memory DB. """ self.db_path = db_path self.conn: sqlite3.Connection = sqlite3.connect(self.db_path) # Row factory makes rows behave like dictionaries self.conn.row_factory = sqlite3.Row self.cursor: sqlite3.Cursor = self.conn.cursor() # ------------------------------------------------------------------ # Table helpers # ------------------------------------------------------------------ def create_users_table(self) -> None: """ Create a simple 'users' table if it does not exist. Table schema: id INTEGER PRIMARY KEY AUTOINCREMENT name TEXT NOT NULL age INTEGER """ sql = """ CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER ); """ self.cursor.execute(sql) self.conn.commit() # ------------------------------------------------------------------ # CRUD # ------------------------------------------------------------------ def insert_user(self, name: str, age: Optional[int] = None) -> int: """ Insert a new user. Returns ------- int The newly created row id. """ sql = "INSERT INTO users (name, age) VALUES (?, ?)" self.cursor.execute(sql, (name, age)) self.conn.commit() return self.cursor.lastrowid def get_user(self, user_id: int) -> Optional[Dict[str, Any]]: """ Retrieve a single user by id. Returns ------- dict or None Row as a dictionary if found, otherwise None. """ sql = "SELECT * FROM users WHERE id = ?" self.cursor.execute(sql, (user_id,)) row = self.cursor.fetchone() return dict(row) if row else None def get_all_users(self) -> List[Dict[str, Any]]: """ Retrieve all users. Returns ------- list of dict All rows as dictionaries. """ sql = "SELECT * FROM users" self.cursor.execute(sql) rows = self.cursor.fetchall() return [dict(row) for row in rows] def update_user(self, user_id: int, name: Optional[str] = None, age: Optional[int] = None) -> bool: """ Update a user's name and/or age. Parameters ---------- user_id : int The id of the user to update. name : str | None New name (or None to leave unchanged). age : int | None New age (or None to leave unchanged). Returns ------- bool True if a row was updated, False otherwise. """ # Build dynamic SET clause fields: List[str] = [] values: List[Any] = [] if name is not None: fields.append("name = ?") values.append(name) if age is not None: fields.append("age = ?") values.append(age) if not fields: # Nothing to update return False values.append(user_id) sql = f"UPDATE users SET {', '.join(fields)} WHERE id = ?" self.cursor.execute(sql, tuple(values)) self.conn.commit() return self.cursor.rowcount > 0 def delete_user(self, user_id: int) -> bool: """ Delete a user by id. Returns ------- bool True if a row was deleted, False otherwise. """ sql = "DELETE FROM users WHERE id = ?" self.cursor.execute(sql, (user_id,)) self.conn.commit() return self.cursor.rowcount > 0 # ------------------------------------------------------------------ # Utility # ------------------------------------------------------------------ def close(self) -> None: """Close the database connection.""" self.cursor.close() self.conn.close() # Context manager support def __enter__(self) -> "SQLiteDB": return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: self.close()