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,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
|