postgresql.py 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206
  1. import os
  2. import subprocess
  3. import logging
  4. import time
  5. from pathlib import Path
  6. import shutil
  7. import json
  8. # 配置日志
  9. logging.basicConfig(
  10. level=logging.INFO,
  11. format='%(asctime)s - %(levelname)s - %(message)s',
  12. handlers=[
  13. logging.StreamHandler(),
  14. logging.FileHandler('postgres_service_manager.log')
  15. ]
  16. )
  17. logger = logging.getLogger(__name__)
  18. def get_postgres_service_name():
  19. """获取 PostgreSQL 服务名称"""
  20. # #region agent log
  21. try:
  22. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  23. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"A","location":"postgresql.py:19","message":"get_postgres_service_name entry","data":{},"timestamp":int(time.time()*1000)})+'\n')
  24. except: pass
  25. # #endregion
  26. service_names = [
  27. "postgresql-x64-16",
  28. "postgresql-x64-15",
  29. "postgresql-x64-14",
  30. "postgresql-x64-13",
  31. "postgresql-x64-12",
  32. "postgresql"
  33. ]
  34. # 检查服务状态
  35. for service in service_names:
  36. try:
  37. # #region agent log
  38. try:
  39. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  40. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"A","location":"postgresql.py:35","message":"checking service","data":{"service":service},"timestamp":int(time.time()*1000)})+'\n')
  41. except: pass
  42. # #endregion
  43. result = subprocess.run(
  44. ["sc", "query", service],
  45. capture_output=True,
  46. text=True,
  47. encoding='utf-8',
  48. errors='ignore'
  49. )
  50. # #region agent log
  51. try:
  52. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  53. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"A","location":"postgresql.py:44","message":"sc query result","data":{"service":service,"returncode":result.returncode,"stdout":result.stdout[:200],"stderr":result.stderr[:200] if result.stderr else ""},"timestamp":int(time.time()*1000)})+'\n')
  54. except: pass
  55. # #endregion
  56. if "RUNNING" in result.stdout or "STOPPED" in result.stdout:
  57. # #region agent log
  58. try:
  59. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  60. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"A","location":"postgresql.py:46","message":"service found","data":{"service":service},"timestamp":int(time.time()*1000)})+'\n')
  61. except: pass
  62. # #endregion
  63. return service
  64. except Exception as e:
  65. # #region agent log
  66. try:
  67. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  68. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"A","location":"postgresql.py:52","message":"exception checking service","data":{"service":service,"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  69. except: pass
  70. # #endregion
  71. continue
  72. default_service = service_names[0] if service_names else "postgresql"
  73. # #region agent log
  74. try:
  75. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  76. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"A","location":"postgresql.py:58","message":"returning default service","data":{"default_service":default_service},"timestamp":int(time.time()*1000)})+'\n')
  77. except: pass
  78. # #endregion
  79. return default_service
  80. def is_service_running(service_name=None):
  81. """检查服务是否正在运行"""
  82. # #region agent log
  83. try:
  84. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  85. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"D","location":"postgresql.py:47","message":"is_service_running entry","data":{"service_name":service_name},"timestamp":int(time.time()*1000)})+'\n')
  86. except: pass
  87. # #endregion
  88. if not service_name:
  89. service_name = get_postgres_service_name()
  90. try:
  91. result = subprocess.run(
  92. ["sc", "query", service_name],
  93. capture_output=True,
  94. text=True,
  95. encoding='utf-8',
  96. errors='ignore'
  97. )
  98. # #region agent log
  99. try:
  100. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  101. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"D","location":"postgresql.py:60","message":"service status check result","data":{"service_name":service_name,"returncode":result.returncode,"stdout":result.stdout[:300],"is_running":"RUNNING" in result.stdout},"timestamp":int(time.time()*1000)})+'\n')
  102. except: pass
  103. # #endregion
  104. return "RUNNING" in result.stdout
  105. except Exception as e:
  106. # #region agent log
  107. try:
  108. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  109. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"D","location":"postgresql.py:66","message":"exception checking service status","data":{"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  110. except: pass
  111. # #endregion
  112. logger.error(f"检查服务状态失败: {str(e)}")
  113. return False
  114. def stop_postgres_service():
  115. """停止 PostgreSQL 服务"""
  116. try:
  117. service_name = get_postgres_service_name()
  118. logger.info(f"尝试停止 PostgreSQL 服务: {service_name}")
  119. # 使用 net stop 停止服务
  120. result = subprocess.run(
  121. ["net", "stop", service_name],
  122. capture_output=True,
  123. text=True,
  124. encoding='utf-8',
  125. errors='ignore'
  126. )
  127. if result.returncode == 0:
  128. logger.info(f"PostgreSQL 服务 '{service_name}' 已停止")
  129. return True
  130. # 如果 net stop 失败,尝试 sc stop
  131. result = subprocess.run(
  132. ["sc", "stop", service_name],
  133. capture_output=True,
  134. text=True,
  135. encoding='utf-8',
  136. errors='ignore'
  137. )
  138. if result.returncode == 0:
  139. logger.info(f"PostgreSQL 服务 '{service_name}' 已停止")
  140. return True
  141. # 如果服务未停止,尝试强制终止进程
  142. logger.warning("服务未正常停止,尝试终止进程")
  143. subprocess.run(
  144. ["taskkill", "/F", "/IM", "postgres.exe"],
  145. capture_output=True,
  146. encoding='utf-8',
  147. errors='ignore'
  148. )
  149. logger.info("PostgreSQL 进程已终止")
  150. return True
  151. except Exception as e:
  152. logger.error(f"停止 PostgreSQL 服务失败: {str(e)}")
  153. return False
  154. def start_postgres_service():
  155. """启动 PostgreSQL 服务"""
  156. # #region agent log
  157. try:
  158. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  159. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,C","location":"postgresql.py:112","message":"start_postgres_service entry","data":{},"timestamp":int(time.time()*1000)})+'\n')
  160. except: pass
  161. # #endregion
  162. try:
  163. service_name = get_postgres_service_name()
  164. # #region agent log
  165. try:
  166. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  167. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"A,B","location":"postgresql.py:118","message":"got service name","data":{"service_name":service_name},"timestamp":int(time.time()*1000)})+'\n')
  168. except: pass
  169. # #endregion
  170. logger.info(f"尝试启动 PostgreSQL 服务: {service_name}")
  171. # 使用 net start 启动服务
  172. # #region agent log
  173. try:
  174. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  175. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,C","location":"postgresql.py:123","message":"before net start","data":{"service_name":service_name},"timestamp":int(time.time()*1000)})+'\n')
  176. except: pass
  177. # #endregion
  178. result = subprocess.run(
  179. ["net", "start", service_name],
  180. capture_output=True,
  181. text=True,
  182. encoding='utf-8',
  183. errors='ignore'
  184. )
  185. # #region agent log
  186. try:
  187. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  188. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,C","location":"postgresql.py:132","message":"net start result","data":{"returncode":result.returncode,"stdout":result.stdout[:500],"stderr":result.stderr[:500] if result.stderr else ""},"timestamp":int(time.time()*1000)})+'\n')
  189. except: pass
  190. # #endregion
  191. if result.returncode == 0:
  192. logger.info(f"PostgreSQL 服务 '{service_name}' 已启动")
  193. # #region agent log
  194. try:
  195. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  196. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B","location":"postgresql.py:137","message":"net start succeeded","data":{"service_name":service_name},"timestamp":int(time.time()*1000)})+'\n')
  197. except: pass
  198. # #endregion
  199. return True
  200. # 如果 net start 失败,尝试 sc start
  201. # #region agent log
  202. try:
  203. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  204. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,C","location":"postgresql.py:142","message":"net start failed, trying sc start","data":{"service_name":service_name},"timestamp":int(time.time()*1000)})+'\n')
  205. except: pass
  206. # #endregion
  207. result = subprocess.run(
  208. ["sc", "start", service_name],
  209. capture_output=True,
  210. text=True,
  211. encoding='utf-8',
  212. errors='ignore'
  213. )
  214. # #region agent log
  215. try:
  216. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  217. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,C","location":"postgresql.py:150","message":"sc start result","data":{"returncode":result.returncode,"stdout":result.stdout[:500],"stderr":result.stderr[:500] if result.stderr else ""},"timestamp":int(time.time()*1000)})+'\n')
  218. except: pass
  219. # #endregion
  220. if result.returncode == 0:
  221. logger.info(f"PostgreSQL 服务 '{service_name}' 启动命令执行成功")
  222. # #region agent log
  223. try:
  224. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  225. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B","location":"postgresql.py:238","message":"sc start command succeeded, waiting to verify","data":{"service_name":service_name},"timestamp":int(time.time()*1000)})+'\n')
  226. except: pass
  227. # #endregion
  228. # 等待服务启动完成
  229. time.sleep(5)
  230. # 验证服务是否真的在运行
  231. # #region agent log
  232. try:
  233. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  234. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,E","location":"postgresql.py:246","message":"checking service status after start","data":{"service_name":service_name},"timestamp":int(time.time()*1000)})+'\n')
  235. except: pass
  236. # #endregion
  237. status_result = subprocess.run(
  238. ["sc", "query", service_name],
  239. capture_output=True,
  240. text=True,
  241. encoding='utf-8',
  242. errors='ignore'
  243. )
  244. # #region agent log
  245. try:
  246. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  247. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,E","location":"postgresql.py:256","message":"service status after start","data":{"service_name":service_name,"returncode":status_result.returncode,"stdout":status_result.stdout[:500],"is_running":"RUNNING" in status_result.stdout},"timestamp":int(time.time()*1000)})+'\n')
  248. except: pass
  249. # #endregion
  250. if "RUNNING" in status_result.stdout:
  251. logger.info(f"PostgreSQL 服务 '{service_name}' 已启动")
  252. return True
  253. else:
  254. # 服务启动失败,检查错误日志
  255. logger.warning(f"PostgreSQL 服务 '{service_name}' 启动后立即停止")
  256. # #region agent log
  257. try:
  258. data_dir = None
  259. try:
  260. data_dir = get_postgres_data_dir()
  261. except:
  262. pass
  263. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  264. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:268","message":"service stopped after start, checking logs","data":{"service_name":service_name,"data_dir":str(data_dir) if data_dir else None,"status_output":status_result.stdout[:500]},"timestamp":int(time.time()*1000)})+'\n')
  265. except: pass
  266. # #endregion
  267. # 检查数据目录有效性
  268. try:
  269. data_dir = get_postgres_data_dir()
  270. pg_version_file = data_dir / "PG_VERSION"
  271. # #region agent log
  272. try:
  273. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  274. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:287","message":"checking data directory validity","data":{"data_dir":str(data_dir),"pg_version_exists":pg_version_file.exists()},"timestamp":int(time.time()*1000)})+'\n')
  275. except: pass
  276. # #endregion
  277. if not pg_version_file.exists():
  278. logger.error(f"数据目录无效:缺少 PG_VERSION 文件。数据目录: {data_dir}")
  279. # #region agent log
  280. try:
  281. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  282. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:293","message":"PG_VERSION file missing","data":{"data_dir":str(data_dir)},"timestamp":int(time.time()*1000)})+'\n')
  283. except: pass
  284. # #endregion
  285. except Exception as e:
  286. # #region agent log
  287. try:
  288. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  289. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:299","message":"exception checking data directory","data":{"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  290. except: pass
  291. # #endregion
  292. # 尝试读取 Windows 事件日志
  293. try:
  294. # #region agent log
  295. try:
  296. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  297. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:307","message":"reading windows event log","data":{},"timestamp":int(time.time()*1000)})+'\n')
  298. except: pass
  299. # #endregion
  300. event_log_result = subprocess.run(
  301. ["powershell", "-Command", "Get-EventLog -LogName Application -Source 'postgresql*' -Newest 3 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Message | Out-String"],
  302. capture_output=True,
  303. text=True,
  304. encoding='utf-8',
  305. errors='ignore'
  306. )
  307. if event_log_result.returncode == 0 and event_log_result.stdout:
  308. logger.error(f"Windows 事件日志错误信息:\n{event_log_result.stdout}")
  309. # #region agent log
  310. try:
  311. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  312. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:318","message":"windows event log retrieved","data":{"event_log":event_log_result.stdout[:2000]},"timestamp":int(time.time()*1000)})+'\n')
  313. except: pass
  314. # #endregion
  315. except Exception as e:
  316. # #region agent log
  317. try:
  318. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  319. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:324","message":"exception reading event log","data":{"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  320. except: pass
  321. # #endregion
  322. return False
  323. # #region agent log
  324. try:
  325. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  326. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,C","location":"postgresql.py:160","message":"both start methods failed","data":{"service_name":service_name,"net_stdout":result.stdout[:500],"net_stderr":result.stderr[:500] if result.stderr else ""},"timestamp":int(time.time()*1000)})+'\n')
  327. except: pass
  328. # #endregion
  329. raise RuntimeError("启动 PostgreSQL 服务失败")
  330. except Exception as e:
  331. # #region agent log
  332. try:
  333. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  334. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"B,C","location":"postgresql.py:165","message":"exception in start_postgres_service","data":{"error":str(e),"error_type":type(e).__name__},"timestamp":int(time.time()*1000)})+'\n')
  335. except: pass
  336. # #endregion
  337. logger.error(f"启动 PostgreSQL 服务失败: {str(e)}")
  338. return False
  339. def restart_postgres_service():
  340. """重启 PostgreSQL 服务"""
  341. try:
  342. if stop_postgres_service():
  343. time.sleep(3) # 等待服务完全停止
  344. return start_postgres_service()
  345. except Exception as e:
  346. logger.error(f"重启 PostgreSQL 服务失败: {str(e)}")
  347. return False
  348. def get_postgres_data_dir():
  349. """获取 PostgreSQL 数据目录"""
  350. # #region agent log
  351. try:
  352. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  353. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:160","message":"get_postgres_data_dir entry","data":{},"timestamp":int(time.time()*1000)})+'\n')
  354. except: pass
  355. # #endregion
  356. possible_paths = [
  357. r"D:/app/postgresql/data",
  358. r"C:/Program Files/PostgreSQL/15/data",
  359. r"C:/Program Files/PostgreSQL/14/data",
  360. r"C:/Program Files/PostgreSQL/13/data",
  361. r"C:/Program Files/PostgreSQL/12/data",
  362. ]
  363. env_path = os.environ.get('PGDATA')
  364. # #region agent log
  365. try:
  366. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  367. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:175","message":"checking PGDATA env","data":{"env_path":env_path,"exists":Path(env_path).exists() if env_path else False},"timestamp":int(time.time()*1000)})+'\n')
  368. except: pass
  369. # #endregion
  370. if env_path and Path(env_path).exists():
  371. return Path(env_path)
  372. for path in possible_paths:
  373. # #region agent log
  374. try:
  375. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  376. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:182","message":"checking data path","data":{"path":path,"exists":Path(path).exists()},"timestamp":int(time.time()*1000)})+'\n')
  377. except: pass
  378. # #endregion
  379. if Path(path).exists():
  380. return Path(path)
  381. # #region agent log
  382. try:
  383. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  384. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:186","message":"data directory not found","data":{},"timestamp":int(time.time()*1000)})+'\n')
  385. except: pass
  386. # #endregion
  387. raise FileNotFoundError("无法找到 PostgreSQL 数据目录")
  388. def fix_postgres_service():
  389. """修复 PostgreSQL 服务启动问题"""
  390. try:
  391. logger.info("开始修复 PostgreSQL 服务启动问题...")
  392. # 1. 停止服务
  393. if not stop_postgres_service():
  394. logger.warning("无法停止服务,尝试强制修复")
  395. # 2. 检查数据目录
  396. try:
  397. data_dir = get_postgres_data_dir()
  398. logger.info(f"找到 PostgreSQL 数据目录: {data_dir}")
  399. # 2.1 检查数据目录有效性(关键文件 PG_VERSION)
  400. pg_version_file = data_dir / "PG_VERSION"
  401. if not pg_version_file.exists():
  402. logger.error(f"数据目录无效:缺少关键文件 PG_VERSION")
  403. logger.error(f"数据目录: {data_dir}")
  404. logger.error("这通常意味着数据目录被损坏或删除。")
  405. logger.error("解决方案:")
  406. logger.error("1. 如果您有数据备份,请恢复备份")
  407. logger.error("2. 如果没有备份,需要重新初始化数据库(会丢失所有数据)")
  408. logger.error(" 重新初始化命令: initdb -D \"{}\"".format(data_dir))
  409. logger.error(" 或者使用 PostgreSQL 安装程序重新配置数据目录")
  410. return False
  411. # 3. 检查并处理恢复配置文件
  412. recovery_signal = data_dir / "recovery.signal"
  413. recovery_done = data_dir / "recovery.done"
  414. auto_conf = data_dir / "postgresql.auto.conf"
  415. if recovery_signal.exists():
  416. logger.info("发现 recovery.signal 文件,尝试完成恢复过程")
  417. if recovery_done.exists():
  418. recovery_done.unlink()
  419. recovery_signal.rename(recovery_done)
  420. logger.info("已将 recovery.signal 重命名为 recovery.done")
  421. # 4. 删除可能引起问题的自动配置文件
  422. if auto_conf.exists():
  423. backup_auto_conf = auto_conf.with_suffix(".auto.conf.bak")
  424. shutil.copy(auto_conf, backup_auto_conf)
  425. auto_conf.unlink()
  426. logger.info("已备份并删除 postgresql.auto.conf 文件")
  427. # 5. 检查 postgresql.conf 文件
  428. conf_file = data_dir / "postgresql.conf"
  429. if conf_file.exists():
  430. logger.info("检查 postgresql.conf 文件完整性")
  431. # 这里可以添加更多的配置文件检查逻辑
  432. except FileNotFoundError as e:
  433. logger.warning(f"无法找到数据目录: {str(e)}")
  434. # 6. 确保所有 PostgreSQL 进程已终止
  435. subprocess.run(
  436. ["taskkill", "/F", "/IM", "postgres.exe"],
  437. capture_output=True,
  438. encoding='utf-8',
  439. errors='ignore'
  440. )
  441. # 7. 尝试启动服务
  442. time.sleep(2)
  443. if start_postgres_service():
  444. logger.info("PostgreSQL 服务修复成功")
  445. return True
  446. else:
  447. logger.error("修复后仍无法启动服务")
  448. return False
  449. except Exception as e:
  450. logger.error(f"修复过程中发生错误: {str(e)}")
  451. return False
  452. def get_postgres_bin_dir():
  453. """获取 PostgreSQL bin 目录"""
  454. possible_paths = [
  455. r"C:\Program Files\PostgreSQL\16\bin",
  456. r"C:\Program Files\PostgreSQL\15\bin",
  457. r"C:\Program Files\PostgreSQL\14\bin",
  458. r"C:\Program Files\PostgreSQL\13\bin",
  459. r"C:\Program Files\PostgreSQL\12\bin",
  460. r"D:\app\postgresql\bin",
  461. ]
  462. for path in possible_paths:
  463. if Path(path).exists() and (Path(path) / "initdb.exe").exists():
  464. return Path(path)
  465. # 尝试从环境变量获取
  466. pg_bin = os.environ.get('PG_BIN')
  467. if pg_bin and Path(pg_bin).exists():
  468. return Path(pg_bin)
  469. raise FileNotFoundError("无法找到 PostgreSQL bin 目录")
  470. def init_postgres_database():
  471. """初始化 PostgreSQL 数据库"""
  472. # #region agent log
  473. try:
  474. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  475. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:479","message":"init_postgres_database entry","data":{},"timestamp":int(time.time()*1000)})+'\n')
  476. except: pass
  477. # #endregion
  478. try:
  479. # 1. 停止服务
  480. logger.info("正在停止 PostgreSQL 服务...")
  481. stop_postgres_service()
  482. time.sleep(2)
  483. # 2. 获取数据目录
  484. data_dir = get_postgres_data_dir()
  485. logger.info(f"数据目录: {data_dir}")
  486. # 3. 检查数据目录是否已存在且有效
  487. pg_version_file = data_dir / "PG_VERSION"
  488. if pg_version_file.exists():
  489. logger.warning(f"数据目录已存在且包含 PG_VERSION 文件")
  490. response = input("数据目录已存在,是否要重新初始化?这将删除所有现有数据!(yes/no): ").strip().lower()
  491. if response != 'yes':
  492. logger.info("用户取消初始化操作")
  493. return False
  494. # 备份现有数据目录
  495. backup_dir = data_dir.parent / f"{data_dir.name}_backup_{int(time.time())}"
  496. logger.info(f"备份现有数据目录到: {backup_dir}")
  497. if data_dir.exists():
  498. try:
  499. shutil.move(str(data_dir), str(backup_dir))
  500. logger.info("数据目录已备份")
  501. except Exception as e:
  502. logger.error(f"备份数据目录失败: {str(e)}")
  503. return False
  504. # 4. 获取 PostgreSQL bin 目录
  505. try:
  506. bin_dir = get_postgres_bin_dir()
  507. logger.info(f"PostgreSQL bin 目录: {bin_dir}")
  508. except FileNotFoundError as e:
  509. logger.error(f"无法找到 PostgreSQL bin 目录: {str(e)}")
  510. logger.error("请确保 PostgreSQL 已正确安装")
  511. return False
  512. # 5. 确保数据目录存在
  513. data_dir.parent.mkdir(parents=True, exist_ok=True)
  514. # 6. 执行 initdb
  515. initdb_exe = bin_dir / "initdb.exe"
  516. logger.info(f"正在初始化数据库,这可能需要几分钟...")
  517. logger.info(f"执行命令: {initdb_exe} -D \"{data_dir}\" -U postgres -A trust -E UTF8 --locale=C")
  518. # #region agent log
  519. try:
  520. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  521. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:530","message":"before initdb","data":{"initdb_exe":str(initdb_exe),"data_dir":str(data_dir)},"timestamp":int(time.time()*1000)})+'\n')
  522. except: pass
  523. # #endregion
  524. result = subprocess.run(
  525. [str(initdb_exe), "-D", str(data_dir), "-U", "postgres", "-A", "trust", "-E", "UTF8", "--locale=C"],
  526. capture_output=True,
  527. text=True,
  528. encoding='utf-8',
  529. errors='ignore'
  530. )
  531. # #region agent log
  532. try:
  533. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  534. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:540","message":"initdb result","data":{"returncode":result.returncode,"stdout":result.stdout[:1000],"stderr":result.stderr[:1000] if result.stderr else ""},"timestamp":int(time.time()*1000)})+'\n')
  535. except: pass
  536. # #endregion
  537. if result.returncode == 0:
  538. logger.info("数据库初始化成功!")
  539. logger.info(result.stdout)
  540. # 7. 验证 PG_VERSION 文件
  541. if pg_version_file.exists():
  542. logger.info(f"验证成功:PG_VERSION 文件已创建")
  543. # 读取版本号
  544. try:
  545. with open(pg_version_file, 'r') as f:
  546. version = f.read().strip()
  547. logger.info(f"PostgreSQL 版本: {version}")
  548. except:
  549. pass
  550. else:
  551. logger.warning("警告:PG_VERSION 文件未找到,但 initdb 返回成功")
  552. # 8. 尝试启动服务
  553. logger.info("正在启动 PostgreSQL 服务...")
  554. time.sleep(2)
  555. if start_postgres_service():
  556. logger.info("PostgreSQL 服务启动成功!")
  557. return True
  558. else:
  559. logger.warning("数据库初始化成功,但服务启动失败")
  560. return False
  561. else:
  562. logger.error("数据库初始化失败")
  563. logger.error(f"返回码: {result.returncode}")
  564. if result.stderr:
  565. logger.error(f"错误信息: {result.stderr}")
  566. if result.stdout:
  567. logger.error(f"输出信息: {result.stdout}")
  568. return False
  569. except Exception as e:
  570. # #region agent log
  571. try:
  572. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  573. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"E","location":"postgresql.py:575","message":"exception in init_postgres_database","data":{"error":str(e),"error_type":type(e).__name__},"timestamp":int(time.time()*1000)})+'\n')
  574. except: pass
  575. # #endregion
  576. logger.error(f"初始化数据库时发生错误: {str(e)}")
  577. return False
  578. def restore_from_backup(backup_path):
  579. """从备份恢复 PostgreSQL 数据库
  580. Args:
  581. backup_path: 备份目录路径(包含backup_label的目录)
  582. Returns:
  583. bool: 恢复是否成功
  584. """
  585. # #region agent log
  586. try:
  587. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  588. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:636","message":"restore_from_backup entry","data":{"backup_path":backup_path},"timestamp":int(time.time()*1000)})+'\n')
  589. except: pass
  590. # #endregion
  591. try:
  592. backup_dir = Path(backup_path)
  593. # 验证备份目录
  594. if not backup_dir.exists():
  595. logger.error(f"备份目录不存在: {backup_path}")
  596. # #region agent log
  597. try:
  598. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  599. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:649","message":"backup directory not exists","data":{"backup_path":backup_path},"timestamp":int(time.time()*1000)})+'\n')
  600. except: pass
  601. # #endregion
  602. return False
  603. backup_label_file = backup_dir / "backup_label"
  604. if not backup_label_file.exists():
  605. logger.error(f"备份目录无效:缺少 backup_label 文件")
  606. logger.error(f"请确保这是有效的 PostgreSQL base backup")
  607. # #region agent log
  608. try:
  609. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  610. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:654","message":"backup_label missing","data":{"backup_path":backup_path},"timestamp":int(time.time()*1000)})+'\n')
  611. except: pass
  612. # #endregion
  613. return False
  614. # 检查PG_VERSION
  615. pg_version_file = backup_dir / "PG_VERSION"
  616. if not pg_version_file.exists():
  617. logger.error(f"备份目录无效:缺少 PG_VERSION 文件")
  618. # #region agent log
  619. try:
  620. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  621. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:661","message":"PG_VERSION missing","data":{"backup_path":backup_path},"timestamp":int(time.time()*1000)})+'\n')
  622. except: pass
  623. # #endregion
  624. return False
  625. # 读取备份的PostgreSQL版本
  626. try:
  627. with open(pg_version_file, 'r') as f:
  628. backup_version = f.read().strip()
  629. logger.info(f"备份的 PostgreSQL 版本: {backup_version}")
  630. # #region agent log
  631. try:
  632. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  633. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:669","message":"backup version read","data":{"backup_version":backup_version},"timestamp":int(time.time()*1000)})+'\n')
  634. except: pass
  635. # #endregion
  636. except Exception as e:
  637. logger.warning(f"无法读取备份版本: {str(e)}")
  638. # 1. 停止服务
  639. logger.info("正在停止 PostgreSQL 服务...")
  640. # #region agent log
  641. try:
  642. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  643. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:675","message":"stopping postgres service","data":{},"timestamp":int(time.time()*1000)})+'\n')
  644. except: pass
  645. # #endregion
  646. stop_postgres_service()
  647. time.sleep(3)
  648. # 2. 获取数据目录
  649. try:
  650. data_dir = get_postgres_data_dir()
  651. logger.info(f"PostgreSQL 数据目录: {data_dir}")
  652. # #region agent log
  653. try:
  654. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  655. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:680","message":"got data directory","data":{"data_dir":str(data_dir)},"timestamp":int(time.time()*1000)})+'\n')
  656. except: pass
  657. # #endregion
  658. except FileNotFoundError as e:
  659. logger.error(f"无法找到 PostgreSQL 数据目录: {str(e)}")
  660. logger.error("请先初始化数据库或确保PostgreSQL已正确安装")
  661. # #region agent log
  662. try:
  663. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  664. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:683","message":"data directory not found","data":{"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  665. except: pass
  666. # #endregion
  667. return False
  668. # 3. 备份当前数据目录(如果存在且不为空)
  669. data_dir_exists = data_dir.exists()
  670. data_dir_has_content = data_dir_exists and any(data_dir.iterdir()) if data_dir_exists else False
  671. # #region agent log
  672. try:
  673. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  674. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:688","message":"checking existing data directory","data":{"data_dir":str(data_dir),"exists":data_dir_exists,"has_content":data_dir_has_content},"timestamp":int(time.time()*1000)})+'\n')
  675. except: pass
  676. # #endregion
  677. if data_dir_has_content:
  678. logger.info("检测到现有数据目录,正在备份...")
  679. timestamp = int(time.time())
  680. current_backup_dir = data_dir.parent / f"{data_dir.name}_backup_{timestamp}"
  681. try:
  682. if data_dir.exists():
  683. shutil.move(str(data_dir), str(current_backup_dir))
  684. logger.info(f"当前数据目录已备份到: {current_backup_dir}")
  685. # #region agent log
  686. try:
  687. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  688. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:695","message":"current data directory backed up","data":{"backup_dir":str(current_backup_dir)},"timestamp":int(time.time()*1000)})+'\n')
  689. except: pass
  690. # #endregion
  691. except Exception as e:
  692. logger.error(f"备份当前数据目录失败: {str(e)}")
  693. logger.error("恢复操作已取消,以保护现有数据")
  694. # #region agent log
  695. try:
  696. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  697. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:697","message":"backup current data directory failed","data":{"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  698. except: pass
  699. # #endregion
  700. return False
  701. # 4. 创建新的数据目录
  702. data_dir.mkdir(parents=True, exist_ok=True)
  703. logger.info(f"正在从备份恢复数据到: {data_dir}")
  704. # #region agent log
  705. try:
  706. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  707. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:702","message":"starting file copy","data":{"backup_dir":str(backup_dir),"data_dir":str(data_dir)},"timestamp":int(time.time()*1000)})+'\n')
  708. except: pass
  709. # #endregion
  710. # 5. 复制备份文件到数据目录
  711. logger.info("正在复制备份文件...")
  712. try:
  713. copied_items = []
  714. # 复制所有文件和目录
  715. for item in backup_dir.iterdir():
  716. # 跳过某些不需要的文件
  717. if item.name in ['.', '..']:
  718. continue
  719. dest = data_dir / item.name
  720. if item.is_dir():
  721. if dest.exists():
  722. shutil.rmtree(str(dest))
  723. shutil.copytree(str(item), str(dest))
  724. logger.debug(f"已复制目录: {item.name}")
  725. copied_items.append(f"dir:{item.name}")
  726. else:
  727. shutil.copy2(str(item), str(dest))
  728. logger.debug(f"已复制文件: {item.name}")
  729. copied_items.append(f"file:{item.name}")
  730. logger.info("备份文件复制完成")
  731. # #region agent log
  732. try:
  733. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  734. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:724","message":"file copy completed","data":{"copied_count":len(copied_items),"items":copied_items[:10]},"timestamp":int(time.time()*1000)})+'\n')
  735. except: pass
  736. # #endregion
  737. except Exception as e:
  738. logger.error(f"复制备份文件失败: {str(e)}")
  739. # #region agent log
  740. try:
  741. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  742. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:726","message":"file copy failed","data":{"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  743. except: pass
  744. # #endregion
  745. return False
  746. # 6. 验证关键文件
  747. required_files = ['PG_VERSION', 'postgresql.conf', 'pg_hba.conf']
  748. for file_name in required_files:
  749. file_path = data_dir / file_name
  750. if not file_path.exists():
  751. logger.warning(f"警告:缺少关键文件 {file_name}")
  752. # 6.1 清理可能存在的postmaster.pid文件(可能导致权限错误)
  753. postmaster_pid = data_dir / "postmaster.pid"
  754. if postmaster_pid.exists():
  755. logger.info("发现残留的 postmaster.pid 文件,正在删除...")
  756. try:
  757. postmaster_pid.unlink()
  758. logger.info("已删除 postmaster.pid 文件")
  759. # #region agent log
  760. try:
  761. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  762. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:736","message":"removed postmaster.pid","data":{},"timestamp":int(time.time()*1000)})+'\n')
  763. except: pass
  764. # #endregion
  765. except Exception as e:
  766. logger.warning(f"无法删除 postmaster.pid 文件: {str(e)}")
  767. # #region agent log
  768. try:
  769. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  770. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:740","message":"failed to remove postmaster.pid","data":{"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  771. except: pass
  772. # #endregion
  773. # 6.2 设置数据目录权限(确保PostgreSQL服务账户有写入权限)
  774. logger.info("正在设置数据目录权限...")
  775. try:
  776. # 获取PostgreSQL服务账户
  777. service_name = get_postgres_service_name()
  778. result = subprocess.run(
  779. ["sc", "qc", service_name],
  780. capture_output=True,
  781. text=True,
  782. encoding='utf-8',
  783. errors='ignore'
  784. )
  785. # 尝试为常见服务账户设置权限
  786. # 通常PostgreSQL服务以NETWORK SERVICE或Local System运行
  787. service_accounts = [
  788. "NT AUTHORITY\\NETWORK SERVICE",
  789. "NT AUTHORITY\\SYSTEM",
  790. "NT SERVICE\\postgresql-x64-16"
  791. ]
  792. # 使用icacls设置权限
  793. data_dir_str = str(data_dir)
  794. for account in service_accounts:
  795. try:
  796. # 授予完全控制权限
  797. result = subprocess.run(
  798. ["icacls", data_dir_str, "/grant", f"{account}:(OI)(CI)F", "/T"],
  799. capture_output=True,
  800. text=True,
  801. encoding='utf-8',
  802. errors='ignore'
  803. )
  804. if result.returncode == 0:
  805. logger.info(f"已为 {account} 设置数据目录权限")
  806. # #region agent log
  807. try:
  808. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  809. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:760","message":"set permissions","data":{"account":account,"data_dir":data_dir_str},"timestamp":int(time.time()*1000)})+'\n')
  810. except: pass
  811. # #endregion
  812. break
  813. except Exception as e:
  814. logger.debug(f"无法为 {account} 设置权限: {str(e)}")
  815. # 也确保当前用户有权限(用于调试)
  816. try:
  817. import getpass
  818. current_user = getpass.getuser()
  819. result = subprocess.run(
  820. ["icacls", data_dir_str, "/grant", f"{current_user}:(OI)(CI)F", "/T"],
  821. capture_output=True,
  822. text=True,
  823. encoding='utf-8',
  824. errors='ignore'
  825. )
  826. except:
  827. pass
  828. except Exception as e:
  829. logger.warning(f"设置数据目录权限时出错: {str(e)}")
  830. # #region agent log
  831. try:
  832. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  833. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:775","message":"failed to set permissions","data":{"error":str(e)},"timestamp":int(time.time()*1000)})+'\n')
  834. except: pass
  835. # #endregion
  836. # 7. 处理backup_label文件
  837. # PostgreSQL在启动时会自动处理backup_label文件
  838. # 如果存在backup_label,PostgreSQL会进入恢复模式
  839. backup_label = data_dir / "backup_label"
  840. if backup_label.exists():
  841. logger.info("检测到 backup_label 文件,PostgreSQL 将在启动时自动进入恢复模式")
  842. # 可以创建recovery.signal来强制恢复模式,但通常backup_label就足够了
  843. # 8. 设置正确的文件权限(Windows上通常不需要,但为了兼容性)
  844. logger.info("正在验证数据目录完整性...")
  845. pg_version_check = data_dir / "PG_VERSION"
  846. if not pg_version_check.exists():
  847. logger.error("恢复失败:PG_VERSION 文件未找到")
  848. return False
  849. # 9. 启动服务
  850. logger.info("正在启动 PostgreSQL 服务...")
  851. # #region agent log
  852. try:
  853. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  854. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:752","message":"starting postgres service","data":{},"timestamp":int(time.time()*1000)})+'\n')
  855. except: pass
  856. # #endregion
  857. time.sleep(2)
  858. service_started = start_postgres_service()
  859. # #region agent log
  860. try:
  861. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  862. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:754","message":"service start result","data":{"started":service_started},"timestamp":int(time.time()*1000)})+'\n')
  863. except: pass
  864. # #endregion
  865. if service_started:
  866. logger.info("PostgreSQL 服务启动成功!")
  867. logger.info("数据库恢复完成!")
  868. # 等待服务完全启动
  869. time.sleep(5)
  870. # 验证服务状态
  871. is_running = is_service_running()
  872. # #region agent log
  873. try:
  874. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  875. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:762","message":"service status check","data":{"is_running":is_running},"timestamp":int(time.time()*1000)})+'\n')
  876. except: pass
  877. # #endregion
  878. if is_running:
  879. logger.info("服务验证:PostgreSQL 正在运行")
  880. # #region agent log
  881. try:
  882. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  883. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:764","message":"restore completed successfully","data":{},"timestamp":int(time.time()*1000)})+'\n')
  884. except: pass
  885. # #endregion
  886. return True
  887. else:
  888. logger.warning("服务启动后立即停止,请检查日志")
  889. # #region agent log
  890. try:
  891. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  892. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:766","message":"service stopped after start","data":{},"timestamp":int(time.time()*1000)})+'\n')
  893. except: pass
  894. # #endregion
  895. return False
  896. else:
  897. logger.error("服务启动失败")
  898. # #region agent log
  899. try:
  900. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  901. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:769","message":"service start failed","data":{},"timestamp":int(time.time()*1000)})+'\n')
  902. except: pass
  903. # #endregion
  904. return False
  905. except Exception as e:
  906. logger.error(f"恢复数据库时发生错误: {str(e)}")
  907. import traceback
  908. logger.error(traceback.format_exc())
  909. # #region agent log
  910. try:
  911. with open(r'd:\code\vue\greater_wms\.cursor\debug.log', 'a', encoding='utf-8') as f:
  912. f.write(json.dumps({"sessionId":"debug-session","runId":"run1","hypothesisId":"RESTORE","location":"postgresql.py:772","message":"exception in restore","data":{"error":str(e),"error_type":type(e).__name__},"timestamp":int(time.time()*1000)})+'\n')
  913. except: pass
  914. # #endregion
  915. return False
  916. def list_backup_directories(backup_base_path=None):
  917. """列出可用的备份目录
  918. Args:
  919. backup_base_path: 备份基础路径,默认为当前脚本目录下的base_backup
  920. Returns:
  921. list: 备份目录列表
  922. """
  923. if backup_base_path is None:
  924. # 默认使用脚本所在目录的base_backup
  925. script_dir = Path(__file__).parent
  926. backup_base_path = script_dir / "base_backup"
  927. else:
  928. backup_base_path = Path(backup_base_path)
  929. if not backup_base_path.exists():
  930. return []
  931. backups = []
  932. for item in backup_base_path.iterdir():
  933. if item.is_dir():
  934. backup_label = item / "backup_label"
  935. pg_version = item / "PG_VERSION"
  936. if backup_label.exists() and pg_version.exists():
  937. # 读取版本信息
  938. try:
  939. with open(pg_version, 'r') as f:
  940. version = f.read().strip()
  941. except:
  942. version = "未知"
  943. # 读取备份时间
  944. try:
  945. with open(backup_label, 'r') as f:
  946. label_content = f.read()
  947. # 提取时间信息
  948. for line in label_content.split('\n'):
  949. if 'START TIME:' in line:
  950. time_str = line.split('START TIME:')[1].strip()
  951. break
  952. else:
  953. time_str = "未知时间"
  954. except:
  955. time_str = "未知时间"
  956. backups.append({
  957. 'path': str(item),
  958. 'name': item.name,
  959. 'version': version,
  960. 'time': time_str
  961. })
  962. return backups
  963. def check_service_status():
  964. """检查服务状态并显示详细信息"""
  965. service_name = get_postgres_service_name()
  966. is_running = is_service_running(service_name)
  967. print(f"服务名称: {service_name}")
  968. print(f"运行状态: {'运行中' if is_running else '已停止'}")
  969. if is_running:
  970. try:
  971. # 尝试连接数据库验证服务状态
  972. import psycopg2
  973. try:
  974. conn = psycopg2.connect(
  975. dbname='postgres',
  976. user='postgres',
  977. password='abc@1234',
  978. host='localhost',
  979. port='5432'
  980. )
  981. conn.close()
  982. print("数据库连接: 正常")
  983. except Exception as e:
  984. print(f"数据库连接: 异常 ({str(e)})")
  985. except ImportError:
  986. print("数据库连接: 未安装 psycopg2 库,无法测试连接")
  987. return is_running
  988. def main():
  989. """主函数:提供命令行界面"""
  990. print("=" * 50)
  991. print("PostgreSQL 服务管理工具")
  992. print("=" * 50)
  993. while True:
  994. print("\n请选择操作:")
  995. print("1. 检查服务状态")
  996. print("2. 启动服务")
  997. print("3. 停止服务")
  998. print("4. 重启服务")
  999. print("5. 修复服务启动问题")
  1000. print("6. 初始化数据库(会删除现有数据!)")
  1001. print("7. 从备份恢复数据库(会删除现有数据!)")
  1002. print("8. 退出")
  1003. choice = input("请输入选项 (1-8): ").strip()
  1004. if choice == "1":
  1005. print("\n检查服务状态...")
  1006. check_service_status()
  1007. elif choice == "2":
  1008. print("\n启动服务...")
  1009. if start_postgres_service():
  1010. print("服务启动成功")
  1011. else:
  1012. print("服务启动失败,请尝试修复")
  1013. elif choice == "3":
  1014. print("\n停止服务...")
  1015. if stop_postgres_service():
  1016. print("服务停止成功")
  1017. else:
  1018. print("服务停止失败")
  1019. elif choice == "4":
  1020. print("\n重启服务...")
  1021. if restart_postgres_service():
  1022. print("服务重启成功")
  1023. else:
  1024. print("服务重启失败")
  1025. elif choice == "5":
  1026. print("\n修复服务启动问题...")
  1027. if fix_postgres_service():
  1028. print("服务修复成功")
  1029. else:
  1030. print("服务修复失败,请查看日志文件")
  1031. elif choice == "6":
  1032. print("\n初始化数据库...")
  1033. print("警告:这将删除所有现有数据!")
  1034. confirm = input("确认要继续吗?(yes/no): ").strip().lower()
  1035. if confirm == 'yes':
  1036. if init_postgres_database():
  1037. print("数据库初始化成功!")
  1038. else:
  1039. print("数据库初始化失败,请查看日志文件")
  1040. else:
  1041. print("操作已取消")
  1042. elif choice == "7":
  1043. print("\n从备份恢复数据库...")
  1044. print("警告:这将删除所有现有数据并恢复备份!")
  1045. # 列出可用的备份
  1046. backups = list_backup_directories()
  1047. if not backups:
  1048. print("未找到可用的备份目录")
  1049. print("请确保备份目录位于: backup/base_backup/")
  1050. backup_path = input("或者手动输入备份目录路径: ").strip()
  1051. if not backup_path:
  1052. print("操作已取消")
  1053. continue
  1054. else:
  1055. print("\n可用的备份:")
  1056. for i, backup in enumerate(backups, 1):
  1057. print(f"{i}. {backup['name']} (PostgreSQL {backup['version']}, {backup['time']})")
  1058. print(f"{len(backups) + 1}. 手动输入路径")
  1059. backup_choice = input(f"请选择备份 (1-{len(backups) + 1}): ").strip()
  1060. try:
  1061. choice_num = int(backup_choice)
  1062. if 1 <= choice_num <= len(backups):
  1063. backup_path = backups[choice_num - 1]['path']
  1064. elif choice_num == len(backups) + 1:
  1065. backup_path = input("请输入备份目录路径: ").strip()
  1066. else:
  1067. print("无效选项")
  1068. continue
  1069. except ValueError:
  1070. print("无效输入")
  1071. continue
  1072. if not backup_path:
  1073. print("操作已取消")
  1074. continue
  1075. confirm = input("确认要从备份恢复吗?这将覆盖现有数据!(yes/no): ").strip().lower()
  1076. if confirm == 'yes':
  1077. if restore_from_backup(backup_path):
  1078. print("数据库恢复成功!")
  1079. else:
  1080. print("数据库恢复失败,请查看日志文件")
  1081. else:
  1082. print("操作已取消")
  1083. elif choice == "8":
  1084. print("退出程序")
  1085. break
  1086. else:
  1087. print("无效选项,请重新选择")
  1088. # input("\n按回车键继续...")
  1089. if __name__ == "__main__":
  1090. # 直接运行测试
  1091. print("正在启动 PostgreSQL 服务管理工具...")
  1092. main()