dapper-sqls 1.1.3__py3-none-any.whl → 1.2.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.
- dapper_sqls/__init__.py +3 -1
- dapper_sqls/_types.py +25 -2
- dapper_sqls/async_dapper/async_executors.py +127 -52
- dapper_sqls/builders/model/model.py +418 -33
- dapper_sqls/builders/model/utils.py +334 -42
- dapper_sqls/builders/query.py +164 -43
- dapper_sqls/builders/stored.py +15 -5
- dapper_sqls/builders/stp.py +6 -2
- dapper_sqls/config.py +40 -31
- dapper_sqls/dapper/executors.py +130 -55
- dapper_sqls/http/__init__.py +4 -0
- dapper_sqls/http/aiohttp.py +155 -0
- dapper_sqls/http/decorators.py +123 -0
- dapper_sqls/http/models.py +58 -0
- dapper_sqls/http/request.py +140 -0
- dapper_sqls/models/__init__.py +3 -5
- dapper_sqls/models/base.py +246 -20
- dapper_sqls/models/connection.py +1 -1
- dapper_sqls/models/query_field.py +214 -0
- dapper_sqls/models/result.py +314 -44
- dapper_sqls/sqlite/__init__.py +1 -0
- dapper_sqls/sqlite/async_local_database.py +69 -5
- dapper_sqls/sqlite/decorators.py +69 -0
- dapper_sqls/sqlite/installer.py +8 -4
- dapper_sqls/sqlite/local_database.py +39 -5
- dapper_sqls/sqlite/models.py +25 -1
- dapper_sqls/sqlite/utils.py +2 -1
- dapper_sqls/utils.py +16 -4
- dapper_sqls-1.2.0.dist-info/METADATA +41 -0
- dapper_sqls-1.2.0.dist-info/RECORD +40 -0
- {dapper_sqls-1.1.3.dist-info → dapper_sqls-1.2.0.dist-info}/WHEEL +1 -1
- dapper_sqls-1.1.3.dist-info/METADATA +0 -10
- dapper_sqls-1.1.3.dist-info/RECORD +0 -33
- {dapper_sqls-1.1.3.dist-info → dapper_sqls-1.2.0.dist-info}/top_level.txt +0 -0
@@ -1,24 +1,9 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
import re
|
3
|
-
from pydantic import Field, BaseModel
|
3
|
+
from pydantic import Field, BaseModel, model_validator
|
4
|
+
from typing import Optional, List, Set, Any
|
5
|
+
import json
|
4
6
|
|
5
|
-
class InformationSchemaTables(BaseModel):
|
6
|
-
TABLE_CATALOG : str = Field(None, description="")
|
7
|
-
TABLE_SCHEMA : str = Field(None, description="")
|
8
|
-
TABLE_NAME : str = Field(None, description="")
|
9
|
-
COLUMN_NAME : str = Field(None, description="")
|
10
|
-
DATA_TYPE : str = Field(None, description="")
|
11
|
-
IS_NULLABLE : str = Field(None, description="")
|
12
|
-
|
13
|
-
class InformationSchemaRoutines(BaseModel):
|
14
|
-
SPECIFIC_CATALOG : str = Field(None, description="")
|
15
|
-
SPECIFIC_SCHEMA : str = Field(None, description="")
|
16
|
-
SPECIFIC_NAME : str = Field(None, description="")
|
17
|
-
ORDINAL_POSITION : int = Field(None, description="")
|
18
|
-
PARAMETER_NAME : str = Field(None, description="")
|
19
|
-
DATA_TYPE : str = Field(None, description="")
|
20
|
-
PROCEDURE_DEFINITION : str = Field(None, description="")
|
21
|
-
|
22
7
|
type_mapping = {
|
23
8
|
'int': 'int',
|
24
9
|
'bigint': 'int',
|
@@ -44,22 +29,318 @@ type_mapping = {
|
|
44
29
|
'rowversion': 'bytes',
|
45
30
|
}
|
46
31
|
|
47
|
-
|
32
|
+
QUERY_FIELD_TYPES = {
|
33
|
+
'str': 'StringQueryField',
|
34
|
+
'int': 'NumericQueryField',
|
35
|
+
'float': 'NumericQueryField',
|
36
|
+
'bool': 'BoolQueryField',
|
37
|
+
'datetime': 'DateQueryField',
|
38
|
+
'datetime.time': 'DateQueryField',
|
39
|
+
'bytes': 'BytesQueryField',
|
40
|
+
}
|
41
|
+
|
42
|
+
JOIN_CONDITIONAL_FIELD_TYPES = {
|
43
|
+
'str': 'JoinStringCondition',
|
44
|
+
'int': 'JoinNumericCondition',
|
45
|
+
'float': 'JoinNumericCondition',
|
46
|
+
'bool': 'JoinBooleanCondition',
|
47
|
+
'datetime': 'JoinDateCondition',
|
48
|
+
'datetime.time': 'JoinDateCondition',
|
49
|
+
'bytes': 'JoinBytesCondition',
|
50
|
+
}
|
51
|
+
|
52
|
+
class Relation(BaseModel):
|
53
|
+
original : bool = True
|
54
|
+
table : str
|
55
|
+
column : str
|
56
|
+
|
57
|
+
class SqlTableAuth(BaseModel):
|
58
|
+
table : str = ""
|
59
|
+
column : str = ""
|
60
|
+
|
61
|
+
class TableSettings(BaseModel):
|
62
|
+
insert : bool = False
|
63
|
+
update : bool = False
|
64
|
+
delete : bool = False
|
65
|
+
search : bool = True
|
66
|
+
|
67
|
+
class ForeignKey(BaseModel):
|
68
|
+
ref_table: str = ""
|
69
|
+
ref_column: str = ""
|
70
|
+
on_delete: str = ""
|
71
|
+
on_update: str = ""
|
72
|
+
|
73
|
+
class AutoColumnDescription(BaseModel):
|
74
|
+
nullable: Optional[bool] = None
|
75
|
+
type: Optional[str] = None
|
76
|
+
identity: Optional[bool] = None
|
77
|
+
default: Optional[Any] = None
|
78
|
+
primary_key: Optional[bool] = None
|
79
|
+
unique: Optional[bool] = None
|
80
|
+
foreign_key: Optional[ForeignKey] = None
|
81
|
+
|
82
|
+
class ColumnInformation(BaseModel):
|
83
|
+
TABLE_CATALOG : str = Field("", description="")
|
84
|
+
TABLE_SCHEMA : str = Field("", description="")
|
85
|
+
TABLE_NAME : str = Field("", description="")
|
86
|
+
COLUMN_NAME : str = Field("", description="")
|
87
|
+
DATA_TYPE : str = Field("", description="")
|
88
|
+
IS_NULLABLE : str = Field("", description="")
|
89
|
+
IS_IDENTITY : str = Field("", description="")
|
90
|
+
IS_PRIMARY_KEY : str = Field("", description="")
|
91
|
+
IS_UNIQUE : str = Field("", description="")
|
92
|
+
CHARACTER_MAXIMUM_LENGTH : Optional[int] = Field(None, description="")
|
93
|
+
COLUMN_DESCRIPTION : Optional[str] = Field("", description="")
|
94
|
+
AUTO_COLUMN_DESCRIPTION : Optional[AutoColumnDescription] = None
|
95
|
+
|
96
|
+
column_authentication : str = Field("", description="")
|
97
|
+
relation : Optional[Relation] = None
|
98
|
+
available : bool = True
|
99
|
+
|
100
|
+
@model_validator(mode="before")
|
101
|
+
@classmethod
|
102
|
+
def parse_json_fields(cls, data):
|
103
|
+
if isinstance(data, dict):
|
104
|
+
# Se o campo vier como string JSON, converte para dict e depois para AutoColumnDescription
|
105
|
+
if isinstance(data.get("AUTO_COLUMN_DESCRIPTION"), str):
|
106
|
+
try:
|
107
|
+
auto_col_dict = json.loads(data["AUTO_COLUMN_DESCRIPTION"])
|
108
|
+
if not auto_col_dict['foreign_key']:
|
109
|
+
auto_col_dict['foreign_key'] = None
|
110
|
+
data["AUTO_COLUMN_DESCRIPTION"] = auto_col_dict
|
111
|
+
description : AutoColumnDescription = AutoColumnDescription(**auto_col_dict)
|
112
|
+
if description.foreign_key:
|
113
|
+
data['relation'] = Relation(table=description.foreign_key.ref_table, column=description.foreign_key.ref_column)
|
114
|
+
|
115
|
+
except json.JSONDecodeError:
|
116
|
+
data["AUTO_COLUMN_DESCRIPTION"] = None
|
117
|
+
|
118
|
+
return data
|
119
|
+
|
120
|
+
class ForeignKey(BaseModel):
|
121
|
+
name: str
|
122
|
+
column: str
|
123
|
+
ref_table: str
|
124
|
+
ref_column: str
|
125
|
+
on_delete: str
|
126
|
+
on_update: str
|
127
|
+
|
128
|
+
class UniqueConstraint(BaseModel):
|
129
|
+
name: str
|
130
|
+
columns: List[str]
|
131
|
+
|
132
|
+
class PrimaryKey(BaseModel):
|
133
|
+
name: str
|
134
|
+
columns: List[str]
|
135
|
+
|
136
|
+
class AutoTableDescription(BaseModel):
|
137
|
+
columns: int
|
138
|
+
identity: List[str]
|
139
|
+
primary_keys: Optional[PrimaryKey]
|
140
|
+
unique_constraints: List[UniqueConstraint]
|
141
|
+
foreign_keys: List[ForeignKey]
|
142
|
+
|
143
|
+
class TableInformation(BaseModel):
|
144
|
+
TABLE_CATALOG : str = Field("", description="")
|
145
|
+
TABLE_SCHEMA : str = Field("", description="")
|
146
|
+
TABLE_NAME : str = Field("", description="")
|
147
|
+
AUTO_TABLE_DESCRIPTION : AutoTableDescription = Field(..., description="")
|
148
|
+
TABLE_DESCRIPTION : Optional[str] = Field("", description="")
|
149
|
+
settings : TableSettings = TableSettings()
|
150
|
+
available : bool = True
|
151
|
+
|
152
|
+
@model_validator(mode="before")
|
153
|
+
@classmethod
|
154
|
+
def parse_json_fields(cls, data):
|
155
|
+
if isinstance(data, dict):
|
156
|
+
if isinstance(data.get("AUTO_TABLE_DESCRIPTION"), str):
|
157
|
+
data["AUTO_TABLE_DESCRIPTION"] = json.loads(data["AUTO_TABLE_DESCRIPTION"])
|
158
|
+
return data
|
159
|
+
|
160
|
+
class SqlTable(TableInformation):
|
161
|
+
COLUMNS : list[ColumnInformation] = []
|
162
|
+
|
163
|
+
class InformationSchemaRoutines(BaseModel):
|
164
|
+
SPECIFIC_CATALOG : str = Field("", description="")
|
165
|
+
SPECIFIC_SCHEMA : str = Field("", description="")
|
166
|
+
SPECIFIC_NAME : str = Field("", description="")
|
167
|
+
ORDINAL_POSITION : int = Field(None, description="")
|
168
|
+
PARAMETER_NAME : str = Field("", description="")
|
169
|
+
DATA_TYPE : str = Field("", description="")
|
170
|
+
PROCEDURE_DEFINITION : str = Field("", description="")
|
48
171
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
172
|
+
def create_database_description(tables: List[TableInformation]) -> str:
|
173
|
+
lines = []
|
174
|
+
for table in tables:
|
175
|
+
auto = table.AUTO_TABLE_DESCRIPTION
|
176
|
+
lines.append(f"\n[Tabela: {table.TABLE_NAME}]")
|
177
|
+
|
178
|
+
# Chave primária
|
179
|
+
if auto.primary_keys:
|
180
|
+
pk_cols = ", ".join(auto.primary_keys.columns or [])
|
181
|
+
lines.append(f"- PK: {pk_cols}")
|
182
|
+
|
183
|
+
# Restrições únicas
|
184
|
+
if auto.unique_constraints:
|
185
|
+
for uc in auto.unique_constraints:
|
186
|
+
cols = ", ".join(uc.columns or [])
|
187
|
+
lines.append(f"- UNIQUE ({uc.name}): {cols}")
|
188
|
+
|
189
|
+
# Chaves estrangeiras
|
190
|
+
if auto.foreign_keys:
|
191
|
+
for fk in auto.foreign_keys:
|
192
|
+
lines.append(
|
193
|
+
f"- FK: {fk.column} → {fk.ref_table}.{fk.ref_column} "
|
194
|
+
f"[ON DELETE {fk.on_delete}, ON UPDATE {fk.on_update}]"
|
195
|
+
)
|
196
|
+
|
197
|
+
# Colunas identidade
|
198
|
+
if auto.identity:
|
199
|
+
identity_cols = ", ".join(auto.identity)
|
200
|
+
lines.append(f"- Identity: {identity_cols}")
|
201
|
+
|
202
|
+
return "\n".join(lines)
|
203
|
+
|
204
|
+
def create_table_description(table_info: TableInformation) -> str:
|
205
|
+
parts = []
|
206
|
+
|
207
|
+
# Identity columns
|
208
|
+
identity = table_info.AUTO_TABLE_DESCRIPTION.identity or []
|
209
|
+
if identity:
|
210
|
+
identity_cols = ", ".join(identity)
|
211
|
+
parts.append(f"Identity columns: {identity_cols}")
|
212
|
+
|
213
|
+
# Primary key
|
214
|
+
pk = table_info.AUTO_TABLE_DESCRIPTION.primary_keys
|
215
|
+
if pk:
|
216
|
+
pk_cols = ", ".join(pk.columns or [])
|
217
|
+
parts.append(f"Primary key ({pk.name}: {pk_cols})" if pk_cols else f"Primary key ({pk.name})")
|
218
|
+
|
219
|
+
# Unique constraints
|
220
|
+
unique_list = table_info.AUTO_TABLE_DESCRIPTION.unique_constraints or []
|
221
|
+
for uc in unique_list:
|
222
|
+
cols = ", ".join(uc.columns or [])
|
223
|
+
parts.append(f"Unique constraint ({uc.name}: {cols})" if cols else f"Unique constraint ({uc.name})")
|
224
|
+
|
225
|
+
# Foreign keys
|
226
|
+
# for fk in table_info.AUTO_TABLE_DESCRIPTION.foreign_keys or []:
|
227
|
+
# ref = f"{fk.ref_table}.{fk.ref_column}"
|
228
|
+
# action = f"ON DELETE {fk.on_delete}, ON UPDATE {fk.on_update}"
|
229
|
+
# parts.append(f"Foreign key ({fk.column} → {ref}) [{action}]")
|
230
|
+
|
231
|
+
# Final assembly
|
232
|
+
auto_summary = "; ".join(parts)
|
233
|
+
|
234
|
+
if table_info.TABLE_DESCRIPTION and table_info.TABLE_DESCRIPTION.strip():
|
235
|
+
return f"{table_info.TABLE_DESCRIPTION.strip()} — [{auto_summary}]"
|
236
|
+
else:
|
237
|
+
return f"[{auto_summary}]"
|
238
|
+
|
239
|
+
def create_column_description(column_desc: Optional[str], auto_desc: Optional[AutoColumnDescription]) -> str:
|
240
|
+
if auto_desc is None:
|
241
|
+
return column_desc or ""
|
242
|
+
|
243
|
+
parts = []
|
244
|
+
|
245
|
+
# Nullable
|
246
|
+
if auto_desc.nullable is False:
|
247
|
+
parts.append("Required")
|
248
|
+
elif auto_desc.nullable is True:
|
249
|
+
parts.append("Optional")
|
250
|
+
|
251
|
+
# Type
|
252
|
+
if auto_desc.type:
|
253
|
+
parts.append(f"Type: {auto_desc.type}")
|
254
|
+
|
255
|
+
# Identity (Auto Increment)
|
256
|
+
if auto_desc.identity:
|
257
|
+
parts.append("Identity (auto increment)")
|
258
|
+
|
259
|
+
# Default value
|
260
|
+
if auto_desc.default is not None and str(auto_desc.default).lower() != 'null':
|
261
|
+
parts.append(f"Default: {auto_desc.default}")
|
262
|
+
|
263
|
+
# Primary key
|
264
|
+
if auto_desc.primary_key:
|
265
|
+
parts.append("Primary key")
|
266
|
+
|
267
|
+
# Unique
|
268
|
+
if auto_desc.unique:
|
269
|
+
parts.append("Unique")
|
270
|
+
|
271
|
+
# Foreign key
|
272
|
+
# if auto_desc.foreign_key:
|
273
|
+
# fk = auto_desc.foreign_key
|
274
|
+
# details = []
|
275
|
+
# if fk.ref_table and fk.ref_column:
|
276
|
+
# details.append(f"{fk.ref_table}.{fk.ref_column}")
|
277
|
+
# if fk.on_delete:
|
278
|
+
# details.append(f"ON DELETE {fk.on_delete}")
|
279
|
+
# if fk.on_update:
|
280
|
+
# details.append(f"ON UPDATE {fk.on_update}")
|
281
|
+
# fk_info = " | ".join(details) if details else "Foreign key"
|
282
|
+
# parts.append(f"Foreign key → {fk_info}")
|
283
|
+
|
284
|
+
auto_summary = "; ".join(parts)
|
285
|
+
|
286
|
+
if column_desc:
|
287
|
+
return f"{column_desc.strip()} — [{auto_summary}]"
|
288
|
+
else:
|
289
|
+
return f"[{auto_summary}]"
|
53
290
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
291
|
+
def create_field(data: ColumnInformation, all_optional=True):
|
292
|
+
sql_data_type = data.DATA_TYPE.lower()
|
293
|
+
python_type = type_mapping.get(sql_data_type, 'str')
|
294
|
+
|
295
|
+
field_args = [
|
296
|
+
f'description="{create_column_description(data.COLUMN_DESCRIPTION, data.AUTO_COLUMN_DESCRIPTION)}"'
|
297
|
+
]
|
298
|
+
|
299
|
+
# Define QueryField type adicional
|
300
|
+
query_field_type = QUERY_FIELD_TYPES.get(python_type)
|
301
|
+
|
302
|
+
join_conditional_field_type = JOIN_CONDITIONAL_FIELD_TYPES.get(python_type)
|
303
|
+
|
304
|
+
# Combina os tipos
|
305
|
+
if python_type in ['datetime', 'datetime.time']:
|
306
|
+
annotated_type = f'Union[{python_type}, str]'
|
307
|
+
else:
|
308
|
+
annotated_type = python_type
|
309
|
+
|
310
|
+
if query_field_type:
|
311
|
+
annotated_type = f'Union[{annotated_type}, {query_field_type}, {join_conditional_field_type}]'
|
312
|
+
|
313
|
+
if data.IS_NULLABLE == "YES" or all_optional:
|
314
|
+
annotated_type = f'Optional[{annotated_type}]'
|
315
|
+
field_def = f'Field(None, {", ".join(field_args)})' if field_args else 'Field(None)'
|
316
|
+
else:
|
317
|
+
field_def = f'Field(..., {", ".join(field_args)})' if field_args else 'Field(...)'
|
318
|
+
|
319
|
+
return f'{data.COLUMN_NAME}: {annotated_type} = {field_def}'
|
320
|
+
|
321
|
+
def create_arg(
|
322
|
+
data: ColumnInformation,
|
323
|
+
default_type: str = None,
|
324
|
+
default_value: str = "None",
|
325
|
+
with_query_field: bool = False
|
326
|
+
):
|
327
|
+
sql_data_type = data.DATA_TYPE.lower()
|
328
|
+
python_type = default_type or type_mapping.get(sql_data_type, 'str')
|
329
|
+
|
330
|
+
# Define o tipo base
|
331
|
+
if python_type in ['datetime', 'datetime.time']:
|
332
|
+
annotated_type = f'Union[{python_type}, str]'
|
333
|
+
else:
|
334
|
+
annotated_type = python_type
|
335
|
+
|
336
|
+
# Adiciona o QueryField, se for solicitado
|
337
|
+
if with_query_field:
|
338
|
+
query_field_type = QUERY_FIELD_TYPES.get(python_type)
|
339
|
+
join_conditional_field_type = JOIN_CONDITIONAL_FIELD_TYPES.get(python_type)
|
340
|
+
if query_field_type:
|
341
|
+
annotated_type = f'Union[{annotated_type}, {query_field_type}, {join_conditional_field_type}]'
|
342
|
+
|
343
|
+
return f'{data.COLUMN_NAME}: {annotated_type} = {default_value}'
|
63
344
|
|
64
345
|
def get_parameters_with_defaults(stored_procedure):
|
65
346
|
# Regular expression to capture parameters and their default values
|
@@ -100,6 +381,17 @@ def get_parameters_with_defaults(stored_procedure):
|
|
100
381
|
|
101
382
|
return params_with_defaults
|
102
383
|
|
384
|
+
def create_queue_update(fields_args_str : str):
|
385
|
+
return f'''def queue_update(self, *, {fields_args_str}):
|
386
|
+
super().queue_update(**locals())
|
387
|
+
'''
|
388
|
+
|
389
|
+
def create_set_ignored_fields(fields_args_str : str):
|
390
|
+
return f'''@classmethod
|
391
|
+
def set_ignored_fields(cls, *, {fields_args_str}):
|
392
|
+
super().set_ignored_fields(**locals())
|
393
|
+
'''
|
394
|
+
|
103
395
|
def create_params_routine(data : InformationSchemaRoutines, defaults_values : dict[str, str | int | None]):
|
104
396
|
sql_data_type = data.DATA_TYPE.lower()
|
105
397
|
python_type = type_mapping.get(sql_data_type)
|
@@ -139,10 +431,13 @@ class BaseExecutorORM(BaseExecutor):
|
|
139
431
|
def executor(self):
|
140
432
|
return self._executor
|
141
433
|
|
142
|
-
def
|
434
|
+
def count(self, additional_sql : str = "", *, {fields_args_str}):
|
435
|
+
return self.executor.count(self, {class_name}(**get_dict_args(locals(), ['additional_sql'])), additional_sql)
|
436
|
+
|
437
|
+
def fetchone(self, additional_sql : str = "", *, {fields_args_str}) -> Result.FetchoneModel[{class_name}]:
|
143
438
|
return self.executor.fetchone(self, {class_name}(**get_dict_args(locals(), ['additional_sql'])), additional_sql)
|
144
439
|
|
145
|
-
def fetchall(self, additional_sql : str = "", select_top : int = None, *, {fields_args_str}) ->
|
440
|
+
def fetchall(self, additional_sql : str = "", select_top : int = None, *, {fields_args_str}) -> Result.FetchallModel[{class_name}]:
|
146
441
|
return self.executor.fetchall(self, {class_name}(**get_dict_args(locals(), ['additional_sql', 'select_top'])), additional_sql, select_top)
|
147
442
|
|
148
443
|
def delete(self, *, {fields_args_str}) -> Result.Send:
|
@@ -212,9 +507,6 @@ class {class_name}ORM(object):
|
|
212
507
|
def __init__(self, dapper : Dapper):
|
213
508
|
self._dapper = dapper
|
214
509
|
|
215
|
-
class {class_name}({class_name}):
|
216
|
-
...
|
217
|
-
|
218
510
|
@property
|
219
511
|
def dapper(self):
|
220
512
|
return self._dapper
|
@@ -289,10 +581,13 @@ class AsyncBaseExecutorORM(AsyncBaseExecutor):
|
|
289
581
|
def executor(self):
|
290
582
|
return self._executor
|
291
583
|
|
292
|
-
async def
|
584
|
+
async def count(self, additional_sql : str = "", *, {fields_args_str}):
|
585
|
+
return await self.executor.count(self, {class_name}(**get_dict_args(locals(), ['additional_sql'])), additional_sql)
|
586
|
+
|
587
|
+
async def fetchone(self, additional_sql : str = "", *, {fields_args_str}) -> Result.FetchoneModel[{class_name}]:
|
293
588
|
return await self.executor.fetchone(self, {class_name}(**get_dict_args(locals(), ['additional_sql'])), additional_sql)
|
294
589
|
|
295
|
-
async def fetchall(self, additional_sql : str = "", select_top : int = None, *, {fields_args_str}) ->
|
590
|
+
async def fetchall(self, additional_sql : str = "", select_top : int = None, *, {fields_args_str}) -> Result.FetchallModel[{class_name}]:
|
296
591
|
return await self.executor.fetchall(self, {class_name}(**get_dict_args(locals(), ['additional_sql', 'select_top'])), additional_sql, select_top)
|
297
592
|
|
298
593
|
async def delete(self, *, {fields_args_str}) -> Result.Send:
|
@@ -362,9 +657,6 @@ class Async{class_name}ORM(object):
|
|
362
657
|
def __init__(self, async_dapper : AsyncDapper):
|
363
658
|
self._async_dapper = async_dapper
|
364
659
|
|
365
|
-
class {class_name}({class_name}):
|
366
|
-
...
|
367
|
-
|
368
660
|
@property
|
369
661
|
def async_dapper(self):
|
370
662
|
return self._async_dapper
|
dapper_sqls/builders/query.py
CHANGED
@@ -1,55 +1,120 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
from typing import
|
3
|
-
from pydantic import BaseModel
|
2
|
+
from typing import Union
|
4
3
|
from datetime import datetime, date
|
5
|
-
import
|
6
|
-
|
7
|
-
class Value:
|
8
|
-
def __init__(self, value : Union[str, int, bytes, float, datetime, date, bool], prefix : str, suffix : str):
|
9
|
-
self.value = value
|
10
|
-
self.prefix = prefix
|
11
|
-
self.suffix = suffix
|
4
|
+
from ..models import TableBaseModel, QueryFieldBase, BaseJoinConditionField, SearchTable, JoinSearchTable
|
12
5
|
|
13
6
|
class QueryBuilder(object):
|
14
7
|
|
15
|
-
def value(value : Union[str, int, bytes, float, datetime, date], prefix = "=", suffix = ""):
|
16
|
-
json_value = json.dumps({'prefix': prefix, 'value': value, 'suffix': suffix})
|
17
|
-
return f"#QueryBuilderValue#{json_value}#QueryBuilderValue#"
|
18
|
-
|
19
8
|
@classmethod
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
9
|
+
def build_where_clause(cls, model: TableBaseModel, table_alias: str = "", base_table_alias : str = "") -> str:
|
10
|
+
clause_parts = []
|
11
|
+
|
12
|
+
for field_name, field_value in model:
|
13
|
+
|
14
|
+
if field_value is None:
|
15
|
+
continue
|
16
|
+
|
17
|
+
# Constrói o nome qualificado com alias, se houver
|
18
|
+
qualified_field = f"{table_alias}.{field_name}" if table_alias.strip() else field_name
|
19
|
+
|
20
|
+
if isinstance(field_value, BaseJoinConditionField):
|
21
|
+
sql = field_value.to_sql(base_table_alias, qualified_field)
|
22
|
+
if sql:
|
23
|
+
clause_parts.append(sql)
|
24
|
+
|
25
|
+
elif isinstance(field_value, QueryFieldBase):
|
26
|
+
sql = field_value.to_sql(qualified_field)
|
27
|
+
if sql:
|
28
|
+
clause_parts.append(sql)
|
29
|
+
|
30
|
+
else:
|
31
|
+
if isinstance(field_value, str):
|
32
|
+
escaped_value = field_value.replace("'", "''")
|
33
|
+
clause_parts.append(f"{qualified_field} = '{escaped_value}'")
|
34
|
+
elif isinstance(field_value, bool):
|
35
|
+
clause_parts.append(f"{qualified_field} = {'1' if field_value else '0'}")
|
36
|
+
elif isinstance(field_value, datetime):
|
37
|
+
clause_parts.append(f"{qualified_field} = '{field_value.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}'")
|
38
|
+
elif isinstance(field_value, date):
|
39
|
+
clause_parts.append(f"{qualified_field} = '{field_value.strftime('%Y-%m-%d')}'")
|
40
|
+
elif isinstance(field_value, bytes):
|
41
|
+
# Converte os bytes em uma string hexadecimal para SQL (ex: 0x4E6574)
|
42
|
+
hex_value = field_value.hex()
|
43
|
+
clause_parts.append(f"{qualified_field} = 0x{hex_value}")
|
34
44
|
else:
|
35
|
-
|
36
|
-
|
45
|
+
clause_parts.append(f"{qualified_field} = {field_value}")
|
46
|
+
|
47
|
+
return " AND ".join(clause_parts)
|
48
|
+
|
49
|
+
@staticmethod
|
50
|
+
def format_sql_value(value):
|
51
|
+
if isinstance(value, str):
|
52
|
+
value = value.replace("'", "''")
|
53
|
+
return f"'{value}'"
|
54
|
+
elif isinstance(value, bool):
|
55
|
+
return '1' if value else '0'
|
56
|
+
elif isinstance(value, datetime):
|
57
|
+
return f"'{value.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}'"
|
58
|
+
elif isinstance(value, date):
|
59
|
+
return f"'{value.strftime('%Y-%m-%d')}'"
|
60
|
+
elif isinstance(value, bytes):
|
61
|
+
return f"0x{value.hex()}"
|
62
|
+
elif value is None:
|
63
|
+
return "NULL"
|
64
|
+
else:
|
65
|
+
return str(value)
|
66
|
+
|
67
|
+
@staticmethod
|
68
|
+
def build_select_fields(search_table: SearchTable, table_alias: str, rename_fields: bool = True) -> str:
|
69
|
+
model_fields = search_table.model.model_fields.keys()
|
70
|
+
|
71
|
+
if not rename_fields:
|
72
|
+
# Se include estiver presente, listar colunas com alias normal
|
73
|
+
if search_table.include:
|
74
|
+
fields = [f"{table_alias}.{f}" for f in search_table.include]
|
75
|
+
else:
|
76
|
+
# Nenhuma regra definida, usar '*'
|
77
|
+
return f"{table_alias}.*"
|
78
|
+
return ", ".join(fields)
|
79
|
+
|
80
|
+
# Caso rename_fields=True
|
81
|
+
if search_table.include:
|
82
|
+
fields = search_table.include
|
83
|
+
else:
|
84
|
+
fields = list(model_fields)
|
85
|
+
|
86
|
+
select_parts = [
|
87
|
+
f"{table_alias}.{field} AS {table_alias}{field}" for field in fields
|
88
|
+
]
|
89
|
+
return ", ".join(select_parts)
|
37
90
|
|
38
91
|
@classmethod
|
39
|
-
def update(cls, model:
|
40
|
-
|
92
|
+
def update(cls, model: TableBaseModel, where : Union[str , TableBaseModel]):
|
93
|
+
model._reset_defaults()
|
94
|
+
update_data = {k: int(v) if isinstance(v, bool) else v for k, v in model.model_dump(mode="json", exclude_none=True).items()}
|
41
95
|
if not isinstance(where, str):
|
42
|
-
|
43
|
-
|
96
|
+
where._reset_defaults()
|
97
|
+
for key in where.model_dump(mode="json", exclude_none=True):
|
98
|
+
if key in update_data:
|
99
|
+
update_data.pop(key, None)
|
100
|
+
where = cls.build_where_clause(where)
|
44
101
|
|
45
|
-
set_clause = ", ".join(
|
102
|
+
set_clause = ", ".join(
|
103
|
+
f"{key} = {cls.format_sql_value(value)}"
|
104
|
+
for key, value in update_data.items()
|
105
|
+
)
|
46
106
|
sql_query = f"UPDATE {model.TABLE_NAME} SET {set_clause} WHERE {where}"
|
47
107
|
return sql_query
|
48
108
|
|
49
|
-
|
50
|
-
|
109
|
+
@classmethod
|
110
|
+
def insert(cls, model: TableBaseModel, name_column_id = 'Id'):
|
111
|
+
model._reset_defaults()
|
112
|
+
insert_data = {
|
113
|
+
k: int(v) if isinstance(v, bool) else v
|
114
|
+
for k, v in model.model_dump(mode="json", exclude_none=True).items()
|
115
|
+
}
|
51
116
|
columns = ", ".join(insert_data.keys())
|
52
|
-
values = ", ".join(
|
117
|
+
values = ", ".join(cls.format_sql_value(v) for v in insert_data.values())
|
53
118
|
sql_query = f"""
|
54
119
|
INSERT INTO {model.TABLE_NAME} ({columns})
|
55
120
|
OUTPUT INSERTED.{name_column_id} AS Id
|
@@ -58,21 +123,77 @@ class QueryBuilder(object):
|
|
58
123
|
return sql_query
|
59
124
|
|
60
125
|
@classmethod
|
61
|
-
def select(cls, model:
|
126
|
+
def select(cls, model: TableBaseModel, additional_sql : str = "" ,select_top : int= None):
|
127
|
+
model._reset_defaults()
|
62
128
|
top_clause = f"TOP ({select_top}) * " if select_top else "*"
|
63
|
-
|
64
|
-
where_clause = cls._build_where_clause(**select_data)
|
129
|
+
where_clause = cls.build_where_clause(model)
|
65
130
|
|
66
131
|
sql_query = f"SELECT {top_clause} FROM {model.TABLE_NAME}"
|
67
132
|
if where_clause:
|
68
133
|
sql_query += f" WHERE {where_clause}"
|
69
134
|
sql_query = f'{sql_query} {additional_sql}'
|
70
135
|
return sql_query
|
136
|
+
|
137
|
+
@staticmethod
|
138
|
+
def build_on_clause(join_model: TableBaseModel, join_alias: str, base_alias: str) -> str:
|
139
|
+
clause_parts = []
|
140
|
+
for field_name, field_value in join_model:
|
141
|
+
if isinstance(field_value, BaseJoinConditionField):
|
142
|
+
sql = field_value.to_sql(base_alias, f"{join_alias}.{field_name}")
|
143
|
+
if sql:
|
144
|
+
clause_parts.append(sql)
|
145
|
+
return " AND ".join(clause_parts)
|
146
|
+
|
147
|
+
@classmethod
|
148
|
+
def select_with_joins(cls, main_search: SearchTable, joins: list[JoinSearchTable] = [], additional_sql: str = "", select_top: int = None) -> str:
|
149
|
+
main_model = main_search.model
|
150
|
+
main_model._reset_defaults()
|
151
|
+
main_table = main_model.TABLE_NAME
|
152
|
+
main_table_alias = main_model.TABLE_ALIAS
|
153
|
+
top_clause = f"TOP ({select_top})" if select_top else ""
|
154
|
+
|
155
|
+
# Campos SELECT da tabela principal
|
156
|
+
select_fields = cls.build_select_fields(main_search, main_table_alias, False)
|
157
|
+
|
158
|
+
# JOINs
|
159
|
+
join_clauses = []
|
160
|
+
for join_search in joins:
|
161
|
+
join_model = join_search.model
|
162
|
+
join_model._reset_defaults()
|
163
|
+
join_table = join_model.TABLE_NAME
|
164
|
+
join_table_alias = join_model.TABLE_ALIAS
|
165
|
+
|
166
|
+
join_type = join_search.join_type.upper()
|
167
|
+
on_conditions = cls.build_on_clause(join_model, join_table_alias, main_table_alias)
|
71
168
|
|
169
|
+
# Usa o alias no JOIN
|
170
|
+
join_clause = (
|
171
|
+
f"{join_type} JOIN {join_table} AS {join_table_alias} "
|
172
|
+
f"ON {on_conditions}"
|
173
|
+
)
|
174
|
+
join_clauses.append(join_clause)
|
175
|
+
|
176
|
+
# Campos SELECT do JOIN
|
177
|
+
select_fields += ", " + cls.build_select_fields(join_search, join_table_alias)
|
178
|
+
|
179
|
+
# WHERE principal
|
180
|
+
where_clause = cls.build_where_clause(main_model, main_table_alias)
|
181
|
+
where_part = f"WHERE {where_clause}" if where_clause else ""
|
182
|
+
|
183
|
+
# SQL final
|
184
|
+
sql_query = (
|
185
|
+
f"SELECT {top_clause} {select_fields} "
|
186
|
+
f"FROM {main_table} AS {main_table_alias} "
|
187
|
+
+ " ".join(join_clauses)
|
188
|
+
+ f" {where_part} {additional_sql}"
|
189
|
+
).strip()
|
190
|
+
|
191
|
+
return sql_query
|
192
|
+
|
72
193
|
@classmethod
|
73
|
-
def delete(cls, model:
|
74
|
-
|
75
|
-
where_clause = cls.
|
194
|
+
def delete(cls, model: TableBaseModel):
|
195
|
+
model._reset_defaults()
|
196
|
+
where_clause = cls.build_where_clause(model)
|
76
197
|
if not where_clause:
|
77
198
|
raise ValueError("DELETE operation requires at least one condition.")
|
78
199
|
sql_query = f"DELETE FROM {model.TABLE_NAME} WHERE {where_clause}"
|