Coverage for chalicelib/core/db_request_handler.py: 0%
146 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:56 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:56 +0000
1import logging
3from chalicelib.utils import pg_client
4from chalicelib.utils.log import sanitize
6logger = logging.getLogger(__name__)
9class DatabaseRequestHandler:
10 def __init__(self, table_name):
11 self.table_name = table_name
12 self.constraints = []
13 self.params = {}
14 self.order_clause = ""
15 self.sort_clause = ""
16 self.select_columns = []
17 self.sub_queries = []
18 self.joins = []
19 self.group_by_clause = ""
20 self.client = pg_client
21 self.logger = logging.getLogger(__name__)
22 self.pagination = {}
24 def add_constraint(self, constraint, param=None):
25 self.constraints.append(constraint)
26 if param:
27 self.params.update(param)
29 def add_subquery(self, subquery, alias, param=None):
30 self.sub_queries.append((subquery, alias))
31 if param:
32 self.params.update(param)
34 def add_join(self, join_clause):
35 self.joins.append(join_clause)
37 def add_param(self, key, value):
38 self.params[key] = value
40 def set_order_by(self, order_by):
41 self.order_clause = order_by
43 def set_sort_by(self, sort_by):
44 self.sort_clause = sort_by
46 def set_select_columns(self, columns):
47 self.select_columns = columns
49 def set_group_by(self, group_by_clause):
50 self.group_by_clause = group_by_clause
52 def set_pagination(self, page, page_size):
53 """
54 Set pagination parameters for the query.
55 :param page: The page number (1-indexed)
56 :param page_size: Number of items per page
57 """
58 self.pagination = {
59 'offset': (page - 1) * page_size,
60 'limit': page_size
61 }
63 def build_query(self, action="select", additional_clauses=None, data=None):
65 if action == "select":
66 query = f"SELECT {', '.join(self.select_columns)} FROM {self.table_name}"
67 elif action == "insert":
68 columns = ', '.join(data.keys())
69 placeholders = ', '.join(f'%({k})s' for k in data.keys())
70 query = f"INSERT INTO {self.table_name} ({columns}) VALUES ({placeholders})"
71 elif action == "update":
72 set_clause = ', '.join(f"{k} = %({k})s" for k in data.keys())
73 query = f"UPDATE {self.table_name} SET {set_clause}"
74 elif action == "delete":
75 query = f"DELETE FROM {self.table_name}"
77 for join in self.joins:
78 query += f" {join}"
79 for subquery, alias in self.sub_queries:
80 query += f", ({subquery}) AS {alias}"
81 if self.constraints:
82 query += " WHERE " + " AND ".join(self.constraints)
83 if action == "select":
84 if self.group_by_clause:
85 query += " GROUP BY " + self.group_by_clause
86 if self.sort_clause:
87 query += " ORDER BY " + self.sort_clause
88 if self.order_clause:
89 query += " " + self.order_clause
90 if hasattr(self, 'pagination') and self.pagination:
91 query += " LIMIT %(limit)s OFFSET %(offset)s"
92 self.params.update(self.pagination)
94 if additional_clauses:
95 query += " " + additional_clauses
97 logger.debug(f"Query: {query}")
98 return query
100 def execute_query(self, query, data=None):
101 try:
102 with self.client.PostgresClient() as cur:
103 mogrified_query = cur.mogrify(query, {**data, **self.params} if data else self.params)
104 cur.execute(mogrified_query)
105 return cur.fetchall() if cur.description else None
106 except Exception as e:
107 self.logger.error(f"Database operation failed: {sanitize(str(e))}")
108 raise
110 def fetchall(self):
111 query = self.build_query()
112 return self.execute_query(query)
114 def fetchone(self):
115 query = self.build_query()
116 result = self.execute_query(query)
117 return result[0] if result else None
119 def insert(self, data):
120 query = self.build_query(action="insert", data=data)
121 query += " RETURNING *;"
123 result = self.execute_query(query, data)
124 return result[0] if result else None
126 def update(self, data):
127 query = self.build_query(action="update", data=data)
128 query += " RETURNING *;"
130 result = self.execute_query(query, data)
131 return result[0] if result else None
133 def delete(self):
134 query = self.build_query(action="delete")
135 return self.execute_query(query)
137 def batch_insert(self, items):
138 if not items:
139 return None
141 columns = ', '.join(items[0].keys())
143 # Building a values string with unique parameter names for each item
144 all_values_query = ', '.join(
145 '(' + ', '.join([f"%({key}_{i})s" for key in item]) + ')'
146 for i, item in enumerate(items)
147 )
149 query = f"INSERT INTO {self.table_name} ({columns}) VALUES {all_values_query} RETURNING *;"
151 try:
152 with self.client.PostgresClient() as cur:
153 # Flatten items into a single dictionary with unique keys
154 combined_params = {f"{k}_{i}": v for i, item in enumerate(items) for k, v in item.items()}
155 mogrified_query = cur.mogrify(query, combined_params)
156 cur.execute(mogrified_query)
157 return cur.fetchall()
158 except Exception as e:
159 self.logger.error(f"Database batch insert operation failed: {sanitize(str(e))}")
160 raise
162 def raw_query(self, query, params=None):
163 try:
164 with self.client.PostgresClient() as cur:
165 mogrified_query = cur.mogrify(query, params)
166 cur.execute(mogrified_query)
167 return cur.fetchall() if cur.description else None
168 except Exception as e:
169 self.logger.error(f"Database operation failed: {sanitize(str(e))}")
170 raise
172 def batch_update(self, items):
173 if not items:
174 return None
176 id_column = list(items[0])[0]
178 # Building the set clause for the update statement
179 update_columns = list(items[0].keys())
180 update_columns.remove(id_column)
181 set_clause = ', '.join([f"{col} = v.{col}" for col in update_columns])
183 # Building the values part for the 'VALUES' section
184 values_rows = []
185 for item in items:
186 values = ', '.join([f"%({key})s" for key in item.keys()])
187 values_rows.append(f"({values})")
188 values_query = ', '.join(values_rows)
190 # Constructing the full update query
191 query = f"""
192 UPDATE {self.table_name} AS t
193 SET {set_clause}
194 FROM (VALUES {values_query}) AS v ({', '.join(items[0].keys())})
195 WHERE t.{id_column} = v.{id_column};
196 """
198 try:
199 with self.client.PostgresClient() as cur:
200 # Flatten items into a single dictionary for mogrify
201 combined_params = {k: v for item in items for k, v in item.items()}
202 mogrified_query = cur.mogrify(query, combined_params)
203 cur.execute(mogrified_query)
204 except Exception as e:
205 self.logger.error(f"Database batch update operation failed: {sanitize(str(e))}")
206 raise