pqconnection.pp 13 KB

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