pqconnection.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. unit pqconnection;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, postgres3, sqldb, db;
  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 FreeSelect(cursor : TSQLHandle); override;
  32. procedure PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string); override;
  33. procedure PrepareSelect(cursor : TSQLHandle); override;
  34. procedure FreeFldBuffers(cursor : TSQLHandle); override;
  35. procedure Execute(cursor: TSQLHandle;atransaction:tSQLtransaction); override;
  36. procedure AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs); override;
  37. function GetFieldSizes(cursor : TSQLHandle) : integer; override;
  38. function Fetch(cursor : TSQLHandle) : boolean; override;
  39. procedure LoadFieldsFromBuffer(cursor : TSQLHandle;buffer: pchar); override;
  40. function GetFieldData(cursor : TSQLHandle; Field: TField; Buffer: Pointer;currbuff:pchar): Boolean; override;
  41. function GetStatementType(cursor : TSQLHandle) : tStatementType; override;
  42. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  43. function RollBack(trans : TSQLHandle) : boolean; override;
  44. function StartTransaction(trans : TSQLHandle) : boolean; override;
  45. procedure RollBackRetaining(trans : TSQLHandle); override;
  46. published
  47. property DatabaseName;
  48. property KeepConnection;
  49. property LoginPrompt;
  50. property Params;
  51. property OnLogin;
  52. end;
  53. implementation
  54. ResourceString
  55. SErrRollbackFailed = 'Rollback 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. type
  72. TTm = packed record
  73. tm_sec : longint;
  74. tm_min : longint;
  75. tm_hour : longint;
  76. tm_mday : longint;
  77. tm_mon : longint;
  78. tm_year : longint;
  79. tm_wday : longint;
  80. tm_yday : longint;
  81. tm_isdst : longint;
  82. __tm_gmtoff : longint;
  83. __tm_zone : Pchar;
  84. end;
  85. function TPQConnection.GetTransactionHandle(trans : TSQLHandle): pointer;
  86. begin
  87. Result := (trans as TPQtrans).TransactionHandle;
  88. end;
  89. function TPQConnection.RollBack(trans : TSQLHandle) : boolean;
  90. var
  91. res : PPGresult;
  92. tr : TPQTrans;
  93. begin
  94. result := false;
  95. tr := trans as TPQTrans;
  96. res := PQexec(tr.TransactionHandle, 'ROLLBACK');
  97. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  98. begin
  99. PQclear(res);
  100. result := false;
  101. DatabaseError(SErrRollbackFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  102. end
  103. else
  104. begin
  105. PQclear(res);
  106. PQFinish(tr.TransactionHandle);
  107. result := true;
  108. end;
  109. end;
  110. function TPQConnection.StartTransaction(trans : TSQLHandle) : boolean;
  111. var
  112. res : PPGresult;
  113. tr : TPQTrans;
  114. msg : string;
  115. begin
  116. result := false;
  117. tr := trans as TPQTrans;
  118. tr.TransactionHandle := PQconnectdb(pchar(FConnectString));
  119. if (PQstatus(tr.TransactionHandle) = CONNECTION_BAD) then
  120. begin
  121. result := false;
  122. PQFinish(tr.TransactionHandle);
  123. DatabaseError(SErrConnectionFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  124. end
  125. else
  126. begin
  127. res := PQexec(tr.TransactionHandle, 'BEGIN');
  128. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  129. begin
  130. result := false;
  131. PQclear(res);
  132. msg := PQerrorMessage(tr.transactionhandle);
  133. PQFinish(tr.TransactionHandle);
  134. DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
  135. end
  136. else
  137. begin
  138. PQclear(res);
  139. result := true;
  140. end;
  141. end;
  142. end;
  143. procedure TPQConnection.RollBackRetaining(trans : TSQLHandle);
  144. var
  145. res : PPGresult;
  146. tr : TPQTrans;
  147. msg : string;
  148. begin
  149. tr := trans as TPQTrans;
  150. res := PQexec(tr.TransactionHandle, 'ROLLBACK');
  151. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  152. begin
  153. PQclear(res);
  154. DatabaseError(SErrRollbackFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
  155. end
  156. else
  157. begin
  158. PQclear(res);
  159. res := PQexec(tr.TransactionHandle, 'BEGIN');
  160. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  161. begin
  162. PQclear(res);
  163. msg := PQerrorMessage(tr.transactionhandle);
  164. PQFinish(tr.TransactionHandle);
  165. DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
  166. end
  167. else
  168. PQclear(res);
  169. end;
  170. end;
  171. procedure TPQConnection.DoInternalConnect;
  172. var msg : string;
  173. begin
  174. inherited dointernalconnect;
  175. if (DatabaseName = '') then
  176. DatabaseError(SErrNoDatabaseName,self);
  177. FConnectString := '';
  178. if (UserName <> '') then FConnectString := FConnectString + ' user=''' + UserName + '''';
  179. if (Password <> '') then FConnectString := FConnectString + ' password=''' + Password + '''';
  180. if (DatabaseName <> '') then FConnectString := FConnectString + ' dbname=''' + DatabaseName + '''';
  181. FSQLDatabaseHandle := PQconnectdb(pchar(FConnectString));
  182. if (PQstatus(FSQLDatabaseHandle) = CONNECTION_BAD) then
  183. begin
  184. msg := PQerrorMessage(FSQLDatabaseHandle);
  185. dointernaldisconnect;
  186. DatabaseError(sErrConnectionFailed + ' (PostgreSQL: ' + msg + ')',self);
  187. end;
  188. end;
  189. procedure TPQConnection.DoInternalDisconnect;
  190. begin
  191. PQfinish(FSQLDatabaseHandle);
  192. end;
  193. function TPQConnection.TranslateFldType(Type_Oid : integer) : TFieldType;
  194. begin
  195. case Type_Oid of
  196. Oid_varchar,Oid_bpchar : Result := ftstring;
  197. Oid_text : REsult := ftmemo;
  198. Oid_int8 : Result := ftLargeInt;
  199. Oid_int4 : Result := ftInteger;
  200. Oid_int2 : Result := ftSmallInt;
  201. Oid_Float4 : Result := ftFloat;
  202. Oid_Float8 : Result := ftFloat;
  203. end;
  204. end;
  205. Function TPQConnection.AllocateCursorHandle : TSQLHandle;
  206. begin
  207. result := TPQCursor.create;
  208. end;
  209. Function TPQConnection.AllocateTransactionHandle : TSQLHandle;
  210. begin
  211. result := TPQTrans.create;
  212. end;
  213. procedure TPQConnection.PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string);
  214. begin
  215. (cursor as TPQCursor).statement := buf;
  216. end;
  217. procedure TPQConnection.PrepareSelect(cursor : TSQLHandle);
  218. begin
  219. with (cursor as TPQCursor) do
  220. statement := 'DECLARE selectst' + name + ' BINARY CURSOR FOR ' + statement;
  221. end;
  222. procedure TPQConnection.FreeSelect(cursor : TSQLHandle);
  223. var st : string;
  224. begin
  225. with cursor as TPQCursor do
  226. begin
  227. st := 'CLOSE selectst' + name;
  228. Res := pqexec(tr,pchar(st));
  229. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  230. begin
  231. pqclear(res);
  232. DatabaseError(SErrClearSelection + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self);
  233. end
  234. end;
  235. end;
  236. procedure TPQConnection.FreeStatement(cursor : TSQLHandle);
  237. begin
  238. with cursor as TPQCursor do
  239. begin
  240. pqclear(baseres);
  241. pqclear(res);
  242. end;
  243. end;
  244. procedure TPQConnection.FreeFldBuffers(cursor : TSQLHandle);
  245. begin
  246. // Do nothing
  247. end;
  248. procedure TPQConnection.Execute(cursor: TSQLHandle;atransaction:tSQLtransaction);
  249. begin
  250. with cursor as TPQCursor do
  251. begin
  252. tr := aTransaction.Handle;
  253. // res := pqexecParams(tr,pchar(statement),0,nil,nil,nil,nil,1);
  254. res := pqexec(tr,pchar(statement));
  255. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  256. begin
  257. pqclear(res);
  258. DatabaseError(SErrExecuteFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  259. end;
  260. end;
  261. end;
  262. procedure TPQConnection.AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs);
  263. var
  264. i : integer;
  265. size : integer;
  266. st : string;
  267. fieldtype : tfieldtype;
  268. begin
  269. with cursor as TPQCursor do
  270. begin
  271. // BaseRes := pqexecParams(tr,'FETCH 0 IN selectst' + pchar(name) ,0,nil,nil,nil,nil,1);
  272. st := 'FETCH 0 IN selectst' + pchar(name);
  273. BaseRes := pqexec(tr,pchar(st));
  274. if (PQresultStatus(BaseRes) <> PGRES_TUPLES_OK) then
  275. begin
  276. pqclear(BaseRes);
  277. DatabaseError(SErrFieldDefsFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  278. end;
  279. nFields := PQnfields(BaseRes);
  280. for i := 0 to nFields-1 do
  281. begin
  282. size := PQfsize(BaseRes, i);
  283. fieldtype := TranslateFldType(PQftype(BaseRes, i));
  284. if fieldtype = ftstring then
  285. size := pqfmod(baseres,i)-4;
  286. TFieldDef.Create(FieldDefs, PQfname(BaseRes, i), fieldtype,size, False, (i + 1));
  287. end;
  288. end;
  289. end;
  290. function TPQConnection.GetFieldSizes(cursor : TSQLHandle) : integer;
  291. var
  292. x,recsize : integer;
  293. size : integer;
  294. begin
  295. recsize := 0;
  296. {$R-}
  297. with cursor as TPQCursor do
  298. for x := 0 to PQnfields(baseres)-1 do
  299. begin
  300. size := PQfsize(baseres, x);
  301. if TranslateFldType(PQftype(BaseRes, x)) = ftString then
  302. size := pqfmod(baseres,x);
  303. if size = -1 then size := sizeof(pchar);
  304. Inc(recsize, size);
  305. end;
  306. {$R+}
  307. result := recsize;
  308. end;
  309. function TPQConnection.GetHandle: pointer;
  310. begin
  311. Result := FSQLDatabaseHandle;
  312. end;
  313. function TPQConnection.Fetch(cursor : TSQLHandle) : boolean;
  314. var st : string;
  315. begin
  316. with cursor as TPQCursor do
  317. begin
  318. st := 'FETCH NEXT IN selectst' + pchar(name);
  319. Res := pqexec(tr,pchar(st));
  320. if (PQresultStatus(res) <> PGRES_TUPLES_OK) then
  321. begin
  322. pqclear(Res);
  323. DatabaseError(SErrfetchFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
  324. end;
  325. Result := (PQntuples(res)=0);
  326. end;
  327. end;
  328. procedure TPQConnection.LoadFieldsFromBuffer(cursor : TSQLHandle;buffer : pchar);
  329. var
  330. x,i : integer;
  331. begin
  332. {$R-}
  333. with cursor as TPQCursor do for x := 0 to PQnfields(res)-1 do
  334. begin
  335. // writeln('Getdata:' + pqgetvalue(res,0,x));
  336. i := PQfsize(res, x);
  337. buffer[0] := chr(pqgetisnull(res,0,x));
  338. inc(buffer);
  339. if i = -1 then
  340. begin
  341. i := pqgetlength(res,0,x);
  342. move(i,buffer^,sizeof(integer));
  343. inc(buffer,sizeof(integer));
  344. Move(pqgetvalue(res,0,x)^,Buffer^, i);
  345. inc(buffer,i);
  346. end
  347. else
  348. begin
  349. Move(pqgetvalue(res,0,x)^, Buffer^, i);
  350. Inc(Buffer, i);
  351. end;
  352. end;
  353. {$R+}
  354. end;
  355. function TPQConnection.GetFieldData(Cursor : TSQLHandle;Field: TField; Buffer: Pointer;currbuff : pchar): Boolean;
  356. var
  357. x : longint;
  358. size : integer;
  359. tel : byte;
  360. begin
  361. Result := False;
  362. with cursor as TPQCursor do
  363. begin
  364. for x := 0 to Field.Fieldno-1 do
  365. begin
  366. size := PQfsize(BaseRes, x);
  367. inc(currbuff);
  368. if size = -1 then
  369. begin
  370. size := integer(CurrBuff^);
  371. inc(CurrBuff,sizeof(integer));
  372. end;
  373. if x < Field.Fieldno-1 then
  374. Inc(CurrBuff, size);
  375. end;
  376. dec(currbuff);
  377. if currbuff[0]<>#1 then
  378. begin
  379. inc(currbuff);
  380. case Field.DataType of
  381. ftInteger, ftSmallint, ftLargeInt,ftfloat :
  382. begin
  383. for tel := 1 to size do // postgres returns big-endian integers
  384. pchar(Buffer)[tel-1] := CurrBuff[size-tel];
  385. end;
  386. ftString :
  387. begin
  388. Move(CurrBuff^, Buffer^, size);
  389. PChar(Buffer + Size)^ := #0;
  390. end;
  391. end;
  392. Result := True;
  393. end
  394. end;
  395. end;
  396. function TPQConnection.GetStatementType(cursor : TSQLhandle) : TStatementType;
  397. begin
  398. result := stselect;
  399. end;
  400. end.