pqconnection.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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. begin
  261. if StatementType = stselect then
  262. begin
  263. Res := pqexec(tr,pchar('CLOSE selectst' + name));
  264. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  265. begin
  266. pqclear(res);
  267. DatabaseError(SErrClearSelection + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self);
  268. end
  269. end;
  270. pqclear(baseres);
  271. pqclear(res);
  272. end;
  273. end;
  274. procedure TPQConnection.FreeFldBuffers(cursor : TSQLHandle);
  275. begin
  276. // Do nothing
  277. end;
  278. procedure TPQConnection.Execute(cursor: TSQLHandle;atransaction:tSQLtransaction);
  279. var st : string;
  280. begin
  281. with cursor as TPQCursor do
  282. begin
  283. tr := aTransaction.Handle;
  284. // res := pqexecParams(tr,pchar(statement),0,nil,nil,nil,nil,1);
  285. st := statement;
  286. res := pqexec(tr,pchar(st));
  287. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  288. begin
  289. pqclear(res);
  290. DatabaseError(SErrExecuteFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  291. end;
  292. end;
  293. end;
  294. procedure TPQConnection.AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs);
  295. var
  296. i : integer;
  297. size : integer;
  298. st : string;
  299. fieldtype : tfieldtype;
  300. begin
  301. with cursor as TPQCursor do
  302. begin
  303. // BaseRes := pqexecParams(tr,'FETCH 0 IN selectst' + pchar(name) ,0,nil,nil,nil,nil,1);
  304. st := 'FETCH 0 IN selectst' + pchar(name);
  305. BaseRes := pqexec(tr,pchar(st));
  306. if (PQresultStatus(BaseRes) <> PGRES_TUPLES_OK) then
  307. begin
  308. pqclear(BaseRes);
  309. DatabaseError(SErrFieldDefsFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  310. end;
  311. nFields := PQnfields(BaseRes);
  312. for i := 0 to nFields-1 do
  313. begin
  314. size := PQfsize(BaseRes, i);
  315. fieldtype := TranslateFldType(PQftype(BaseRes, i));
  316. if fieldtype = ftstring then
  317. size := pqfmod(baseres,i)-4;
  318. TFieldDef.Create(FieldDefs, PQfname(BaseRes, i), fieldtype,size, False, (i + 1));
  319. end;
  320. end;
  321. end;
  322. function TPQConnection.GetFieldSizes(cursor : TSQLHandle) : integer;
  323. var
  324. x,recsize : integer;
  325. size : integer;
  326. begin
  327. recsize := 0;
  328. {$R-}
  329. with cursor as TPQCursor do
  330. for x := 0 to PQnfields(baseres)-1 do
  331. begin
  332. size := PQfsize(baseres, x);
  333. if TranslateFldType(PQftype(BaseRes, x)) = ftString then
  334. size := pqfmod(baseres,x);
  335. if size = -1 then size := sizeof(pchar);
  336. Inc(recsize, size);
  337. end;
  338. {$R+}
  339. result := recsize;
  340. end;
  341. function TPQConnection.GetHandle: pointer;
  342. begin
  343. Result := FSQLDatabaseHandle;
  344. end;
  345. function TPQConnection.Fetch(cursor : TSQLHandle) : boolean;
  346. var st : string;
  347. begin
  348. with cursor as TPQCursor do
  349. begin
  350. st := 'FETCH NEXT IN selectst' + pchar(name);
  351. Res := pqexec(tr,pchar(st));
  352. if (PQresultStatus(res) <> PGRES_TUPLES_OK) then
  353. begin
  354. pqclear(Res);
  355. DatabaseError(SErrfetchFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  356. end;
  357. Result := (PQntuples(res)<>0);
  358. end;
  359. end;
  360. procedure TPQConnection.LoadFieldsFromBuffer(cursor : TSQLHandle;buffer : pchar);
  361. var
  362. x,i : integer;
  363. begin
  364. {$R-}
  365. with cursor as TPQCursor do for x := 0 to PQnfields(res)-1 do
  366. begin
  367. i := PQfsize(res, x);
  368. buffer[0] := chr(pqgetisnull(res,0,x));
  369. inc(buffer);
  370. if i = -1 then
  371. begin
  372. i := pqgetlength(res,0,x);
  373. move(i,buffer^,sizeof(integer));
  374. inc(buffer,sizeof(integer));
  375. Move(pqgetvalue(res,0,x)^,Buffer^, i);
  376. inc(buffer,i);
  377. end
  378. else
  379. begin
  380. Move(pqgetvalue(res,0,x)^, Buffer^, i);
  381. Inc(Buffer, i);
  382. end;
  383. end;
  384. {$R+}
  385. end;
  386. function TPQConnection.GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean;
  387. var
  388. x : longint;
  389. size : integer;
  390. tel : byte;
  391. begin
  392. Result := False;
  393. with cursor as TPQCursor do
  394. begin
  395. for x := 0 to Field.Fieldno-1 do
  396. begin
  397. size := PQfsize(BaseRes, x);
  398. inc(currbuff);
  399. if size = -1 then
  400. begin
  401. size := integer(CurrBuff^);
  402. inc(CurrBuff,sizeof(integer));
  403. end;
  404. if x < Field.Fieldno-1 then
  405. Inc(CurrBuff, size);
  406. end;
  407. dec(currbuff);
  408. if currbuff[0]<>#1 then
  409. begin
  410. inc(currbuff);
  411. case Field.DataType of
  412. ftInteger, ftSmallint, ftLargeInt,ftfloat :
  413. begin
  414. for tel := 1 to size do // postgres returns big-endian integers
  415. pchar(Buffer)[tel-1] := CurrBuff[size-tel];
  416. end;
  417. ftString :
  418. begin
  419. Move(CurrBuff^, Buffer^, size);
  420. PChar(Buffer + Size)^ := #0;
  421. end;
  422. end;
  423. Result := True;
  424. end
  425. end;
  426. end;
  427. end.