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.
@@ -0,0 +1,20 @@
1
+ """
2
+ 数据库客户端模块
3
+ 包含所有数据库客户端实现
4
+ """
5
+
6
+ from .mysql import MySQLClient
7
+ from .postgresql import PostgreSQLClient
8
+ from .sqlite import SQLiteClient
9
+ from .mongodb import MongoDBClient
10
+ from .redis import RedisClient
11
+ from .supabase import SupabaseClient
12
+
13
+ __all__ = [
14
+ 'MySQLClient',
15
+ 'PostgreSQLClient',
16
+ 'SQLiteClient',
17
+ 'MongoDBClient',
18
+ 'RedisClient',
19
+ 'SupabaseClient',
20
+ ]
@@ -0,0 +1,251 @@
1
+ """
2
+ MongoDB数据库客户端
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+ import logging
7
+
8
+ from ..core.base import BaseClient
9
+ from ..exceptions import ConnectionError, QueryError, NotSupportedError
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class MongoDBClient(BaseClient):
15
+ """MongoDB数据库客户端实现"""
16
+
17
+ def __init__(self, config: Dict[str, Any]):
18
+ """初始化MongoDB客户端"""
19
+ super().__init__(config)
20
+ self.client = None
21
+ self.db = None
22
+
23
+ def _validate_config(self) -> None:
24
+ """验证MongoDB配置"""
25
+ super()._validate_config()
26
+ if 'database' not in self.config:
27
+ raise ValueError("MongoDB配置缺少必需字段: database")
28
+
29
+ # 需要connection_string或host
30
+ if 'connection_string' not in self.config and 'host' not in self.config:
31
+ raise ValueError("MongoDB配置需要提供connection_string或host")
32
+
33
+ def connect(self) -> bool:
34
+ """连接MongoDB数据库"""
35
+ try:
36
+ from pymongo import MongoClient
37
+
38
+ connection_string = self.config.get('connection_string')
39
+
40
+ if connection_string:
41
+ self.client = MongoClient(connection_string)
42
+ else:
43
+ self.client = MongoClient(
44
+ host=self.config.get('host', 'localhost'),
45
+ port=self.config.get('port', 27017),
46
+ username=self.config.get('user'),
47
+ password=self.config.get('password'),
48
+ **self.config.get('options', {})
49
+ )
50
+
51
+ self.db = self.client[self.config['database']]
52
+ self.connection = self.db # 兼容性
53
+
54
+ # 测试连接
55
+ self.client.server_info()
56
+
57
+ self._connected = True
58
+ logger.info(f"MongoDB连接成功: {self.config['database']}")
59
+ return True
60
+
61
+ except ImportError:
62
+ raise ConnectionError("未安装pymongo库,请运行: pip install pymongo")
63
+ except Exception as e:
64
+ self._connected = False
65
+ logger.error(f"MongoDB连接失败: {e}")
66
+ raise ConnectionError(f"MongoDB连接失败: {e}")
67
+
68
+ def disconnect(self) -> bool:
69
+ """断开MongoDB连接"""
70
+ if self.client:
71
+ try:
72
+ self.client.close()
73
+ self._connected = False
74
+ logger.info("MongoDB连接已关闭")
75
+ return True
76
+ except Exception as e:
77
+ logger.error(f"关闭MongoDB连接失败: {e}")
78
+ return False
79
+ return True
80
+
81
+ def is_connected(self) -> bool:
82
+ """检查MongoDB连接状态"""
83
+ if not self.client:
84
+ return False
85
+ try:
86
+ self.client.server_info()
87
+ return True
88
+ except Exception:
89
+ self._connected = False
90
+ return False
91
+
92
+ def execute(self, query: str, params: Optional[tuple] = None) -> Any:
93
+ """
94
+ MongoDB不支持SQL查询
95
+
96
+ Raises:
97
+ NotSupportedError: 总是抛出此异常
98
+ """
99
+ raise NotSupportedError("MongoDB不支持SQL查询,请使用insert/update/delete/select方法")
100
+
101
+ def insert(self, table: str, data: Dict[str, Any]) -> Any:
102
+ """插入文档到MongoDB"""
103
+ if not self.is_connected():
104
+ raise ConnectionError("数据库未连接")
105
+
106
+ try:
107
+ collection = self.db[table]
108
+ result = collection.insert_one(data)
109
+ doc_id = str(result.inserted_id)
110
+ logger.debug(f"插入文档成功,ID: {doc_id}")
111
+ return doc_id
112
+
113
+ except Exception as e:
114
+ logger.error(f"插入文档失败: {e}")
115
+ raise QueryError(f"插入文档失败: {e}")
116
+
117
+ def insert_many(self, table: str, data_list: List[Dict[str, Any]]) -> List[str]:
118
+ """
119
+ 批量插入文档
120
+
121
+ Args:
122
+ table: 集合名
123
+ data_list: 文档列表
124
+
125
+ Returns:
126
+ List[str]: 插入的文档ID列表
127
+ """
128
+ if not self.is_connected():
129
+ raise ConnectionError("数据库未连接")
130
+
131
+ try:
132
+ collection = self.db[table]
133
+ result = collection.insert_many(data_list)
134
+ doc_ids = [str(id) for id in result.inserted_ids]
135
+ logger.debug(f"批量插入 {len(doc_ids)} 个文档")
136
+ return doc_ids
137
+
138
+ except Exception as e:
139
+ logger.error(f"批量插入文档失败: {e}")
140
+ raise QueryError(f"批量插入文档失败: {e}")
141
+
142
+ def update(self, table: str, data: Dict[str, Any],
143
+ condition: Dict[str, Any]) -> int:
144
+ """更新MongoDB文档"""
145
+ if not self.is_connected():
146
+ raise ConnectionError("数据库未连接")
147
+
148
+ try:
149
+ collection = self.db[table]
150
+ result = collection.update_many(condition, {'$set': data})
151
+ count = result.modified_count
152
+ logger.debug(f"更新了 {count} 个文档")
153
+ return count
154
+
155
+ except Exception as e:
156
+ logger.error(f"更新文档失败: {e}")
157
+ raise QueryError(f"更新文档失败: {e}")
158
+
159
+ def delete(self, table: str, condition: Dict[str, Any]) -> int:
160
+ """删除MongoDB文档"""
161
+ if not self.is_connected():
162
+ raise ConnectionError("数据库未连接")
163
+
164
+ try:
165
+ collection = self.db[table]
166
+ result = collection.delete_many(condition)
167
+ count = result.deleted_count
168
+ logger.debug(f"删除了 {count} 个文档")
169
+ return count
170
+
171
+ except Exception as e:
172
+ logger.error(f"删除文档失败: {e}")
173
+ raise QueryError(f"删除文档失败: {e}")
174
+
175
+ def select(self, table: str,
176
+ fields: Optional[List[str]] = None,
177
+ condition: Optional[Dict[str, Any]] = None,
178
+ limit: Optional[int] = None,
179
+ offset: Optional[int] = None,
180
+ order_by: Optional[List[tuple]] = None) -> List[Dict]:
181
+ """查询MongoDB文档"""
182
+ if not self.is_connected():
183
+ raise ConnectionError("数据库未连接")
184
+
185
+ try:
186
+ collection = self.db[table]
187
+
188
+ # 构建投影
189
+ projection = {field: 1 for field in fields} if fields else None
190
+
191
+ # 执行查询
192
+ cursor = collection.find(condition or {}, projection)
193
+
194
+ # 排序
195
+ if order_by:
196
+ # MongoDB排序: 1为升序,-1为降序
197
+ sort_list = []
198
+ for field, direction in order_by:
199
+ sort_direction = 1 if direction.upper() == 'ASC' else -1
200
+ sort_list.append((field, sort_direction))
201
+ cursor = cursor.sort(sort_list)
202
+
203
+ # 跳过和限制
204
+ if offset:
205
+ cursor = cursor.skip(offset)
206
+ if limit:
207
+ cursor = cursor.limit(limit)
208
+
209
+ # 转换结果
210
+ results = []
211
+ for doc in cursor:
212
+ # 将ObjectId转为字符串
213
+ doc['_id'] = str(doc['_id'])
214
+ results.append(doc)
215
+
216
+ logger.debug(f"查询到 {len(results)} 个文档")
217
+ return results
218
+
219
+ except Exception as e:
220
+ logger.error(f"查询文档失败: {e}")
221
+ raise QueryError(f"查询文档失败: {e}")
222
+
223
+ def aggregate(self, table: str, pipeline: List[Dict]) -> List[Dict]:
224
+ """
225
+ 执行聚合查询
226
+
227
+ Args:
228
+ table: 集合名
229
+ pipeline: 聚合管道
230
+
231
+ Returns:
232
+ List[Dict]: 聚合结果
233
+ """
234
+ if not self.is_connected():
235
+ raise ConnectionError("数据库未连接")
236
+
237
+ try:
238
+ collection = self.db[table]
239
+ results = list(collection.aggregate(pipeline))
240
+
241
+ # 转换ObjectId
242
+ for doc in results:
243
+ if '_id' in doc:
244
+ doc['_id'] = str(doc['_id'])
245
+
246
+ logger.debug(f"聚合查询返回 {len(results)} 个结果")
247
+ return results
248
+
249
+ except Exception as e:
250
+ logger.error(f"聚合查询失败: {e}")
251
+ raise QueryError(f"聚合查询失败: {e}")
@@ -0,0 +1,143 @@
1
+ """
2
+ MySQL数据库客户端
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+ import logging
7
+
8
+ from ..core.sql_base import SQLBaseClient
9
+ from ..exceptions import ConnectionError, QueryError
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class MySQLClient(SQLBaseClient):
15
+ """MySQL数据库客户端实现"""
16
+
17
+ @property
18
+ def placeholder(self) -> str:
19
+ return "%s"
20
+
21
+ def _validate_config(self) -> None:
22
+ """验证MySQL配置"""
23
+ super()._validate_config()
24
+ required = ['host', 'user', 'password', 'database']
25
+ missing = [key for key in required if key not in self.config]
26
+ if missing:
27
+ raise ValueError(f"MySQL配置缺少必需字段: {', '.join(missing)}")
28
+
29
+ def connect(self) -> bool:
30
+ """连接MySQL数据库"""
31
+ try:
32
+ import mysql.connector
33
+
34
+ self.connection = mysql.connector.connect(
35
+ host=self.config.get('host', 'localhost'),
36
+ port=self.config.get('port', 3306),
37
+ user=self.config['user'],
38
+ password=self.config['password'],
39
+ database=self.config['database'],
40
+ **self.config.get('options', {})
41
+ )
42
+ self._connected = True
43
+ logger.info(f"MySQL连接成功: {self.config['database']}@{self.config['host']}")
44
+ return True
45
+
46
+ except ImportError:
47
+ raise ConnectionError("未安装mysql-connector-python库,请运行: pip install mysql-connector-python")
48
+ except Exception as e:
49
+ self._connected = False
50
+ logger.error(f"MySQL连接失败: {e}")
51
+ raise ConnectionError(f"MySQL连接失败: {e}")
52
+
53
+ def disconnect(self) -> bool:
54
+ """断开MySQL连接"""
55
+ if self.connection:
56
+ try:
57
+ self.connection.close()
58
+ self._connected = False
59
+ logger.info("MySQL连接已关闭")
60
+ return True
61
+ except Exception as e:
62
+ logger.error(f"关闭MySQL连接失败: {e}")
63
+ return False
64
+ return True
65
+
66
+ def is_connected(self) -> bool:
67
+ """检查MySQL连接状态"""
68
+ if not self.connection:
69
+ return False
70
+ try:
71
+ self.connection.ping(reconnect=False)
72
+ return True
73
+ except Exception:
74
+ self._connected = False
75
+ return False
76
+
77
+ def execute(self, query: str, params: Optional[tuple] = None) -> Any:
78
+ """执行MySQL查询"""
79
+ if not self.is_connected():
80
+ raise ConnectionError("数据库未连接")
81
+
82
+ try:
83
+ cursor = self.connection.cursor(dictionary=True)
84
+ cursor.execute(query, params or ())
85
+
86
+ if query.strip().upper().startswith('SELECT'):
87
+ result = cursor.fetchall()
88
+ else:
89
+ self.connection.commit()
90
+ result = cursor.rowcount
91
+
92
+ cursor.close()
93
+ return result
94
+
95
+ except Exception as e:
96
+ logger.error(f"查询执行失败: {e}")
97
+ raise QueryError(f"查询执行失败: {e}")
98
+
99
+ def insert(self, table: str, data: Dict[str, Any]) -> Any:
100
+ """插入数据到MySQL"""
101
+ query, params = self._build_insert_query(table, data)
102
+
103
+ try:
104
+ cursor = self.connection.cursor()
105
+ cursor.execute(query, params)
106
+ self.connection.commit()
107
+ last_id = cursor.lastrowid
108
+ cursor.close()
109
+ logger.debug(f"插入数据成功,ID: {last_id}")
110
+ return last_id
111
+
112
+ except Exception as e:
113
+ logger.error(f"插入数据失败: {e}")
114
+ raise QueryError(f"插入数据失败: {e}")
115
+
116
+ def update(self, table: str, data: Dict[str, Any],
117
+ condition: Dict[str, Any]) -> int:
118
+ """更新MySQL数据"""
119
+ query, params = self._build_update_query(table, data, condition)
120
+ result = self.execute(query, params)
121
+ logger.debug(f"更新了 {result} 条记录")
122
+ return result
123
+
124
+ def delete(self, table: str, condition: Dict[str, Any]) -> int:
125
+ """删除MySQL数据"""
126
+ query, params = self._build_delete_query(table, condition)
127
+ result = self.execute(query, params)
128
+ logger.debug(f"删除了 {result} 条记录")
129
+ return result
130
+
131
+ def select(self, table: str,
132
+ fields: Optional[List[str]] = None,
133
+ condition: Optional[Dict[str, Any]] = None,
134
+ limit: Optional[int] = None,
135
+ offset: Optional[int] = None,
136
+ order_by: Optional[List[tuple]] = None) -> List[Dict]:
137
+ """查询MySQL数据"""
138
+ query, params = self._build_select_query(
139
+ table, fields, condition, limit, offset, order_by
140
+ )
141
+ result = self.execute(query, params)
142
+ logger.debug(f"查询到 {len(result)} 条记录")
143
+ return result
@@ -0,0 +1,152 @@
1
+ """
2
+ PostgreSQL数据库客户端
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+ import logging
7
+
8
+ from ..core.sql_base import SQLBaseClient
9
+ from ..exceptions import ConnectionError, QueryError
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class PostgreSQLClient(SQLBaseClient):
15
+ """PostgreSQL数据库客户端实现"""
16
+
17
+ @property
18
+ def placeholder(self) -> str:
19
+ return "%s"
20
+
21
+ def _validate_config(self) -> None:
22
+ """验证PostgreSQL配置"""
23
+ super()._validate_config()
24
+ required = ['host', 'user', 'password', 'database']
25
+ missing = [key for key in required if key not in self.config]
26
+ if missing:
27
+ raise ValueError(f"PostgreSQL配置缺少必需字段: {', '.join(missing)}")
28
+
29
+ def connect(self) -> bool:
30
+ """连接PostgreSQL数据库"""
31
+ try:
32
+ import psycopg2
33
+
34
+ self.connection = psycopg2.connect(
35
+ host=self.config.get('host', 'localhost'),
36
+ port=self.config.get('port', 5432),
37
+ user=self.config['user'],
38
+ password=self.config['password'],
39
+ database=self.config['database'],
40
+ **self.config.get('options', {})
41
+ )
42
+ self._connected = True
43
+ logger.info(f"PostgreSQL连接成功: {self.config['database']}@{self.config['host']}")
44
+ return True
45
+
46
+ except ImportError:
47
+ raise ConnectionError("未安装psycopg2库,请运行: pip install psycopg2-binary")
48
+ except Exception as e:
49
+ self._connected = False
50
+ logger.error(f"PostgreSQL连接失败: {e}")
51
+ raise ConnectionError(f"PostgreSQL连接失败: {e}")
52
+
53
+ def disconnect(self) -> bool:
54
+ """断开PostgreSQL连接"""
55
+ if self.connection:
56
+ try:
57
+ self.connection.close()
58
+ self._connected = False
59
+ logger.info("PostgreSQL连接已关闭")
60
+ return True
61
+ except Exception as e:
62
+ logger.error(f"关闭PostgreSQL连接失败: {e}")
63
+ return False
64
+ return True
65
+
66
+ def is_connected(self) -> bool:
67
+ """检查PostgreSQL连接状态"""
68
+ if not self.connection:
69
+ return False
70
+ try:
71
+ # PostgreSQL使用closed属性检查连接
72
+ return not self.connection.closed
73
+ except Exception:
74
+ self._connected = False
75
+ return False
76
+
77
+ def execute(self, query: str, params: Optional[tuple] = None) -> Any:
78
+ """执行PostgreSQL查询"""
79
+ if not self.is_connected():
80
+ raise ConnectionError("数据库未连接")
81
+
82
+ try:
83
+ import psycopg2.extras
84
+
85
+ cursor = self.connection.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
86
+ cursor.execute(query, params or ())
87
+
88
+ if query.strip().upper().startswith('SELECT'):
89
+ result = [dict(row) for row in cursor.fetchall()]
90
+ else:
91
+ self.connection.commit()
92
+ result = cursor.rowcount
93
+
94
+ cursor.close()
95
+ return result
96
+
97
+ except Exception as e:
98
+ logger.error(f"查询执行失败: {e}")
99
+ raise QueryError(f"查询执行失败: {e}")
100
+
101
+ def insert(self, table: str, data: Dict[str, Any]) -> Any:
102
+ """插入数据到PostgreSQL"""
103
+ query, params = self._build_insert_query(table, data)
104
+ # PostgreSQL使用RETURNING获取插入的ID
105
+ query += " RETURNING id"
106
+
107
+ try:
108
+ cursor = self.connection.cursor()
109
+ cursor.execute(query, params)
110
+ self.connection.commit()
111
+
112
+ # 获取返回的ID
113
+ result = cursor.fetchone()
114
+ last_id = result[0] if result else None
115
+
116
+ cursor.close()
117
+ logger.debug(f"插入数据成功,ID: {last_id}")
118
+ return last_id
119
+
120
+ except Exception as e:
121
+ self.connection.rollback()
122
+ logger.error(f"插入数据失败: {e}")
123
+ raise QueryError(f"插入数据失败: {e}")
124
+
125
+ def update(self, table: str, data: Dict[str, Any],
126
+ condition: Dict[str, Any]) -> int:
127
+ """更新PostgreSQL数据"""
128
+ query, params = self._build_update_query(table, data, condition)
129
+ result = self.execute(query, params)
130
+ logger.debug(f"更新了 {result} 条记录")
131
+ return result
132
+
133
+ def delete(self, table: str, condition: Dict[str, Any]) -> int:
134
+ """删除PostgreSQL数据"""
135
+ query, params = self._build_delete_query(table, condition)
136
+ result = self.execute(query, params)
137
+ logger.debug(f"删除了 {result} 条记录")
138
+ return result
139
+
140
+ def select(self, table: str,
141
+ fields: Optional[List[str]] = None,
142
+ condition: Optional[Dict[str, Any]] = None,
143
+ limit: Optional[int] = None,
144
+ offset: Optional[int] = None,
145
+ order_by: Optional[List[tuple]] = None) -> List[Dict]:
146
+ """查询PostgreSQL数据"""
147
+ query, params = self._build_select_query(
148
+ table, fields, condition, limit, offset, order_by
149
+ )
150
+ result = self.execute(query, params)
151
+ logger.debug(f"查询到 {len(result)} 条记录")
152
+ return result