ormlambda 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ormlambda/__init__.py +4 -0
- ormlambda/common/__init__.py +2 -0
- ormlambda/common/abstract_classes/__init__.py +3 -0
- ormlambda/common/abstract_classes/abstract_model.py +302 -0
- ormlambda/common/abstract_classes/non_query_base.py +33 -0
- ormlambda/common/abstract_classes/query_base.py +10 -0
- ormlambda/common/enums/__init__.py +2 -0
- ormlambda/common/enums/condition_types.py +16 -0
- ormlambda/common/enums/join_type.py +10 -0
- ormlambda/common/interfaces/INonQueryCommand.py +9 -0
- ormlambda/common/interfaces/IQueryCommand.py +11 -0
- ormlambda/common/interfaces/IRepositoryBase.py +67 -0
- ormlambda/common/interfaces/IStatements.py +227 -0
- ormlambda/common/interfaces/__init__.py +4 -0
- ormlambda/components/__init__.py +0 -0
- ormlambda/components/delete/IDelete.py +6 -0
- ormlambda/components/delete/__init__.py +2 -0
- ormlambda/components/delete/abstract_delete.py +14 -0
- ormlambda/components/insert/IInsert.py +6 -0
- ormlambda/components/insert/__init__.py +2 -0
- ormlambda/components/insert/abstract_insert.py +21 -0
- ormlambda/components/select/ISelect.py +14 -0
- ormlambda/components/select/__init__.py +2 -0
- ormlambda/components/select/table_column.py +39 -0
- ormlambda/components/update/IUpdate.py +7 -0
- ormlambda/components/update/__init__.py +2 -0
- ormlambda/components/update/abstract_update.py +25 -0
- ormlambda/components/upsert/IUpsert.py +6 -0
- ormlambda/components/upsert/__init__.py +2 -0
- ormlambda/components/upsert/abstract_upsert.py +21 -0
- ormlambda/components/where/__init__.py +1 -0
- ormlambda/components/where/abstract_where.py +11 -0
- ormlambda/databases/__init__.py +0 -0
- ormlambda/databases/my_sql/__init__.py +2 -0
- ormlambda/databases/my_sql/clauses/__init__.py +13 -0
- ormlambda/databases/my_sql/clauses/create_database.py +29 -0
- ormlambda/databases/my_sql/clauses/delete.py +54 -0
- ormlambda/databases/my_sql/clauses/drop_database.py +19 -0
- ormlambda/databases/my_sql/clauses/drop_table.py +23 -0
- ormlambda/databases/my_sql/clauses/insert.py +70 -0
- ormlambda/databases/my_sql/clauses/joins.py +103 -0
- ormlambda/databases/my_sql/clauses/limit.py +17 -0
- ormlambda/databases/my_sql/clauses/offset.py +17 -0
- ormlambda/databases/my_sql/clauses/order.py +29 -0
- ormlambda/databases/my_sql/clauses/select.py +172 -0
- ormlambda/databases/my_sql/clauses/update.py +52 -0
- ormlambda/databases/my_sql/clauses/upsert.py +68 -0
- ormlambda/databases/my_sql/clauses/where_condition.py +219 -0
- ormlambda/databases/my_sql/repository.py +192 -0
- ormlambda/databases/my_sql/statements.py +86 -0
- ormlambda/model_base.py +36 -0
- ormlambda/utils/__init__.py +3 -0
- ormlambda/utils/column.py +65 -0
- ormlambda/utils/dtypes.py +104 -0
- ormlambda/utils/foreign_key.py +36 -0
- ormlambda/utils/lambda_disassembler/__init__.py +4 -0
- ormlambda/utils/lambda_disassembler/dis_types.py +136 -0
- ormlambda/utils/lambda_disassembler/disassembler.py +69 -0
- ormlambda/utils/lambda_disassembler/dtypes.py +103 -0
- ormlambda/utils/lambda_disassembler/name_of.py +41 -0
- ormlambda/utils/lambda_disassembler/nested_element.py +44 -0
- ormlambda/utils/lambda_disassembler/tree_instruction.py +145 -0
- ormlambda/utils/module_tree/__init__.py +0 -0
- ormlambda/utils/module_tree/dfs_traversal.py +60 -0
- ormlambda/utils/module_tree/dynamic_module.py +237 -0
- ormlambda/utils/table_constructor.py +308 -0
- ormlambda-0.1.0.dist-info/LICENSE +21 -0
- ormlambda-0.1.0.dist-info/METADATA +268 -0
- ormlambda-0.1.0.dist-info/RECORD +70 -0
- ormlambda-0.1.0.dist-info/WHEEL +4 -0
ormlambda/__init__.py
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
from typing import Any, Callable, Optional, Type, override, Iterable, Literal
|
2
|
+
from enum import Enum
|
3
|
+
from collections import defaultdict
|
4
|
+
from abc import abstractmethod
|
5
|
+
import inspect
|
6
|
+
|
7
|
+
from ...utils import ForeignKey, Table
|
8
|
+
|
9
|
+
from ..interfaces import IQuery, IRepositoryBase, IStatements_two_generic
|
10
|
+
from ..interfaces.IStatements import OrderType
|
11
|
+
|
12
|
+
from ...components.update import UpdateQueryBase
|
13
|
+
from ...components.select import ISelect
|
14
|
+
from ...components.delete import DeleteQueryBase
|
15
|
+
from ...components.upsert import UpsertQueryBase
|
16
|
+
from ...components.select import TableColumn
|
17
|
+
from ...components.insert import InsertQueryBase
|
18
|
+
from ...components.where.abstract_where import AbstractWhere
|
19
|
+
|
20
|
+
|
21
|
+
class JoinType(Enum):
|
22
|
+
RIGHT_INCLUSIVE = "RIGHT JOIN"
|
23
|
+
LEFT_INCLUSIVE = "LEFT JOIN"
|
24
|
+
RIGHT_EXCLUSIVE = "RIGHT JOIN"
|
25
|
+
LEFT_EXCLUSIVE = "LEFT JOIN"
|
26
|
+
FULL_OUTER_INCLUSIVE = "RIGHT JOIN"
|
27
|
+
FULL_OUTER_EXCLUSIVE = "RIGHT JOIN"
|
28
|
+
INNER_JOIN = "INNER JOIN"
|
29
|
+
|
30
|
+
|
31
|
+
ORDER_QUERIES = Literal["select", "join", "where", "order", "with", "group by", "limit", "offset"]
|
32
|
+
|
33
|
+
|
34
|
+
class AbstractSQLStatements[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
35
|
+
__slots__ = ("_model", "_repository", "_query_list")
|
36
|
+
__order__: tuple[str, ...] = ("select", "join", "where", "order", "with", "group by", "limit", "offset")
|
37
|
+
|
38
|
+
def __init__(self, model: T, repository: IRepositoryBase[TRepo]) -> None:
|
39
|
+
self.valid_repository(repository)
|
40
|
+
|
41
|
+
self._model: T = model
|
42
|
+
self._repository: IRepositoryBase[TRepo] = repository
|
43
|
+
self._query_list: dict[ORDER_QUERIES, list[IQuery]] = defaultdict(list)
|
44
|
+
|
45
|
+
if not issubclass(self._model, Table):
|
46
|
+
# Deben heredar de Table ya que es la forma que tenemos para identificar si estamos pasando una instancia del tipo que corresponde o no cuando llamamos a insert o upsert.
|
47
|
+
# Si no heredase de Table no sabriamos identificar el tipo de dato del que se trata porque al llamar a isinstance, obtendriamos el nombre de la clase que mapea a la tabla, Encargo, Edificio, Presupuesto y no podriamos crear una clase generica
|
48
|
+
raise Exception(f"'{model}' class does not inherit from Table class")
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
def valid_repository(repository: Any) -> bool:
|
52
|
+
if not isinstance(repository, IRepositoryBase):
|
53
|
+
raise ValueError(f"'repository' attribute does not instance of '{IRepositoryBase.__name__}'")
|
54
|
+
return True
|
55
|
+
|
56
|
+
@property
|
57
|
+
@abstractmethod
|
58
|
+
def INSERT_QUERY(self) -> Type[InsertQueryBase[T, TRepo]]: ...
|
59
|
+
@property
|
60
|
+
@abstractmethod
|
61
|
+
def UPSERT_QUERY(self) -> Type[UpsertQueryBase[T, TRepo]]: ...
|
62
|
+
@property
|
63
|
+
@abstractmethod
|
64
|
+
def UPDATE_QUERY(self) -> Type[UpdateQueryBase[T, TRepo]]: ...
|
65
|
+
@property
|
66
|
+
@abstractmethod
|
67
|
+
def DELETE_QUERY(self) -> Type[DeleteQueryBase[T, TRepo]]: ...
|
68
|
+
@property
|
69
|
+
@abstractmethod
|
70
|
+
def LIMIT_QUERY(self) -> Type[IQuery]: ...
|
71
|
+
@property
|
72
|
+
@abstractmethod
|
73
|
+
def OFFSET_QUERY(self) -> Type[IQuery]: ...
|
74
|
+
@property
|
75
|
+
@abstractmethod
|
76
|
+
def JOIN_QUERY(self) -> Type[IQuery]: ...
|
77
|
+
@property
|
78
|
+
@abstractmethod
|
79
|
+
def WHERE_QUERY(self) -> Type[IQuery]: ...
|
80
|
+
@property
|
81
|
+
@abstractmethod
|
82
|
+
def ORDER_QUERY(self) -> Type[IQuery]: ...
|
83
|
+
@property
|
84
|
+
@abstractmethod
|
85
|
+
def SELECT_QUERY(self) -> Type[ISelect]: ...
|
86
|
+
|
87
|
+
@override
|
88
|
+
def create_table(self) -> None:
|
89
|
+
if not self._repository.table_exists(self._model.__table_name__):
|
90
|
+
self._repository.execute(self._model.create_table_query())
|
91
|
+
return None
|
92
|
+
|
93
|
+
@override
|
94
|
+
def table_exists(self) -> bool:
|
95
|
+
return self._repository.table_exists(self._model.__table_name__)
|
96
|
+
|
97
|
+
@override
|
98
|
+
def insert(self, instances: T | list[T]) -> None:
|
99
|
+
insert = self.INSERT_QUERY(self._model, self._repository)
|
100
|
+
insert.insert(instances)
|
101
|
+
insert.execute()
|
102
|
+
self._query_list.clear()
|
103
|
+
return None
|
104
|
+
|
105
|
+
@override
|
106
|
+
def delete(self, instances: Optional[T | list[T]] = None) -> None:
|
107
|
+
if instances is None:
|
108
|
+
response = self.select()
|
109
|
+
if len(response) == 0:
|
110
|
+
return None
|
111
|
+
# [0] because if we do not select anything, we retrieve all columns of the unic model, stored in tuple[tuple[model]] structure.
|
112
|
+
# We always going to have a tuple of one element
|
113
|
+
return self.delete(response)
|
114
|
+
|
115
|
+
delete = self.DELETE_QUERY(self._model, self._repository)
|
116
|
+
delete.delete(instances)
|
117
|
+
delete.execute()
|
118
|
+
# not necessary to call self._query_list.clear() because select() method already call it
|
119
|
+
return None
|
120
|
+
|
121
|
+
@override
|
122
|
+
def upsert(self, instances: T | list[T]) -> None:
|
123
|
+
upsert = self.UPSERT_QUERY(self._model, self._repository)
|
124
|
+
upsert.upsert(instances)
|
125
|
+
upsert.execute()
|
126
|
+
self._query_list.clear()
|
127
|
+
return None
|
128
|
+
|
129
|
+
@override
|
130
|
+
def update(self, dicc: dict[str, Any] | list[dict[str, Any]]) -> None:
|
131
|
+
update = self.UPDATE_QUERY(self._model, self._repository, self._query_list["where"])
|
132
|
+
update.update(dicc)
|
133
|
+
update.execute()
|
134
|
+
self._query_list.clear()
|
135
|
+
return None
|
136
|
+
|
137
|
+
@override
|
138
|
+
def limit(self, number: int) -> "IStatements_two_generic[T,TRepo]":
|
139
|
+
limit = self.LIMIT_QUERY(number)
|
140
|
+
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
141
|
+
limit_list = self._query_list["limit"]
|
142
|
+
if len(limit_list) > 0:
|
143
|
+
self._query_list["limit"] = [limit]
|
144
|
+
else:
|
145
|
+
self._query_list["limit"].append(limit)
|
146
|
+
return self
|
147
|
+
|
148
|
+
@override
|
149
|
+
def offset(self, number: int) -> "IStatements_two_generic[T,TRepo]":
|
150
|
+
offset = self.OFFSET_QUERY(number)
|
151
|
+
self._query_list["offset"].append(offset)
|
152
|
+
return self
|
153
|
+
|
154
|
+
@override
|
155
|
+
def join(self, table_left: Table, table_right: Table, *, by: str) -> "IStatements_two_generic[T,TRepo]":
|
156
|
+
where = ForeignKey.MAPPED[table_left][table_right]
|
157
|
+
join_query = self.JOIN_QUERY[table_left, Table](table_left, table_right, JoinType(by), where=where)
|
158
|
+
self._query_list["join"].append(join_query)
|
159
|
+
return self
|
160
|
+
|
161
|
+
@override
|
162
|
+
def where(self, lambda_: Callable[[T], bool] = lambda: None, **kwargs) -> "IStatements_two_generic[T,TRepo]":
|
163
|
+
# FIXME [x]: I've wrapped self._model into tuple to pass it instance attr. Idk if it's correct
|
164
|
+
where_query = self.WHERE_QUERY[T](function=lambda_, instances=(self._model,), **kwargs)
|
165
|
+
self._query_list["where"].append(where_query)
|
166
|
+
return self
|
167
|
+
|
168
|
+
@override
|
169
|
+
def order[TValue](self, _lambda_col: Callable[[T], TValue], order_type: OrderType) -> "IStatements_two_generic[T,TRepo]":
|
170
|
+
order = self.ORDER_QUERY[T](self._model, _lambda_col, order_type)
|
171
|
+
self._query_list["order"].append(order)
|
172
|
+
return self
|
173
|
+
|
174
|
+
@override
|
175
|
+
def select[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Optional[Type[TFlavour]] = None, by: JoinType = JoinType.INNER_JOIN):
|
176
|
+
if len(inspect.signature(selector).parameters) == 0:
|
177
|
+
# COMMENT: if we do not specify any lambda function we assumed the user want to retreive only elements of the Model itself avoiding other models
|
178
|
+
result = self.select(selector=lambda x: (x,), flavour=flavour, by=by)
|
179
|
+
# COMMENT: Always we want to retrieve tuple[tuple[Any]]. That's the reason to return result[0] when we ensure the user want only objects of the first table.
|
180
|
+
# Otherwise, we wil return the result itself
|
181
|
+
if flavour:
|
182
|
+
return result
|
183
|
+
return () if not result else result[0]
|
184
|
+
select: ISelect = self.SELECT_QUERY(self._model, select_lambda=selector, by=by)
|
185
|
+
self._query_list["select"].append(select)
|
186
|
+
|
187
|
+
query: str = self.build()
|
188
|
+
if flavour:
|
189
|
+
result = self._return_flavour(query, flavour)
|
190
|
+
if issubclass(flavour, tuple) and isinstance(selector(self._model), property):
|
191
|
+
return tuple([x[0] for x in result])
|
192
|
+
return result
|
193
|
+
return self._return_model(select, query)
|
194
|
+
|
195
|
+
def _return_flavour[TValue](self, query, flavour: Type[TValue]) -> tuple[TValue]:
|
196
|
+
return self._repository.read_sql(query, flavour=flavour)
|
197
|
+
|
198
|
+
def _return_model(self, select: ISelect, query: str):
|
199
|
+
response_sql = self._repository.read_sql(query, flavour=dict) # store all columns of the SQL query
|
200
|
+
|
201
|
+
if isinstance(response_sql, Iterable):
|
202
|
+
return ClusterQuery(select, response_sql).clean_response()
|
203
|
+
|
204
|
+
return response_sql
|
205
|
+
|
206
|
+
@override
|
207
|
+
def select_one[TValue, TFlavour, *Ts](self, selector: Optional[Callable[[T], tuple[TValue, *Ts]]] = lambda: None, *, flavour: Optional[Type[TFlavour]] = None, by: JoinType = JoinType.INNER_JOIN):
|
208
|
+
self.limit(1)
|
209
|
+
if len(inspect.signature(selector).parameters) == 0:
|
210
|
+
response = self.select(selector=lambda x: (x,), flavour=flavour, by=by)
|
211
|
+
else:
|
212
|
+
response = self.select(selector=selector, flavour=flavour, by=by)
|
213
|
+
|
214
|
+
if flavour:
|
215
|
+
return response[0]
|
216
|
+
|
217
|
+
# response var could be return more than one element when we work with models an we
|
218
|
+
# select columns from different tables using a join query
|
219
|
+
if len(response) == 1 and len(response[0]) == 1:
|
220
|
+
return response[0][0]
|
221
|
+
return tuple([res[0] for res in response])
|
222
|
+
|
223
|
+
@override
|
224
|
+
def build(self) -> str:
|
225
|
+
query: str = ""
|
226
|
+
|
227
|
+
self._create_necessary_inner_join()
|
228
|
+
for x in self.__order__:
|
229
|
+
if sub_query := self._query_list.get(x, None):
|
230
|
+
if isinstance(sub_query[0], self.WHERE_QUERY):
|
231
|
+
query_ = self.__build_where_clause(sub_query)
|
232
|
+
|
233
|
+
# we must check if any join already exists on query string
|
234
|
+
elif isinstance(sub_query[0], self.JOIN_QUERY):
|
235
|
+
select_query: str = self._query_list["select"][0].query
|
236
|
+
query_ = ""
|
237
|
+
for join in sub_query:
|
238
|
+
if join.query not in select_query:
|
239
|
+
query_ += f"\n{join.query}"
|
240
|
+
else:
|
241
|
+
query_ = "\n".join([x.query for x in sub_query])
|
242
|
+
|
243
|
+
query += f"\n{query_}" if query != "" else query_
|
244
|
+
self._query_list.clear()
|
245
|
+
return query
|
246
|
+
|
247
|
+
def __build_where_clause(self, where_condition: list[AbstractWhere]) -> str:
|
248
|
+
query: str = where_condition[0].query
|
249
|
+
|
250
|
+
for where in where_condition[1:]:
|
251
|
+
q = where.query.replace(where.WHERE, "AND")
|
252
|
+
and_, clause = q.split(" ", maxsplit=1)
|
253
|
+
query += f" {and_} ({clause})"
|
254
|
+
return query
|
255
|
+
|
256
|
+
def _create_necessary_inner_join(self) -> None:
|
257
|
+
# When we applied filters in any table that we wont select any column, we need to add manually all neccessary joins to achieve positive result.
|
258
|
+
if "where" not in self._query_list:
|
259
|
+
return None
|
260
|
+
|
261
|
+
where: AbstractWhere = self._query_list["where"][0]
|
262
|
+
involved_tables = where.get_involved_tables()
|
263
|
+
|
264
|
+
select: ISelect = self._query_list["select"][0]
|
265
|
+
if not involved_tables or (set(involved_tables) == set(select.tables_heritage)):
|
266
|
+
return None
|
267
|
+
|
268
|
+
for l_tbl, r_tbl in involved_tables:
|
269
|
+
# FIXME [ ]: Checked what function was called by the self.join method before the change
|
270
|
+
self.join(l_tbl, r_tbl, by="INNER JOIN")
|
271
|
+
|
272
|
+
|
273
|
+
class ClusterQuery:
|
274
|
+
def __init__(self, select: ISelect, response_sql: tuple[dict[str, Any]]) -> None:
|
275
|
+
self._select: ISelect = select
|
276
|
+
self._response_sql: tuple[dict[str, Any]] = response_sql
|
277
|
+
|
278
|
+
def loop_foo(self) -> dict[Type[Table], list[Table]]:
|
279
|
+
# We must ensure to get the valid attributes for each instance
|
280
|
+
table_initialize = defaultdict(list)
|
281
|
+
|
282
|
+
unic_table: dict[Table, list[TableColumn]] = defaultdict(list)
|
283
|
+
for table_col in self._select.select_list:
|
284
|
+
unic_table[table_col._table].append(table_col)
|
285
|
+
|
286
|
+
for table_, table_col in unic_table.items():
|
287
|
+
for dicc_cols in self._response_sql:
|
288
|
+
valid_attr: dict[str, Any] = {}
|
289
|
+
for col in table_col:
|
290
|
+
valid_attr[col.real_column] = dicc_cols[col.alias]
|
291
|
+
# COMMENT: At this point we are going to instantiate Table class with specific attributes getting directly from database
|
292
|
+
table_initialize[table_].append(table_(**valid_attr))
|
293
|
+
return table_initialize
|
294
|
+
|
295
|
+
def clean_response(self) -> tuple[dict[Type[Table], tuple[Table]]]:
|
296
|
+
tbl_dicc: dict[Type[Table], list[Table]] = self.loop_foo()
|
297
|
+
|
298
|
+
# it not depend of flavour attr
|
299
|
+
for key, val in tbl_dicc.items():
|
300
|
+
tbl_dicc[key] = tuple(val)
|
301
|
+
|
302
|
+
return tuple(tbl_dicc.values())
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
from typing import Any, Optional, override
|
3
|
+
|
4
|
+
from ..interfaces.INonQueryCommand import INonQueryCommand
|
5
|
+
|
6
|
+
from ..interfaces import IRepositoryBase
|
7
|
+
from ...utils import Table
|
8
|
+
|
9
|
+
|
10
|
+
class NonQueryBase[T: Table, TRepo: IRepositoryBase](INonQueryCommand):
|
11
|
+
__slots__: tuple[str, ...] = ("_model", "_repository", "_values", "_query")
|
12
|
+
|
13
|
+
def __init__(self, model: T, repository: TRepo) -> None:
|
14
|
+
self._model: T = model
|
15
|
+
self._repository: TRepo = repository
|
16
|
+
self._values: list[tuple[Any]] = []
|
17
|
+
self._query: Optional[str] = None
|
18
|
+
|
19
|
+
@property
|
20
|
+
@abstractmethod
|
21
|
+
def CLAUSE(self) -> str: ...
|
22
|
+
|
23
|
+
@abstractmethod
|
24
|
+
def execute(self) -> None: ...
|
25
|
+
|
26
|
+
@property
|
27
|
+
@override
|
28
|
+
def query(self) -> str:
|
29
|
+
return self._query
|
30
|
+
|
31
|
+
@property
|
32
|
+
def values(self) -> list[tuple[Any, ...]]:
|
33
|
+
return self._values
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
|
4
|
+
class ConditionType(Enum):
|
5
|
+
EQUAL = "=="
|
6
|
+
LESS_THAN = "<"
|
7
|
+
GREATER_THAN = ">"
|
8
|
+
LESS_THAN_OR_EQUAL = "<="
|
9
|
+
GREATER_THAN_OR_EQUAL = ">="
|
10
|
+
NOT_EQUAL = "!="
|
11
|
+
REGEXP = "REGEXP"
|
12
|
+
BETWEEN = "BETWEEN"
|
13
|
+
LIKE = "LIKE"
|
14
|
+
IN = "IN"
|
15
|
+
IS = "IS"
|
16
|
+
IS_NOT = "IS NOT"
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
class JoinType(Enum):
|
4
|
+
RIGHT_INCLUSIVE = "RIGHT JOIN"
|
5
|
+
LEFT_INCLUSIVE = "LEFT JOIN"
|
6
|
+
RIGHT_EXCLUSIVE = "RIGHT JOIN"
|
7
|
+
LEFT_EXCLUSIVE = "LEFT JOIN"
|
8
|
+
FULL_OUTER_INCLUSIVE = "RIGHT JOIN"
|
9
|
+
FULL_OUTER_EXCLUSIVE = "RIGHT JOIN"
|
10
|
+
INNER_JOIN = "INNER JOIN"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from abc import abstractmethod, ABC
|
2
|
+
|
3
|
+
|
4
|
+
class IQuery(ABC):
|
5
|
+
""" Interface to queries that retrieve any element such as select, limit, offset, where, group by, etc..."""
|
6
|
+
@property
|
7
|
+
@abstractmethod
|
8
|
+
def query(self) -> str: ...
|
9
|
+
|
10
|
+
def __repr__(self) -> str:
|
11
|
+
return f"{IQuery.__name__}: {self.__class__.__name__}"
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
import functools
|
3
|
+
from typing import Any, Callable, Literal, Optional, Type
|
4
|
+
|
5
|
+
TypeExists = Literal["fail", "replace", "append"]
|
6
|
+
|
7
|
+
|
8
|
+
class IRepositoryBase[T](ABC):
|
9
|
+
def check_connection(func: Callable[..., Any]):
|
10
|
+
@functools.wraps(func)
|
11
|
+
def wrapper(self: "IRepositoryBase[T]", *args, **kwargs):
|
12
|
+
if not self.is_connected():
|
13
|
+
self.connect()
|
14
|
+
|
15
|
+
foo = func(self, *args, **kwargs)
|
16
|
+
self.close_connection()
|
17
|
+
return foo
|
18
|
+
|
19
|
+
return wrapper
|
20
|
+
|
21
|
+
def __repr__(self) -> str:
|
22
|
+
return f"{IRepositoryBase.__name__}: {self.__class__.__name__}"
|
23
|
+
|
24
|
+
@abstractmethod
|
25
|
+
def is_connected(self) -> bool: ...
|
26
|
+
|
27
|
+
@abstractmethod
|
28
|
+
def connect(self, **kwargs: Any) -> "IRepositoryBase[T]": ...
|
29
|
+
|
30
|
+
@abstractmethod
|
31
|
+
def close_connection(self) -> None: ...
|
32
|
+
|
33
|
+
@abstractmethod
|
34
|
+
def read_sql[TFlavour](self, query: str, flavour: Optional[Type[TFlavour]], **kwargs) -> tuple[TFlavour]: ...
|
35
|
+
|
36
|
+
@abstractmethod
|
37
|
+
def executemany_with_values(self, query: str, values) -> None: ...
|
38
|
+
|
39
|
+
@abstractmethod
|
40
|
+
def execute_with_values(self, query: str, values) -> None: ...
|
41
|
+
|
42
|
+
@abstractmethod
|
43
|
+
def execute(self, query: str) -> None: ...
|
44
|
+
|
45
|
+
@abstractmethod
|
46
|
+
def drop_database(self, name: str) -> None: ...
|
47
|
+
|
48
|
+
@abstractmethod
|
49
|
+
def create_database(self, name: str, if_exists: TypeExists = "fail") -> None: ...
|
50
|
+
|
51
|
+
@abstractmethod
|
52
|
+
def drop_table(self, name: str) -> None: ...
|
53
|
+
|
54
|
+
@abstractmethod
|
55
|
+
def table_exists(self, name: str) -> bool: ...
|
56
|
+
|
57
|
+
@abstractmethod
|
58
|
+
def database_exists(self, name: str) -> bool: ...
|
59
|
+
|
60
|
+
@property
|
61
|
+
@abstractmethod
|
62
|
+
def connection(self) -> T: ...
|
63
|
+
|
64
|
+
@abstractmethod
|
65
|
+
def set_config(self, value: dict[str, Any]) -> dict[str, Any]:
|
66
|
+
"""Method to update database config"""
|
67
|
+
...
|