db_client_toolkit 0.0.1__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.
- db_client_toolkit-0.0.1.dist-info/METADATA +540 -0
- db_client_toolkit-0.0.1.dist-info/RECORD +22 -0
- db_client_toolkit-0.0.1.dist-info/WHEEL +4 -0
- db_toolkit/__init__.py +151 -0
- db_toolkit/clients/__init__.py +20 -0
- db_toolkit/clients/mongodb.py +251 -0
- db_toolkit/clients/mysql.py +143 -0
- db_toolkit/clients/postgresql.py +152 -0
- db_toolkit/clients/redis.py +321 -0
- db_toolkit/clients/sqlite.py +152 -0
- db_toolkit/clients/supabase.py +230 -0
- db_toolkit/core/__init__.py +9 -0
- db_toolkit/core/base.py +194 -0
- db_toolkit/core/sql_base.py +163 -0
- db_toolkit/exceptions/__init__.py +38 -0
- db_toolkit/mixins/__init__.py +12 -0
- db_toolkit/mixins/batch_ops.py +194 -0
- db_toolkit/mixins/transaction.py +206 -0
- db_toolkit/utils/__init__.py +15 -0
- db_toolkit/utils/config.py +252 -0
- db_toolkit/utils/factory.py +172 -0
- db_toolkit/utils/query_builder.py +316 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SQL查询构建器
|
|
3
|
+
提供流畅的API来构建SQL查询
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any, List, Optional, Union
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class QueryBuilder:
|
|
14
|
+
"""
|
|
15
|
+
SQL查询构建器
|
|
16
|
+
|
|
17
|
+
使用流畅接口(Fluent Interface)模式构建SQL查询
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
"""初始化查询构建器"""
|
|
22
|
+
self.reset()
|
|
23
|
+
|
|
24
|
+
def reset(self) -> 'QueryBuilder':
|
|
25
|
+
"""
|
|
26
|
+
重置查询构建器
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
QueryBuilder: 返回自身以支持链式调用
|
|
30
|
+
"""
|
|
31
|
+
self._table: Optional[str] = None
|
|
32
|
+
self._fields: List[str] = ['*']
|
|
33
|
+
self._conditions: List[str] = []
|
|
34
|
+
self._joins: List[tuple] = []
|
|
35
|
+
self._group_by: List[str] = []
|
|
36
|
+
self._having: List[str] = []
|
|
37
|
+
self._order_by: List[tuple] = []
|
|
38
|
+
self._limit: Optional[int] = None
|
|
39
|
+
self._offset: Optional[int] = None
|
|
40
|
+
self._distinct: bool = False
|
|
41
|
+
return self
|
|
42
|
+
|
|
43
|
+
def table(self, table_name: str) -> 'QueryBuilder':
|
|
44
|
+
"""
|
|
45
|
+
设置表名
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
table_name: 表名
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
QueryBuilder: 返回自身
|
|
52
|
+
"""
|
|
53
|
+
self._table = table_name
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
def select(self, *fields: str) -> 'QueryBuilder':
|
|
57
|
+
"""
|
|
58
|
+
选择字段
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
*fields: 字段列表
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
QueryBuilder: 返回自身
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
>>> builder.select('id', 'name', 'email')
|
|
68
|
+
>>> builder.select('COUNT(*) as total')
|
|
69
|
+
"""
|
|
70
|
+
self._fields = list(fields) if fields else ['*']
|
|
71
|
+
return self
|
|
72
|
+
|
|
73
|
+
def distinct(self) -> 'QueryBuilder':
|
|
74
|
+
"""
|
|
75
|
+
使用DISTINCT
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
QueryBuilder: 返回自身
|
|
79
|
+
"""
|
|
80
|
+
self._distinct = True
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def where(self, condition: str) -> 'QueryBuilder':
|
|
84
|
+
"""
|
|
85
|
+
添加WHERE条件
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
condition: 条件表达式
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
QueryBuilder: 返回自身
|
|
92
|
+
|
|
93
|
+
Examples:
|
|
94
|
+
>>> builder.where("age > 18")
|
|
95
|
+
>>> builder.where("status = 'active'")
|
|
96
|
+
"""
|
|
97
|
+
self._conditions.append(condition)
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def where_in(self, field: str, values: List[Any]) -> 'QueryBuilder':
|
|
101
|
+
"""
|
|
102
|
+
添加IN条件
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
field: 字段名
|
|
106
|
+
values: 值列表
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
QueryBuilder: 返回自身
|
|
110
|
+
"""
|
|
111
|
+
if not values:
|
|
112
|
+
return self
|
|
113
|
+
|
|
114
|
+
values_str = ', '.join([f"'{v}'" if isinstance(v, str) else str(v) for v in values])
|
|
115
|
+
self._conditions.append(f"{field} IN ({values_str})")
|
|
116
|
+
return self
|
|
117
|
+
|
|
118
|
+
def where_between(self, field: str, start: Any, end: Any) -> 'QueryBuilder':
|
|
119
|
+
"""
|
|
120
|
+
添加BETWEEN条件
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
field: 字段名
|
|
124
|
+
start: 起始值
|
|
125
|
+
end: 结束值
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
QueryBuilder: 返回自身
|
|
129
|
+
"""
|
|
130
|
+
start_val = f"'{start}'" if isinstance(start, str) else start
|
|
131
|
+
end_val = f"'{end}'" if isinstance(end, str) else end
|
|
132
|
+
self._conditions.append(f"{field} BETWEEN {start_val} AND {end_val}")
|
|
133
|
+
return self
|
|
134
|
+
|
|
135
|
+
def where_like(self, field: str, pattern: str) -> 'QueryBuilder':
|
|
136
|
+
"""
|
|
137
|
+
添加LIKE条件
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
field: 字段名
|
|
141
|
+
pattern: 匹配模式
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
QueryBuilder: 返回自身
|
|
145
|
+
"""
|
|
146
|
+
self._conditions.append(f"{field} LIKE '{pattern}'")
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
def join(self, table: str, condition: str, join_type: str = 'INNER') -> 'QueryBuilder':
|
|
150
|
+
"""
|
|
151
|
+
添加JOIN
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
table: 要JOIN的表
|
|
155
|
+
condition: JOIN条件
|
|
156
|
+
join_type: JOIN类型 (INNER, LEFT, RIGHT, FULL)
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
QueryBuilder: 返回自身
|
|
160
|
+
|
|
161
|
+
Examples:
|
|
162
|
+
>>> builder.join('orders', 'users.id = orders.user_id')
|
|
163
|
+
>>> builder.join('profiles', 'users.id = profiles.user_id', 'LEFT')
|
|
164
|
+
"""
|
|
165
|
+
self._joins.append((join_type.upper(), table, condition))
|
|
166
|
+
return self
|
|
167
|
+
|
|
168
|
+
def left_join(self, table: str, condition: str) -> 'QueryBuilder':
|
|
169
|
+
"""LEFT JOIN的便捷方法"""
|
|
170
|
+
return self.join(table, condition, 'LEFT')
|
|
171
|
+
|
|
172
|
+
def right_join(self, table: str, condition: str) -> 'QueryBuilder':
|
|
173
|
+
"""RIGHT JOIN的便捷方法"""
|
|
174
|
+
return self.join(table, condition, 'RIGHT')
|
|
175
|
+
|
|
176
|
+
def group_by(self, *fields: str) -> 'QueryBuilder':
|
|
177
|
+
"""
|
|
178
|
+
添加GROUP BY
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
*fields: 分组字段
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
QueryBuilder: 返回自身
|
|
185
|
+
"""
|
|
186
|
+
self._group_by.extend(fields)
|
|
187
|
+
return self
|
|
188
|
+
|
|
189
|
+
def having(self, condition: str) -> 'QueryBuilder':
|
|
190
|
+
"""
|
|
191
|
+
添加HAVING条件
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
condition: HAVING条件
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
QueryBuilder: 返回自身
|
|
198
|
+
"""
|
|
199
|
+
self._having.append(condition)
|
|
200
|
+
return self
|
|
201
|
+
|
|
202
|
+
def order_by(self, field: str, direction: str = 'ASC') -> 'QueryBuilder':
|
|
203
|
+
"""
|
|
204
|
+
添加ORDER BY
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
field: 排序字段
|
|
208
|
+
direction: 排序方向 (ASC/DESC)
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
QueryBuilder: 返回自身
|
|
212
|
+
"""
|
|
213
|
+
self._order_by.append((field, direction.upper()))
|
|
214
|
+
return self
|
|
215
|
+
|
|
216
|
+
def limit(self, limit: int) -> 'QueryBuilder':
|
|
217
|
+
"""
|
|
218
|
+
设置LIMIT
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
limit: 限制数量
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
QueryBuilder: 返回自身
|
|
225
|
+
"""
|
|
226
|
+
self._limit = limit
|
|
227
|
+
return self
|
|
228
|
+
|
|
229
|
+
def offset(self, offset: int) -> 'QueryBuilder':
|
|
230
|
+
"""
|
|
231
|
+
设置OFFSET
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
offset: 偏移量
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
QueryBuilder: 返回自身
|
|
238
|
+
"""
|
|
239
|
+
self._offset = offset
|
|
240
|
+
return self
|
|
241
|
+
|
|
242
|
+
def paginate(self, page: int, per_page: int = 10) -> 'QueryBuilder':
|
|
243
|
+
"""
|
|
244
|
+
分页查询
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
page: 页码(从1开始)
|
|
248
|
+
per_page: 每页数量
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
QueryBuilder: 返回自身
|
|
252
|
+
"""
|
|
253
|
+
self._limit = per_page
|
|
254
|
+
self._offset = (page - 1) * per_page
|
|
255
|
+
return self
|
|
256
|
+
|
|
257
|
+
def build(self) -> str:
|
|
258
|
+
"""
|
|
259
|
+
构建SQL查询
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
str: 完整的SQL查询语句
|
|
263
|
+
|
|
264
|
+
Raises:
|
|
265
|
+
ValueError: 当缺少必需参数时
|
|
266
|
+
"""
|
|
267
|
+
if not self._table:
|
|
268
|
+
raise ValueError("必须指定表名")
|
|
269
|
+
|
|
270
|
+
# SELECT子句
|
|
271
|
+
distinct_keyword = 'DISTINCT ' if self._distinct else ''
|
|
272
|
+
fields = ', '.join(self._fields)
|
|
273
|
+
query = f"SELECT {distinct_keyword}{fields} FROM {self._table}"
|
|
274
|
+
|
|
275
|
+
# JOIN子句
|
|
276
|
+
for join_type, table, condition in self._joins:
|
|
277
|
+
query += f" {join_type} JOIN {table} ON {condition}"
|
|
278
|
+
|
|
279
|
+
# WHERE子句
|
|
280
|
+
if self._conditions:
|
|
281
|
+
query += " WHERE " + " AND ".join(self._conditions)
|
|
282
|
+
|
|
283
|
+
# GROUP BY子句
|
|
284
|
+
if self._group_by:
|
|
285
|
+
query += " GROUP BY " + ", ".join(self._group_by)
|
|
286
|
+
|
|
287
|
+
# HAVING子句
|
|
288
|
+
if self._having:
|
|
289
|
+
query += " HAVING " + " AND ".join(self._having)
|
|
290
|
+
|
|
291
|
+
# ORDER BY子句
|
|
292
|
+
if self._order_by:
|
|
293
|
+
order_parts = [f"{field} {direction}" for field, direction in self._order_by]
|
|
294
|
+
query += " ORDER BY " + ", ".join(order_parts)
|
|
295
|
+
|
|
296
|
+
# LIMIT子句
|
|
297
|
+
if self._limit is not None:
|
|
298
|
+
query += f" LIMIT {self._limit}"
|
|
299
|
+
|
|
300
|
+
# OFFSET子句
|
|
301
|
+
if self._offset is not None:
|
|
302
|
+
query += f" OFFSET {self._offset}"
|
|
303
|
+
|
|
304
|
+
logger.debug(f"构建SQL: {query}")
|
|
305
|
+
return query
|
|
306
|
+
|
|
307
|
+
def __str__(self) -> str:
|
|
308
|
+
"""返回构建的SQL语句"""
|
|
309
|
+
try:
|
|
310
|
+
return self.build()
|
|
311
|
+
except ValueError:
|
|
312
|
+
return "<QueryBuilder: incomplete>"
|
|
313
|
+
|
|
314
|
+
def __repr__(self) -> str:
|
|
315
|
+
"""返回对象表示"""
|
|
316
|
+
return f"<QueryBuilder table={self._table}>"
|