fiuai-sdk-python 0.7.0__py3-none-any.whl → 0.7.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.
- {fiuai_sdk_python-0.7.0.dist-info → fiuai_sdk_python-0.7.1.dist-info}/METADATA +1 -1
- {fiuai_sdk_python-0.7.0.dist-info → fiuai_sdk_python-0.7.1.dist-info}/RECORD +4 -11
- fiuai_sdk_python/pkg/db/__init__.py +0 -35
- fiuai_sdk_python/pkg/db/config.py +0 -25
- fiuai_sdk_python/pkg/db/errors.py +0 -27
- fiuai_sdk_python/pkg/db/manager.py +0 -439
- fiuai_sdk_python/pkg/db/utils.py +0 -78
- fiuai_sdk_python/pkg/vector/__init__.py +0 -23
- fiuai_sdk_python/pkg/vector/vector.py +0 -853
- {fiuai_sdk_python-0.7.0.dist-info → fiuai_sdk_python-0.7.1.dist-info}/WHEEL +0 -0
- {fiuai_sdk_python-0.7.0.dist-info → fiuai_sdk_python-0.7.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fiuai_sdk_python
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: FiuAI Python SDK - 企业级AI服务集成开发工具包
|
|
5
5
|
Project-URL: Homepage, https://github.com/fiuai/fiuai-sdk-python
|
|
6
6
|
Project-URL: Documentation, https://github.com/fiuai/fiuai-sdk-python#readme
|
|
@@ -25,18 +25,11 @@ fiuai_sdk_python/http/__init__.py,sha256=RMtrf0O-iuAGMIqfpQg6sQHj_O9Lo4Lhn7Kejca
|
|
|
25
25
|
fiuai_sdk_python/http/client.py,sha256=omcM8R8NGSszZlKBPikZr-NJXHgwpNYu7qDa_9H1Jug,14321
|
|
26
26
|
fiuai_sdk_python/pkg/cache/__init__.py,sha256=7mVRUKkAxCAHIWVZrIslel_kr0S5Je_l0I9Fy_iZjzI,112
|
|
27
27
|
fiuai_sdk_python/pkg/cache/redis_manager.py,sha256=APSRRmsJKRWfDvzIJiwNMtIqjzbheOdULskj5mI55Fk,6114
|
|
28
|
-
fiuai_sdk_python/pkg/db/__init__.py,sha256=IK-zw5tTiSpVMtT3zdVGMaqup08TACIWcEYWpe1htkc,709
|
|
29
|
-
fiuai_sdk_python/pkg/db/config.py,sha256=tId1db3209oZGVQKDwm-cvKu_GCbpbPerR8rgdeD3xE,645
|
|
30
|
-
fiuai_sdk_python/pkg/db/errors.py,sha256=JJwfM_sHXYAOfP5peg9rg8n2BsCMnPXIKVLrOFMPHOs,489
|
|
31
|
-
fiuai_sdk_python/pkg/db/manager.py,sha256=HapTkFOfCzQLCXWBAOlGHsjGCWVBNBLEMCDH7Kty7tI,14855
|
|
32
|
-
fiuai_sdk_python/pkg/db/utils.py,sha256=qqZ14eV7fy9x_mwcEDKJDz1W54xPndpJ1QfM_D0E3FI,2419
|
|
33
|
-
fiuai_sdk_python/pkg/vector/__init__.py,sha256=fvbetFxbHiFeojnTfKT3roA1hOcoNHF_eCRuG6HwMpI,404
|
|
34
|
-
fiuai_sdk_python/pkg/vector/vector.py,sha256=hdwhDSiKp4a7s6dVwFNTRk1BPqhZppywD8LnOudoMdo,28990
|
|
35
28
|
fiuai_sdk_python/utils/__init__.py,sha256=UwwsvqBsaRCHbWdx-wvM48szT3j50h95k9MZdbfawRQ,72
|
|
36
29
|
fiuai_sdk_python/utils/ids.py,sha256=ZDtEqt_Woth8ytPB2tdnnTIv7noWr8XYhSsUvkZ7Hc0,6448
|
|
37
30
|
fiuai_sdk_python/utils/logger.py,sha256=RuKn9TFmV1IfsRTV3ZdtXLZkM44GkHs32ehbJdrdu-8,3657
|
|
38
31
|
fiuai_sdk_python/utils/text.py,sha256=bnob_W0nj_Vj8Hp93B0cYmFOY8IhUWF0C8UedOYCNvs,1667
|
|
39
|
-
fiuai_sdk_python-0.7.
|
|
40
|
-
fiuai_sdk_python-0.7.
|
|
41
|
-
fiuai_sdk_python-0.7.
|
|
42
|
-
fiuai_sdk_python-0.7.
|
|
32
|
+
fiuai_sdk_python-0.7.1.dist-info/METADATA,sha256=9zuPOwRytT_wRjNeyutOdtkpDHv7UlOA9FRPTJsDgcc,1523
|
|
33
|
+
fiuai_sdk_python-0.7.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
34
|
+
fiuai_sdk_python-0.7.1.dist-info/licenses/LICENSE,sha256=PFMF0dFErrBFqU-rryEby0yW8GBagYqrdbyZQHMUCJg,1062
|
|
35
|
+
fiuai_sdk_python-0.7.1.dist-info/RECORD,,
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# -- coding: utf-8 --
|
|
2
|
-
# Project: fiuai-world
|
|
3
|
-
# Created Date: 2025-01-27
|
|
4
|
-
# Author: liming
|
|
5
|
-
# Email: lmlala@aliyun.com
|
|
6
|
-
# Copyright (c) 2025 FiuAI
|
|
7
|
-
|
|
8
|
-
from pkg.db.errors import (
|
|
9
|
-
PostgresError,
|
|
10
|
-
PostgresConnectionError,
|
|
11
|
-
PostgresQueryError,
|
|
12
|
-
PostgresPoolError,
|
|
13
|
-
)
|
|
14
|
-
from pkg.db.config import PostgresConfig
|
|
15
|
-
from pkg.db.manager import (
|
|
16
|
-
PostgresManager,
|
|
17
|
-
postgres_manager,
|
|
18
|
-
)
|
|
19
|
-
from pkg.db.utils import (
|
|
20
|
-
escape_table_name,
|
|
21
|
-
build_frappe_table_name,
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
__all__ = [
|
|
25
|
-
'PostgresManager',
|
|
26
|
-
'PostgresConfig',
|
|
27
|
-
'PostgresError',
|
|
28
|
-
'PostgresConnectionError',
|
|
29
|
-
'PostgresQueryError',
|
|
30
|
-
'PostgresPoolError',
|
|
31
|
-
'postgres_manager',
|
|
32
|
-
'escape_table_name',
|
|
33
|
-
'build_frappe_table_name',
|
|
34
|
-
]
|
|
35
|
-
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# -- coding: utf-8 --
|
|
2
|
-
# Project: fiuai-world
|
|
3
|
-
# Created Date: 2025-01-27
|
|
4
|
-
# Author: liming
|
|
5
|
-
# Email: lmlala@aliyun.com
|
|
6
|
-
# Copyright (c) 2025 FiuAI
|
|
7
|
-
|
|
8
|
-
from dataclasses import dataclass
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@dataclass
|
|
12
|
-
class PostgresConfig:
|
|
13
|
-
"""PostgreSQL 配置"""
|
|
14
|
-
host: str
|
|
15
|
-
port: int
|
|
16
|
-
user: str
|
|
17
|
-
password: str
|
|
18
|
-
database: str
|
|
19
|
-
pool_minconn: int = 1 # 连接池最小连接数
|
|
20
|
-
pool_maxconn: int = 10 # 连接池最大连接数
|
|
21
|
-
pool_timeout: int = 30 # 获取连接超时时间(秒)
|
|
22
|
-
connect_timeout: int = 10 # 连接超时时间(秒)
|
|
23
|
-
retry_count: int = 3 # 重试次数
|
|
24
|
-
retry_delay: int = 1 # 重试延迟(秒)
|
|
25
|
-
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# -- coding: utf-8 --
|
|
2
|
-
# Project: fiuai-world
|
|
3
|
-
# Created Date: 2025-01-27
|
|
4
|
-
# Author: liming
|
|
5
|
-
# Email: lmlala@aliyun.com
|
|
6
|
-
# Copyright (c) 2025 FiuAI
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class PostgresError(Exception):
|
|
10
|
-
"""PostgreSQL 基础错误类"""
|
|
11
|
-
pass
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class PostgresConnectionError(PostgresError):
|
|
15
|
-
"""PostgreSQL 连接错误"""
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class PostgresQueryError(PostgresError):
|
|
20
|
-
"""PostgreSQL 查询错误"""
|
|
21
|
-
pass
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class PostgresPoolError(PostgresError):
|
|
25
|
-
"""PostgreSQL 连接池错误"""
|
|
26
|
-
pass
|
|
27
|
-
|
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
# -- coding: utf-8 --
|
|
2
|
-
# Project: fiuai-world
|
|
3
|
-
# Created Date: 2025-01-27
|
|
4
|
-
# Author: liming
|
|
5
|
-
# Email: lmlala@aliyun.com
|
|
6
|
-
# Copyright (c) 2025 FiuAI
|
|
7
|
-
|
|
8
|
-
import time
|
|
9
|
-
import threading
|
|
10
|
-
from typing import Optional, Dict, Any, List, Union
|
|
11
|
-
from contextlib import contextmanager
|
|
12
|
-
|
|
13
|
-
import psycopg2
|
|
14
|
-
from psycopg2 import pool
|
|
15
|
-
from psycopg2.extras import RealDictCursor, DictCursor
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
from logging import getLogger
|
|
19
|
-
from .errors import (
|
|
20
|
-
PostgresError,
|
|
21
|
-
PostgresConnectionError,
|
|
22
|
-
PostgresQueryError,
|
|
23
|
-
PostgresPoolError,
|
|
24
|
-
)
|
|
25
|
-
from .config import PostgresConfig
|
|
26
|
-
|
|
27
|
-
logger = getLogger(__name__)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class PostgresManager:
|
|
31
|
-
"""PostgreSQL 连接池管理器单例类
|
|
32
|
-
|
|
33
|
-
提供连接池管理、重连、重试等稳定性机制
|
|
34
|
-
提供基础 CRUD 接口
|
|
35
|
-
"""
|
|
36
|
-
_instance: Optional['PostgresManager'] = None
|
|
37
|
-
_lock = threading.Lock()
|
|
38
|
-
_initialized = False
|
|
39
|
-
|
|
40
|
-
def __new__(cls):
|
|
41
|
-
if cls._instance is None:
|
|
42
|
-
with cls._lock:
|
|
43
|
-
if cls._instance is None:
|
|
44
|
-
cls._instance = super().__new__(cls)
|
|
45
|
-
return cls._instance
|
|
46
|
-
|
|
47
|
-
def __init__(self):
|
|
48
|
-
if hasattr(self, '_initialized') and self._initialized:
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
self._config: Optional[PostgresConfig] = None
|
|
52
|
-
self._pool: Optional[pool.ThreadedConnectionPool] = None
|
|
53
|
-
self._is_connected_ref = {'connected': False}
|
|
54
|
-
self._initialized = True
|
|
55
|
-
|
|
56
|
-
def initialize(self, config: PostgresConfig):
|
|
57
|
-
"""初始化 PostgreSQL 连接池
|
|
58
|
-
|
|
59
|
-
Args:
|
|
60
|
-
config: PostgreSQL 配置
|
|
61
|
-
|
|
62
|
-
Raises:
|
|
63
|
-
PostgresPoolError: 初始化失败时抛出
|
|
64
|
-
"""
|
|
65
|
-
if self._is_connected_ref['connected']:
|
|
66
|
-
logger.warning("PostgreSQL 已经初始化,跳过重复初始化")
|
|
67
|
-
return
|
|
68
|
-
|
|
69
|
-
self._config = config
|
|
70
|
-
|
|
71
|
-
try:
|
|
72
|
-
# 创建连接池
|
|
73
|
-
self._pool = pool.ThreadedConnectionPool(
|
|
74
|
-
minconn=config.pool_minconn,
|
|
75
|
-
maxconn=config.pool_maxconn,
|
|
76
|
-
host=config.host,
|
|
77
|
-
port=config.port,
|
|
78
|
-
user=config.user,
|
|
79
|
-
password=config.password,
|
|
80
|
-
database=config.database,
|
|
81
|
-
connect_timeout=config.connect_timeout,
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# 测试连接
|
|
85
|
-
test_conn = self._pool.getconn()
|
|
86
|
-
if not test_conn:
|
|
87
|
-
raise PostgresPoolError("无法从连接池获取连接")
|
|
88
|
-
|
|
89
|
-
# 执行简单查询测试连接
|
|
90
|
-
with test_conn.cursor() as cursor:
|
|
91
|
-
cursor.execute("SELECT 1")
|
|
92
|
-
cursor.fetchone()
|
|
93
|
-
|
|
94
|
-
self._pool.putconn(test_conn)
|
|
95
|
-
self._is_connected_ref['connected'] = True
|
|
96
|
-
|
|
97
|
-
logger.info(
|
|
98
|
-
f"PostgreSQL 连接池初始化成功: {config.host}:{config.port}/{config.database}, "
|
|
99
|
-
f"pool_size={config.pool_maxconn}"
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
except Exception as e:
|
|
103
|
-
self._is_connected_ref['connected'] = False
|
|
104
|
-
if self._pool:
|
|
105
|
-
try:
|
|
106
|
-
self._pool.closeall()
|
|
107
|
-
except Exception:
|
|
108
|
-
pass
|
|
109
|
-
self._pool = None
|
|
110
|
-
logger.error(f"PostgreSQL 初始化失败: {str(e)}")
|
|
111
|
-
raise PostgresPoolError(f"初始化失败: {str(e)}")
|
|
112
|
-
|
|
113
|
-
def _check_connection(self) -> bool:
|
|
114
|
-
"""检查连接是否有效
|
|
115
|
-
|
|
116
|
-
Returns:
|
|
117
|
-
bool: 连接是否有效
|
|
118
|
-
"""
|
|
119
|
-
if not self._pool or not self._is_connected_ref.get('connected', False):
|
|
120
|
-
return False
|
|
121
|
-
|
|
122
|
-
try:
|
|
123
|
-
conn = self._pool.getconn()
|
|
124
|
-
if not conn:
|
|
125
|
-
return False
|
|
126
|
-
|
|
127
|
-
with conn.cursor() as cursor:
|
|
128
|
-
cursor.execute("SELECT 1")
|
|
129
|
-
cursor.fetchone()
|
|
130
|
-
|
|
131
|
-
self._pool.putconn(conn)
|
|
132
|
-
return True
|
|
133
|
-
|
|
134
|
-
except Exception as e:
|
|
135
|
-
logger.warning(f"连接检查失败: {str(e)}")
|
|
136
|
-
return False
|
|
137
|
-
|
|
138
|
-
def _reconnect(self) -> bool:
|
|
139
|
-
"""重新连接
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
bool: 重连是否成功
|
|
143
|
-
"""
|
|
144
|
-
if not self._config:
|
|
145
|
-
return False
|
|
146
|
-
|
|
147
|
-
logger.info("尝试重新连接 PostgreSQL...")
|
|
148
|
-
|
|
149
|
-
try:
|
|
150
|
-
# 关闭旧连接池
|
|
151
|
-
if self._pool:
|
|
152
|
-
try:
|
|
153
|
-
self._pool.closeall()
|
|
154
|
-
except Exception:
|
|
155
|
-
pass
|
|
156
|
-
self._pool = None
|
|
157
|
-
|
|
158
|
-
# 重新创建连接池
|
|
159
|
-
self._pool = pool.ThreadedConnectionPool(
|
|
160
|
-
minconn=self._config.pool_minconn,
|
|
161
|
-
maxconn=self._config.pool_maxconn,
|
|
162
|
-
host=self._config.host,
|
|
163
|
-
port=self._config.port,
|
|
164
|
-
user=self._config.user,
|
|
165
|
-
password=self._config.password,
|
|
166
|
-
database=self._config.database,
|
|
167
|
-
connect_timeout=self._config.connect_timeout,
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
# 测试连接
|
|
171
|
-
test_conn = self._pool.getconn()
|
|
172
|
-
if not test_conn:
|
|
173
|
-
raise PostgresPoolError("无法从连接池获取连接")
|
|
174
|
-
|
|
175
|
-
with test_conn.cursor() as cursor:
|
|
176
|
-
cursor.execute("SELECT 1")
|
|
177
|
-
cursor.fetchone()
|
|
178
|
-
|
|
179
|
-
self._pool.putconn(test_conn)
|
|
180
|
-
self._is_connected_ref['connected'] = True
|
|
181
|
-
|
|
182
|
-
return True
|
|
183
|
-
|
|
184
|
-
except Exception as e:
|
|
185
|
-
logger.error(f"重连失败: {str(e)}")
|
|
186
|
-
self._is_connected_ref['connected'] = False
|
|
187
|
-
return False
|
|
188
|
-
|
|
189
|
-
@contextmanager
|
|
190
|
-
def _get_connection(self):
|
|
191
|
-
"""获取连接(带重连机制)
|
|
192
|
-
|
|
193
|
-
Yields:
|
|
194
|
-
connection: PostgreSQL 连接对象
|
|
195
|
-
|
|
196
|
-
Raises:
|
|
197
|
-
PostgresConnectionError: 获取连接失败时抛出
|
|
198
|
-
"""
|
|
199
|
-
if not self._is_connected_ref['connected']:
|
|
200
|
-
if not self._reconnect():
|
|
201
|
-
raise PostgresConnectionError("无法连接到 PostgreSQL,请检查配置和网络")
|
|
202
|
-
|
|
203
|
-
conn = None
|
|
204
|
-
try:
|
|
205
|
-
conn = self._pool.getconn()
|
|
206
|
-
if not conn:
|
|
207
|
-
# 尝试重连一次
|
|
208
|
-
if self._reconnect():
|
|
209
|
-
conn = self._pool.getconn()
|
|
210
|
-
|
|
211
|
-
if not conn:
|
|
212
|
-
raise PostgresConnectionError("无法从连接池获取连接")
|
|
213
|
-
|
|
214
|
-
yield conn
|
|
215
|
-
|
|
216
|
-
except psycopg2.Error as e:
|
|
217
|
-
if conn:
|
|
218
|
-
try:
|
|
219
|
-
self._pool.putconn(conn, close=True)
|
|
220
|
-
except Exception:
|
|
221
|
-
pass
|
|
222
|
-
self._is_connected_ref['connected'] = False
|
|
223
|
-
raise PostgresConnectionError(f"数据库连接错误: {str(e)}")
|
|
224
|
-
except Exception as e:
|
|
225
|
-
if conn:
|
|
226
|
-
try:
|
|
227
|
-
self._pool.putconn(conn)
|
|
228
|
-
except Exception:
|
|
229
|
-
pass
|
|
230
|
-
raise PostgresConnectionError(f"获取连接时发生错误: {str(e)}")
|
|
231
|
-
finally:
|
|
232
|
-
if conn:
|
|
233
|
-
try:
|
|
234
|
-
self._pool.putconn(conn)
|
|
235
|
-
except Exception:
|
|
236
|
-
pass
|
|
237
|
-
|
|
238
|
-
def _execute_with_retry(
|
|
239
|
-
self,
|
|
240
|
-
query: str,
|
|
241
|
-
params: Optional[tuple] = None,
|
|
242
|
-
retry_count: Optional[int] = None,
|
|
243
|
-
fetch: bool = True,
|
|
244
|
-
fetch_one: bool = False,
|
|
245
|
-
as_dict: bool = False,
|
|
246
|
-
) -> Optional[Union[List[Dict[str, Any]], List[tuple], int]]:
|
|
247
|
-
"""执行查询(带重试机制)
|
|
248
|
-
|
|
249
|
-
Args:
|
|
250
|
-
query: 查询语句
|
|
251
|
-
params: 查询参数
|
|
252
|
-
retry_count: 重试次数,None 则使用配置中的值
|
|
253
|
-
fetch: 是否获取结果
|
|
254
|
-
fetch_one: 是否只获取一条结果
|
|
255
|
-
as_dict: 是否返回字典格式
|
|
256
|
-
|
|
257
|
-
Returns:
|
|
258
|
-
Optional[List[Dict[str, Any]]]: 查询结果,如果 fetch=False 则返回受影响的行数(int)
|
|
259
|
-
|
|
260
|
-
Raises:
|
|
261
|
-
PostgresQueryError: 执行失败时抛出
|
|
262
|
-
"""
|
|
263
|
-
if retry_count is None:
|
|
264
|
-
retry_count = self._config.retry_count if self._config else 3
|
|
265
|
-
|
|
266
|
-
last_error = None
|
|
267
|
-
for attempt in range(retry_count + 1):
|
|
268
|
-
try:
|
|
269
|
-
with self._get_connection() as conn:
|
|
270
|
-
cursor_factory = RealDictCursor if as_dict else None
|
|
271
|
-
with conn.cursor(cursor_factory=cursor_factory) as cursor:
|
|
272
|
-
logger.debug(f"[postgres-sql] {query}")
|
|
273
|
-
if params:
|
|
274
|
-
logger.debug(f"[postgres-params] {params}")
|
|
275
|
-
cursor.execute(query, params)
|
|
276
|
-
else:
|
|
277
|
-
cursor.execute(query)
|
|
278
|
-
|
|
279
|
-
if not fetch:
|
|
280
|
-
rowcount = cursor.rowcount
|
|
281
|
-
conn.commit()
|
|
282
|
-
return rowcount
|
|
283
|
-
|
|
284
|
-
if fetch_one:
|
|
285
|
-
row = cursor.fetchone()
|
|
286
|
-
if as_dict and row:
|
|
287
|
-
return [dict(row)]
|
|
288
|
-
return [row] if row else []
|
|
289
|
-
else:
|
|
290
|
-
rows = cursor.fetchall()
|
|
291
|
-
if as_dict:
|
|
292
|
-
return [dict(row) for row in rows]
|
|
293
|
-
return rows
|
|
294
|
-
|
|
295
|
-
except psycopg2.Error as e:
|
|
296
|
-
last_error = PostgresQueryError(f"数据库查询错误: {str(e)}")
|
|
297
|
-
# 如果是连接相关错误,尝试重连
|
|
298
|
-
error_msg = str(e).lower()
|
|
299
|
-
if "connection" in error_msg or "timeout" in error_msg or "server closed" in error_msg:
|
|
300
|
-
self._is_connected_ref['connected'] = False
|
|
301
|
-
if attempt < retry_count:
|
|
302
|
-
time.sleep(self._config.retry_delay if self._config else 1)
|
|
303
|
-
continue
|
|
304
|
-
|
|
305
|
-
if attempt < retry_count:
|
|
306
|
-
time.sleep(self._config.retry_delay if self._config else 1)
|
|
307
|
-
continue
|
|
308
|
-
|
|
309
|
-
except Exception as e:
|
|
310
|
-
last_error = PostgresQueryError(f"执行查询时发生错误: {str(e)}")
|
|
311
|
-
if attempt < retry_count:
|
|
312
|
-
time.sleep(self._config.retry_delay if self._config else 1)
|
|
313
|
-
continue
|
|
314
|
-
|
|
315
|
-
raise last_error or PostgresQueryError("查询执行失败")
|
|
316
|
-
|
|
317
|
-
def execute(
|
|
318
|
-
self,
|
|
319
|
-
query: str,
|
|
320
|
-
params: Optional[tuple] = None,
|
|
321
|
-
retry_count: Optional[int] = None,
|
|
322
|
-
) -> int:
|
|
323
|
-
"""执行 SQL 语句(不返回结果)
|
|
324
|
-
|
|
325
|
-
Args:
|
|
326
|
-
query: SQL 语句
|
|
327
|
-
params: 查询参数
|
|
328
|
-
retry_count: 重试次数
|
|
329
|
-
|
|
330
|
-
Returns:
|
|
331
|
-
int: 受影响的行数
|
|
332
|
-
|
|
333
|
-
Raises:
|
|
334
|
-
PostgresQueryError: 执行失败时抛出
|
|
335
|
-
"""
|
|
336
|
-
if not self._is_connected_ref['connected']:
|
|
337
|
-
raise PostgresQueryError("PostgreSQL 未初始化,请先调用 initialize()")
|
|
338
|
-
|
|
339
|
-
result = self._execute_with_retry(query, params, retry_count, fetch=False)
|
|
340
|
-
return result if result is not None else 0
|
|
341
|
-
|
|
342
|
-
def query(
|
|
343
|
-
self,
|
|
344
|
-
query: str,
|
|
345
|
-
params: Optional[tuple] = None,
|
|
346
|
-
retry_count: Optional[int] = None,
|
|
347
|
-
as_dict: bool = True,
|
|
348
|
-
) -> Union[List[Dict[str, Any]], List[tuple]]:
|
|
349
|
-
"""执行查询(返回结果)
|
|
350
|
-
|
|
351
|
-
Args:
|
|
352
|
-
query: 查询语句
|
|
353
|
-
params: 查询参数
|
|
354
|
-
retry_count: 重试次数
|
|
355
|
-
as_dict: 是否返回字典格式
|
|
356
|
-
|
|
357
|
-
Returns:
|
|
358
|
-
Union[List[Dict[str, Any]], List[tuple]]: 查询结果列表,as_dict=True 时返回字典列表,否则返回元组列表
|
|
359
|
-
|
|
360
|
-
Raises:
|
|
361
|
-
PostgresQueryError: 执行失败时抛出
|
|
362
|
-
"""
|
|
363
|
-
if not self._is_connected_ref['connected']:
|
|
364
|
-
raise PostgresQueryError("PostgreSQL 未初始化,请先调用 initialize()")
|
|
365
|
-
|
|
366
|
-
result = self._execute_with_retry(query, params, retry_count, fetch=True, as_dict=as_dict)
|
|
367
|
-
return result or []
|
|
368
|
-
|
|
369
|
-
def query_one(
|
|
370
|
-
self,
|
|
371
|
-
query: str,
|
|
372
|
-
params: Optional[tuple] = None,
|
|
373
|
-
retry_count: Optional[int] = None,
|
|
374
|
-
as_dict: bool = True,
|
|
375
|
-
) -> Optional[Union[Dict[str, Any], tuple]]:
|
|
376
|
-
"""执行查询(返回单条结果)
|
|
377
|
-
|
|
378
|
-
Args:
|
|
379
|
-
query: 查询语句
|
|
380
|
-
params: 查询参数
|
|
381
|
-
retry_count: 重试次数
|
|
382
|
-
as_dict: 是否返回字典格式
|
|
383
|
-
|
|
384
|
-
Returns:
|
|
385
|
-
Optional[Union[Dict[str, Any], tuple]]: 查询结果,as_dict=True 时返回字典,否则返回元组,如果不存在则返回 None
|
|
386
|
-
|
|
387
|
-
Raises:
|
|
388
|
-
PostgresQueryError: 执行失败时抛出
|
|
389
|
-
"""
|
|
390
|
-
if not self._is_connected_ref['connected']:
|
|
391
|
-
raise PostgresQueryError("PostgreSQL 未初始化,请先调用 initialize()")
|
|
392
|
-
|
|
393
|
-
result = self._execute_with_retry(
|
|
394
|
-
query, params, retry_count, fetch=True, fetch_one=True, as_dict=as_dict
|
|
395
|
-
)
|
|
396
|
-
return result[0] if result else None
|
|
397
|
-
|
|
398
|
-
@contextmanager
|
|
399
|
-
def transaction(self):
|
|
400
|
-
"""事务上下文管理器
|
|
401
|
-
|
|
402
|
-
Yields:
|
|
403
|
-
connection: PostgreSQL 连接对象
|
|
404
|
-
|
|
405
|
-
Raises:
|
|
406
|
-
PostgresConnectionError: 获取连接失败时抛出
|
|
407
|
-
"""
|
|
408
|
-
with self._get_connection() as conn:
|
|
409
|
-
try:
|
|
410
|
-
yield conn
|
|
411
|
-
conn.commit()
|
|
412
|
-
except Exception as e:
|
|
413
|
-
conn.rollback()
|
|
414
|
-
raise PostgresQueryError(f"事务执行失败: {str(e)}")
|
|
415
|
-
|
|
416
|
-
def close(self):
|
|
417
|
-
"""关闭连接池"""
|
|
418
|
-
if self._pool:
|
|
419
|
-
try:
|
|
420
|
-
self._pool.closeall()
|
|
421
|
-
except Exception as e:
|
|
422
|
-
logger.warning(f"关闭连接池时发生错误: {str(e)}")
|
|
423
|
-
finally:
|
|
424
|
-
self._pool = None
|
|
425
|
-
self._is_connected_ref['connected'] = False
|
|
426
|
-
logger.info("PostgreSQL 连接池已关闭")
|
|
427
|
-
|
|
428
|
-
def is_connected(self) -> bool:
|
|
429
|
-
"""检查是否已连接
|
|
430
|
-
|
|
431
|
-
Returns:
|
|
432
|
-
bool: 是否已连接
|
|
433
|
-
"""
|
|
434
|
-
return self._is_connected_ref['connected'] and self._check_connection()
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
# 创建全局单例实例
|
|
438
|
-
postgres_manager = PostgresManager()
|
|
439
|
-
|
fiuai_sdk_python/pkg/db/utils.py
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# -- coding: utf-8 --
|
|
2
|
-
# Project: fiuai-world
|
|
3
|
-
# Created Date: 2025-12-06
|
|
4
|
-
# Author: liming
|
|
5
|
-
# Email: lmlala@aliyun.com
|
|
6
|
-
# Copyright (c) 2025 FiuAI
|
|
7
|
-
|
|
8
|
-
from typing import Any
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def escape_table_name(table_name: str) -> str:
|
|
12
|
-
"""
|
|
13
|
-
转义 PostgreSQL 表名,处理大小写和空格
|
|
14
|
-
|
|
15
|
-
PostgreSQL 中,如果表名包含大写字母、空格或特殊字符,需要用双引号包裹
|
|
16
|
-
Frappe 的表名格式是 tab{doctype},其中 doctype 可能包含大写字母和空格
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
table_name: 表名(如 "tabContract" 或 "tabA B")
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
str: 转义后的表名(如 '"tabContract"' 或 '"tabA B"')
|
|
23
|
-
|
|
24
|
-
Example:
|
|
25
|
-
escape_table_name("tabContract") # 返回: '"tabContract"'
|
|
26
|
-
escape_table_name("tabA B") # 返回: '"tabA B"'
|
|
27
|
-
escape_table_name("tabcontract") # 返回: '"tabcontract"' (统一加引号,确保安全)
|
|
28
|
-
"""
|
|
29
|
-
if not table_name:
|
|
30
|
-
raise ValueError("table_name 不能为空")
|
|
31
|
-
|
|
32
|
-
# 确保是字符串类型
|
|
33
|
-
if not isinstance(table_name, str):
|
|
34
|
-
table_name = str(table_name)
|
|
35
|
-
|
|
36
|
-
# 去除首尾空白
|
|
37
|
-
table_name = table_name.strip()
|
|
38
|
-
|
|
39
|
-
# 如果已经用双引号包裹,先去除
|
|
40
|
-
if table_name.startswith('"') and table_name.endswith('"'):
|
|
41
|
-
table_name = table_name[1:-1]
|
|
42
|
-
|
|
43
|
-
# 转义双引号(如果表名中包含双引号)
|
|
44
|
-
escaped = table_name.replace('"', '""')
|
|
45
|
-
|
|
46
|
-
# 用双引号包裹(PostgreSQL 中双引号用于标识符,可以处理大小写和空格)
|
|
47
|
-
return f'"{escaped}"'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def build_frappe_table_name(doctype: str) -> str:
|
|
51
|
-
"""
|
|
52
|
-
构建 Frappe 格式的表名并转义
|
|
53
|
-
|
|
54
|
-
Frappe 的表名格式是 tab{doctype},其中 doctype 可能包含大写字母和空格
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
doctype: 文档类型名称(如 "Contract" 或 "A B")
|
|
58
|
-
|
|
59
|
-
Returns:
|
|
60
|
-
str: 转义后的表名(如 '"tabContract"' 或 '"tabA B"')
|
|
61
|
-
|
|
62
|
-
Example:
|
|
63
|
-
build_frappe_table_name("Contract") # 返回: '"tabContract"'
|
|
64
|
-
build_frappe_table_name("A B") # 返回: '"tabA B"'
|
|
65
|
-
"""
|
|
66
|
-
if not doctype:
|
|
67
|
-
raise ValueError("doctype 不能为空")
|
|
68
|
-
|
|
69
|
-
# 确保是字符串类型
|
|
70
|
-
if not isinstance(doctype, str):
|
|
71
|
-
doctype = str(doctype)
|
|
72
|
-
|
|
73
|
-
# 构建 Frappe 表名格式
|
|
74
|
-
table_name = f"tab{doctype}"
|
|
75
|
-
|
|
76
|
-
# 转义表名
|
|
77
|
-
return escape_table_name(table_name)
|
|
78
|
-
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# -- coding: utf-8 --
|
|
2
|
-
# Project: fiuai-world
|
|
3
|
-
# Created Date: 2025-01-27
|
|
4
|
-
# Author: liming
|
|
5
|
-
# Email: lmlala@aliyun.com
|
|
6
|
-
# Copyright (c) 2025 FiuAI
|
|
7
|
-
|
|
8
|
-
from .vector import (
|
|
9
|
-
QdrantManager,
|
|
10
|
-
QdrantConfig,
|
|
11
|
-
QdrantError,
|
|
12
|
-
qdrant_manager,
|
|
13
|
-
ContextAwareQdrantManager,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
__all__ = [
|
|
17
|
-
'QdrantManager',
|
|
18
|
-
'QdrantConfig',
|
|
19
|
-
'QdrantError',
|
|
20
|
-
'qdrant_manager',
|
|
21
|
-
'ContextAwareQdrantManager',
|
|
22
|
-
]
|
|
23
|
-
|