-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatabase.py
More file actions
99 lines (87 loc) · 3.21 KB
/
Copy pathdatabase.py
File metadata and controls
99 lines (87 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import sqlite3
from contextlib import contextmanager
from typing import Optional
class Database:
"""
Handles all raw database connection and query execution for the bank system.
No business logic lives here - just connection management and query execution.
"""
def __init__(self, db_path: str = "bank.db"):
self.db_path = db_path
self.con = sqlite3.connect(self.db_path, check_same_thread=False)
self.con.row_factory = sqlite3.Row # lets us access columns by name, not just index
self.cur = self.con.cursor()
self._create_tables_if_missing()
def _create_tables_if_missing(self):
self.cur.execute("""
CREATE TABLE IF NOT EXISTS bank (
account_no INTEGER PRIMARY KEY,
full_name TEXT NOT NULL,
date_of_birth TEXT NOT NULL,
gender TEXT NOT NULL,
phone_no TEXT NOT NULL,
current_balance REAL NOT NULL DEFAULT 0,
opening_date TEXT NOT NULL,
card_issued INTEGER NOT NULL DEFAULT 0,
pin_hash TEXT NOT NULL,
email TEXT,
address TEXT,
branch TEXT
)
""")
self.cur.execute("""
CREATE TABLE IF NOT EXISTS staff (
staff_id TEXT PRIMARY KEY,
staff_name TEXT NOT NULL,
password_hash TEXT NOT NULL
)
""")
self.con.commit()
def execute(self, query: str, params: tuple = (), fetch: Optional[str] = None):
"""
Runs a single query safely.
fetch: None (default, for INSERT/UPDATE/DELETE), "one", or "all"
Returns: None, a single row, or a list of rows - depending on fetch.
Raises sqlite3.Error on failure (caller decides how to handle it).
"""
try:
self.cur.execute(query, params)
if fetch == "one":
return self.cur.fetchone()
elif fetch == "all":
return self.cur.fetchall()
return None
except sqlite3.Error as e:
raise RuntimeError(f"Database error: {e}") from e
def executemany(self, query: str, param_list: list):
"""For bulk inserts, e.g. seeding dummy data."""
try:
self.cur.executemany(query, param_list)
except sqlite3.Error as e:
raise RuntimeError(f"Database error: {e}") from e
def commit(self):
self.con.commit()
def rollback(self):
self.con.rollback()
def close(self):
self.con.close()
@contextmanager
def transaction(self):
"""
Use for multi-step operations that must succeed or fail together,
e.g. transfer() which debits one account and credits another.
Usage:
with db.transaction():
db.execute(...)
db.execute(...)
"""
try:
yield
self.commit()
except Exception:
self.rollback()
raise
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()