pqconnection.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. unit pqconnection;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, sqldb, db,postgres3;
  6. type
  7. TPQTrans = Class(TSQLHandle)
  8. protected
  9. TransactionHandle : PPGConn;
  10. end;
  11. TPQCursor = Class(TSQLHandle)
  12. protected
  13. Statement : string;
  14. tr : Pointer;
  15. nFields : integer;
  16. res : PPGresult;
  17. BaseRes : PPGresult;
  18. end;
  19. TPQConnection = class (TSQLConnection)
  20. private
  21. FConnectString : string;
  22. FSQLDatabaseHandle : pointer;
  23. function TranslateFldType(Type_Oid : integer) : TFieldType;
  24. protected
  25. procedure DoInternalConnect; override;
  26. procedure DoInternalDisconnect; override;
  27. function GetHandle : pointer; override;
  28. Function AllocateCursorHandle : TSQLHandle; override;
  29. Function AllocateTransactionHandle : TSQLHandle; override;
  30. procedure FreeStatement(cursor : TSQLHandle); override;
  31. procedure PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string); override;
  32. procedure FreeFldBuffers(cursor : TSQLHandle); override;
  33. procedure Execute(cursor: TSQLHandle;atransaction:tSQLtransaction); override;
  34. procedure AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs); override;
  35. function GetFieldSizes(cursor : TSQLHandle) : integer; override;
  36. function Fetch(cursor : TSQLHandle) : boolean; override;
  37. procedure LoadFieldsFromBuffer(cursor : TSQLHandle;buffer: pchar); override;
  38. function GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean; override;
  39. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  40. function RollBack(trans : TSQLHandle) : boolean; override;
  41. function Commit(trans : TSQLHandle) : boolean; override;
  42. procedure CommitRetaining(trans : TSQLHandle); override;
  43. function StartdbTransaction(trans : TSQLHandle) : boolean; override;
  44. procedure RollBackRetaining(trans : TSQLHandle); override;
  45. published
  46. property DatabaseName;
  47. property KeepConnection;
  48. property LoginPrompt;
  49. property Params;
  50. property OnLogin;
  51. end;
  52. implementation
  53. ResourceString
  54. SErrRollbackFailed = 'Rollback transaction failed';
  55. SErrCommitFailed = 'Commit transaction failed';
  56. SErrConnectionFailed = 'Connection to database failed';
  57. SErrTransactionFailed = 'Start of transacion failed';
  58. SErrClearSelection = 'Clear of selection failed';
  59. SErrExecuteFailed = 'Execution of query failed';
  60. SErrFieldDefsFailed = 'Can not extract field information from query';
  61. SErrFetchFailed = 'Fetch of data failed';
  62. SErrNoDatabaseName = 'Database connect string (DatabaseName) not filled in!';
  63. const Oid_Text = 25;
  64. Oid_Int8 = 20;
  65. Oid_int2 = 21;
  66. Oid_Int4 = 23;
  67. Oid_Float4 = 700;
  68. Oid_Float8 = 701;
  69. Oid_bpchar = 1042;
  70. Oid_varchar = 1043;
  71. function TPQConnection.GetTransactionHandle(trans : TSQLHandle): pointer;
  72. begin
  73. Result := (trans as TPQtrans).TransactionHandle;
  74. end;
  75. function TPQConnection.RollBack(trans : TSQLHandle) : boolean;
  76. var
  77. res : PPGresult;
  78. tr : TPQTrans;
  79. begin
  80. result := false;
  81. tr := trans as TPQTrans;
  82. res := PQexec(tr.TransactionHandle, 'ROLLBACK');
  83. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  84. begin
  85. PQclear(res);
  86. result := false;
  87. DatabaseError(SErrRollbackFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  88. end
  89. else
  90. begin
  91. PQclear(res);
  92. PQFinish(tr.TransactionHandle);
  93. result := true;
  94. end;
  95. end;
  96. function TPQConnection.Commit(trans : TSQLHandle) : boolean;
  97. var
  98. res : PPGresult;
  99. tr : TPQTrans;
  100. begin
  101. result := false;
  102. tr := trans as TPQTrans;
  103. res := PQexec(tr.TransactionHandle, 'COMMIT');
  104. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  105. begin
  106. PQclear(res);
  107. result := false;
  108. DatabaseError(SErrCommitFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  109. end
  110. else
  111. begin
  112. PQclear(res);
  113. PQFinish(tr.TransactionHandle);
  114. result := true;
  115. end;
  116. end;
  117. function TPQConnection.StartdbTransaction(trans : TSQLHandle) : boolean;
  118. var
  119. res : PPGresult;
  120. tr : TPQTrans;
  121. msg : string;
  122. begin
  123. result := false;
  124. tr := trans as TPQTrans;
  125. tr.TransactionHandle := PQconnectdb(pchar(FConnectString));
  126. if (PQstatus(tr.TransactionHandle) = CONNECTION_BAD) then
  127. begin
  128. result := false;
  129. PQFinish(tr.TransactionHandle);
  130. DatabaseError(SErrConnectionFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  131. end
  132. else
  133. begin
  134. res := PQexec(tr.TransactionHandle, 'BEGIN');
  135. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  136. begin
  137. result := false;
  138. PQclear(res);
  139. msg := PQerrorMessage(tr.transactionhandle);
  140. PQFinish(tr.TransactionHandle);
  141. DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
  142. end
  143. else
  144. begin
  145. PQclear(res);
  146. result := true;
  147. end;
  148. end;
  149. end;
  150. procedure TPQConnection.RollBackRetaining(trans : TSQLHandle);
  151. var
  152. res : PPGresult;
  153. tr : TPQTrans;
  154. msg : string;
  155. begin
  156. tr := trans as TPQTrans;
  157. res := PQexec(tr.TransactionHandle, 'ROLLBACK');
  158. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  159. begin
  160. PQclear(res);
  161. DatabaseError(SErrRollbackFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  162. end
  163. else
  164. begin
  165. PQclear(res);
  166. res := PQexec(tr.TransactionHandle, 'BEGIN');
  167. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  168. begin
  169. PQclear(res);
  170. msg := PQerrorMessage(tr.transactionhandle);
  171. PQFinish(tr.TransactionHandle);
  172. DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
  173. end
  174. else
  175. PQclear(res);
  176. end;
  177. end;
  178. procedure TPQConnection.CommitRetaining(trans : TSQLHandle);
  179. var
  180. res : PPGresult;
  181. tr : TPQTrans;
  182. msg : string;
  183. begin
  184. tr := trans as TPQTrans;
  185. res := PQexec(tr.TransactionHandle, 'COMMIT');
  186. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  187. begin
  188. PQclear(res);
  189. DatabaseError(SErrCommitFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  190. end
  191. else
  192. begin
  193. PQclear(res);
  194. res := PQexec(tr.TransactionHandle, 'BEGIN');
  195. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  196. begin
  197. PQclear(res);
  198. msg := PQerrorMessage(tr.transactionhandle);
  199. PQFinish(tr.TransactionHandle);
  200. DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
  201. end
  202. else
  203. PQclear(res);
  204. end;
  205. end;
  206. procedure TPQConnection.DoInternalConnect;
  207. var msg : string;
  208. begin
  209. inherited dointernalconnect;
  210. if (DatabaseName = '') then
  211. DatabaseError(SErrNoDatabaseName,self);
  212. FConnectString := '';
  213. if (UserName <> '') then FConnectString := FConnectString + ' user=''' + UserName + '''';
  214. if (Password <> '') then FConnectString := FConnectString + ' password=''' + Password + '''';
  215. if (DatabaseName <> '') then FConnectString := FConnectString + ' dbname=''' + DatabaseName + '''';
  216. FSQLDatabaseHandle := PQconnectdb(pchar(FConnectString));
  217. if (PQstatus(FSQLDatabaseHandle) = CONNECTION_BAD) then
  218. begin
  219. msg := PQerrorMessage(FSQLDatabaseHandle);
  220. dointernaldisconnect;
  221. DatabaseError(sErrConnectionFailed + ' (PostgreSQL: ' + msg + ')',self);
  222. end;
  223. end;
  224. procedure TPQConnection.DoInternalDisconnect;
  225. begin
  226. PQfinish(FSQLDatabaseHandle);
  227. end;
  228. function TPQConnection.TranslateFldType(Type_Oid : integer) : TFieldType;
  229. begin
  230. case Type_Oid of
  231. Oid_varchar,Oid_bpchar : Result := ftstring;
  232. Oid_text : REsult := ftmemo;
  233. Oid_int8 : Result := ftLargeInt;
  234. Oid_int4 : Result := ftInteger;
  235. Oid_int2 : Result := ftSmallInt;
  236. Oid_Float4 : Result := ftFloat;
  237. Oid_Float8 : Result := ftFloat;
  238. end;
  239. end;
  240. Function TPQConnection.AllocateCursorHandle : TSQLHandle;
  241. begin
  242. result := TPQCursor.create;
  243. end;
  244. Function TPQConnection.AllocateTransactionHandle : TSQLHandle;
  245. begin
  246. result := TPQTrans.create;
  247. end;
  248. procedure TPQConnection.PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string);
  249. begin
  250. with (cursor as TPQCursor) do
  251. begin
  252. (cursor as TPQCursor).statement := buf;
  253. if StatementType = stselect then
  254. statement := 'DECLARE selectst' + name + ' BINARY CURSOR FOR ' + statement;
  255. end;
  256. end;
  257. procedure TPQConnection.FreeStatement(cursor : TSQLHandle);
  258. begin
  259. with cursor as TPQCursor do
  260. if (PQresultStatus(res) <> PGRES_FATAL_ERROR) then //Don't try to do anything if the transaction has already encountered an error.
  261. begin
  262. if StatementType = stselect then
  263. begin
  264. Res := pqexec(tr,pchar('CLOSE selectst' + name));
  265. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  266. begin
  267. pqclear(res);
  268. DatabaseError(SErrClearSelection + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  269. end
  270. end;
  271. pqclear(baseres);
  272. pqclear(res);
  273. end;
  274. end;
  275. procedure TPQConnection.FreeFldBuffers(cursor : TSQLHandle);
  276. begin
  277. // Do nothing
  278. end;
  279. procedure TPQConnection.Execute(cursor: TSQLHandle;atransaction:tSQLtransaction);
  280. var st : string;
  281. begin
  282. with cursor as TPQCursor do
  283. begin
  284. tr := aTransaction.Handle;
  285. // res := pqexecParams(tr,pchar(statement),0,nil,nil,nil,nil,1);
  286. st := statement;
  287. res := pqexec(tr,pchar(st));
  288. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  289. begin
  290. pqclear(res);
  291. DatabaseError(SErrExecuteFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self);
  292. end;
  293. end;
  294. end;
  295. procedure TPQConnection.AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs);
  296. var
  297. i : integer;
  298. size : integer;
  299. st : string;
  300. fieldtype : tfieldtype;
  301. begin
  302. with cursor as TPQCursor do
  303. begin
  304. // BaseRes := pqexecParams(tr,'FETCH 0 IN selectst' + pchar(name) ,0,nil,nil,nil,nil,1);
  305. st := 'FETCH 0 IN selectst' + pchar(name);
  306. BaseRes := pqexec(tr,pchar(st));
  307. if (PQresultStatus(BaseRes) <> PGRES_TUPLES_OK) then
  308. begin
  309. pqclear(BaseRes);
  310. DatabaseError(SErrFieldDefsFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  311. end;
  312. nFields := PQnfields(BaseRes);
  313. for i := 0 to nFields-1 do
  314. begin
  315. size := PQfsize(BaseRes, i);
  316. fieldtype := TranslateFldType(PQftype(BaseRes, i));
  317. if fieldtype = ftstring then
  318. size := pqfmod(baseres,i)-4;
  319. TFieldDef.Create(FieldDefs, PQfname(BaseRes, i), fieldtype,size, False, (i + 1));
  320. end;
  321. end;
  322. end;
  323. function TPQConnection.GetFieldSizes(cursor : TSQLHandle) : integer;
  324. var
  325. x,recsize : integer;
  326. size : integer;
  327. begin
  328. recsize := 0;
  329. {$R-}
  330. with cursor as TPQCursor do
  331. for x := 0 to PQnfields(baseres)-1 do
  332. begin
  333. size := PQfsize(baseres, x);
  334. if TranslateFldType(PQftype(BaseRes, x)) = ftString then
  335. size := pqfmod(baseres,x);
  336. if size = -1 then size := sizeof(pchar);
  337. Inc(recsize, size);
  338. end;
  339. {$R+}
  340. result := recsize;
  341. end;
  342. function TPQConnection.GetHandle: pointer;
  343. begin
  344. Result := FSQLDatabaseHandle;
  345. end;
  346. function TPQConnection.Fetch(cursor : TSQLHandle) : boolean;
  347. var st : string;
  348. begin
  349. with cursor as TPQCursor do
  350. begin
  351. st := 'FETCH NEXT IN selectst' + pchar(name);
  352. Res := pqexec(tr,pchar(st));
  353. if (PQresultStatus(res) <> PGRES_TUPLES_OK) then
  354. begin
  355. pqclear(Res);
  356. DatabaseError(SErrfetchFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  357. end;
  358. Result := (PQntuples(res)<>0);
  359. end;
  360. end;
  361. procedure TPQConnection.LoadFieldsFromBuffer(cursor : TSQLHandle;buffer : pchar);
  362. var
  363. x,i : integer;
  364. begin
  365. {$R-}
  366. with cursor as TPQCursor do for x := 0 to PQnfields(res)-1 do
  367. begin
  368. i := PQfsize(res, x);
  369. buffer[0] := chr(pqgetisnull(res,0,x));
  370. inc(buffer);
  371. if i = -1 then
  372. begin
  373. i := pqgetlength(res,0,x);
  374. move(i,buffer^,sizeof(integer));
  375. inc(buffer,sizeof(integer));
  376. Move(pqgetvalue(res,0,x)^,Buffer^, i);
  377. inc(buffer,i);
  378. end
  379. else
  380. begin
  381. Move(pqgetvalue(res,0,x)^, Buffer^, i);
  382. Inc(Buffer, i);
  383. end;
  384. end;
  385. {$R+}
  386. end;
  387. function TPQConnection.GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean;
  388. var
  389. x : longint;
  390. size : integer;
  391. tel : byte;
  392. begin
  393. Result := False;
  394. with cursor as TPQCursor do
  395. begin
  396. for x := 0 to Field.Fieldno-1 do
  397. begin
  398. size := PQfsize(BaseRes, x);
  399. inc(currbuff);
  400. if size = -1 then
  401. begin
  402. size := integer(CurrBuff^);
  403. inc(CurrBuff,sizeof(integer));
  404. end;
  405. if x < Field.Fieldno-1 then
  406. Inc(CurrBuff, size);
  407. end;
  408. dec(currbuff);
  409. if currbuff[0]<>#1 then
  410. begin
  411. inc(currbuff);
  412. case Field.DataType of
  413. ftInteger, ftSmallint, ftLargeInt,ftfloat :
  414. begin
  415. for tel := 1 to size do // postgres returns big-endian integers
  416. pchar(Buffer)[tel-1] := CurrBuff[size-tel];
  417. end;
  418. ftString :
  419. begin
  420. Move(CurrBuff^, Buffer^, size);
  421. PChar(Buffer + Size)^ := #0;
  422. end;
  423. end;
  424. Result := True;
  425. end
  426. end;
  427. end;
  428. end.