pqconnection.pp 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898
  1. unit PQConnection;
  2. {
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2022 by Michael van Canney and other members of the
  5. Free Pascal development team
  6. Postgres database connection component
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. {$mode objfpc}{$H+}
  14. {$Define LinkDynamically}
  15. { $define PQDEBUG}
  16. interface
  17. uses
  18. Classes, SysUtils, sqldb, db, dbconst,bufdataset,
  19. {$IfDef LinkDynamically}
  20. postgres3dyn;
  21. {$Else}
  22. postgres3;
  23. {$EndIf}
  24. type
  25. TPQCursor = Class;
  26. TPQConnection = Class;
  27. { TPGHandle }
  28. TCheckResultAction = (craClose,craClear);
  29. TCheckResultActions = set of TCheckResultAction;
  30. TPGHandle = Class(TSQLHandle)
  31. strict private
  32. class var _HID : {$IFDEF CPU64} Int64 {$ELSE} Integer {$ENDIF};
  33. private
  34. FHandleID : {$IFDEF CPU64} Int64 {$ELSE} Integer {$ENDIF};
  35. FConnected: Boolean;
  36. FCOnnection: TPQConnection;
  37. FDBName : String;
  38. FActive : Boolean;
  39. FUsed: Boolean;
  40. function GetConnected: Boolean;
  41. protected
  42. FNativeConn: PPGConn;
  43. FCursorList : TThreadList;
  44. Procedure RegisterCursor(Cursor : TPQCursor);
  45. Procedure UnRegisterCursor(Cursor : TPQCursor);
  46. procedure UnprepareStatement(Cursor: TPQCursor; Force: Boolean);
  47. Public
  48. Constructor Create(aConnection : TPQConnection;aDBName :string);
  49. Destructor Destroy; override;
  50. Procedure Connect;
  51. Procedure Disconnect;
  52. Procedure StartTransaction;
  53. Procedure RollBack;
  54. Procedure Commit;
  55. Procedure Reset;
  56. Function CheckConnectionStatus(doRaise : Boolean = True) : Boolean;
  57. Function DescribePrepared(StmtName : String): PPGresult;
  58. Function Exec(aSQL : String; aClearResult : Boolean; aError : String = '') : PPGresult;
  59. function ExecPrepared(stmtName: AnsiString; nParams:longint; paramValues:PPchar; paramLengths:Plongint;paramFormats:Plongint; aClearResult : Boolean) : PPGresult;
  60. procedure CheckResultError(var res: PPGresult; Actions : TCheckResultActions; const ErrMsg: string);
  61. Property Connection : TPQConnection Read FCOnnection;
  62. Property NativeConn : PPGConn Read FNativeConn;
  63. Property Active : Boolean Read Factive;
  64. Property Used : Boolean Read FUsed Write FUsed;
  65. Property Connected : Boolean Read GetConnected;
  66. end;
  67. // TField and TFieldDef only support a limited amount of fields.
  68. // TFieldBinding and TExtendedFieldType can be used to map PQ types
  69. // on standard fields and retain mapping info.
  70. TExtendedFieldType = (eftNone,eftEnum,eftCitext);
  71. TFieldBinding = record
  72. FieldDef : TSQLDBFieldDef; // FieldDef this is associated with
  73. Index : Integer; // Tuple index
  74. TypeOID : oid; // Filled with type OID if it is not standard.
  75. TypeName : String; // Filled with type name by GetExtendedFieldInfo
  76. ExtendedFieldType: TExtendedFieldType; //
  77. end;
  78. PFieldBinding = ^TFieldBinding;
  79. TFieldBindings = Array of TFieldBinding;
  80. { TPQTransactionHandle }
  81. TPQTransactionHandle = Class(TSQLHandle)
  82. strict private
  83. FHandle: TPGHandle;
  84. procedure SetHandle(AValue: TPGHandle);
  85. Public
  86. Property Handle : TPGHandle Read FHandle Write SetHandle;
  87. end;
  88. { TPQCursor }
  89. TPQCursor = Class(TSQLCursor)
  90. private
  91. procedure SetHandle(AValue: TPGhandle);
  92. protected
  93. Statement : string;
  94. StmtName : string;
  95. Fhandle : TPGHandle;
  96. res : PPGresult;
  97. CurTuple : integer;
  98. FieldBinding : TFieldBindings;
  99. Function GetFieldBinding(F : TFieldDef): PFieldBinding;
  100. Public
  101. Destructor Destroy; override;
  102. Property Handle : TPGhandle Read FHandle Write SetHandle;
  103. end;
  104. { EPQDatabaseError }
  105. EPQDatabaseError = class(EDatabaseError)
  106. public
  107. SEVERITY:string;
  108. SQLSTATE: string;
  109. MESSAGE_PRIMARY:string;
  110. MESSAGE_DETAIL:string;
  111. MESSAGE_HINT:string;
  112. STATEMENT_POSITION:string;
  113. SCHEMA_NAME: string;
  114. TABLE_NAME: string;
  115. COLUMN_NAME: string;
  116. DATATYPE_NAME: string;
  117. CONSTRAINT_NAME: string;
  118. end;
  119. { TPQConnection }
  120. TPQConnection = class (TSQLConnection)
  121. private
  122. FHandlePool : TThreadList;
  123. FCursorCount : dword;
  124. FIntegerDateTimes : boolean;
  125. FVerboseErrors : Boolean;
  126. protected
  127. // Protected so they can be used by descendents.
  128. function GetConnectionString(const aDBName : String) : string;
  129. function TranslateFldType(res : PPGresult; Tuple : integer; out Size : integer; Out ATypeOID : oid) : TFieldType;
  130. procedure ExecuteDirectPG(const Query : String);
  131. Procedure GetExtendedFieldInfo(cursor: TPQCursor; Bindings : TFieldBindings);
  132. procedure ApplyFieldUpdate(C : TSQLCursor; P: TSQLDBParam; F: TField; UseOldValue: Boolean); override;
  133. Function ErrorOnUnknownType : Boolean;
  134. // Add connection to pool.
  135. procedure AddHandle(T: TPGHandle);
  136. {$IFNDEF VER3_2}
  137. function PortParamName: string; override;
  138. {$endif}
  139. procedure DoInternalConnect; override;
  140. procedure DoInternalDisconnect; override;
  141. function GetHandle : pointer; override;
  142. Function AllocateCursorHandle : TSQLCursor; override;
  143. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  144. Function AllocateTransactionHandle : TSQLHandle; override;
  145. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
  146. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); override;
  147. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  148. function Fetch(cursor : TSQLCursor) : boolean; override;
  149. procedure UnPrepareStatement(cursor : TSQLCursor); override;
  150. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  151. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  152. function RollBack(trans : TSQLHandle) : boolean; override;
  153. function Commit(trans : TSQLHandle) : boolean; override;
  154. procedure CommitRetaining(trans : TSQLHandle); override;
  155. function StartImplicitTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  156. function StartDBTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  157. procedure RollBackRetaining(trans : TSQLHandle); override;
  158. procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override;
  159. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor;ATransaction : TSQLTransaction); override;
  160. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  161. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  162. function GetNextValueSQL(const SequenceName: string; IncrementBy: Integer): string; override;
  163. public
  164. constructor Create(AOwner : TComponent); override;
  165. destructor Destroy; override;
  166. function GetConnectionInfo(InfoType:TConnInfoType): string; override;
  167. procedure CreateDB; override;
  168. procedure DropDB; override;
  169. published
  170. property DatabaseName;
  171. property KeepConnection;
  172. property LoginPrompt;
  173. property Params;
  174. property OnLogin;
  175. Property VerboseErrors : Boolean Read FVerboseErrors Write FVerboseErrors default true;
  176. end;
  177. { TPQConnectionDef }
  178. TPQConnectionDef = Class(TConnectionDef)
  179. Class Function TypeName : String; override;
  180. Class Function ConnectionClass : TSQLConnectionClass; override;
  181. Class Function Description : String; override;
  182. Class Function DefaultLibraryName : String; override;
  183. Class Function LoadFunction : TLibraryLoadFunction; override;
  184. Class Function UnLoadFunction : TLibraryUnLoadFunction; override;
  185. Class Function LoadedLibraryName: string; override;
  186. end;
  187. implementation
  188. uses math, strutils, FmtBCD;
  189. ResourceString
  190. SErrRollbackFailed = 'Rollback transaction failed';
  191. SErrCommitFailed = 'Commit transaction failed';
  192. SErrConnectionFailed = 'Connection to database failed';
  193. SErrTransactionFailed = 'Start of transacion failed';
  194. SErrExecuteFailed = 'Execution of query failed';
  195. SErrPrepareFailed = 'Preparation of query failed.';
  196. SErrUnPrepareFailed = 'Unpreparation of query failed.';
  197. const Oid_Bool = 16;
  198. Oid_Bytea = 17;
  199. Oid_char = 18;
  200. Oid_Text = 25;
  201. Oid_Oid = 26;
  202. Oid_Name = 19;
  203. Oid_Int8 = 20;
  204. Oid_int2 = 21;
  205. Oid_Int4 = 23;
  206. Oid_JSON = 114;
  207. Oid_Float4 = 700;
  208. Oid_Money = 790;
  209. Oid_Float8 = 701;
  210. Oid_Unknown = 705;
  211. Oid_MacAddr = 829;
  212. Oid_Inet = 869;
  213. Oid_bpchar = 1042;
  214. Oid_varchar = 1043;
  215. oid_date = 1082;
  216. oid_time = 1083;
  217. Oid_timeTZ = 1266;
  218. Oid_timestamp = 1114;
  219. Oid_timestampTZ = 1184;
  220. Oid_interval = 1186;
  221. oid_numeric = 1700;
  222. Oid_uuid = 2950;
  223. { TPQTransactionHandle }
  224. procedure TPQTransactionHandle.SetHandle(AValue: TPGHandle);
  225. begin
  226. if FHandle=AValue then Exit;
  227. FHandle:=AValue;
  228. end;
  229. { TPGHandle }
  230. constructor TPGHandle.Create(aConnection: TPQConnection; aDBName: string);
  231. begin
  232. FDBName:=aDBName;
  233. FConnection:=aConnection;
  234. FCursorList:=TThreadList.Create;
  235. FCursorList.Duplicates:=dupIgnore;
  236. {$IFDEF CPU64}
  237. FHandleID:=InterlockedIncrement64(_HID);
  238. {$ElSE}
  239. FHandleID:=InterlockedIncrement(_HID);
  240. {$ENDIF}
  241. {$IFDEF PQDEBUG}
  242. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] allocating handle ');
  243. {$ENDIF}
  244. end;
  245. destructor TPGHandle.Destroy;
  246. Var
  247. L : TList;
  248. I : integer;
  249. begin
  250. {$IFDEF PQDEBUG}
  251. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] Destroying handle ');
  252. {$ENDIF}
  253. L:=FCursorList.LockList;
  254. try
  255. For I:=0 to L.Count-1 do
  256. TPQCursor(L[i]).Handle:=Nil;
  257. finally
  258. FCursorList.UnlockList;
  259. end;
  260. FreeAndNil(FCursorList);
  261. inherited Destroy;
  262. end;
  263. function TPGHandle.GetConnected: Boolean;
  264. begin
  265. Result:=FNativeConn<>Nil;
  266. end;
  267. procedure TPGHandle.RegisterCursor(Cursor: TPQCursor);
  268. begin
  269. if Cursor.handle=Self then
  270. begin
  271. {$IFDEF PQDEBUG}
  272. Writeln('>>> ',FHandleID, ' [',TThread.CurrentThread.ThreadID, '] cursor ',PtrInt(Cursor),' already registered');
  273. {$ENDIF}
  274. exit;
  275. end;
  276. {$IFDEF PQDEBUG}
  277. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] registering cursor ',PtrInt(Cursor));
  278. {$ENDIF}
  279. FCursorList.Add(Cursor);
  280. Cursor.Handle:=Self;
  281. end;
  282. procedure TPGHandle.UnRegisterCursor(Cursor: TPQCursor);
  283. Var
  284. L : TList;
  285. begin
  286. {$IFDEF PQDEBUG}
  287. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] unregistering cursor ',PtrInt(Cursor));
  288. {$ENDIF}
  289. Cursor.Handle:=Nil;
  290. FCursorList.Remove(Cursor);
  291. L:=FCursorList.LockList;
  292. try
  293. Used:=L.Count>0;
  294. {$IFDEF PQDEBUG}
  295. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] unregistering cursor ',PtrInt(Cursor),'. Handle still used: ',Used);
  296. {$ENDIF}
  297. finally
  298. FCursorList.UnlockList;
  299. end;
  300. end;
  301. procedure TPGHandle.UnprepareStatement(Cursor: TPQCursor; Force : Boolean);
  302. var
  303. SQL : String;
  304. begin
  305. if Cursor.handle<>Self then
  306. DatabaseError('Internal error: unpreparing in different transaction!');
  307. if Assigned(Cursor.res) then
  308. PQclear(Cursor.res);
  309. Cursor.res:=nil;
  310. SQL:='deallocate '+Cursor.StmtName;
  311. Cursor.StmtName:='';
  312. if Force then
  313. Cursor.FPrepared := False;
  314. if not Cursor.FPrepared then
  315. Exit;
  316. if (PQtransactionStatus(FNativeConn) <> PQTRANS_INERROR) then
  317. begin
  318. Exec(SQL,True,SErrUnPrepareFailed);
  319. Cursor.FPrepared := False;
  320. end;
  321. UnregisterCursor(Cursor);
  322. end;
  323. procedure TPGHandle.Connect;
  324. var
  325. S : AnsiString;
  326. begin
  327. S:=Connection.GetConnectionString(FDBName);
  328. FNativeConn:=PQconnectdb(PAnsiChar(S));
  329. CheckConnectionStatus;
  330. FConnected:=True;
  331. S:=Connection.CharSet;
  332. if (S<>'') then
  333. PQsetClientEncoding(FNativeConn,PAnsiChar(S));
  334. end;
  335. { TPQCursor }
  336. destructor TPQCursor.Destroy;
  337. begin
  338. if Assigned(Handle) then
  339. Handle.UnRegisterCursor(Self);
  340. inherited Destroy;
  341. end;
  342. procedure TPQCursor.SetHandle(AValue: TPGhandle);
  343. begin
  344. if FHandle=AValue then Exit;
  345. if (FHandle<>Nil) and (aValue<>Nil) then
  346. begin
  347. {$IFDEF PQDEBUG}
  348. writeln('>>> ',ptrint(Self),' [',TThread.CurrentThread.ThreadID, '] Setting handle while handle still valid');
  349. {$endif}
  350. end;
  351. FHandle:=AValue;
  352. end;
  353. function TPQCursor.GetFieldBinding(F: TFieldDef): PFieldBinding;
  354. Var
  355. I : Integer;
  356. begin
  357. Result:=Nil;
  358. if (F=Nil) then exit;
  359. // This is an optimization: it is so for 99% of cases (FieldNo-1=array index)
  360. if F is TSQLDBFieldDef then
  361. Result:=PFieldBinding(TSQLDBFieldDef(F).SQLDBData)
  362. else If (FieldBinding[F.FieldNo-1].FieldDef=F) then
  363. Result:=@FieldBinding[F.FieldNo-1]
  364. else
  365. begin
  366. I:=Length(FieldBinding)-1;
  367. While (I>=0) and (FieldBinding[i].FieldDef<>F) do
  368. Dec(I);
  369. if I>=0 then
  370. Result:=@FieldBinding[i];
  371. end;
  372. end;
  373. { TPQConnection }
  374. constructor TPQConnection.Create(AOwner : TComponent);
  375. begin
  376. inherited;
  377. FConnOptions := FConnOptions + [sqSupportParams, sqSupportEmptyDatabaseName, sqEscapeRepeat, sqEscapeSlash, sqImplicitTransaction,sqSupportReturning,sqSequences];
  378. FieldNameQuoteChars:=DoubleQuotes;
  379. VerboseErrors:=True;
  380. FHandlePool:=TThreadlist.Create;
  381. end;
  382. destructor TPQConnection.Destroy;
  383. begin
  384. // We must disconnect here. If it is done in inherited, then connection pool is gone.
  385. Connected:=False;
  386. FreeAndNil(FHandlePool);
  387. inherited destroy;
  388. end;
  389. procedure TPQConnection.CreateDB;
  390. begin
  391. ExecuteDirectPG('CREATE DATABASE ' +DatabaseName);
  392. end;
  393. procedure TPQConnection.DropDB;
  394. begin
  395. ExecuteDirectPG('DROP DATABASE ' +DatabaseName);
  396. end;
  397. procedure TPQConnection.ExecuteDirectPG(const Query: String);
  398. var
  399. AHandle : TPGHandle;
  400. begin
  401. CheckDisConnected;
  402. {$IfDef LinkDynamically}
  403. InitialisePostgres3;
  404. {$EndIf}
  405. aHandle:=TPGHandle.Create(Self,'template1');
  406. try
  407. aHandle.Connect;
  408. aHandle.Exec(Query,True,'Error executing query');
  409. finally
  410. aHandle.Free;
  411. end;
  412. {$IfDef LinkDynamically}
  413. ReleasePostgres3;
  414. {$EndIf}
  415. end;
  416. procedure TPQConnection.GetExtendedFieldInfo(cursor: TPQCursor;
  417. Bindings: TFieldBindings);
  418. Var
  419. tt,tc,Tn,S : String;
  420. I,J : Integer;
  421. Res : PPGResult;
  422. toid : oid;
  423. begin
  424. s:='';
  425. For I:=0 to Length(Bindings)-1 do
  426. if (Bindings[i].TypeOID>0) then
  427. begin
  428. if (S<>'') then
  429. S:=S+', ';
  430. S:=S+IntToStr(Bindings[i].TypeOID);
  431. end;
  432. if (S='') then
  433. exit;
  434. Res:=Cursor.Handle.Exec(S,False,'Error getting typeinfo');
  435. try
  436. For I:=0 to PQntuples(Res)-1 do
  437. begin
  438. toid:=Strtoint(pqgetvalue(Res,i,0));
  439. tn:=pqgetvalue(Res,i,1);
  440. tt:=pqgetvalue(Res,i,2);
  441. tc:=pqgetvalue(Res,i,3);
  442. J:=length(Bindings)-1;
  443. while (J>= 0) do
  444. begin
  445. if (Bindings[j].TypeOID=toid) then
  446. Case tt of
  447. 'e':
  448. Bindings[j].ExtendedFieldType:=eftEnum;
  449. 'citext':
  450. Bindings[j].ExtendedFieldType:=eftCitext;
  451. end;
  452. Dec(J);
  453. end;
  454. end;
  455. finally
  456. PQClear(Res);
  457. end;
  458. end;
  459. procedure TPQConnection.ApplyFieldUpdate(C : TSQLCursor; P: TSQLDBParam; F: TField;
  460. UseOldValue: Boolean);
  461. begin
  462. inherited ApplyFieldUpdate(C,P, F, UseOldValue);
  463. if (C is TPQCursor) then
  464. P.SQLDBData:=TPQCursor(C).GetFieldBinding(F.FieldDef);
  465. end;
  466. function TPQConnection.ErrorOnUnknownType: Boolean;
  467. begin
  468. Result:=False;
  469. end;
  470. procedure TPQConnection.AddHandle(T: TPGHandle);
  471. begin
  472. FHandlePool.Add(T);
  473. end;
  474. function TPQConnection.GetTransactionHandle(trans : TSQLHandle): pointer;
  475. begin
  476. if (trans is TPQTransactionHandle) then
  477. Result:=Trans
  478. else
  479. DatabaseErrorFmt('Expected %s, got %s',[TPQTransactionHandle.ClassName,Trans.ClassName]);
  480. end;
  481. function TPQConnection.RollBack(trans : TSQLHandle) : boolean;
  482. var
  483. tr : TPGHandle;
  484. begin
  485. tr := (trans as TPQTransactionHandle).Handle as TPGHandle;
  486. TR.RollBack;
  487. result := true;
  488. end;
  489. function TPQConnection.Commit(trans : TSQLHandle) : boolean;
  490. var
  491. tr : TPGHandle;
  492. begin
  493. tr := (trans as TPQTransactionHandle).Handle;
  494. tr.Commit;
  495. Result:=True;
  496. end;
  497. procedure TPQConnection.RollBackRetaining(trans : TSQLHandle);
  498. var
  499. tr : TPGHandle;
  500. begin
  501. tr := (trans as TPQTransactionHandle).Handle;
  502. TR.RollBack;
  503. TR.StartTransaction;
  504. end;
  505. procedure TPQConnection.CommitRetaining(trans : TSQLHandle);
  506. var
  507. tr : TPGHandle;
  508. begin
  509. tr := (trans as TPQTransactionHandle).Handle as TPGHandle;
  510. TR.Commit;
  511. TR.StartTransaction;
  512. end;
  513. function TPQConnection.StartImplicitTransaction(trans : TSQLHandle; AParams : string) : boolean;
  514. var
  515. i : Integer;
  516. T : TPGHandle;
  517. L : TList;
  518. begin
  519. //find an unused connection in the pool
  520. i:=0;
  521. T:=Nil;
  522. L:=FHandlePool.LockList;
  523. try
  524. while (i<L.Count) do
  525. begin
  526. T:=TPGHandle(L[i]);
  527. if Not (T.Connected and T.Used) then
  528. break
  529. else
  530. T:=Nil;
  531. i:=i+1;
  532. end;
  533. // set to active now, so when we exit critical section,
  534. // it will be marked active and will not be found.
  535. if Assigned(T) then
  536. T.Used:=true;
  537. finally
  538. FHandlePool.UnLockList;
  539. end;
  540. if (T=Nil) then
  541. begin
  542. T:=TPGHandle.Create(Self,DatabaseName);
  543. T.Used:=True;
  544. AddHandle(T);
  545. end
  546. else
  547. begin
  548. {$IFDEF PQDEBUG}
  549. Writeln('>>> ',T.FHandleID,' [',TThread.CurrentThread.ThreadID, '] Reusing connection ');
  550. {$ENDIF}
  551. end;
  552. if (Not T.Connected) then
  553. T.Connect;
  554. (Trans as TPQTransactionHandle).handle:=T;
  555. Result := true;
  556. end;
  557. function TPQConnection.StartDBTransaction(trans: TSQLHandle; AParams: string
  558. ): boolean;
  559. Var
  560. tr : TPQTransactionHandle;
  561. begin
  562. Result:=StartImplicitTransaction(trans, AParams);
  563. if Result then
  564. begin
  565. tr:= trans as TPQTransactionHandle;
  566. tr.Handle.StartTransaction;
  567. end;
  568. end;
  569. procedure TPQConnection.DoInternalConnect;
  570. var
  571. T : TPGHandle;
  572. begin
  573. {$IfDef LinkDynamically}
  574. InitialisePostgres3;
  575. {$EndIf}
  576. inherited DoInternalConnect;
  577. T:=TPGHandle.Create(Self,DatabaseName);
  578. try
  579. T.Connect;
  580. T.Used:=false;
  581. // This only works for pg>=8.0, so timestamps won't work with earlier versions of pg which are compiled with integer_datetimes on
  582. if PQparameterStatus<>nil then
  583. FIntegerDateTimes := PQparameterStatus(T.NativeConn,'integer_datetimes') = 'on';
  584. except
  585. T.Free;
  586. DoInternalDisconnect;
  587. raise;
  588. end;
  589. AddHandle(T);
  590. end;
  591. procedure TPQConnection.DoInternalDisconnect;
  592. var
  593. i:integer;
  594. L : TList;
  595. T : TPGHandle;
  596. begin
  597. Inherited;
  598. L:=FHandlePool.LockList;
  599. try
  600. for i:=0 to L.Count-1 do
  601. begin
  602. T:=TPGHandle(L[i]);
  603. if T.Connected then
  604. T.Disconnect;
  605. T.Free;
  606. end;
  607. L.Clear;
  608. finally
  609. FHandlePool.UnLockList;
  610. end;
  611. {$IfDef LinkDynamically}
  612. ReleasePostgres3;
  613. {$EndIf}
  614. end;
  615. function TPQConnection.GetConnectionString(const aDBName : String): string;
  616. Procedure MaybeAdd(aName,aValue : String);
  617. begin
  618. if aValue<>'' then
  619. begin
  620. if aName<>'' then
  621. Result:=Result+' '+aName+'='''+aValue+''''
  622. else
  623. Result:=result+' '+aValue;
  624. end;
  625. end;
  626. begin
  627. Result:='';
  628. MaybeAdd('user',UserName);
  629. MaybeAdd('password',Password);
  630. MaybeAdd('host',HostName);
  631. MaybeAdd('dbname',aDBName);
  632. MaybeAdd('',Params.Text);
  633. end;
  634. procedure TPGHandle.Disconnect;
  635. Var
  636. PG : PPGconn;
  637. begin
  638. if FNativeConn=Nil then
  639. DatabaseError('Not connected to Postgres Server');
  640. PG:=FNativeConn;
  641. {$IFDEF PQDEBUG}
  642. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] ,Finishing connection');
  643. {$ENDIF}
  644. FNativeConn:=Nil;
  645. PQFinish(PG);
  646. end;
  647. procedure TPGHandle.StartTransaction;
  648. begin
  649. Exec('BEGIN',True,sErrTransactionFailed);
  650. FActive:=True;
  651. end;
  652. procedure TPGHandle.RollBack;
  653. Var
  654. L : TList;
  655. I : Integer;
  656. C : TPQCursor;
  657. begin
  658. if not Active then
  659. Exit;
  660. // unprepare statements associated with given transaction
  661. L:=FCursorList.LockList;
  662. try
  663. For I:=0 to L.Count-1 do
  664. begin
  665. C:=TPQCursor(L[i]);
  666. UnprepareStatement(C,False);
  667. end;
  668. L.Clear;
  669. finally
  670. FCursorList.UnlockList;
  671. end;
  672. FActive:=False;
  673. Exec('ROLLBACK',True,SErrRollbackFailed);
  674. end;
  675. procedure TPGHandle.Commit;
  676. begin
  677. Exec('COMMIT',True,SErrCommitFailed);
  678. end;
  679. procedure TPGHandle.Reset;
  680. begin
  681. {$IFDEF PQDEBUG}
  682. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] : Resetting');
  683. {$ENDIF}
  684. PQReset(FNativeConn);
  685. end;
  686. function TPGHandle.CheckConnectionStatus(doRaise: Boolean): Boolean;
  687. var sErr: string;
  688. begin
  689. Result:=False;
  690. if (PQstatus(FNativeConn) <> CONNECTION_BAD) then
  691. Exit(True);
  692. sErr := PQerrorMessage(FNativeConn);
  693. //make connection available in pool
  694. Disconnect;
  695. if DoRaise then
  696. DatabaseError(sErrConnectionFailed + ' (PostgreSQL: ' + sErr + ')');
  697. end;
  698. function TPGHandle.DescribePrepared(StmtName: String): PPGresult;
  699. Var
  700. S : AnsiString;
  701. begin
  702. S:=StmtName;
  703. Result:=PQdescribePrepared(FNativeConn,pchar(S));
  704. end;
  705. function TPGHandle.Exec(aSQL: String; aClearResult: Boolean; aError: String): PPGresult;
  706. Var
  707. S : UTF8String;
  708. Acts : TCheckResultActions;
  709. begin
  710. if FNativeConn=Nil then
  711. DatabaseError(IntToStr(FHandleID)+': No native PQ connection available');
  712. CheckConnectionStatus();
  713. S:=aSQL;
  714. {$IFDEF PQDEBUG}
  715. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] exec: ',S);
  716. {$ENDIF}
  717. Result:=PQexec(FNativeConn,PAnsiChar(S));
  718. acts:=[];
  719. if aClearResult then
  720. include(acts,craClear);
  721. CheckResultError(Result,acts,aError);
  722. end;
  723. function TPGHandle.ExecPrepared(stmtName: AnsiString; nParams: longint;
  724. paramValues: PPchar; paramLengths: Plongint; paramFormats: Plongint;
  725. aClearResult: Boolean): PPGresult;
  726. var
  727. acts : TCheckResultActions;
  728. begin
  729. {$IFDEF PQDEBUG}
  730. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] executr prepared ',StmtName);
  731. {$ENDIF}
  732. Result:=PQexecPrepared(NativeConn,pansichar(StmtName),nParams,ParamValues,paramlengths,paramformats,1);
  733. acts:=[];
  734. if aClearResult then
  735. include(acts,craClear);
  736. CheckResultError(Result,acts,'Error executing prepared statement '+stmtName);
  737. end;
  738. procedure TPGHandle.CheckResultError(var res: PPGresult; Actions: TCheckResultActions; const ErrMsg: string);
  739. Procedure MaybeAdd(Var S : String; Prefix,Msg : String);
  740. begin
  741. if (Msg='') then
  742. exit;
  743. S:=S+LineEnding+Prefix+': '+Msg;
  744. end;
  745. var
  746. E: EPQDatabaseError;
  747. sErr: string;
  748. CompName: string;
  749. SEVERITY: string;
  750. SQLSTATE: string;
  751. MESSAGE_PRIMARY: string;
  752. MESSAGE_DETAIL: string;
  753. MESSAGE_HINT: string;
  754. STATEMENT_POSITION: string;
  755. SCHEMA_NAME: string;
  756. TABLE_NAME: string;
  757. COLUMN_NAME: string;
  758. DATATYPE_NAME: string;
  759. CONSTRAINT_NAME: string;
  760. P : Pchar;
  761. haveError : Boolean;
  762. lMessage : String;
  763. begin
  764. lMessage:=ErrMsg;
  765. HaveError:=False;
  766. if (Res=Nil) then
  767. begin
  768. {$IFDEF PQDEBUG}
  769. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] nil result');
  770. {$ENDIF}
  771. HaveError:=True;
  772. P:=PQerrorMessage(FNativeConn);
  773. If Assigned(p) then
  774. lMessage:=lMessage+StrPas(P);
  775. Reset;
  776. end
  777. else if Not (PQresultStatus(res) in [PGRES_COMMAND_OK,PGRES_TUPLES_OK]) then
  778. begin
  779. HaveError:=True;
  780. {$IFNDEF VER3_2}
  781. SEVERITY:=PQresultErrorField(res,PG_DIAG_SEVERITY);
  782. SQLSTATE:=PQresultErrorField(res,PG_DIAG_SQLSTATE);
  783. MESSAGE_PRIMARY:=PQresultErrorField(res,PG_DIAG_MESSAGE_PRIMARY);
  784. MESSAGE_DETAIL:=PQresultErrorField(res,PG_DIAG_MESSAGE_DETAIL);
  785. MESSAGE_HINT:=PQresultErrorField(res,PG_DIAG_MESSAGE_HINT);
  786. STATEMENT_POSITION:=PQresultErrorField(res,PG_DIAG_STATEMENT_POSITION);
  787. SCHEMA_NAME:=PQresultErrorField(res,PG_DIAG_SCHEMA_NAME);
  788. TABLE_NAME:=PQresultErrorField(res,PG_DIAG_TABLE_NAME);
  789. COLUMN_NAME:=PQresultErrorField(res,PG_DIAG_COLUMN_NAME);
  790. DATATYPE_NAME:=PQresultErrorField(res,PG_DIAG_DATATYPE_NAME);
  791. CONSTRAINT_NAME:=PQresultErrorField(res,PG_DIAG_CONSTRAINT_NAME);
  792. {$ENDIF}
  793. sErr:=PQresultErrorMessage(res);
  794. if Connection.VerboseErrors then
  795. begin
  796. MaybeAdd(sErr,'Severity',SEVERITY);
  797. MaybeAdd(sErr,'SQL State',SQLSTATE);
  798. MaybeAdd(sErr,'Primary Error',MESSAGE_PRIMARY);
  799. MaybeAdd(sErr,'Error Detail',MESSAGE_DETAIL);
  800. MaybeAdd(sErr,'Hint',MESSAGE_HINT);
  801. MaybeAdd(sErr,'Character',STATEMENT_POSITION);
  802. MaybeAdd(sErr,'Schema',SCHEMA_NAME);
  803. MaybeAdd(sErr,'Table',TABLE_NAME);
  804. MaybeAdd(sErr,'Column',COLUMN_NAME);
  805. MaybeAdd(sErr,'Data Type',DATATYPE_NAME);
  806. MaybeAdd(sErr,'Constraint',CONSTRAINT_NAME);
  807. end;
  808. end;
  809. if HaveError then
  810. begin
  811. if Assigned(Connection) then
  812. CompName := Connection.Name;
  813. if CompName='' then
  814. CompName:=FDBName;
  815. E:=EPQDatabaseError.CreateFmt('%s : %s (PostgreSQL: %s)', [CompName, lMessage, sErr]);
  816. E.SEVERITY:=SEVERITY;
  817. E.SQLSTATE:=SQLSTATE;
  818. E.MESSAGE_PRIMARY:=MESSAGE_PRIMARY;
  819. E.MESSAGE_DETAIL:=MESSAGE_DETAIL;
  820. E.MESSAGE_HINT:=MESSAGE_HINT;
  821. E.STATEMENT_POSITION:=STATEMENT_POSITION;
  822. E.SCHEMA_NAME:=SCHEMA_NAME;
  823. E.TABLE_NAME:=TABLE_NAME;
  824. E.COLUMN_NAME:=COLUMN_NAME;
  825. E.DATATYPE_NAME:=DATATYPE_NAME;
  826. E.CONSTRAINT_NAME:=CONSTRAINT_NAME;
  827. PQclear(res);
  828. res:=nil;
  829. if craClose in Actions then
  830. Disconnect;
  831. {$IFDEF PQDEBUG}
  832. Writeln('>>> ',IntToStr(FHandleID)+' [',TThread.CurrentThread.ThreadID, '] Error: ',lMessage,' - ',Serr);
  833. {$ENDIF}
  834. raise E;
  835. end
  836. else
  837. if craClear in Actions then
  838. begin
  839. PQClear(res);
  840. res:=nil;
  841. end;
  842. end;
  843. function TPQConnection.TranslateFldType(res: PPGresult; Tuple: integer; out
  844. Size: integer; out ATypeOID: oid): TFieldType;
  845. const
  846. VARHDRSZ=sizeof(longint);
  847. var
  848. li : longint;
  849. aoid : oid;
  850. begin
  851. Size := 0;
  852. ATypeOID:=0;
  853. AOID:=PQftype(res,Tuple);
  854. case AOID of
  855. Oid_varchar,Oid_bpchar,
  856. Oid_name : begin
  857. Result := ftString;
  858. size := PQfsize(Res, Tuple);
  859. if (size = -1) then
  860. begin
  861. li := PQfmod(res,Tuple);
  862. if li = -1 then
  863. size := dsMaxStringSize
  864. else
  865. size := (li-VARHDRSZ) and $FFFF;
  866. end;
  867. if size > MaxSmallint then size := MaxSmallint;
  868. end;
  869. // Oid_text : Result := ftString;
  870. Oid_text,Oid_JSON : Result := ftMemo;
  871. Oid_Bytea : Result := ftBlob;
  872. Oid_oid : Result := ftInteger;
  873. Oid_int8 : Result := ftLargeInt;
  874. Oid_int4 : Result := ftInteger;
  875. Oid_int2 : Result := ftSmallInt;
  876. Oid_Float4 : Result := ftFloat;
  877. Oid_Float8 : Result := ftFloat;
  878. Oid_TimeStamp,
  879. Oid_TimeStampTZ : Result := ftDateTime;
  880. Oid_Date : Result := ftDate;
  881. Oid_Interval,
  882. Oid_Time,
  883. Oid_TimeTZ : Result := ftTime;
  884. Oid_Bool : Result := ftBoolean;
  885. Oid_Numeric : begin
  886. Result := ftBCD;
  887. li := PQfmod(res,Tuple);
  888. if li = -1 then
  889. size := 4 // No information about the size available, use the maximum value
  890. else
  891. // The precision is the high 16 bits, the scale the
  892. // low 16 bits with an offset of sizeof(int32).
  893. begin
  894. size := (li-VARHDRSZ) and $FFFF;
  895. if (size > MaxBCDScale) or ((li shr 16)-size > MaxBCDPrecision-MaxBCDScale) then
  896. Result := ftFmtBCD;
  897. end;
  898. end;
  899. Oid_Money : Result := ftCurrency;
  900. Oid_char : begin
  901. Result := ftFixedChar;
  902. Size := 1;
  903. end;
  904. Oid_uuid : begin
  905. Result := ftGuid;
  906. Size := 38;
  907. end;
  908. Oid_MacAddr : begin
  909. Result := ftFixedChar;
  910. Size := 17;
  911. end;
  912. Oid_Inet : begin
  913. Result := ftString;
  914. Size := 39;
  915. end;
  916. Oid_Unknown : Result := ftUnknown;
  917. else
  918. Result:=ftUnknown;
  919. ATypeOID:=AOID;
  920. end;
  921. end;
  922. function TPQConnection.AllocateCursorHandle: TSQLCursor;
  923. begin
  924. result := TPQCursor.create;
  925. end;
  926. procedure TPQConnection.DeAllocateCursorHandle(var cursor: TSQLCursor);
  927. begin
  928. FreeAndNil(cursor);
  929. end;
  930. function TPQConnection.AllocateTransactionHandle: TSQLHandle;
  931. begin
  932. result := TPQTransactionHandle.Create;
  933. end;
  934. procedure TPQConnection.PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams);
  935. const TypeStrings : array[TFieldType] of string =
  936. (
  937. 'Unknown', // ftUnknown
  938. 'text', // ftString
  939. 'smallint', // ftSmallint
  940. 'int', // ftInteger
  941. 'int', // ftWord
  942. 'bool', // ftBoolean
  943. 'float', // ftFloat
  944. 'money', // ftCurrency
  945. 'numeric', // ftBCD
  946. 'date', // ftDate
  947. 'time', // ftTime
  948. 'timestamp', // ftDateTime
  949. 'Unknown', // ftBytes
  950. 'bytea', // ftVarBytes
  951. 'Unknown', // ftAutoInc
  952. 'bytea', // ftBlob
  953. 'text', // ftMemo
  954. 'bytea', // ftGraphic
  955. 'text', // ftFmtMemo
  956. 'Unknown', // ftParadoxOle
  957. 'Unknown', // ftDBaseOle
  958. 'Unknown', // ftTypedBinary
  959. 'Unknown', // ftCursor
  960. 'char', // ftFixedChar
  961. 'text', // ftWideString
  962. 'bigint', // ftLargeint
  963. 'Unknown', // ftADT
  964. 'Unknown', // ftArray
  965. 'Unknown', // ftReference
  966. 'Unknown', // ftDataSet
  967. 'Unknown', // ftOraBlob
  968. 'Unknown', // ftOraClob
  969. 'Unknown', // ftVariant
  970. 'Unknown', // ftInterface
  971. 'Unknown', // ftIDispatch
  972. 'uuid', // ftGuid
  973. 'Unknown', // ftTimeStamp
  974. 'numeric', // ftFMTBcd
  975. 'Unknown', // ftFixedWideChar
  976. 'Unknown' // ftWideMemo
  977. {$IFNDEF VER3_2}
  978. ,
  979. 'Unknown', // ftOraTimeStamp
  980. 'Unknown', // ftOraInterval
  981. 'Unknown', // ftLongWord
  982. 'Unknown', // ftShortint
  983. 'Unknown', // ftByte
  984. 'Unknown', // ftExtended
  985. 'real' // ftSingle
  986. {$ENDIF}
  987. );
  988. var
  989. s,ts : string;
  990. i : integer;
  991. P : TParam;
  992. PQ : TSQLDBParam;
  993. PQCurs : TPQCursor;
  994. begin
  995. PQCurs:=cursor as TPQCursor;
  996. PQCurs.FPrepared := False;
  997. PQCurs.FDirect := False;
  998. // Prior to v8 there is no support for cursors and parameters.
  999. // So that's not supported.
  1000. if PQCurs.FStatementType in [stInsert,stUpdate,stDelete, stSelect] then
  1001. begin
  1002. PQCurs.StmtName := 'prepst'+inttostr(FCursorCount);
  1003. InterlockedIncrement(FCursorCount);
  1004. if PQCurs.Handle=Nil then
  1005. (TObject(aTransaction.Handle) as TPQTransactionHandle).Handle.RegisterCursor(PQCurs);
  1006. // Only available for pq 8.0, so don't use it...
  1007. // Res := pqprepare(tr,'prepst'+name+nr,pchar(buf),params.Count,pchar(''));
  1008. s := 'prepare '+PQCurs.StmtName+' ';
  1009. if Assigned(AParams) and (AParams.Count > 0) then
  1010. begin
  1011. s := s + '(';
  1012. for i := 0 to AParams.Count-1 do
  1013. begin
  1014. P:=AParams[i];
  1015. If (P is TSQLDBParam) then
  1016. PQ:=TSQLDBParam(P)
  1017. else
  1018. PQ:=Nil;
  1019. TS:=TypeStrings[P.DataType];
  1020. if (TS<>'Unknown') then
  1021. begin
  1022. If Assigned(PQ)
  1023. and Assigned(PQ.SQLDBData)
  1024. and (PFieldBinding(PQ.SQLDBData)^.ExtendedFieldType=eftEnum) then
  1025. ts:='unknown';
  1026. s := s + ts + ','
  1027. end
  1028. else
  1029. begin
  1030. if P.DataType = ftUnknown then
  1031. begin
  1032. if P.IsNull then
  1033. s:=s+' unknown ,'
  1034. else
  1035. DatabaseErrorFmt(SUnknownParamFieldType,[P.Name],self)
  1036. end
  1037. else
  1038. DatabaseErrorFmt(SUnsupportedParameter,[Fieldtypenames[P.DataType]],self);
  1039. end;
  1040. end;
  1041. s[length(s)] := ')';
  1042. buf := AParams.ParseSQL(buf,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psPostgreSQL);
  1043. end;
  1044. s := s + ' as ' + buf;
  1045. if LogEvent(detActualSQL) then
  1046. Log(detActualSQL,S);
  1047. PQCurs.Res:=Nil;
  1048. PQCurs.Res:=PQCurs.Handle.Exec(S,False,SErrPrepareFailed);
  1049. // if statement is INSERT, UPDATE, DELETE with RETURNING clause, then
  1050. // override the statement type derived by parsing the query.
  1051. if (PQCurs.FStatementType in [stInsert,stUpdate,stDelete]) and (pos('RETURNING', upcase(s)) > 0) then
  1052. begin
  1053. PQclear(PQCurs.res);
  1054. PQCurs.res:=PQCurs.Handle.DescribePrepared(PQCurs.StmtName);
  1055. if (PQresultStatus(PQCurs.res) = PGRES_COMMAND_OK) and (PQnfields(PQCurs.res) > 0) then
  1056. PQCurs.FStatementType := stSelect;
  1057. end;
  1058. PQCurs.FPrepared := True;
  1059. end
  1060. else
  1061. begin
  1062. if Assigned(AParams) then
  1063. PQCurs.Statement := AParams.ParseSQL(buf,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psPostgreSQL)
  1064. else
  1065. PQCurs.Statement:=Buf;
  1066. PQCurs.FDirect:=True;
  1067. end;
  1068. end;
  1069. procedure TPQConnection.UnPrepareStatement(cursor : TSQLCursor);
  1070. Var
  1071. C : TPQCursor;
  1072. begin
  1073. C:=Cursor as TPQCursor;
  1074. if Assigned(C.Handle) then
  1075. C.Handle.UnPrepareStatement(C,ForcedClose);
  1076. end;
  1077. procedure TPQConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams);
  1078. var ar : array of PAnsiChar;
  1079. handled : boolean;
  1080. l,i : integer;
  1081. s : RawByteString;
  1082. bd : TBlobData;
  1083. lengths,formats : array of integer;
  1084. ParamNames,
  1085. ParamValues : array of string;
  1086. cash: int64;
  1087. PQCurs : TPQCursor;
  1088. function FormatTimeInterval(Time: TDateTime): string; // supports Time >= '24:00:00'
  1089. var hour, minute, second, millisecond: word;
  1090. begin
  1091. DecodeTime(Time, hour, minute, second, millisecond);
  1092. Result := Format('%.2d:%.2d:%.2d.%.3d',[Trunc(Time)*24+hour,minute,second,millisecond]);
  1093. end;
  1094. begin
  1095. ar:=[];
  1096. ParamNames:=[];
  1097. ParamValues:=[];
  1098. Lengths:=[];
  1099. Formats:=[];
  1100. PQCurs:=cursor as TPQCursor;
  1101. PQCurs.CurTuple:=-1;
  1102. PQclear(PQCurs.res);
  1103. PQCurs.Res:=Nil;
  1104. if PQCurs.FStatementType in [stInsert,stUpdate,stDelete,stSelect] then
  1105. begin
  1106. if LogEvent(detParamValue) then
  1107. LogParams(AParams);
  1108. if Assigned(AParams) and (AParams.Count > 0) then
  1109. begin
  1110. l:=AParams.Count;
  1111. setlength(ar,l);
  1112. for i := 0 to l-1 do
  1113. ar[i]:=nil;
  1114. setlength(lengths,l);
  1115. setlength(formats,l);
  1116. try
  1117. for i := 0 to AParams.Count -1 do if not AParams[i].IsNull then
  1118. begin
  1119. handled:=False;
  1120. case AParams[i].DataType of
  1121. ftDateTime:
  1122. s := FormatDateTime('yyyy"-"mm"-"dd hh":"nn":"ss.zzz', AParams[i].AsDateTime);
  1123. ftDate:
  1124. s := FormatDateTime('yyyy"-"mm"-"dd', AParams[i].AsDateTime);
  1125. ftTime:
  1126. s := FormatTimeInterval(AParams[i].AsDateTime);
  1127. ftFloat:
  1128. Str(AParams[i].AsFloat, s);
  1129. ftBCD:
  1130. Str(AParams[i].AsCurrency, s);
  1131. ftCurrency:
  1132. begin
  1133. cash:=NtoBE(round(AParams[i].AsCurrency*100));
  1134. setlength(s, sizeof(cash));
  1135. Move(cash, s[1], sizeof(cash));
  1136. end;
  1137. ftFmtBCD:
  1138. s := BCDToStr(AParams[i].AsFMTBCD, FSQLFormatSettings);
  1139. ftBlob, ftGraphic, ftVarBytes:
  1140. begin
  1141. Handled:=true;
  1142. bd:= AParams[i].AsBlob;
  1143. l:=length(BD);
  1144. if l>0 then
  1145. begin
  1146. GetMem(ar[i],l+1);
  1147. ar[i][l]:=#0;
  1148. Move(BD[0],ar[i]^, L);
  1149. lengths[i]:=l;
  1150. end;
  1151. end
  1152. else
  1153. s := GetAsString(AParams[i]);
  1154. end; {case}
  1155. {$IFDEF PQDEBUG}
  1156. WriteLn('Setting param ',aParams[i].Name,'(',aParams[i].DataType,') to ',S);
  1157. {$ENDIF}
  1158. if not handled then
  1159. begin
  1160. l:=length(s);
  1161. GetMem(ar[i],l+1);
  1162. StrMove(PAnsiChar(ar[i]), PAnsiChar(s), L+1);
  1163. lengths[i]:=L;
  1164. end;
  1165. if (AParams[i].DataType in [ftBlob,ftMemo,ftGraphic,ftCurrency,ftVarBytes]) then
  1166. Formats[i]:=1
  1167. else
  1168. Formats[i]:=0;
  1169. end;
  1170. PQCurs.res := PQCurs.Handle.ExecPrepared(PQCurs.StmtName,AParams.Count,@Ar[0],@Lengths[0],@Formats[0],False);
  1171. // PQCurs.res := PQexecPrepared(PQCurs.Handle.NativeConn,pchar(PQCurs.StmtName),AParams.Count,@Ar[0],@Lengths[0],@Formats[0],1);
  1172. finally
  1173. for i := 0 to AParams.Count -1 do
  1174. FreeMem(ar[i]);
  1175. end;
  1176. end
  1177. else
  1178. PQCurs.res := PQCurs.Handle.ExecPrepared(PQCurs.StmtName,0,nil,nil,nil,False);
  1179. end
  1180. else
  1181. begin
  1182. if PQCurs.handle=Nil then
  1183. (TObject(aTransaction.Handle) as TPQTransactionHandle).Handle.RegisterCursor(PQCurs);
  1184. if Assigned(AParams) and (AParams.Count > 0) then
  1185. begin
  1186. setlength(ParamNames,AParams.Count);
  1187. setlength(ParamValues,AParams.Count);
  1188. for i := 0 to AParams.Count -1 do
  1189. begin
  1190. ParamNames[AParams.Count-i-1] := '$'+inttostr(AParams[i].index+1);
  1191. ParamValues[AParams.Count-i-1] := GetAsSQLText(AParams[i]);
  1192. end;
  1193. s := stringsreplace(PQCurs.Statement,ParamNames,ParamValues,[rfReplaceAll]);
  1194. end
  1195. else
  1196. s := PQCurs.Statement;
  1197. PQCurs.Res:=PQCurs.Handle.Exec(S,False,SErrExecuteFailed);
  1198. end;
  1199. PQCurs.FSelectable := assigned(PQCurs.res) and (PQresultStatus(PQCurs.res)=PGRES_TUPLES_OK);
  1200. end;
  1201. procedure TPQConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs);
  1202. var
  1203. i : integer;
  1204. asize : integer;
  1205. aoid : oid;
  1206. fieldtype : tfieldtype;
  1207. nFields : integer;
  1208. b : Boolean;
  1209. Q : TPQCursor;
  1210. FD : TSQLDBFieldDef;
  1211. FB : PFieldBinding;
  1212. begin
  1213. B:=False;
  1214. Q:=cursor as TPQCursor;
  1215. with Q do
  1216. begin
  1217. nFields := PQnfields(Res);
  1218. setlength(FieldBinding,nFields);
  1219. for i := 0 to nFields-1 do
  1220. begin
  1221. fieldtype := TranslateFldType(Res, i, asize, aoid );
  1222. FD := AddFieldDef(FieldDefs, i+1, PQfname(Res, i), fieldtype, asize, -1, False, False, False) as TSQLDBFieldDef;
  1223. With FD do
  1224. begin
  1225. SQLDBData:=@FieldBinding[i];
  1226. FieldBinding[i].Index:=i;
  1227. FieldBinding[i].FieldDef:=FD;
  1228. FieldBinding[i].TypeOID:=aOID;
  1229. B:=B or (aOID>0);
  1230. end;
  1231. end;
  1232. CurTuple := -1;
  1233. end;
  1234. if B then
  1235. begin
  1236. // get all information in 1 go.
  1237. GetExtendedFieldInfo(Q,Q.FieldBinding);
  1238. For I:=0 to Length(Q.FieldBinding)-1 do
  1239. begin
  1240. FB:[email protected][i];
  1241. if (FB^.TypeOID>0) then
  1242. begin
  1243. FD:=FB^.FieldDef;
  1244. Case FB^.ExtendedFieldType of
  1245. eftEnum :
  1246. begin
  1247. FD.DataType:=ftString;
  1248. FD.Size:=64;
  1249. //FD.Attributes:=FD.Attributes+[faReadonly];
  1250. end;
  1251. eftCitext:
  1252. begin
  1253. FD.DataType:=ftMemo;
  1254. end
  1255. else
  1256. if ErrorOnUnknownType then
  1257. DatabaseError('Unhandled field type :'+FB^.TypeName,Self);
  1258. end;
  1259. end;
  1260. end;
  1261. end;
  1262. end;
  1263. function TPQConnection.GetHandle: pointer;
  1264. var
  1265. i:integer;
  1266. L : TList;
  1267. T : TPGHandle;
  1268. begin
  1269. result:=nil;
  1270. if not Connected then
  1271. exit;
  1272. //Get any handle that is (still) connected
  1273. L:=FHandlePool.LockList;
  1274. try
  1275. I:=L.Count-1;
  1276. While (I>=0) and (Result=Nil) do
  1277. begin
  1278. T:=TPGHandle(L[i]);
  1279. if T.Connected and T.CheckConnectionStatus(False) then
  1280. Result:=T;
  1281. Dec(I);
  1282. end;
  1283. finally
  1284. FHandlePool.UnLockList;
  1285. end;
  1286. if Result<>Nil then
  1287. exit;
  1288. //Nothing connected!! Reconnect
  1289. // T is element 0 after loop
  1290. if Not Assigned(T) then
  1291. begin
  1292. T:=TPGHandle.Create(Self,DatabaseName);
  1293. AddHandle(T);
  1294. end;
  1295. T.Connect;
  1296. Result:=T;
  1297. end;
  1298. function TPQConnection.Fetch(cursor : TSQLCursor) : boolean;
  1299. begin
  1300. with cursor as TPQCursor do
  1301. begin
  1302. inc(CurTuple);
  1303. Result := (PQntuples(res)>CurTuple);
  1304. end;
  1305. end;
  1306. function TPQConnection.LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  1307. const NBASE=10000;
  1308. DAYS_PER_MONTH=30;
  1309. type TNumericRecord = record
  1310. Digits : SmallInt;
  1311. Weight : SmallInt;
  1312. Sign : SmallInt;
  1313. Scale : Smallint;
  1314. end;
  1315. TIntervalRec = packed record
  1316. time : int64;
  1317. day : longint;
  1318. month : longint;
  1319. end;
  1320. TMacAddrRec = packed record
  1321. a, b, c, d, e, f: byte;
  1322. end;
  1323. TInetRec = packed record
  1324. family : byte;
  1325. bits : byte;
  1326. is_cidr: byte;
  1327. nb : byte;
  1328. ipaddr : array[1..16] of byte;
  1329. end;
  1330. var
  1331. x,i : integer;
  1332. s : string;
  1333. li : Longint;
  1334. CurrBuff : pchar;
  1335. dbl : pdouble;
  1336. cur : currency;
  1337. NumericRecord : ^TNumericRecord;
  1338. guid : TGUID;
  1339. bcd : TBCD;
  1340. macaddr : ^TMacAddrRec;
  1341. inet : ^TInetRec;
  1342. begin
  1343. Createblob := False;
  1344. with cursor as TPQCursor do
  1345. begin
  1346. x := GetFieldBinding(FieldDef)^.Index;
  1347. // Joost, 5 jan 2006: I disabled the following, since it's useful for
  1348. // debugging, but it also slows things down. In principle things can only go
  1349. // wrong when FieldDefs is changed while the dataset is opened. A user just
  1350. // shoudn't do that. ;) (The same is done in IBConnection)
  1351. //if PQfname(Res, x) <> FieldDef.Name then
  1352. // DatabaseErrorFmt(SFieldNotFound,[FieldDef.Name],self);
  1353. if pqgetisnull(res,CurTuple,x)=1 then
  1354. result := false
  1355. else
  1356. begin
  1357. CurrBuff := pqgetvalue(res,CurTuple,x);
  1358. result := true;
  1359. case FieldDef.DataType of
  1360. ftInteger, ftSmallint, ftLargeInt :
  1361. case PQfsize(res, x) of // postgres returns big-endian numbers
  1362. sizeof(int64) : pint64(buffer)^ := BEtoN(pint64(CurrBuff)^); // INT8
  1363. sizeof(integer) : pinteger(buffer)^ := BEtoN(pinteger(CurrBuff)^); // INT4
  1364. sizeof(smallint) : psmallint(buffer)^ := BEtoN(psmallint(CurrBuff)^); // INT2
  1365. end; {case}
  1366. ftFloat :
  1367. case PQfsize(res, x) of // postgres returns big-endian numbers
  1368. sizeof(int64) : // FLOAT8
  1369. pint64(buffer)^ := BEtoN(pint64(CurrBuff)^);
  1370. sizeof(integer) : // FLOAT4
  1371. begin
  1372. li := BEtoN(pinteger(CurrBuff)^);
  1373. pdouble(buffer)^ := psingle(@li)^
  1374. end;
  1375. end; {case}
  1376. ftString, ftFixedChar :
  1377. begin
  1378. case PQftype(res, x) of
  1379. Oid_MacAddr:
  1380. begin
  1381. macaddr := Pointer(CurrBuff);
  1382. li := FormatBuf(Buffer^, FieldDef.Size, '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x', 29,
  1383. [macaddr^.a,macaddr^.b,macaddr^.c,macaddr^.d,macaddr^.e,macaddr^.f]);
  1384. end;
  1385. Oid_Inet:
  1386. begin
  1387. inet := Pointer(CurrBuff);
  1388. if inet^.nb = 4 then
  1389. li := FormatBuf(Buffer^, FieldDef.Size, '%d.%d.%d.%d', 11,
  1390. [inet^.ipaddr[1],inet^.ipaddr[2],inet^.ipaddr[3],inet^.ipaddr[4]])
  1391. else if inet^.nb = 16 then
  1392. li := FormatBuf(Buffer^, FieldDef.Size, '%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x', 55,
  1393. [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]])
  1394. else
  1395. li := 0;
  1396. end
  1397. else
  1398. begin
  1399. li := pqgetlength(res,curtuple,x);
  1400. if li > FieldDef.Size then li := FieldDef.Size;
  1401. Move(CurrBuff^, Buffer^, li);
  1402. end;
  1403. end;
  1404. pchar(Buffer + li)^ := #0;
  1405. end;
  1406. ftBlob, ftMemo, ftVarBytes :
  1407. CreateBlob := True;
  1408. ftDate :
  1409. begin
  1410. dbl := pointer(buffer);
  1411. dbl^ := BEtoN(plongint(CurrBuff)^) + 36526;
  1412. end;
  1413. ftDateTime, ftTime :
  1414. begin
  1415. dbl := pointer(buffer);
  1416. if FIntegerDateTimes then
  1417. dbl^ := BEtoN(pint64(CurrBuff)^) / 1000000
  1418. else
  1419. pint64(dbl)^ := BEtoN(pint64(CurrBuff)^);
  1420. case PQftype(res, x) of
  1421. Oid_Timestamp, Oid_TimestampTZ:
  1422. dbl^ := dbl^ + 3.1558464E+009; // postgres counts seconds elapsed since 1-1-2000
  1423. Oid_Interval:
  1424. dbl^ := dbl^ + BEtoN(plongint(CurrBuff+ 8)^) * SecsPerDay
  1425. + BEtoN(plongint(CurrBuff+12)^) * SecsPerDay * DAYS_PER_MONTH;
  1426. end;
  1427. dbl^ := dbl^ / SecsPerDay;
  1428. // Now convert the mathematically-correct datetime to the
  1429. // illogical windows/delphi/fpc TDateTime:
  1430. if (dbl^ <= 0) and (frac(dbl^) < 0) then
  1431. dbl^ := trunc(dbl^)-2-frac(dbl^);
  1432. end;
  1433. ftBCD, ftFmtBCD:
  1434. begin
  1435. NumericRecord := pointer(CurrBuff);
  1436. NumericRecord^.Digits := BEtoN(NumericRecord^.Digits);
  1437. NumericRecord^.Weight := BEtoN(NumericRecord^.Weight);
  1438. NumericRecord^.Sign := BEtoN(NumericRecord^.Sign);
  1439. NumericRecord^.Scale := BEtoN(NumericRecord^.Scale);
  1440. inc(pointer(currbuff),sizeof(TNumericRecord));
  1441. if (NumericRecord^.Digits = 0) and (NumericRecord^.Scale = 0) then // = NaN, which is not supported by Currency-type, so we return NULL
  1442. result := false
  1443. else if FieldDef.DataType = ftBCD then
  1444. begin
  1445. cur := 0;
  1446. for i := 0 to NumericRecord^.Digits-1 do
  1447. begin
  1448. cur := cur + beton(pword(CurrBuff)^) * intpower(NBASE, NumericRecord^.weight-i);
  1449. inc(pointer(CurrBuff),2);
  1450. end;
  1451. if NumericRecord^.Sign <> 0 then cur := -cur;
  1452. Move(Cur, Buffer^, sizeof(currency));
  1453. end
  1454. else //ftFmtBCD
  1455. begin
  1456. bcd := 0;
  1457. for i := 0 to NumericRecord^.Digits-1 do
  1458. begin
  1459. BCDAdd(bcd, beton(pword(CurrBuff)^) * intpower(NBASE, NumericRecord^.weight-i), bcd);
  1460. inc(pointer(CurrBuff),2);
  1461. end;
  1462. if NumericRecord^.Sign <> 0 then BCDNegate(bcd);
  1463. Move(bcd, Buffer^, sizeof(bcd));
  1464. end;
  1465. end;
  1466. ftCurrency :
  1467. begin
  1468. dbl := pointer(buffer);
  1469. dbl^ := BEtoN(PInt64(CurrBuff)^) / 100;
  1470. end;
  1471. ftBoolean:
  1472. pchar(buffer)[0] := CurrBuff[0];
  1473. ftGuid:
  1474. begin
  1475. Move(CurrBuff^, guid, sizeof(guid));
  1476. guid.D1:=BEtoN(guid.D1);
  1477. guid.D2:=BEtoN(guid.D2);
  1478. guid.D3:=BEtoN(guid.D3);
  1479. s:=GUIDToString(guid);
  1480. StrPLCopy(PChar(Buffer), s, FieldDef.Size);
  1481. end
  1482. else
  1483. result := false;
  1484. end;
  1485. end;
  1486. end;
  1487. end;
  1488. {$IFNDEF VER3_2}
  1489. function TPQConnection.PortParamName: string;
  1490. begin
  1491. Result := 'port';
  1492. end;
  1493. {$ENDIF}
  1494. procedure TPQConnection.UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string);
  1495. var qry : TSQLQuery;
  1496. relname : string;
  1497. begin
  1498. if not assigned(Transaction) then
  1499. DatabaseError(SErrConnTransactionnSet);
  1500. if (length(TableName)>2) and (TableName[1]='"') and (TableName[length(TableName)]='"') then
  1501. relname := QuotedStr(AnsiDequotedStr(TableName, '"'))
  1502. else
  1503. relname := 'lower(' + QuotedStr(TableName) + ')'; // unquoted names are stored lower case in PostgreSQL which is incompatible with the SQL standard
  1504. qry := tsqlquery.Create(nil);
  1505. qry.transaction := Transaction;
  1506. qry.database := Self;
  1507. with qry do
  1508. begin
  1509. ReadOnly := True;
  1510. sql.clear;
  1511. sql.add('select '+
  1512. 'ic.relname as indexname, '+
  1513. 'tc.relname as tablename, '+
  1514. 'ia.attname, '+
  1515. 'i.indisprimary, '+
  1516. 'i.indisunique '+
  1517. 'from '+
  1518. 'pg_attribute ta, '+
  1519. 'pg_attribute ia, '+
  1520. 'pg_class tc, '+
  1521. 'pg_class ic, '+
  1522. 'pg_index i '+
  1523. 'where '+
  1524. '(i.indrelid = tc.oid) and '+
  1525. '(ta.attrelid = tc.oid) and '+
  1526. '(ia.attrelid = i.indexrelid) and '+
  1527. '(ic.oid = i.indexrelid) and '+
  1528. '(ta.attnum = i.indkey[ia.attnum-1]) and '+
  1529. '(tc.relname = ' + relname + ') '+
  1530. 'order by '+
  1531. 'ic.relname;');
  1532. open;
  1533. end;
  1534. while not qry.eof do with IndexDefs.AddIndexDef do
  1535. begin
  1536. Name := trim(qry.fields[0].asstring);
  1537. Fields := trim(qry.Fields[2].asstring);
  1538. If qry.fields[3].asboolean then options := options + [ixPrimary];
  1539. If qry.fields[4].asboolean then options := options + [ixUnique];
  1540. qry.next;
  1541. while (name = qry.fields[0].asstring) and (not qry.eof) do
  1542. begin
  1543. Fields := Fields + ';' + trim(qry.Fields[2].asstring);
  1544. qry.next;
  1545. end;
  1546. end;
  1547. qry.close;
  1548. qry.free;
  1549. end;
  1550. function TPQConnection.GetSchemaInfoSQL(SchemaType: TSchemaType;
  1551. SchemaObjectName, SchemaPattern: string): string;
  1552. var s : string;
  1553. begin
  1554. // select * from information_schema.tables with
  1555. // where table_schema [not] in ('pg_catalog','information_schema') may be better.
  1556. // But the following should work:
  1557. case SchemaType of
  1558. stTables : s := 'select '+
  1559. 'relfilenode as recno, '+
  1560. 'current_database() as catalog_name, '+
  1561. 'nspname as schema_name, '+
  1562. 'relname as table_name, '+
  1563. '0 as table_type '+
  1564. 'from pg_class c '+
  1565. 'left join pg_namespace n on c.relnamespace=n.oid '+
  1566. 'where (relkind=''r'') and not (nspname in (''pg_catalog'',''information_schema''))' +
  1567. 'order by relname';
  1568. stSysTables : s := 'select '+
  1569. 'relfilenode as recno, '+
  1570. 'current_database() as catalog_name, '+
  1571. 'nspname as schema_name, '+
  1572. 'relname as table_name, '+
  1573. '0 as table_type '+
  1574. 'from pg_class c '+
  1575. 'left join pg_namespace n on c.relnamespace=n.oid '+
  1576. 'where (relkind=''r'') and nspname in ((''pg_catalog'',''information_schema'')) ' + // only system tables
  1577. 'order by relname';
  1578. stColumns : s := 'select '+
  1579. 'a.attnum as recno, '+
  1580. 'current_database() as catalog_name, '+
  1581. 'nspname as schema_name, '+
  1582. 'c.relname as table_name, '+
  1583. 'a.attname as column_name, '+
  1584. 'a.attnum as column_position, '+
  1585. '0 as column_type, '+
  1586. 'a.atttypid as column_datatype, '+
  1587. 't.typname as column_typename, '+
  1588. '0 as column_subtype, '+
  1589. '0 as column_precision, '+
  1590. '0 as column_scale, '+
  1591. 'a.atttypmod as column_length, '+
  1592. 'not a.attnotnull as column_nullable '+
  1593. 'from pg_class c '+
  1594. 'join pg_attribute a on c.oid=a.attrelid '+
  1595. 'join pg_type t on t.oid=a.atttypid '+
  1596. 'left join pg_namespace n on c.relnamespace=n.oid '+
  1597. // This can lead to problems when case-sensitive tablenames are used.
  1598. 'where (a.attnum>0) and (not a.attisdropped) and (upper(c.relname)=''' + Uppercase(SchemaObjectName) + ''') '+
  1599. 'order by a.attname';
  1600. else
  1601. s := inherited;
  1602. end; {case}
  1603. result := s;
  1604. end;
  1605. function TPQConnection.GetNextValueSQL(const SequenceName: string; IncrementBy: Integer): string;
  1606. begin
  1607. Result := Format('SELECT nextval(''%s'')', [SequenceName]);
  1608. end;
  1609. procedure TPQConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef;
  1610. ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  1611. var
  1612. x : integer;
  1613. li : Longint;
  1614. begin
  1615. with cursor as TPQCursor do
  1616. begin
  1617. x := FieldBinding[FieldDef.FieldNo-1].Index;
  1618. li := pqgetlength(res,curtuple,x);
  1619. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer,li);
  1620. Move(pqgetvalue(res,CurTuple,x)^, ABlobBuf^.BlobBuffer^.Buffer^, li);
  1621. ABlobBuf^.BlobBuffer^.Size := li;
  1622. end;
  1623. end;
  1624. function TPQConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  1625. begin
  1626. if assigned(cursor) and assigned((cursor as TPQCursor).res) then
  1627. Result := StrToIntDef(PQcmdTuples((cursor as TPQCursor).res),-1)
  1628. else
  1629. Result := -1;
  1630. end;
  1631. function TPQConnection.GetConnectionInfo(InfoType: TConnInfoType): string;
  1632. begin
  1633. Result:='';
  1634. try
  1635. {$IFDEF LinkDynamically}
  1636. InitialisePostgres3;
  1637. {$ENDIF}
  1638. case InfoType of
  1639. citServerType:
  1640. Result:=TPQConnectionDef.TypeName;
  1641. citServerVersion,
  1642. citServerVersionString:
  1643. if Connected then
  1644. Result:=format('%6.6d', [PQserverVersion(GetHandle)]);
  1645. citClientName:
  1646. Result:=TPQConnectionDef.LoadedLibraryName;
  1647. else
  1648. Result:=inherited GetConnectionInfo(InfoType);
  1649. end;
  1650. finally
  1651. {$IFDEF LinkDynamically}
  1652. ReleasePostgres3;
  1653. {$ENDIF}
  1654. end;
  1655. end;
  1656. { TPQConnectionDef }
  1657. class function TPQConnectionDef.TypeName: String;
  1658. begin
  1659. Result:='PostgreSQL';
  1660. end;
  1661. class function TPQConnectionDef.ConnectionClass: TSQLConnectionClass;
  1662. begin
  1663. Result:=TPQConnection;
  1664. end;
  1665. class function TPQConnectionDef.Description: String;
  1666. begin
  1667. Result:='Connect to a PostgreSQL database directly via the client library';
  1668. end;
  1669. class function TPQConnectionDef.DefaultLibraryName: String;
  1670. begin
  1671. {$IfDef LinkDynamically}
  1672. Result:=pqlib;
  1673. {$else}
  1674. Result:='';
  1675. {$endif}
  1676. end;
  1677. class function TPQConnectionDef.LoadFunction: TLibraryLoadFunction;
  1678. begin
  1679. {$IfDef LinkDynamically}
  1680. Result:=@InitialisePostgres3;
  1681. {$else}
  1682. Result:=Nil;
  1683. {$endif}
  1684. end;
  1685. class function TPQConnectionDef.UnLoadFunction: TLibraryUnLoadFunction;
  1686. begin
  1687. {$IfDef LinkDynamically}
  1688. Result:=@ReleasePostgres3;
  1689. {$else}
  1690. Result:=Nil;
  1691. {$endif}
  1692. end;
  1693. class function TPQConnectionDef.LoadedLibraryName: string;
  1694. begin
  1695. {$IfDef LinkDynamically}
  1696. Result:=Postgres3LoadedLibrary;
  1697. {$else}
  1698. Result:='';
  1699. {$endif}
  1700. end;
  1701. initialization
  1702. RegisterConnection(TPQConnectionDef);
  1703. finalization
  1704. UnRegisterConnection(TPQConnectionDef);
  1705. end.