pqconnection.pp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240
  1. unit pqconnection;
  2. {$mode objfpc}{$H+}
  3. {$Define LinkDynamically}
  4. interface
  5. uses
  6. Classes, SysUtils, sqldb, db, dbconst,bufdataset,
  7. {$IfDef LinkDynamically}
  8. postgres3dyn;
  9. {$Else}
  10. postgres3;
  11. {$EndIf}
  12. type
  13. TPQTrans = Class(TSQLHandle)
  14. protected
  15. PGConn : PPGConn;
  16. end;
  17. TPQCursor = Class(TSQLCursor)
  18. protected
  19. Statement : string;
  20. StmtName : string;
  21. tr : TPQTrans;
  22. res : PPGresult;
  23. CurTuple : integer;
  24. FieldBinding : array of integer;
  25. end;
  26. EPQDatabaseError = class(EDatabaseError)
  27. public
  28. SEVERITY:string;
  29. SQLSTATE: string;
  30. MESSAGE_PRIMARY:string;
  31. MESSAGE_DETAIL:string;
  32. MESSAGE_HINT:string;
  33. STATEMENT_POSITION:string;
  34. end;
  35. TTranConnection= class
  36. protected
  37. FPGConn : PPGConn;
  38. FTranActive : boolean
  39. end;
  40. { TPQConnection }
  41. TPQConnection = class (TSQLConnection)
  42. private
  43. FConnectionPool : array of TTranConnection;
  44. FCursorCount : word;
  45. FConnectString : string;
  46. FSQLDatabaseHandle : pointer;
  47. FIntegerDateTimes : boolean;
  48. procedure CheckResultError(var res: PPGresult; conn:PPGconn; ErrMsg: string);
  49. function TranslateFldType(res : PPGresult; Tuple : integer; out Size : integer) : TFieldType;
  50. procedure ExecuteDirectPG(const Query : String);
  51. protected
  52. procedure DoInternalConnect; override;
  53. procedure DoInternalDisconnect; override;
  54. function GetHandle : pointer; override;
  55. Function AllocateCursorHandle : TSQLCursor; override;
  56. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  57. Function AllocateTransactionHandle : TSQLHandle; override;
  58. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
  59. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); override;
  60. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  61. function Fetch(cursor : TSQLCursor) : boolean; override;
  62. procedure UnPrepareStatement(cursor : TSQLCursor); override;
  63. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  64. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  65. function RollBack(trans : TSQLHandle) : boolean; override;
  66. function Commit(trans : TSQLHandle) : boolean; override;
  67. procedure CommitRetaining(trans : TSQLHandle); override;
  68. function StartdbTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  69. procedure RollBackRetaining(trans : TSQLHandle); override;
  70. procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override;
  71. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  72. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor;ATransaction : TSQLTransaction); override;
  73. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  74. public
  75. constructor Create(AOwner : TComponent); override;
  76. function GetConnectionInfo(InfoType:TConnInfoType): string; override;
  77. procedure CreateDB; override;
  78. procedure DropDB; override;
  79. published
  80. property DatabaseName;
  81. property KeepConnection;
  82. property LoginPrompt;
  83. property Params;
  84. property OnLogin;
  85. end;
  86. { TPQConnectionDef }
  87. TPQConnectionDef = Class(TConnectionDef)
  88. Class Function TypeName : String; override;
  89. Class Function ConnectionClass : TSQLConnectionClass; override;
  90. Class Function Description : String; override;
  91. Class Function DefaultLibraryName : String; override;
  92. Class Function LoadFunction : TLibraryLoadFunction; override;
  93. Class Function UnLoadFunction : TLibraryUnLoadFunction; override;
  94. Class Function LoadedLibraryName: string; override;
  95. end;
  96. implementation
  97. uses math, strutils, FmtBCD;
  98. ResourceString
  99. SErrRollbackFailed = 'Rollback transaction failed';
  100. SErrCommitFailed = 'Commit transaction failed';
  101. SErrConnectionFailed = 'Connection to database failed';
  102. SErrTransactionFailed = 'Start of transacion failed';
  103. SErrClearSelection = 'Clear of selection failed';
  104. SErrExecuteFailed = 'Execution of query failed';
  105. SErrFieldDefsFailed = 'Can not extract field information from query';
  106. SErrFetchFailed = 'Fetch of data failed';
  107. SErrPrepareFailed = 'Preparation of query failed.';
  108. SErrUnPrepareFailed = 'Unpreparation of query failed.';
  109. const Oid_Bool = 16;
  110. Oid_Bytea = 17;
  111. Oid_char = 18;
  112. Oid_Text = 25;
  113. Oid_Oid = 26;
  114. Oid_Name = 19;
  115. Oid_Int8 = 20;
  116. Oid_int2 = 21;
  117. Oid_Int4 = 23;
  118. Oid_Float4 = 700;
  119. Oid_Money = 790;
  120. Oid_Float8 = 701;
  121. Oid_Unknown = 705;
  122. Oid_MacAddr = 829;
  123. Oid_Inet = 869;
  124. Oid_bpchar = 1042;
  125. Oid_varchar = 1043;
  126. oid_date = 1082;
  127. oid_time = 1083;
  128. Oid_timeTZ = 1266;
  129. Oid_timestamp = 1114;
  130. Oid_timestampTZ = 1184;
  131. Oid_interval = 1186;
  132. oid_numeric = 1700;
  133. Oid_uuid = 2950;
  134. constructor TPQConnection.Create(AOwner : TComponent);
  135. begin
  136. inherited;
  137. FConnOptions := FConnOptions + [sqSupportParams] + [sqEscapeRepeat] + [sqEscapeSlash];
  138. FieldNameQuoteChars:=DoubleQuotes;
  139. end;
  140. procedure TPQConnection.CreateDB;
  141. begin
  142. ExecuteDirectPG('CREATE DATABASE ' +DatabaseName);
  143. end;
  144. procedure TPQConnection.DropDB;
  145. begin
  146. ExecuteDirectPG('DROP DATABASE ' +DatabaseName);
  147. end;
  148. procedure TPQConnection.ExecuteDirectPG(const query : string);
  149. var ASQLDatabaseHandle : PPGConn;
  150. res : PPGresult;
  151. msg : String;
  152. begin
  153. CheckDisConnected;
  154. {$IfDef LinkDynamically}
  155. InitialisePostgres3;
  156. {$EndIf}
  157. FConnectString := '';
  158. if (UserName <> '') then FConnectString := FConnectString + ' user=''' + UserName + '''';
  159. if (Password <> '') then FConnectString := FConnectString + ' password=''' + Password + '''';
  160. if (HostName <> '') then FConnectString := FConnectString + ' host=''' + HostName + '''';
  161. FConnectString := FConnectString + ' dbname=''template1''';
  162. if (Params.Text <> '') then FConnectString := FConnectString + ' '+Params.Text;
  163. ASQLDatabaseHandle := PQconnectdb(pchar(FConnectString));
  164. if (PQstatus(ASQLDatabaseHandle) = CONNECTION_BAD) then
  165. begin
  166. msg := PQerrorMessage(ASQLDatabaseHandle);
  167. PQFinish(ASQLDatabaseHandle);
  168. DatabaseError(sErrConnectionFailed + ' (PostgreSQL: ' + Msg + ')',self);
  169. end;
  170. res := PQexec(ASQLDatabaseHandle,pchar(query));
  171. CheckResultError(res,ASQLDatabaseHandle,SDBCreateDropFailed);
  172. PQclear(res);
  173. PQFinish(ASQLDatabaseHandle);
  174. {$IfDef LinkDynamically}
  175. ReleasePostgres3;
  176. {$EndIf}
  177. end;
  178. function TPQConnection.GetTransactionHandle(trans : TSQLHandle): pointer;
  179. begin
  180. Result := trans;
  181. end;
  182. function TPQConnection.RollBack(trans : TSQLHandle) : boolean;
  183. var
  184. res : PPGresult;
  185. tr : TPQTrans;
  186. i : Integer;
  187. begin
  188. result := false;
  189. tr := trans as TPQTrans;
  190. res := PQexec(tr.PGConn, 'ROLLBACK');
  191. CheckResultError(res,tr.PGConn,SErrRollbackFailed);
  192. PQclear(res);
  193. //make connection available in pool
  194. for i:=0 to length(FConnectionPool)-1 do
  195. if FConnectionPool[i].FPGConn=tr.PGConn then
  196. begin
  197. FConnectionPool[i].FTranActive:=false;
  198. break;
  199. end;
  200. result := true;
  201. end;
  202. function TPQConnection.Commit(trans : TSQLHandle) : boolean;
  203. var
  204. res : PPGresult;
  205. tr : TPQTrans;
  206. i : Integer;
  207. begin
  208. result := false;
  209. tr := trans as TPQTrans;
  210. res := PQexec(tr.PGConn, 'COMMIT');
  211. CheckResultError(res,tr.PGConn,SErrCommitFailed);
  212. PQclear(res);
  213. //make connection available in pool
  214. for i:=0 to length(FConnectionPool)-1 do
  215. if FConnectionPool[i].FPGConn=tr.PGConn then
  216. begin
  217. FConnectionPool[i].FTranActive:=false;
  218. break;
  219. end;
  220. result := true;
  221. end;
  222. function TPQConnection.StartdbTransaction(trans : TSQLHandle; AParams : string) : boolean;
  223. var
  224. res : PPGresult;
  225. tr : TPQTrans;
  226. i : Integer;
  227. begin
  228. result:=false;
  229. tr := trans as TPQTrans;
  230. //find an unused connection in the pool
  231. i:=0;
  232. while i<length(FConnectionPool) do
  233. if (FConnectionPool[i].FPGConn=nil) or not FConnectionPool[i].FTranActive then
  234. break
  235. else
  236. i:=i+1;
  237. if i=length(FConnectionPool) then //create a new connection
  238. begin
  239. tr.PGConn := PQconnectdb(pchar(FConnectString));
  240. if (PQstatus(tr.PGConn) = CONNECTION_BAD) then
  241. begin
  242. result := false;
  243. PQFinish(tr.PGConn);
  244. DatabaseError(SErrConnectionFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.PGConn) + ')',self);
  245. end
  246. else
  247. begin
  248. if CharSet <> '' then
  249. PQsetClientEncoding(tr.PGConn, pchar(CharSet));
  250. //store the new connection
  251. SetLength(FConnectionPool,i+1);
  252. FConnectionPool[i]:=TTranConnection.Create;
  253. FConnectionPool[i].FPGConn:=tr.PGConn;
  254. FConnectionPool[i].FTranActive:=true;
  255. end;
  256. end
  257. else //re-use existing connection
  258. begin
  259. tr.PGConn:=FConnectionPool[i].FPGConn;
  260. FConnectionPool[i].FTranActive:=true;
  261. end;
  262. res := PQexec(tr.PGConn, 'BEGIN');
  263. CheckResultError(res,tr.PGConn,sErrTransactionFailed);
  264. PQclear(res);
  265. result := true;
  266. end;
  267. procedure TPQConnection.RollBackRetaining(trans : TSQLHandle);
  268. var
  269. res : PPGresult;
  270. tr : TPQTrans;
  271. begin
  272. tr := trans as TPQTrans;
  273. res := PQexec(tr.PGConn, 'ROLLBACK');
  274. CheckResultError(res,tr.PGConn,SErrRollbackFailed);
  275. PQclear(res);
  276. res := PQexec(tr.PGConn, 'BEGIN');
  277. CheckResultError(res,tr.PGConn,sErrTransactionFailed);
  278. PQclear(res);
  279. end;
  280. procedure TPQConnection.CommitRetaining(trans : TSQLHandle);
  281. var
  282. res : PPGresult;
  283. tr : TPQTrans;
  284. begin
  285. tr := trans as TPQTrans;
  286. res := PQexec(tr.PGConn, 'COMMIT');
  287. CheckResultError(res,tr.PGConn,SErrCommitFailed);
  288. PQclear(res);
  289. res := PQexec(tr.PGConn, 'BEGIN');
  290. CheckResultError(res,tr.PGConn,sErrTransactionFailed);
  291. PQclear(res);
  292. end;
  293. procedure TPQConnection.DoInternalConnect;
  294. var msg : string;
  295. begin
  296. {$IfDef LinkDynamically}
  297. InitialisePostgres3;
  298. {$EndIf}
  299. inherited dointernalconnect;
  300. FConnectString := '';
  301. if (UserName <> '') then FConnectString := FConnectString + ' user=''' + UserName + '''';
  302. if (Password <> '') then FConnectString := FConnectString + ' password=''' + Password + '''';
  303. if (HostName <> '') then FConnectString := FConnectString + ' host=''' + HostName + '''';
  304. if (DatabaseName <> '') then FConnectString := FConnectString + ' dbname=''' + DatabaseName + '''';
  305. if (Params.Text <> '') then FConnectString := FConnectString + ' '+Params.Text;
  306. FSQLDatabaseHandle := PQconnectdb(pchar(FConnectString));
  307. if (PQstatus(FSQLDatabaseHandle) = CONNECTION_BAD) then
  308. begin
  309. msg := PQerrorMessage(FSQLDatabaseHandle);
  310. dointernaldisconnect;
  311. DatabaseError(sErrConnectionFailed + ' (PostgreSQL: ' + msg + ')',self);
  312. end;
  313. // This only works for pg>=8.0, so timestamps won't work with earlier versions of pg which are compiled with integer_datetimes on
  314. if PQparameterStatus<>nil then
  315. FIntegerDateTimes := PQparameterStatus(FSQLDatabaseHandle,'integer_datetimes') = 'on';
  316. SetLength(FConnectionPool,1);
  317. FConnectionPool[0]:=TTranConnection.Create;
  318. FConnectionPool[0].FPGConn:=FSQLDatabaseHandle;
  319. FConnectionPool[0].FTranActive:=false;
  320. end;
  321. procedure TPQConnection.DoInternalDisconnect;
  322. var i:integer;
  323. begin
  324. for i:=0 to length(FConnectionPool)-1 do
  325. begin
  326. if assigned(FConnectionPool[i].FPGConn) then
  327. PQfinish(FConnectionPool[i].FPGConn);
  328. FConnectionPool[i].Free;
  329. end;
  330. Setlength(FConnectionPool,0);
  331. {$IfDef LinkDynamically}
  332. ReleasePostgres3;
  333. {$EndIf}
  334. end;
  335. procedure TPQConnection.CheckResultError(var res: PPGresult; conn: PPGconn;
  336. ErrMsg: string);
  337. var
  338. E: EPQDatabaseError;
  339. sErr: string;
  340. CompName: string;
  341. SEVERITY: string;
  342. SQLSTATE: string;
  343. MESSAGE_PRIMARY: string;
  344. MESSAGE_DETAIL: string;
  345. MESSAGE_HINT: string;
  346. STATEMENT_POSITION: string;
  347. i:Integer;
  348. begin
  349. if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
  350. begin
  351. SEVERITY:=PQresultErrorField(res,ord('S'));
  352. SQLSTATE:=PQresultErrorField(res,ord('C'));
  353. MESSAGE_PRIMARY:=PQresultErrorField(res,ord('M'));
  354. MESSAGE_DETAIL:=PQresultErrorField(res,ord('D'));
  355. MESSAGE_HINT:=PQresultErrorField(res,ord('H'));
  356. STATEMENT_POSITION:=PQresultErrorField(res,ord('P'));
  357. sErr:=PQresultErrorMessage(res)+
  358. 'Severity: '+ SEVERITY +LineEnding+
  359. 'SQL State: '+ SQLSTATE +LineEnding+
  360. 'Primary Error: '+ MESSAGE_PRIMARY +LineEnding+
  361. 'Error Detail: '+ MESSAGE_DETAIL +LineEnding+
  362. 'Hint: '+ MESSAGE_HINT +LineEnding+
  363. 'Character: '+ STATEMENT_POSITION +LineEnding;
  364. if Self.Name = '' then CompName := Self.ClassName else CompName := Self.Name;
  365. E:=EPQDatabaseError.CreateFmt('%s : %s (PostgreSQL: %s)', [CompName, ErrMsg, sErr]);
  366. E.SEVERITY:=SEVERITY;
  367. E.SQLSTATE:=SQLSTATE;
  368. E.MESSAGE_PRIMARY:=MESSAGE_PRIMARY;
  369. E.MESSAGE_DETAIL:=MESSAGE_DETAIL;
  370. E.MESSAGE_HINT:=MESSAGE_HINT;
  371. E.STATEMENT_POSITION:=STATEMENT_POSITION;
  372. PQclear(res);
  373. res:=nil;
  374. if assigned(conn) then
  375. begin
  376. PQFinish(conn);
  377. //make connection available in pool
  378. for i:=0 to length(FConnectionPool)-1 do
  379. if FConnectionPool[i].FPGConn=conn then
  380. begin
  381. FConnectionPool[i].FPGConn:=nil;
  382. FConnectionPool[i].FTranActive:=false;
  383. break;
  384. end;
  385. end;
  386. raise E;
  387. end;
  388. end;
  389. function TPQConnection.TranslateFldType(res : PPGresult; Tuple : integer; out Size : integer) : TFieldType;
  390. const VARHDRSZ=sizeof(longint);
  391. var li : longint;
  392. begin
  393. Size := 0;
  394. case PQftype(res,Tuple) of
  395. Oid_varchar,Oid_bpchar,
  396. Oid_name : begin
  397. Result := ftstring;
  398. size := PQfsize(Res, Tuple);
  399. if (size = -1) then
  400. begin
  401. li := PQfmod(res,Tuple);
  402. if li = -1 then
  403. size := dsMaxStringSize
  404. else
  405. size := (li-VARHDRSZ) and $FFFF;
  406. end;
  407. if size > MaxSmallint then size := MaxSmallint;
  408. end;
  409. // Oid_text : Result := ftstring;
  410. Oid_text : Result := ftMemo;
  411. Oid_Bytea : Result := ftBlob;
  412. Oid_oid : Result := ftInteger;
  413. Oid_int8 : Result := ftLargeInt;
  414. Oid_int4 : Result := ftInteger;
  415. Oid_int2 : Result := ftSmallInt;
  416. Oid_Float4 : Result := ftFloat;
  417. Oid_Float8 : Result := ftFloat;
  418. Oid_TimeStamp,
  419. Oid_TimeStampTZ : Result := ftDateTime;
  420. Oid_Date : Result := ftDate;
  421. Oid_Interval,
  422. Oid_Time,
  423. Oid_TimeTZ : Result := ftTime;
  424. Oid_Bool : Result := ftBoolean;
  425. Oid_Numeric : begin
  426. Result := ftBCD;
  427. li := PQfmod(res,Tuple);
  428. if li = -1 then
  429. size := 4 // No information about the size available, use the maximum value
  430. else
  431. // The precision is the high 16 bits, the scale the
  432. // low 16 bits with an offset of sizeof(int32).
  433. begin
  434. size := (li-VARHDRSZ) and $FFFF;
  435. if (size > MaxBCDScale) or ((li shr 16)-size > MaxBCDPrecision-MaxBCDScale) then
  436. Result := ftFmtBCD;
  437. end;
  438. end;
  439. Oid_Money : Result := ftCurrency;
  440. Oid_char : begin
  441. Result := ftFixedChar;
  442. Size := 1;
  443. end;
  444. Oid_uuid : begin
  445. Result := ftGuid;
  446. Size := 38;
  447. end;
  448. Oid_MacAddr : begin
  449. Result := ftFixedChar;
  450. Size := 17;
  451. end;
  452. Oid_Inet : begin
  453. Result := ftString;
  454. Size := 39;
  455. end;
  456. Oid_Unknown : Result := ftUnknown;
  457. else
  458. Result := ftUnknown;
  459. end;
  460. end;
  461. Function TPQConnection.AllocateCursorHandle : TSQLCursor;
  462. begin
  463. result := TPQCursor.create;
  464. end;
  465. Procedure TPQConnection.DeAllocateCursorHandle(var cursor : TSQLCursor);
  466. begin
  467. FreeAndNil(cursor);
  468. end;
  469. Function TPQConnection.AllocateTransactionHandle : TSQLHandle;
  470. begin
  471. result := TPQTrans.create;
  472. end;
  473. procedure TPQConnection.PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams);
  474. const TypeStrings : array[TFieldType] of string =
  475. (
  476. 'Unknown', // ftUnknown
  477. 'text', // ftString
  478. 'smallint', // ftSmallint
  479. 'int', // ftInteger
  480. 'int', // ftWord
  481. 'bool', // ftBoolean
  482. 'float', // ftFloat
  483. 'money', // ftCurrency
  484. 'numeric', // ftBCD
  485. 'date', // ftDate
  486. 'time', // ftTime
  487. 'timestamp', // ftDateTime
  488. 'Unknown', // ftBytes
  489. 'Unknown', // ftVarBytes
  490. 'Unknown', // ftAutoInc
  491. 'bytea', // ftBlob
  492. 'text', // ftMemo
  493. 'bytea', // ftGraphic
  494. 'text', // ftFmtMemo
  495. 'Unknown', // ftParadoxOle
  496. 'Unknown', // ftDBaseOle
  497. 'Unknown', // ftTypedBinary
  498. 'Unknown', // ftCursor
  499. 'char', // ftFixedChar
  500. 'text', // ftWideString
  501. 'bigint', // ftLargeint
  502. 'Unknown', // ftADT
  503. 'Unknown', // ftArray
  504. 'Unknown', // ftReference
  505. 'Unknown', // ftDataSet
  506. 'Unknown', // ftOraBlob
  507. 'Unknown', // ftOraClob
  508. 'Unknown', // ftVariant
  509. 'Unknown', // ftInterface
  510. 'Unknown', // ftIDispatch
  511. 'uuid', // ftGuid
  512. 'Unknown', // ftTimeStamp
  513. 'numeric', // ftFMTBcd
  514. 'Unknown', // ftFixedWideChar
  515. 'Unknown' // ftWideMemo
  516. );
  517. var s : string;
  518. i : integer;
  519. begin
  520. with (cursor as TPQCursor) do
  521. begin
  522. FPrepared := False;
  523. // Prior to v8 there is no support for cursors and parameters.
  524. // So that's not supported.
  525. if FStatementType in [stInsert,stUpdate,stDelete, stSelect] then
  526. begin
  527. StmtName := 'prepst'+inttostr(FCursorCount);
  528. inc(FCursorCount);
  529. tr := TPQTrans(aTransaction.Handle);
  530. // Only available for pq 8.0, so don't use it...
  531. // Res := pqprepare(tr,'prepst'+name+nr,pchar(buf),params.Count,pchar(''));
  532. s := 'prepare '+StmtName+' ';
  533. if Assigned(AParams) and (AParams.Count > 0) then
  534. begin
  535. s := s + '(';
  536. for i := 0 to AParams.Count-1 do if TypeStrings[AParams[i].DataType] <> 'Unknown' then
  537. s := s + TypeStrings[AParams[i].DataType] + ','
  538. else
  539. begin
  540. if AParams[i].DataType = ftUnknown then
  541. DatabaseErrorFmt(SUnknownParamFieldType,[AParams[i].Name],self)
  542. else
  543. DatabaseErrorFmt(SUnsupportedParameter,[Fieldtypenames[AParams[i].DataType]],self);
  544. end;
  545. s[length(s)] := ')';
  546. buf := AParams.ParseSQL(buf,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psPostgreSQL);
  547. end;
  548. s := s + ' as ' + buf;
  549. res := PQexec(tr.PGConn,pchar(s));
  550. CheckResultError(res,nil,SErrPrepareFailed);
  551. // if statement is INSERT, UPDATE, DELETE with RETURNING clause, then
  552. // override the statement type derrived by parsing the query.
  553. if (FStatementType in [stInsert,stUpdate,stDelete]) and (pos('RETURNING', upcase(s)) > 0) then
  554. begin
  555. PQclear(res);
  556. res := PQdescribePrepared(tr.PGConn,pchar(StmtName));
  557. if (PQresultStatus(res) = PGRES_COMMAND_OK) and (PQnfields(res) > 0) then
  558. FStatementType := stSelect;
  559. end;
  560. FPrepared := True;
  561. end
  562. else
  563. Statement := AParams.ParseSQL(buf,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psPostgreSQL);
  564. end;
  565. end;
  566. procedure TPQConnection.UnPrepareStatement(cursor : TSQLCursor);
  567. begin
  568. with (cursor as TPQCursor) do
  569. begin
  570. PQclear(res);
  571. res:=nil;
  572. if FPrepared then
  573. begin
  574. if PQtransactionStatus(tr.PGConn) <> PQTRANS_INERROR then
  575. begin
  576. res := PQexec(tr.PGConn,pchar('deallocate '+StmtName));
  577. CheckResultError(res,nil,SErrUnPrepareFailed);
  578. PQclear(res);
  579. res:=nil;
  580. end;
  581. FPrepared := False;
  582. end;
  583. end;
  584. end;
  585. procedure TPQConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams);
  586. var ar : array of pchar;
  587. l,i : integer;
  588. s : string;
  589. lengths,formats : array of integer;
  590. ParamNames,
  591. ParamValues : array of string;
  592. cash: int64;
  593. begin
  594. with cursor as TPQCursor do
  595. begin
  596. PQclear(res);
  597. if FStatementType in [stInsert,stUpdate,stDelete,stSelect] then
  598. begin
  599. if Assigned(AParams) and (AParams.Count > 0) then
  600. begin
  601. l:=AParams.Count;
  602. setlength(ar,l);
  603. setlength(lengths,l);
  604. setlength(formats,l);
  605. for i := 0 to AParams.Count -1 do if not AParams[i].IsNull then
  606. begin
  607. case AParams[i].DataType of
  608. ftDateTime:
  609. s := FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', AParams[i].AsDateTime);
  610. ftDate:
  611. s := FormatDateTime('yyyy-mm-dd', AParams[i].AsDateTime);
  612. ftTime:
  613. s := FormatDateTime('hh:nn:ss.zzz', AParams[i].AsDateTime);
  614. ftFloat, ftBCD:
  615. Str(AParams[i].AsFloat, s);
  616. ftCurrency:
  617. begin
  618. cash:=NtoBE(round(AParams[i].AsCurrency*100));
  619. setlength(s, sizeof(cash));
  620. Move(cash, s[1], sizeof(cash));
  621. end;
  622. ftFmtBCD:
  623. s := BCDToStr(AParams[i].AsFMTBCD, FSQLFormatSettings);
  624. else
  625. s := AParams[i].AsString;
  626. end; {case}
  627. GetMem(ar[i],length(s)+1);
  628. StrMove(PChar(ar[i]),Pchar(s),Length(S)+1);
  629. lengths[i]:=Length(s);
  630. if (AParams[i].DataType in [ftBlob,ftMemo,ftGraphic,ftCurrency]) then
  631. Formats[i]:=1
  632. else
  633. Formats[i]:=0;
  634. end
  635. else
  636. FreeAndNil(ar[i]);
  637. res := PQexecPrepared(tr.PGConn,pchar(StmtName),AParams.Count,@Ar[0],@Lengths[0],@Formats[0],1);
  638. for i := 0 to AParams.Count -1 do
  639. FreeMem(ar[i]);
  640. end
  641. else
  642. res := PQexecPrepared(tr.PGConn,pchar(StmtName),0,nil,nil,nil,1);
  643. end
  644. else
  645. begin
  646. tr := TPQTrans(aTransaction.Handle);
  647. if Assigned(AParams) and (AParams.Count > 0) then
  648. begin
  649. setlength(ParamNames,AParams.Count);
  650. setlength(ParamValues,AParams.Count);
  651. for i := 0 to AParams.Count -1 do
  652. begin
  653. ParamNames[AParams.Count-i-1] := '$'+inttostr(AParams[i].index+1);
  654. ParamValues[AParams.Count-i-1] := GetAsSQLText(AParams[i]);
  655. end;
  656. s := stringsreplace(Statement,ParamNames,ParamValues,[rfReplaceAll]);
  657. end
  658. else
  659. s := Statement;
  660. res := PQexec(tr.PGConn,pchar(s));
  661. if (PQresultStatus(res) in [PGRES_COMMAND_OK]) then
  662. begin
  663. PQclear(res);
  664. res:=nil;
  665. end;
  666. end;
  667. if assigned(res) and not (PQresultStatus(res) in [PGRES_COMMAND_OK,PGRES_TUPLES_OK]) then
  668. begin
  669. // Don't perform the rollback, only make it possible to do a rollback.
  670. // The other databases also don't do this.
  671. //atransaction.Rollback;
  672. CheckResultError(res,nil,SErrExecuteFailed);
  673. end;
  674. FSelectable := assigned(res) and (PQresultStatus(res)=PGRES_TUPLES_OK);
  675. end;
  676. end;
  677. procedure TPQConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs);
  678. var
  679. i : integer;
  680. size : integer;
  681. fieldtype : tfieldtype;
  682. nFields : integer;
  683. begin
  684. with cursor as TPQCursor do
  685. begin
  686. nFields := PQnfields(Res);
  687. setlength(FieldBinding,nFields);
  688. for i := 0 to nFields-1 do
  689. begin
  690. fieldtype := TranslateFldType(Res, i,size);
  691. with TFieldDef.Create(FieldDefs, FieldDefs.MakeNameUnique(PQfname(Res, i)), fieldtype,size, False, (i + 1)) do
  692. FieldBinding[FieldNo-1] := i;
  693. end;
  694. CurTuple := -1;
  695. end;
  696. end;
  697. function TPQConnection.GetHandle: pointer;
  698. var
  699. i:integer;
  700. begin
  701. result:=nil;
  702. if not Connected then
  703. exit;
  704. //Get any handle that is (still) connected
  705. for i:=0 to length(FConnectionPool)-1 do
  706. if assigned(FConnectionPool[i].FPGConn) and (PQstatus(FConnectionPool[i].FPGConn)<>CONNECTION_BAD) then
  707. begin
  708. Result :=FConnectionPool[i].FPGConn;
  709. exit;
  710. end;
  711. //Nothing connected!! Reconnect
  712. if assigned(FConnectionPool[0].FPGConn) then
  713. PQreset(FConnectionPool[0].FPGConn)
  714. else
  715. FConnectionPool[0].FPGConn := PQconnectdb(pchar(FConnectString));
  716. if (PQstatus(FConnectionPool[0].FPGConn) = CONNECTION_BAD) then
  717. begin
  718. result := nil;
  719. PQFinish(FConnectionPool[0].FPGConn);
  720. FConnectionPool[0].FPGConn:=nil;
  721. FConnectionPool[0].FTranActive:=false;
  722. DatabaseError(SErrConnectionFailed + ' (PostgreSQL: ' + PQerrorMessage(FConnectionPool[0].FPGConn) + ')',self);
  723. end
  724. else
  725. if CharSet <> '' then
  726. PQsetClientEncoding(FConnectionPool[0].FPGConn, pchar(CharSet));
  727. result:=FConnectionPool[0].FPGConn;
  728. end;
  729. function TPQConnection.Fetch(cursor : TSQLCursor) : boolean;
  730. begin
  731. with cursor as TPQCursor do
  732. begin
  733. inc(CurTuple);
  734. Result := (PQntuples(res)>CurTuple);
  735. end;
  736. end;
  737. function TPQConnection.LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  738. const NBASE=10000;
  739. DAYS_PER_MONTH=30;
  740. type TNumericRecord = record
  741. Digits : SmallInt;
  742. Weight : SmallInt;
  743. Sign : SmallInt;
  744. Scale : Smallint;
  745. end;
  746. TIntervalRec = packed record
  747. time : int64;
  748. day : longint;
  749. month : longint;
  750. end;
  751. TMacAddrRec = packed record
  752. a, b, c, d, e, f: byte;
  753. end;
  754. TInetRec = packed record
  755. family : byte;
  756. bits : byte;
  757. is_cidr: byte;
  758. nb : byte;
  759. ipaddr : array[1..16] of byte;
  760. end;
  761. var
  762. x,i : integer;
  763. s : string;
  764. li : Longint;
  765. CurrBuff : pchar;
  766. dbl : pdouble;
  767. cur : currency;
  768. NumericRecord : ^TNumericRecord;
  769. guid : TGUID;
  770. bcd : TBCD;
  771. macaddr : ^TMacAddrRec;
  772. inet : ^TInetRec;
  773. begin
  774. Createblob := False;
  775. with cursor as TPQCursor do
  776. begin
  777. x := FieldBinding[FieldDef.FieldNo-1];
  778. // Joost, 5 jan 2006: I disabled the following, since it's useful for
  779. // debugging, but it also slows things down. In principle things can only go
  780. // wrong when FieldDefs is changed while the dataset is opened. A user just
  781. // shoudn't do that. ;) (The same is done in IBConnection)
  782. //if PQfname(Res, x) <> FieldDef.Name then
  783. // DatabaseErrorFmt(SFieldNotFound,[FieldDef.Name],self);
  784. if pqgetisnull(res,CurTuple,x)=1 then
  785. result := false
  786. else
  787. begin
  788. CurrBuff := pqgetvalue(res,CurTuple,x);
  789. result := true;
  790. case FieldDef.DataType of
  791. ftInteger, ftSmallint, ftLargeInt :
  792. case PQfsize(res, x) of // postgres returns big-endian numbers
  793. sizeof(int64) : pint64(buffer)^ := BEtoN(pint64(CurrBuff)^); // INT8
  794. sizeof(integer) : pinteger(buffer)^ := BEtoN(pinteger(CurrBuff)^); // INT4
  795. sizeof(smallint) : psmallint(buffer)^ := BEtoN(psmallint(CurrBuff)^); // INT2
  796. end; {case}
  797. ftFloat :
  798. case PQfsize(res, x) of // postgres returns big-endian numbers
  799. sizeof(int64) : // FLOAT8
  800. pint64(buffer)^ := BEtoN(pint64(CurrBuff)^);
  801. sizeof(integer) : // FLOAT4
  802. begin
  803. li := BEtoN(pinteger(CurrBuff)^);
  804. pdouble(buffer)^ := psingle(@li)^
  805. end;
  806. end; {case}
  807. ftString, ftFixedChar :
  808. begin
  809. case PQftype(res, x) of
  810. Oid_MacAddr:
  811. begin
  812. macaddr := Pointer(CurrBuff);
  813. li := FormatBuf(Buffer^, FieldDef.Size, '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x', 29,
  814. [macaddr^.a,macaddr^.b,macaddr^.c,macaddr^.d,macaddr^.e,macaddr^.f]);
  815. end;
  816. Oid_Inet:
  817. begin
  818. inet := Pointer(CurrBuff);
  819. if inet^.nb = 4 then
  820. li := FormatBuf(Buffer^, FieldDef.Size, '%d.%d.%d.%d', 11,
  821. [inet^.ipaddr[1],inet^.ipaddr[2],inet^.ipaddr[3],inet^.ipaddr[4]])
  822. else if inet^.nb = 16 then
  823. li := FormatBuf(Buffer^, FieldDef.Size, '%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x', 55,
  824. [inet^.ipaddr[1],inet^.ipaddr[2],inet^.ipaddr[3],inet^.ipaddr[4],inet^.ipaddr[5],inet^.ipaddr[6],inet^.ipaddr[7],inet^.ipaddr[8],inet^.ipaddr[9],inet^.ipaddr[10],inet^.ipaddr[11],inet^.ipaddr[12],inet^.ipaddr[13],inet^.ipaddr[14],inet^.ipaddr[15],inet^.ipaddr[16]])
  825. else
  826. li := 0;
  827. end
  828. else
  829. begin
  830. li := pqgetlength(res,curtuple,x);
  831. if li > FieldDef.Size then li := FieldDef.Size;
  832. Move(CurrBuff^, Buffer^, li);
  833. end;
  834. end;
  835. pchar(Buffer + li)^ := #0;
  836. end;
  837. ftBlob, ftMemo :
  838. CreateBlob := True;
  839. ftDate :
  840. begin
  841. dbl := pointer(buffer);
  842. dbl^ := BEtoN(plongint(CurrBuff)^) + 36526;
  843. end;
  844. ftDateTime, ftTime :
  845. begin
  846. dbl := pointer(buffer);
  847. if FIntegerDateTimes then
  848. dbl^ := BEtoN(pint64(CurrBuff)^) / 1000000
  849. else
  850. pint64(dbl)^ := BEtoN(pint64(CurrBuff)^);
  851. case PQftype(res, x) of
  852. Oid_Timestamp, Oid_TimestampTZ:
  853. dbl^ := dbl^ + 3.1558464E+009; // postgres counts seconds elapsed since 1-1-2000
  854. Oid_Interval:
  855. dbl^ := dbl^ + BEtoN(plongint(CurrBuff+ 8)^) * SecsPerDay
  856. + BEtoN(plongint(CurrBuff+12)^) * SecsPerDay * DAYS_PER_MONTH;
  857. end;
  858. dbl^ := dbl^ / SecsPerDay;
  859. // Now convert the mathematically-correct datetime to the
  860. // illogical windows/delphi/fpc TDateTime:
  861. if (dbl^ <= 0) and (frac(dbl^) < 0) then
  862. dbl^ := trunc(dbl^)-2-frac(dbl^);
  863. end;
  864. ftBCD, ftFmtBCD:
  865. begin
  866. NumericRecord := pointer(CurrBuff);
  867. NumericRecord^.Digits := BEtoN(NumericRecord^.Digits);
  868. NumericRecord^.Weight := BEtoN(NumericRecord^.Weight);
  869. NumericRecord^.Sign := BEtoN(NumericRecord^.Sign);
  870. NumericRecord^.Scale := BEtoN(NumericRecord^.Scale);
  871. inc(pointer(currbuff),sizeof(TNumericRecord));
  872. if (NumericRecord^.Digits = 0) and (NumericRecord^.Scale = 0) then // = NaN, which is not supported by Currency-type, so we return NULL
  873. result := false
  874. else if FieldDef.DataType = ftBCD then
  875. begin
  876. cur := 0;
  877. for i := 0 to NumericRecord^.Digits-1 do
  878. begin
  879. cur := cur + beton(pword(CurrBuff)^) * intpower(NBASE, NumericRecord^.weight-i);
  880. inc(pointer(CurrBuff),2);
  881. end;
  882. if NumericRecord^.Sign <> 0 then cur := -cur;
  883. Move(Cur, Buffer^, sizeof(currency));
  884. end
  885. else //ftFmtBCD
  886. begin
  887. bcd := 0;
  888. for i := 0 to NumericRecord^.Digits-1 do
  889. begin
  890. BCDAdd(bcd, beton(pword(CurrBuff)^) * intpower(NBASE, NumericRecord^.weight-i), bcd);
  891. inc(pointer(CurrBuff),2);
  892. end;
  893. if NumericRecord^.Sign <> 0 then BCDNegate(bcd);
  894. Move(bcd, Buffer^, sizeof(bcd));
  895. end;
  896. end;
  897. ftCurrency :
  898. begin
  899. dbl := pointer(buffer);
  900. dbl^ := BEtoN(PInt64(CurrBuff)^) / 100;
  901. end;
  902. ftBoolean:
  903. pchar(buffer)[0] := CurrBuff[0];
  904. ftGuid:
  905. begin
  906. Move(CurrBuff^, guid, sizeof(guid));
  907. guid.D1:=BEtoN(guid.D1);
  908. guid.D2:=BEtoN(guid.D2);
  909. guid.D3:=BEtoN(guid.D3);
  910. s:=GUIDToString(guid);
  911. StrPLCopy(PChar(Buffer), s, FieldDef.Size);
  912. end
  913. else
  914. result := false;
  915. end;
  916. end;
  917. end;
  918. end;
  919. procedure TPQConnection.UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string);
  920. var qry : TSQLQuery;
  921. begin
  922. if not assigned(Transaction) then
  923. DatabaseError(SErrConnTransactionnSet);
  924. qry := tsqlquery.Create(nil);
  925. qry.transaction := Transaction;
  926. qry.database := Self;
  927. with qry do
  928. begin
  929. ReadOnly := True;
  930. sql.clear;
  931. sql.add('select '+
  932. 'ic.relname as indexname, '+
  933. 'tc.relname as tablename, '+
  934. 'ia.attname, '+
  935. 'i.indisprimary, '+
  936. 'i.indisunique '+
  937. 'from '+
  938. 'pg_attribute ta, '+
  939. 'pg_attribute ia, '+
  940. 'pg_class tc, '+
  941. 'pg_class ic, '+
  942. 'pg_index i '+
  943. 'where '+
  944. '(i.indrelid = tc.oid) and '+
  945. '(ta.attrelid = tc.oid) and '+
  946. '(ia.attrelid = i.indexrelid) and '+
  947. '(ic.oid = i.indexrelid) and '+
  948. '(ta.attnum = i.indkey[ia.attnum-1]) and '+
  949. '(upper(tc.relname)=''' + UpperCase(TableName) +''') '+
  950. 'order by '+
  951. 'ic.relname;');
  952. open;
  953. end;
  954. while not qry.eof do with IndexDefs.AddIndexDef do
  955. begin
  956. Name := trim(qry.fields[0].asstring);
  957. Fields := trim(qry.Fields[2].asstring);
  958. If qry.fields[3].asboolean then options := options + [ixPrimary];
  959. If qry.fields[4].asboolean then options := options + [ixUnique];
  960. qry.next;
  961. while (name = qry.fields[0].asstring) and (not qry.eof) do
  962. begin
  963. Fields := Fields + ';' + trim(qry.Fields[2].asstring);
  964. qry.next;
  965. end;
  966. end;
  967. qry.close;
  968. qry.free;
  969. end;
  970. function TPQConnection.GetSchemaInfoSQL(SchemaType: TSchemaType;
  971. SchemaObjectName, SchemaPattern: string): string;
  972. var s : string;
  973. begin
  974. // select * from information_schema.tables with
  975. // where table_schema [not] in ('pg_catalog','information_schema') may be better.
  976. // But the following should work:
  977. case SchemaType of
  978. stTables : s := 'select '+
  979. 'relfilenode as recno, '+
  980. 'current_database() as catalog_name, '+
  981. 'nspname as schema_name, '+
  982. 'relname as table_name, '+
  983. '0 as table_type '+
  984. 'from pg_class c '+
  985. 'left join pg_namespace n on c.relnamespace=n.oid '+
  986. 'where (relkind=''r'') and not (nspname in (''pg_catalog'',''information_schema''))' +
  987. 'order by relname';
  988. stSysTables : s := 'select '+
  989. 'relfilenode as recno, '+
  990. 'current_database() as catalog_name, '+
  991. 'nspname as schema_name, '+
  992. 'relname as table_name, '+
  993. '0 as table_type '+
  994. 'from pg_class c '+
  995. 'left join pg_namespace n on c.relnamespace=n.oid '+
  996. 'where (relkind=''r'') and nspname in ((''pg_catalog'',''information_schema'')) ' + // only system tables
  997. 'order by relname';
  998. stColumns : s := 'select '+
  999. 'a.attnum as recno, '+
  1000. 'current_database() as catalog_name, '+
  1001. 'nspname as schema_name, '+
  1002. 'c.relname as table_name, '+
  1003. 'a.attname as column_name, '+
  1004. '0 as column_position, '+
  1005. '0 as column_type, '+
  1006. '0 as column_datatype, '+
  1007. ''''' as column_typename, '+
  1008. '0 as column_subtype, '+
  1009. '0 as column_precision, '+
  1010. '0 as column_scale, '+
  1011. 'a.atttypmod as column_length, '+
  1012. 'not a.attnotnull as column_nullable '+
  1013. 'from pg_class c '+
  1014. 'join pg_attribute a on c.oid=a.attrelid '+
  1015. 'left join pg_namespace n on c.relnamespace=n.oid '+
  1016. // This can lead to problems when case-sensitive tablenames are used.
  1017. 'where (a.attnum>0) and (not a.attisdropped) and (upper(c.relname)=''' + Uppercase(SchemaObjectName) + ''') '+
  1018. 'order by a.attname';
  1019. else
  1020. DatabaseError(SMetadataUnavailable)
  1021. end; {case}
  1022. result := s;
  1023. end;
  1024. procedure TPQConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef;
  1025. ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  1026. var
  1027. x : integer;
  1028. li : Longint;
  1029. begin
  1030. with cursor as TPQCursor do
  1031. begin
  1032. x := FieldBinding[FieldDef.FieldNo-1];
  1033. li := pqgetlength(res,curtuple,x);
  1034. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer,li);
  1035. Move(pqgetvalue(res,CurTuple,x)^, ABlobBuf^.BlobBuffer^.Buffer^, li);
  1036. ABlobBuf^.BlobBuffer^.Size := li;
  1037. end;
  1038. end;
  1039. function TPQConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  1040. begin
  1041. if assigned(cursor) and assigned((cursor as TPQCursor).res) then
  1042. Result := StrToIntDef(PQcmdTuples((cursor as TPQCursor).res),-1)
  1043. else
  1044. Result := -1;
  1045. end;
  1046. function TPQConnection.GetConnectionInfo(InfoType: TConnInfoType): string;
  1047. begin
  1048. Result:='';
  1049. try
  1050. {$IFDEF LinkDynamically}
  1051. InitialisePostgres3;
  1052. {$ENDIF}
  1053. case InfoType of
  1054. citServerType:
  1055. Result:=TPQConnectionDef.TypeName;
  1056. citServerVersion,
  1057. citServerVersionString:
  1058. if Connected then
  1059. Result:=format('%6.6d', [PQserverVersion(GetHandle)]);
  1060. citClientName:
  1061. Result:=TPQConnectionDef.LoadedLibraryName;
  1062. else
  1063. Result:=inherited GetConnectionInfo(InfoType);
  1064. end;
  1065. finally
  1066. {$IFDEF LinkDynamically}
  1067. ReleasePostgres3;
  1068. {$ENDIF}
  1069. end;
  1070. end;
  1071. { TPQConnectionDef }
  1072. class function TPQConnectionDef.TypeName: String;
  1073. begin
  1074. Result:='PostgreSQL';
  1075. end;
  1076. class function TPQConnectionDef.ConnectionClass: TSQLConnectionClass;
  1077. begin
  1078. Result:=TPQConnection;
  1079. end;
  1080. class function TPQConnectionDef.Description: String;
  1081. begin
  1082. Result:='Connect to a PostgreSQL database directly via the client library';
  1083. end;
  1084. class function TPQConnectionDef.DefaultLibraryName: String;
  1085. begin
  1086. {$IfDef LinkDynamically}
  1087. Result:=pqlib;
  1088. {$else}
  1089. Result:='';
  1090. {$endif}
  1091. end;
  1092. class function TPQConnectionDef.LoadFunction: TLibraryLoadFunction;
  1093. begin
  1094. {$IfDef LinkDynamically}
  1095. Result:=@InitialisePostgres3;
  1096. {$else}
  1097. Result:=Nil;
  1098. {$endif}
  1099. end;
  1100. class function TPQConnectionDef.UnLoadFunction: TLibraryUnLoadFunction;
  1101. begin
  1102. {$IfDef LinkDynamically}
  1103. Result:=@ReleasePostgres3;
  1104. {$else}
  1105. Result:=Nil;
  1106. {$endif}
  1107. end;
  1108. class function TPQConnectionDef.LoadedLibraryName: string;
  1109. begin
  1110. {$IfDef LinkDynamically}
  1111. Result:=Postgres3LoadedLibrary;
  1112. {$else}
  1113. Result:='';
  1114. {$endif}
  1115. end;
  1116. initialization
  1117. RegisterConnection(TPQConnectionDef);
  1118. finalization
  1119. UnRegisterConnection(TPQConnectionDef);
  1120. end.