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
db_toolkit/core/base.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
数据库客户端抽象基类
|
|
3
|
+
定义了所有数据库客户端必须实现的接口
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Any, Dict, List, Optional, Union
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BaseClient(ABC):
|
|
15
|
+
"""
|
|
16
|
+
数据库客户端抽象基类
|
|
17
|
+
|
|
18
|
+
所有数据库客户端都应继承此类并实现抽象方法
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, config: Dict[str, Any]):
|
|
22
|
+
"""
|
|
23
|
+
初始化数据库客户端
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
config: 数据库配置字典
|
|
27
|
+
"""
|
|
28
|
+
self.config = config
|
|
29
|
+
self.connection = None
|
|
30
|
+
self._connected = False
|
|
31
|
+
self._validate_config()
|
|
32
|
+
|
|
33
|
+
def _validate_config(self) -> None:
|
|
34
|
+
"""
|
|
35
|
+
验证配置参数
|
|
36
|
+
子类可以重写此方法以实现特定的配置验证
|
|
37
|
+
"""
|
|
38
|
+
if not isinstance(self.config, dict):
|
|
39
|
+
raise ValueError("配置必须是字典类型")
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def connect(self) -> bool:
|
|
43
|
+
"""
|
|
44
|
+
连接到数据库
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
bool: 连接是否成功
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ConnectionError: 连接失败时抛出
|
|
51
|
+
"""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def disconnect(self) -> bool:
|
|
56
|
+
"""
|
|
57
|
+
断开数据库连接
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
bool: 断开是否成功
|
|
61
|
+
"""
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def is_connected(self) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
检查是否已连接
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
bool: 是否已连接
|
|
71
|
+
"""
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def execute(self, query: str, params: Optional[Union[tuple, dict]] = None) -> Any:
|
|
76
|
+
"""
|
|
77
|
+
执行原生查询
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
query: 查询语句
|
|
81
|
+
params: 查询参数
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Any: 查询结果
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
QueryError: 查询执行失败时抛出
|
|
88
|
+
"""
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
@abstractmethod
|
|
92
|
+
def insert(self, table: str, data: Dict[str, Any]) -> Any:
|
|
93
|
+
"""
|
|
94
|
+
插入单条数据
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
table: 表名或集合名
|
|
98
|
+
data: 要插入的数据
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Any: 插入的记录ID或结果
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
QueryError: 插入失败时抛出
|
|
105
|
+
"""
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
@abstractmethod
|
|
109
|
+
def update(self, table: str, data: Dict[str, Any],
|
|
110
|
+
condition: Dict[str, Any]) -> int:
|
|
111
|
+
"""
|
|
112
|
+
更新数据
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
table: 表名或集合名
|
|
116
|
+
data: 要更新的数据
|
|
117
|
+
condition: 更新条件
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
int: 受影响的行数
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
QueryError: 更新失败时抛出
|
|
124
|
+
"""
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
@abstractmethod
|
|
128
|
+
def delete(self, table: str, condition: Dict[str, Any]) -> int:
|
|
129
|
+
"""
|
|
130
|
+
删除数据
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
table: 表名或集合名
|
|
134
|
+
condition: 删除条件
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
int: 受影响的行数
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
QueryError: 删除失败时抛出
|
|
141
|
+
"""
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
@abstractmethod
|
|
145
|
+
def select(self, table: str,
|
|
146
|
+
fields: Optional[List[str]] = None,
|
|
147
|
+
condition: Optional[Dict[str, Any]] = None,
|
|
148
|
+
limit: Optional[int] = None,
|
|
149
|
+
offset: Optional[int] = None,
|
|
150
|
+
order_by: Optional[List[tuple]] = None) -> List[Dict]:
|
|
151
|
+
"""
|
|
152
|
+
查询数据
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
table: 表名或集合名
|
|
156
|
+
fields: 要查询的字段列表
|
|
157
|
+
condition: 查询条件
|
|
158
|
+
limit: 限制返回记录数
|
|
159
|
+
offset: 偏移量
|
|
160
|
+
order_by: 排序规则,格式为 [('field', 'ASC/DESC'), ...]
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
List[Dict]: 查询结果列表
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
QueryError: 查询失败时抛出
|
|
167
|
+
"""
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
def ping(self) -> bool:
|
|
171
|
+
"""
|
|
172
|
+
测试数据库连接
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
bool: 连接是否正常
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
return self.is_connected()
|
|
179
|
+
except Exception:
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
def __enter__(self):
|
|
183
|
+
"""上下文管理器入口"""
|
|
184
|
+
self.connect()
|
|
185
|
+
return self
|
|
186
|
+
|
|
187
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
188
|
+
"""上下文管理器出口"""
|
|
189
|
+
self.disconnect()
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
def __repr__(self) -> str:
|
|
193
|
+
"""返回对象的字符串表示"""
|
|
194
|
+
return f"<{self.__class__.__name__} connected={self._connected}>"
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SQL数据库客户端基类
|
|
3
|
+
提供关系型数据库的通用功能
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from abc import abstractmethod
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from .base import BaseClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SQLBaseClient(BaseClient):
|
|
13
|
+
"""
|
|
14
|
+
SQL数据库客户端基类
|
|
15
|
+
为关系型数据库提供通用的SQL构建方法
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def placeholder(self) -> str:
|
|
21
|
+
"""
|
|
22
|
+
SQL参数占位符
|
|
23
|
+
不同数据库使用不同的占位符:
|
|
24
|
+
- MySQL/PostgreSQL: %s
|
|
25
|
+
- SQLite: ?
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
str: 占位符字符
|
|
29
|
+
"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
def _build_insert_query(self, table: str, data: Dict[str, Any]) -> tuple:
|
|
33
|
+
"""
|
|
34
|
+
构建INSERT查询
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
table: 表名
|
|
38
|
+
data: 要插入的数据
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
tuple: (查询语句, 参数元组)
|
|
42
|
+
"""
|
|
43
|
+
fields = ', '.join(data.keys())
|
|
44
|
+
placeholders = ', '.join([self.placeholder] * len(data))
|
|
45
|
+
query = f"INSERT INTO {table} ({fields}) VALUES ({placeholders})"
|
|
46
|
+
return query, tuple(data.values())
|
|
47
|
+
|
|
48
|
+
def _build_update_query(self, table: str, data: Dict[str, Any],
|
|
49
|
+
condition: Dict[str, Any]) -> tuple:
|
|
50
|
+
"""
|
|
51
|
+
构建UPDATE查询
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
table: 表名
|
|
55
|
+
data: 要更新的数据
|
|
56
|
+
condition: 更新条件
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
tuple: (查询语句, 参数元组)
|
|
60
|
+
"""
|
|
61
|
+
set_clause = ', '.join([f"{k} = {self.placeholder}" for k in data.keys()])
|
|
62
|
+
where_clause = ' AND '.join([f"{k} = {self.placeholder}" for k in condition.keys()])
|
|
63
|
+
query = f"UPDATE {table} SET {set_clause} WHERE {where_clause}"
|
|
64
|
+
params = tuple(data.values()) + tuple(condition.values())
|
|
65
|
+
return query, params
|
|
66
|
+
|
|
67
|
+
def _build_delete_query(self, table: str, condition: Dict[str, Any]) -> tuple:
|
|
68
|
+
"""
|
|
69
|
+
构建DELETE查询
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
table: 表名
|
|
73
|
+
condition: 删除条件
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
tuple: (查询语句, 参数元组)
|
|
77
|
+
"""
|
|
78
|
+
where_clause = ' AND '.join([f"{k} = {self.placeholder}" for k in condition.keys()])
|
|
79
|
+
query = f"DELETE FROM {table} WHERE {where_clause}"
|
|
80
|
+
return query, tuple(condition.values())
|
|
81
|
+
|
|
82
|
+
def _build_select_query(self, table: str,
|
|
83
|
+
fields: Optional[List[str]] = None,
|
|
84
|
+
condition: Optional[Dict[str, Any]] = None,
|
|
85
|
+
limit: Optional[int] = None,
|
|
86
|
+
offset: Optional[int] = None,
|
|
87
|
+
order_by: Optional[List[tuple]] = None) -> tuple:
|
|
88
|
+
"""
|
|
89
|
+
构建SELECT查询
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
table: 表名
|
|
93
|
+
fields: 字段列表
|
|
94
|
+
condition: 查询条件
|
|
95
|
+
limit: 限制数量
|
|
96
|
+
offset: 偏移量
|
|
97
|
+
order_by: 排序规则
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
tuple: (查询语句, 参数元组)
|
|
101
|
+
"""
|
|
102
|
+
# SELECT子句
|
|
103
|
+
fields_str = ', '.join(fields) if fields else '*'
|
|
104
|
+
query = f"SELECT {fields_str} FROM {table}"
|
|
105
|
+
params = []
|
|
106
|
+
|
|
107
|
+
# WHERE子句
|
|
108
|
+
if condition:
|
|
109
|
+
where_clause = ' AND '.join([f"{k} = {self.placeholder}" for k in condition.keys()])
|
|
110
|
+
query += f" WHERE {where_clause}"
|
|
111
|
+
params.extend(condition.values())
|
|
112
|
+
|
|
113
|
+
# ORDER BY子句
|
|
114
|
+
if order_by:
|
|
115
|
+
order_parts = [f"{field} {direction}" for field, direction in order_by]
|
|
116
|
+
query += " ORDER BY " + ", ".join(order_parts)
|
|
117
|
+
|
|
118
|
+
# LIMIT子句
|
|
119
|
+
if limit is not None:
|
|
120
|
+
query += f" LIMIT {limit}"
|
|
121
|
+
|
|
122
|
+
# OFFSET子句
|
|
123
|
+
if offset is not None:
|
|
124
|
+
query += f" OFFSET {offset}"
|
|
125
|
+
|
|
126
|
+
return query, tuple(params)
|
|
127
|
+
|
|
128
|
+
def count(self, table: str, condition: Optional[Dict[str, Any]] = None) -> int:
|
|
129
|
+
"""
|
|
130
|
+
计数查询
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
table: 表名
|
|
134
|
+
condition: 查询条件
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
int: 记录数量
|
|
138
|
+
"""
|
|
139
|
+
query = f"SELECT COUNT(*) as count FROM {table}"
|
|
140
|
+
params = ()
|
|
141
|
+
|
|
142
|
+
if condition:
|
|
143
|
+
where_clause = ' AND '.join([f"{k} = {self.placeholder}" for k in condition.keys()])
|
|
144
|
+
query += f" WHERE {where_clause}"
|
|
145
|
+
params = tuple(condition.values())
|
|
146
|
+
|
|
147
|
+
result = self.execute(query, params)
|
|
148
|
+
if result and len(result) > 0:
|
|
149
|
+
return result[0].get('count', 0)
|
|
150
|
+
return 0
|
|
151
|
+
|
|
152
|
+
def exists(self, table: str, condition: Dict[str, Any]) -> bool:
|
|
153
|
+
"""
|
|
154
|
+
检查记录是否存在
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
table: 表名
|
|
158
|
+
condition: 查询条件
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
bool: 是否存在
|
|
162
|
+
"""
|
|
163
|
+
return self.count(table, condition) > 0
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
数据库工具包的自定义异常
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DatabaseError(Exception):
|
|
7
|
+
"""数据库基础异常类"""
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConnectionError(DatabaseError):
|
|
12
|
+
"""连接异常"""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class QueryError(DatabaseError):
|
|
17
|
+
"""查询执行异常"""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ConfigurationError(DatabaseError):
|
|
22
|
+
"""配置异常"""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ValidationError(DatabaseError):
|
|
27
|
+
"""数据验证异常"""
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class NotSupportedError(DatabaseError):
|
|
32
|
+
"""不支持的操作异常"""
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TransactionError(DatabaseError):
|
|
37
|
+
"""事务异常"""
|
|
38
|
+
pass
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
批量操作混入类
|
|
3
|
+
为数据库客户端提供批量操作功能
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import List, Dict, Any
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BatchOperationsMixin:
|
|
13
|
+
"""
|
|
14
|
+
批量操作混入类
|
|
15
|
+
|
|
16
|
+
为数据库客户端添加批量插入、更新、删除功能
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def batch_insert(self, table: str, data_list: List[Dict[str, Any]],
|
|
20
|
+
chunk_size: int = 100) -> List[Any]:
|
|
21
|
+
"""
|
|
22
|
+
批量插入数据
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
table: 表名
|
|
26
|
+
data_list: 数据列表
|
|
27
|
+
chunk_size: 每批次大小
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
List[Any]: 插入结果列表
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> data = [
|
|
34
|
+
... {'name': 'Alice', 'age': 25},
|
|
35
|
+
... {'name': 'Bob', 'age': 30}
|
|
36
|
+
... ]
|
|
37
|
+
>>> results = client.batch_insert('users', data)
|
|
38
|
+
"""
|
|
39
|
+
if not data_list:
|
|
40
|
+
logger.warning("批量插入数据列表为空")
|
|
41
|
+
return []
|
|
42
|
+
|
|
43
|
+
results = []
|
|
44
|
+
total = len(data_list)
|
|
45
|
+
|
|
46
|
+
# 分批处理
|
|
47
|
+
for i in range(0, total, chunk_size):
|
|
48
|
+
chunk = data_list[i:i + chunk_size]
|
|
49
|
+
|
|
50
|
+
for data in chunk:
|
|
51
|
+
try:
|
|
52
|
+
result = self.insert(table, data)
|
|
53
|
+
results.append(result)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
logger.error(f"批量插入失败: {e}")
|
|
56
|
+
results.append(None)
|
|
57
|
+
|
|
58
|
+
logger.debug(f"批量插入进度: {min(i + chunk_size, total)}/{total}")
|
|
59
|
+
|
|
60
|
+
success_count = sum(1 for r in results if r is not None)
|
|
61
|
+
logger.info(f"批量插入完成: {success_count}/{total} 成功")
|
|
62
|
+
|
|
63
|
+
return results
|
|
64
|
+
|
|
65
|
+
def batch_update(self, table: str, updates: List[Dict[str, Any]],
|
|
66
|
+
chunk_size: int = 100) -> int:
|
|
67
|
+
"""
|
|
68
|
+
批量更新数据
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
table: 表名
|
|
72
|
+
updates: 更新列表,每项包含 {'data': {...}, 'condition': {...}}
|
|
73
|
+
chunk_size: 每批次大小
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
int: 总共更新的行数
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
>>> updates = [
|
|
80
|
+
... {'data': {'age': 26}, 'condition': {'name': 'Alice'}},
|
|
81
|
+
... {'data': {'age': 31}, 'condition': {'name': 'Bob'}}
|
|
82
|
+
... ]
|
|
83
|
+
>>> count = client.batch_update('users', updates)
|
|
84
|
+
"""
|
|
85
|
+
if not updates:
|
|
86
|
+
logger.warning("批量更新列表为空")
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
total_count = 0
|
|
90
|
+
total = len(updates)
|
|
91
|
+
|
|
92
|
+
# 分批处理
|
|
93
|
+
for i in range(0, total, chunk_size):
|
|
94
|
+
chunk = updates[i:i + chunk_size]
|
|
95
|
+
|
|
96
|
+
for update_item in chunk:
|
|
97
|
+
try:
|
|
98
|
+
data = update_item.get('data', {})
|
|
99
|
+
condition = update_item.get('condition', {})
|
|
100
|
+
|
|
101
|
+
if not data or not condition:
|
|
102
|
+
logger.warning(f"更新项缺少data或condition: {update_item}")
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
count = self.update(table, data, condition)
|
|
106
|
+
total_count += count
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.error(f"批量更新失败: {e}")
|
|
110
|
+
|
|
111
|
+
logger.debug(f"批量更新进度: {min(i + chunk_size, total)}/{total}")
|
|
112
|
+
|
|
113
|
+
logger.info(f"批量更新完成: {total_count} 条记录")
|
|
114
|
+
return total_count
|
|
115
|
+
|
|
116
|
+
def batch_delete(self, table: str, conditions: List[Dict[str, Any]],
|
|
117
|
+
chunk_size: int = 100) -> int:
|
|
118
|
+
"""
|
|
119
|
+
批量删除数据
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
table: 表名
|
|
123
|
+
conditions: 删除条件列表
|
|
124
|
+
chunk_size: 每批次大小
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
int: 总共删除的行数
|
|
128
|
+
|
|
129
|
+
Examples:
|
|
130
|
+
>>> conditions = [
|
|
131
|
+
... {'name': 'Alice'},
|
|
132
|
+
... {'name': 'Bob'}
|
|
133
|
+
... ]
|
|
134
|
+
>>> count = client.batch_delete('users', conditions)
|
|
135
|
+
"""
|
|
136
|
+
if not conditions:
|
|
137
|
+
logger.warning("批量删除条件列表为空")
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
total_count = 0
|
|
141
|
+
total = len(conditions)
|
|
142
|
+
|
|
143
|
+
# 分批处理
|
|
144
|
+
for i in range(0, total, chunk_size):
|
|
145
|
+
chunk = conditions[i:i + chunk_size]
|
|
146
|
+
|
|
147
|
+
for condition in chunk:
|
|
148
|
+
try:
|
|
149
|
+
count = self.delete(table, condition)
|
|
150
|
+
total_count += count
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.error(f"批量删除失败: {e}")
|
|
153
|
+
|
|
154
|
+
logger.debug(f"批量删除进度: {min(i + chunk_size, total)}/{total}")
|
|
155
|
+
|
|
156
|
+
logger.info(f"批量删除完成: {total_count} 条记录")
|
|
157
|
+
return total_count
|
|
158
|
+
|
|
159
|
+
def upsert(self, table: str, data: Dict[str, Any],
|
|
160
|
+
unique_fields: List[str]) -> Any:
|
|
161
|
+
"""
|
|
162
|
+
插入或更新(如果存在则更新,否则插入)
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
table: 表名
|
|
166
|
+
data: 数据
|
|
167
|
+
unique_fields: 用于判断唯一性的字段
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
插入或更新结果
|
|
171
|
+
|
|
172
|
+
Examples:
|
|
173
|
+
>>> data = {'email': 'user@example.com', 'name': 'Alice', 'age': 25}
|
|
174
|
+
>>> client.upsert('users', data, ['email'])
|
|
175
|
+
"""
|
|
176
|
+
# 构建查询条件
|
|
177
|
+
condition = {field: data[field] for field in unique_fields if field in data}
|
|
178
|
+
|
|
179
|
+
if not condition:
|
|
180
|
+
logger.warning("upsert条件为空,执行插入操作")
|
|
181
|
+
return self.insert(table, data)
|
|
182
|
+
|
|
183
|
+
# 检查记录是否存在
|
|
184
|
+
existing = self.select(table, condition=condition, limit=1)
|
|
185
|
+
|
|
186
|
+
if existing:
|
|
187
|
+
# 更新
|
|
188
|
+
logger.debug(f"记录已存在,执行更新: {condition}")
|
|
189
|
+
self.update(table, data, condition)
|
|
190
|
+
return condition
|
|
191
|
+
else:
|
|
192
|
+
# 插入
|
|
193
|
+
logger.debug(f"记录不存在,执行插入: {condition}")
|
|
194
|
+
return self.insert(table, data)
|