pqconnection.pp 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895
  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. Procedure Connect;
  48. Procedure Disconnect;
  49. Procedure StartTransaction;
  50. Procedure RollBack;
  51. Procedure Commit;
  52. Procedure Reset;
  53. Function CheckConnectionStatus(doRaise : Boolean = True) : Boolean;
  54. Function DescribePrepared(StmtName : String): PPGresult;
  55. Function Exec(aSQL : String; aClearResult : Boolean; aError : String = '') : PPGresult;
  56. function ExecPrepared(stmtName: AnsiString; nParams:longint; paramValues:PPchar; paramLengths:Plongint;paramFormats:Plongint; aClearResult : Boolean) : PPGresult;
  57. Constructor Create(aConnection : TPQConnection;aDBName :string);
  58. Destructor Destroy; override;
  59. procedure CheckResultError(var res: PPGresult; Actions : TCheckResultActions; const ErrMsg: string);
  60. Property Connection : TPQConnection Read FCOnnection;
  61. Property NativeConn : PPGConn Read FNativeConn;
  62. Property Active : Boolean Read Factive;
  63. Property Used : Boolean Read FUsed Write FUsed;
  64. Property Connected : Boolean Read GetConnected;
  65. end;
  66. // TField and TFieldDef only support a limited amount of fields.
  67. // TFieldBinding and TExtendedFieldType can be used to map PQ types
  68. // on standard fields and retain mapping info.
  69. TExtendedFieldType = (eftNone,eftEnum,eftCitext);
  70. TFieldBinding = record
  71. FieldDef : TSQLDBFieldDef; // FieldDef this is associated with
  72. Index : Integer; // Tuple index
  73. TypeOID : oid; // Filled with type OID if it is not standard.
  74. TypeName : String; // Filled with type name by GetExtendedFieldInfo
  75. ExtendedFieldType: TExtendedFieldType; //
  76. end;
  77. PFieldBinding = ^TFieldBinding;
  78. TFieldBindings = Array of TFieldBinding;
  79. { TPQTransactionHandle }
  80. TPQTransactionHandle = Class(TSQLHandle)
  81. strict private
  82. FHandle: TPGHandle;
  83. procedure SetHandle(AValue: TPGHandle);
  84. Public
  85. Property Handle : TPGHandle Read FHandle Write SetHandle;
  86. end;
  87. { TPQCursor }
  88. TPQCursor = Class(TSQLCursor)
  89. private
  90. procedure SetHandle(AValue: TPGhandle);
  91. protected
  92. Statement : string;
  93. StmtName : string;
  94. Fhandle : TPGHandle;
  95. res : PPGresult;
  96. CurTuple : integer;
  97. FieldBinding : TFieldBindings;
  98. Function GetFieldBinding(F : TFieldDef): PFieldBinding;
  99. Public
  100. Destructor Destroy; override;
  101. Property Handle : TPGhandle Read FHandle Write SetHandle;
  102. end;
  103. { EPQDatabaseError }
  104. EPQDatabaseError = class(EDatabaseError)
  105. public
  106. SEVERITY:string;
  107. SQLSTATE: string;
  108. MESSAGE_PRIMARY:string;
  109. MESSAGE_DETAIL:string;
  110. MESSAGE_HINT:string;
  111. STATEMENT_POSITION:string;
  112. SCHEMA_NAME: string;
  113. TABLE_NAME: string;
  114. COLUMN_NAME: string;
  115. DATATYPE_NAME: string;
  116. CONSTRAINT_NAME: string;
  117. end;
  118. { TPQConnection }
  119. TPQConnection = class (TSQLConnection)
  120. private
  121. FHandlePool : TThreadList;
  122. FCursorCount : dword;
  123. FIntegerDateTimes : boolean;
  124. FVerboseErrors : Boolean;
  125. protected
  126. // Protected so they can be used by descendents.
  127. function GetConnectionString(const aDBName : String) : string;
  128. function TranslateFldType(res : PPGresult; Tuple : integer; out Size : integer; Out ATypeOID : oid) : TFieldType;
  129. procedure ExecuteDirectPG(const Query : String);
  130. Procedure GetExtendedFieldInfo(cursor: TPQCursor; Bindings : TFieldBindings);
  131. procedure ApplyFieldUpdate(C : TSQLCursor; P: TSQLDBParam; F: TField; UseOldValue: Boolean); override;
  132. Function ErrorOnUnknownType : Boolean;
  133. // Add connection to pool.
  134. procedure AddHandle(T: TPGHandle);
  135. {$IFNDEF VER3_2}
  136. function PortParamName: string; override;
  137. {$endif}
  138. procedure DoInternalConnect; override;
  139. procedure DoInternalDisconnect; override;
  140. function GetHandle : pointer; override;
  141. Function AllocateCursorHandle : TSQLCursor; override;
  142. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  143. Function AllocateTransactionHandle : TSQLHandle; override;
  144. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
  145. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); override;
  146. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  147. function Fetch(cursor : TSQLCursor) : boolean; override;
  148. procedure UnPrepareStatement(cursor : TSQLCursor); override;
  149. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  150. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  151. function RollBack(trans : TSQLHandle) : boolean; override;
  152. function Commit(trans : TSQLHandle) : boolean; override;
  153. procedure CommitRetaining(trans : TSQLHandle); override;
  154. function StartImplicitTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  155. function StartDBTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  156. procedure RollBackRetaining(trans : TSQLHandle); override;
  157. procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override;
  158. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor;ATransaction : TSQLTransaction); override;
  159. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  160. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  161. function GetNextValueSQL(const SequenceName: string; IncrementBy: Integer): string; override;
  162. public
  163. constructor Create(AOwner : TComponent); override;
  164. destructor Destroy; override;
  165. function GetConnectionInfo(InfoType:TConnInfoType): string; override;
  166. procedure CreateDB; override;
  167. procedure DropDB; override;
  168. published
  169. property DatabaseName;
  170. property KeepConnection;
  171. property LoginPrompt;
  172. property Params;
  173. property OnLogin;
  174. Property VerboseErrors : Boolean Read FVerboseErrors Write FVerboseErrors default true;
  175. end;
  176. { TPQConnectionDef }
  177. TPQConnectionDef = Class(TConnectionDef)
  178. Class Function TypeName : String; override;
  179. Class Function ConnectionClass : TSQLConnectionClass; override;
  180. Class Function Description : String; override;
  181. Class Function DefaultLibraryName : String; override;
  182. Class Function LoadFunction : TLibraryLoadFunction; override;
  183. Class Function UnLoadFunction : TLibraryUnLoadFunction; override;
  184. Class Function LoadedLibraryName: string; override;
  185. end;
  186. implementation
  187. uses math, strutils, FmtBCD;
  188. ResourceString
  189. SErrRollbackFailed = 'Rollback transaction failed';
  190. SErrCommitFailed = 'Commit transaction failed';
  191. SErrConnectionFailed = 'Connection to database failed';
  192. SErrTransactionFailed = 'Start of transacion failed';
  193. SErrExecuteFailed = 'Execution of query failed';
  194. SErrPrepareFailed = 'Preparation of query failed.';
  195. SErrUnPrepareFailed = 'Unpreparation of query failed.';
  196. const Oid_Bool = 16;
  197. Oid_Bytea = 17;
  198. Oid_char = 18;
  199. Oid_Text = 25;
  200. Oid_Oid = 26;
  201. Oid_Name = 19;
  202. Oid_Int8 = 20;
  203. Oid_int2 = 21;
  204. Oid_Int4 = 23;
  205. Oid_JSON = 114;
  206. Oid_Float4 = 700;
  207. Oid_Money = 790;
  208. Oid_Float8 = 701;
  209. Oid_Unknown = 705;
  210. Oid_MacAddr = 829;
  211. Oid_Inet = 869;
  212. Oid_bpchar = 1042;
  213. Oid_varchar = 1043;
  214. oid_date = 1082;
  215. oid_time = 1083;
  216. Oid_timeTZ = 1266;
  217. Oid_timestamp = 1114;
  218. Oid_timestampTZ = 1184;
  219. Oid_interval = 1186;
  220. oid_numeric = 1700;
  221. Oid_uuid = 2950;
  222. { TPQTransactionHandle }
  223. procedure TPQTransactionHandle.SetHandle(AValue: TPGHandle);
  224. begin
  225. if FHandle=AValue then Exit;
  226. FHandle:=AValue;
  227. end;
  228. { TPGHandle }
  229. constructor TPGHandle.Create(aConnection: TPQConnection; aDBName: string);
  230. begin
  231. FDBName:=aDBName;
  232. FConnection:=aConnection;
  233. FCursorList:=TThreadList.Create;
  234. FCursorList.Duplicates:=dupIgnore;
  235. {$IFDEF CPU64}
  236. FHandleID:=InterlockedIncrement64(_HID);
  237. {$ElSE}
  238. FHandleID:=InterlockedIncrement(_HID);
  239. {$ENDIF}
  240. {$IFDEF PQDEBUG}
  241. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] allocating handle ');
  242. {$ENDIF}
  243. end;
  244. destructor TPGHandle.Destroy;
  245. Var
  246. L : TList;
  247. I : integer;
  248. begin
  249. {$IFDEF PQDEBUG}
  250. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] Destroying handle ');
  251. {$ENDIF}
  252. L:=FCursorList.LockList;
  253. try
  254. For I:=0 to L.Count-1 do
  255. TPQCursor(L[i]).Handle:=Nil;
  256. finally
  257. FCursorList.UnlockList;
  258. end;
  259. FreeAndNil(FCursorList);
  260. inherited Destroy;
  261. end;
  262. function TPGHandle.GetConnected: Boolean;
  263. begin
  264. Result:=FNativeConn<>Nil;
  265. end;
  266. procedure TPGHandle.RegisterCursor(Cursor: TPQCursor);
  267. begin
  268. if Cursor.handle=Self then
  269. begin
  270. {$IFDEF PQDEBUG}
  271. Writeln('>>> ',FHandleID, ' [',TThread.CurrentThread.ThreadID, '] cursor ',PtrInt(Cursor),' already registered');
  272. {$ENDIF}
  273. exit;
  274. end;
  275. {$IFDEF PQDEBUG}
  276. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] registering cursor ',PtrInt(Cursor));
  277. {$ENDIF}
  278. FCursorList.Add(Cursor);
  279. Cursor.Handle:=Self;
  280. end;
  281. procedure TPGHandle.UnRegisterCursor(Cursor: TPQCursor);
  282. Var
  283. L : TList;
  284. begin
  285. {$IFDEF PQDEBUG}
  286. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] unregistering cursor ',PtrInt(Cursor));
  287. {$ENDIF}
  288. Cursor.Handle:=Nil;
  289. FCursorList.Remove(Cursor);
  290. L:=FCursorList.LockList;
  291. try
  292. Used:=L.Count>0;
  293. {$IFDEF PQDEBUG}
  294. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] unregistering cursor ',PtrInt(Cursor),'. Handle still used: ',Used);
  295. {$ENDIF}
  296. finally
  297. FCursorList.UnlockList;
  298. end;
  299. end;
  300. procedure TPGHandle.UnprepareStatement(Cursor: TPQCursor; Force : Boolean);
  301. var
  302. SQL : String;
  303. begin
  304. if Cursor.handle<>Self then
  305. DatabaseError('Internal error: unpreparing in different transaction!');
  306. if Assigned(Cursor.res) then
  307. PQclear(Cursor.res);
  308. Cursor.res:=nil;
  309. SQL:='deallocate '+Cursor.StmtName;
  310. Cursor.StmtName:='';
  311. if Force then
  312. Cursor.FPrepared := False;
  313. if not Cursor.FPrepared then
  314. Exit;
  315. if (PQtransactionStatus(FNativeConn) <> PQTRANS_INERROR) then
  316. begin
  317. Exec(SQL,True,SErrUnPrepareFailed);
  318. Cursor.FPrepared := False;
  319. end;
  320. UnregisterCursor(Cursor);
  321. end;
  322. procedure TPGHandle.Connect;
  323. var
  324. S : AnsiString;
  325. begin
  326. S:=Connection.GetConnectionString(FDBName);
  327. FNativeConn:=PQconnectdb(PAnsiChar(S));
  328. CheckConnectionStatus;
  329. FConnected:=True;
  330. S:=Connection.CharSet;
  331. if (S<>'') then
  332. PQsetClientEncoding(FNativeConn,PAnsiChar(S));
  333. end;
  334. { TPQCursor }
  335. destructor TPQCursor.Destroy;
  336. begin
  337. if Assigned(Handle) then
  338. Handle.UnRegisterCursor(Self);
  339. inherited Destroy;
  340. end;
  341. procedure TPQCursor.SetHandle(AValue: TPGhandle);
  342. begin
  343. if FHandle=AValue then Exit;
  344. if (FHandle<>Nil) and (aValue<>Nil) then
  345. begin
  346. {$IFDEF PQDEBUG}
  347. writeln('>>> ',ptrint(Self),' [',TThread.CurrentThread.ThreadID, '] Setting handle while handle still valid');
  348. {$endif}
  349. end;
  350. FHandle:=AValue;
  351. end;
  352. function TPQCursor.GetFieldBinding(F: TFieldDef): PFieldBinding;
  353. Var
  354. I : Integer;
  355. begin
  356. Result:=Nil;
  357. if (F=Nil) then exit;
  358. // This is an optimization: it is so for 99% of cases (FieldNo-1=array index)
  359. if F is TSQLDBFieldDef then
  360. Result:=PFieldBinding(TSQLDBFieldDef(F).SQLDBData)
  361. else If (FieldBinding[F.FieldNo-1].FieldDef=F) then
  362. Result:=@FieldBinding[F.FieldNo-1]
  363. else
  364. begin
  365. I:=Length(FieldBinding)-1;
  366. While (I>=0) and (FieldBinding[i].FieldDef<>F) do
  367. Dec(I);
  368. if I>=0 then
  369. Result:=@FieldBinding[i];
  370. end;
  371. end;
  372. { TPQConnection }
  373. constructor TPQConnection.Create(AOwner : TComponent);
  374. begin
  375. inherited;
  376. FConnOptions := FConnOptions + [sqSupportParams, sqSupportEmptyDatabaseName, sqEscapeRepeat, sqEscapeSlash, sqImplicitTransaction,sqSupportReturning,sqSequences];
  377. FieldNameQuoteChars:=DoubleQuotes;
  378. VerboseErrors:=True;
  379. FHandlePool:=TThreadlist.Create;
  380. end;
  381. destructor TPQConnection.Destroy;
  382. begin
  383. // We must disconnect here. If it is done in inherited, then connection pool is gone.
  384. Connected:=False;
  385. FreeAndNil(FHandlePool);
  386. inherited destroy;
  387. end;
  388. procedure TPQConnection.CreateDB;
  389. begin
  390. ExecuteDirectPG('CREATE DATABASE ' +DatabaseName);
  391. end;
  392. procedure TPQConnection.DropDB;
  393. begin
  394. ExecuteDirectPG('DROP DATABASE ' +DatabaseName);
  395. end;
  396. procedure TPQConnection.ExecuteDirectPG(const Query: String);
  397. var
  398. AHandle : TPGHandle;
  399. begin
  400. CheckDisConnected;
  401. {$IfDef LinkDynamically}
  402. InitialisePostgres3;
  403. {$EndIf}
  404. aHandle:=TPGHandle.Create(Self,'template1');
  405. try
  406. aHandle.Connect;
  407. aHandle.Exec(Query,True,'Error executing query');
  408. finally
  409. aHandle.Free;
  410. end;
  411. {$IfDef LinkDynamically}
  412. ReleasePostgres3;
  413. {$EndIf}
  414. end;
  415. procedure TPQConnection.GetExtendedFieldInfo(cursor: TPQCursor;
  416. Bindings: TFieldBindings);
  417. Var
  418. tt,tc,Tn,S : String;
  419. I,J : Integer;
  420. Res : PPGResult;
  421. toid : oid;
  422. begin
  423. s:='';
  424. For I:=0 to Length(Bindings)-1 do
  425. if (Bindings[i].TypeOID>0) then
  426. begin
  427. if (S<>'') then
  428. S:=S+', ';
  429. S:=S+IntToStr(Bindings[i].TypeOID);
  430. end;
  431. if (S='') then
  432. exit;
  433. Res:=Cursor.Handle.Exec(S,False,'Error getting typeinfo');
  434. try
  435. For I:=0 to PQntuples(Res)-1 do
  436. begin
  437. toid:=Strtoint(pqgetvalue(Res,i,0));
  438. tn:=pqgetvalue(Res,i,1);
  439. tt:=pqgetvalue(Res,i,2);
  440. tc:=pqgetvalue(Res,i,3);
  441. J:=length(Bindings)-1;
  442. while (J>= 0) do
  443. begin
  444. if (Bindings[j].TypeOID=toid) then
  445. Case tt of
  446. 'e':
  447. Bindings[j].ExtendedFieldType:=eftEnum;
  448. 'citext':
  449. Bindings[j].ExtendedFieldType:=eftCitext;
  450. end;
  451. Dec(J);
  452. end;
  453. end;
  454. finally
  455. PQClear(Res);
  456. end;
  457. end;
  458. procedure TPQConnection.ApplyFieldUpdate(C : TSQLCursor; P: TSQLDBParam; F: TField;
  459. UseOldValue: Boolean);
  460. begin
  461. inherited ApplyFieldUpdate(C,P, F, UseOldValue);
  462. if (C is TPQCursor) then
  463. P.SQLDBData:=TPQCursor(C).GetFieldBinding(F.FieldDef);
  464. end;
  465. function TPQConnection.ErrorOnUnknownType: Boolean;
  466. begin
  467. Result:=False;
  468. end;
  469. procedure TPQConnection.AddHandle(T: TPGHandle);
  470. begin
  471. FHandlePool.Add(T);
  472. end;
  473. function TPQConnection.GetTransactionHandle(trans : TSQLHandle): pointer;
  474. begin
  475. if (trans is TPQTransactionHandle) then
  476. Result:=Trans
  477. else
  478. DatabaseErrorFmt('Expected %s, got %s',[TPQTransactionHandle.ClassName,Trans.ClassName]);
  479. end;
  480. function TPQConnection.RollBack(trans : TSQLHandle) : boolean;
  481. var
  482. tr : TPGHandle;
  483. begin
  484. tr := (trans as TPQTransactionHandle).Handle as TPGHandle;
  485. TR.RollBack;
  486. result := true;
  487. end;
  488. function TPQConnection.Commit(trans : TSQLHandle) : boolean;
  489. var
  490. tr : TPGHandle;
  491. begin
  492. tr := (trans as TPQTransactionHandle).Handle;
  493. tr.Commit;
  494. Result:=True;
  495. end;
  496. procedure TPQConnection.RollBackRetaining(trans : TSQLHandle);
  497. var
  498. tr : TPGHandle;
  499. begin
  500. tr := (trans as TPQTransactionHandle).Handle;
  501. TR.RollBack;
  502. TR.StartTransaction;
  503. end;
  504. procedure TPQConnection.CommitRetaining(trans : TSQLHandle);
  505. var
  506. tr : TPGHandle;
  507. begin
  508. tr := (trans as TPQTransactionHandle).Handle as TPGHandle;
  509. TR.Commit;
  510. TR.StartTransaction;
  511. end;
  512. function TPQConnection.StartImplicitTransaction(trans : TSQLHandle; AParams : string) : boolean;
  513. var
  514. i : Integer;
  515. T : TPGHandle;
  516. L : TList;
  517. begin
  518. //find an unused connection in the pool
  519. i:=0;
  520. T:=Nil;
  521. L:=FHandlePool.LockList;
  522. try
  523. while (i<L.Count) do
  524. begin
  525. T:=TPGHandle(L[i]);
  526. if Not (T.Connected and T.Used) then
  527. break
  528. else
  529. T:=Nil;
  530. i:=i+1;
  531. end;
  532. // set to active now, so when we exit critical section,
  533. // it will be marked active and will not be found.
  534. if Assigned(T) then
  535. T.Used:=true;
  536. finally
  537. FHandlePool.UnLockList;
  538. end;
  539. if (T=Nil) then
  540. begin
  541. T:=TPGHandle.Create(Self,DatabaseName);
  542. T.Used:=True;
  543. AddHandle(T);
  544. end
  545. else
  546. begin
  547. {$IFDEF PQDEBUG}
  548. Writeln('>>> ',T.FHandleID,' [',TThread.CurrentThread.ThreadID, '] Reusing connection ');
  549. {$ENDIF}
  550. end;
  551. if (Not T.Connected) then
  552. T.Connect;
  553. (Trans as TPQTransactionHandle).handle:=T;
  554. Result := true;
  555. end;
  556. function TPQConnection.StartDBTransaction(trans: TSQLHandle; AParams: string
  557. ): boolean;
  558. Var
  559. tr : TPQTransactionHandle;
  560. begin
  561. Result:=StartImplicitTransaction(trans, AParams);
  562. if Result then
  563. begin
  564. tr:= trans as TPQTransactionHandle;
  565. tr.Handle.StartTransaction;
  566. end;
  567. end;
  568. procedure TPQConnection.DoInternalConnect;
  569. var
  570. T : TPGHandle;
  571. begin
  572. {$IfDef LinkDynamically}
  573. InitialisePostgres3;
  574. {$EndIf}
  575. inherited DoInternalConnect;
  576. T:=TPGHandle.Create(Self,DatabaseName);
  577. try
  578. T.Connect;
  579. T.Used:=false;
  580. // This only works for pg>=8.0, so timestamps won't work with earlier versions of pg which are compiled with integer_datetimes on
  581. if PQparameterStatus<>nil then
  582. FIntegerDateTimes := PQparameterStatus(T.NativeConn,'integer_datetimes') = 'on';
  583. except
  584. T.Free;
  585. DoInternalDisconnect;
  586. raise;
  587. end;
  588. AddHandle(T);
  589. end;
  590. procedure TPQConnection.DoInternalDisconnect;
  591. var
  592. i:integer;
  593. L : TList;
  594. T : TPGHandle;
  595. begin
  596. Inherited;
  597. L:=FHandlePool.LockList;
  598. try
  599. for i:=0 to L.Count-1 do
  600. begin
  601. T:=TPGHandle(L[i]);
  602. if T.Connected then
  603. T.Disconnect;
  604. T.Free;
  605. end;
  606. L.Clear;
  607. finally
  608. FHandlePool.UnLockList;
  609. end;
  610. {$IfDef LinkDynamically}
  611. ReleasePostgres3;
  612. {$EndIf}
  613. end;
  614. function TPQConnection.GetConnectionString(const aDBName : String): string;
  615. Procedure MaybeAdd(aName,aValue : String);
  616. begin
  617. if aValue<>'' then
  618. begin
  619. if aName<>'' then
  620. Result:=Result+' '+aName+'='''+aValue+''''
  621. else
  622. Result:=result+' '+aValue;
  623. end;
  624. end;
  625. begin
  626. Result:='';
  627. MaybeAdd('user',UserName);
  628. MaybeAdd('password',Password);
  629. MaybeAdd('host',HostName);
  630. MaybeAdd('dbname',aDBName);
  631. MaybeAdd('',Params.Text);
  632. end;
  633. procedure TPGHandle.Disconnect;
  634. Var
  635. PG : PPGconn;
  636. begin
  637. if FNativeConn=Nil then
  638. DatabaseError('Not connected to Postgres Server');
  639. PG:=FNativeConn;
  640. {$IFDEF PQDEBUG}
  641. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] ,Finishing connection');
  642. {$ENDIF}
  643. FNativeConn:=Nil;
  644. PQFinish(PG);
  645. end;
  646. procedure TPGHandle.StartTransaction;
  647. begin
  648. Exec('BEGIN',True,sErrTransactionFailed);
  649. FActive:=True;
  650. end;
  651. procedure TPGHandle.RollBack;
  652. Var
  653. L : TList;
  654. I : Integer;
  655. C : TPQCursor;
  656. begin
  657. if not Active then
  658. Exit;
  659. // unprepare statements associated with given transaction
  660. L:=FCursorList.LockList;
  661. try
  662. For I:=0 to L.Count-1 do
  663. begin
  664. C:=TPQCursor(L[i]);
  665. UnprepareStatement(C,False);
  666. end;
  667. L.Clear;
  668. finally
  669. FCursorList.UnlockList;
  670. end;
  671. FActive:=False;
  672. Exec('ROLLBACK',True,SErrRollbackFailed);
  673. end;
  674. procedure TPGHandle.Commit;
  675. begin
  676. Exec('COMMIT',True,SErrCommitFailed);
  677. end;
  678. procedure TPGHandle.Reset;
  679. begin
  680. {$IFDEF PQDEBUG}
  681. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] : Resetting');
  682. {$ENDIF}
  683. PQReset(FNativeConn);
  684. end;
  685. function TPGHandle.CheckConnectionStatus(doRaise: Boolean): Boolean;
  686. var sErr: string;
  687. begin
  688. Result:=False;
  689. if (PQstatus(FNativeConn) <> CONNECTION_BAD) then
  690. Exit(True);
  691. sErr := PQerrorMessage(FNativeConn);
  692. //make connection available in pool
  693. Disconnect;
  694. if DoRaise then
  695. DatabaseError(sErrConnectionFailed + ' (PostgreSQL: ' + sErr + ')');
  696. end;
  697. function TPGHandle.DescribePrepared(StmtName: String): PPGresult;
  698. Var
  699. S : AnsiString;
  700. begin
  701. S:=StmtName;
  702. Result:=PQdescribePrepared(FNativeConn,pchar(S));
  703. end;
  704. function TPGHandle.Exec(aSQL: String; aClearResult: Boolean; aError: String): PPGresult;
  705. Var
  706. S : UTF8String;
  707. Acts : TCheckResultActions;
  708. begin
  709. if FNativeConn=Nil then
  710. DatabaseError(IntToStr(FHandleID)+': No native PQ connection available');
  711. CheckConnectionStatus();
  712. S:=aSQL;
  713. {$IFDEF PQDEBUG}
  714. Writeln('>>> ',FHandleID,' [',TThread.CurrentThread.ThreadID, '] exec: ',S);
  715. {$ENDIF}
  716. Result:=PQexec(FNativeConn,PAnsiChar(S));
  717. acts:=[];
  718. if aClearResult then
  719. include(acts,craClear);
  720. CheckResultError(Result,acts,aError);
  721. end;
  722. function TPGHandle.ExecPrepared(stmtName: AnsiString; nParams: longint;
  723. paramValues: PPchar; paramLengths: Plongint; paramFormats: Plongint;
  724. aClearResult: Boolean): PPGresult;
  725. var
  726. s : string;
  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. setlength(lengths,l);
  1113. setlength(formats,l);
  1114. for i := 0 to AParams.Count -1 do if not AParams[i].IsNull then
  1115. begin
  1116. handled:=False;
  1117. case AParams[i].DataType of
  1118. ftDateTime:
  1119. s := FormatDateTime('yyyy"-"mm"-"dd hh":"nn":"ss.zzz', AParams[i].AsDateTime);
  1120. ftDate:
  1121. s := FormatDateTime('yyyy"-"mm"-"dd', AParams[i].AsDateTime);
  1122. ftTime:
  1123. s := FormatTimeInterval(AParams[i].AsDateTime);
  1124. ftFloat:
  1125. Str(AParams[i].AsFloat, s);
  1126. ftBCD:
  1127. Str(AParams[i].AsCurrency, s);
  1128. ftCurrency:
  1129. begin
  1130. cash:=NtoBE(round(AParams[i].AsCurrency*100));
  1131. setlength(s, sizeof(cash));
  1132. Move(cash, s[1], sizeof(cash));
  1133. end;
  1134. ftFmtBCD:
  1135. s := BCDToStr(AParams[i].AsFMTBCD, FSQLFormatSettings);
  1136. ftBlob, ftGraphic, ftVarBytes:
  1137. begin
  1138. Handled:=true;
  1139. bd:= AParams[i].AsBlob;
  1140. l:=length(BD);
  1141. if l>0 then
  1142. begin
  1143. GetMem(ar[i],l+1);
  1144. ar[i][l]:=#0;
  1145. Move(BD[0],ar[i]^, L);
  1146. lengths[i]:=l;
  1147. end;
  1148. end
  1149. else
  1150. s := GetAsString(AParams[i]);
  1151. end; {case}
  1152. {$IFDEF PQDEBUG}
  1153. WriteLn('Setting param ',aParams[i].Name,'(',aParams[i].DataType,') to ',S);
  1154. {$ENDIF}
  1155. if not handled then
  1156. begin
  1157. l:=length(s);
  1158. GetMem(ar[i],l+1);
  1159. StrMove(PAnsiChar(ar[i]), PAnsiChar(s), L+1);
  1160. lengths[i]:=L;
  1161. end;
  1162. if (AParams[i].DataType in [ftBlob,ftMemo,ftGraphic,ftCurrency,ftVarBytes]) then
  1163. Formats[i]:=1
  1164. else
  1165. Formats[i]:=0;
  1166. end
  1167. else
  1168. FreeAndNil(ar[i]);
  1169. PQCurs.res := PQCurs.Handle.ExecPrepared(PQCurs.StmtName,AParams.Count,@Ar[0],@Lengths[0],@Formats[0],False);
  1170. // PQCurs.res := PQexecPrepared(PQCurs.Handle.NativeConn,pchar(PQCurs.StmtName),AParams.Count,@Ar[0],@Lengths[0],@Formats[0],1);
  1171. for i := 0 to AParams.Count -1 do
  1172. FreeMem(ar[i]);
  1173. end
  1174. else
  1175. PQCurs.res := PQCurs.Handle.ExecPrepared(PQCurs.StmtName,0,nil,nil,nil,False);
  1176. end
  1177. else
  1178. begin
  1179. if PQCurs.handle=Nil then
  1180. (TObject(aTransaction.Handle) as TPQTransactionHandle).Handle.RegisterCursor(PQCurs);
  1181. if Assigned(AParams) and (AParams.Count > 0) then
  1182. begin
  1183. setlength(ParamNames,AParams.Count);
  1184. setlength(ParamValues,AParams.Count);
  1185. for i := 0 to AParams.Count -1 do
  1186. begin
  1187. ParamNames[AParams.Count-i-1] := '$'+inttostr(AParams[i].index+1);
  1188. ParamValues[AParams.Count-i-1] := GetAsSQLText(AParams[i]);
  1189. end;
  1190. s := stringsreplace(PQCurs.Statement,ParamNames,ParamValues,[rfReplaceAll]);
  1191. end
  1192. else
  1193. s := PQCurs.Statement;
  1194. PQCurs.Res:=PQCurs.Handle.Exec(S,False,SErrExecuteFailed);
  1195. end;
  1196. PQCurs.FSelectable := assigned(PQCurs.res) and (PQresultStatus(PQCurs.res)=PGRES_TUPLES_OK);
  1197. end;
  1198. procedure TPQConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs);
  1199. var
  1200. i : integer;
  1201. asize : integer;
  1202. aoid : oid;
  1203. fieldtype : tfieldtype;
  1204. nFields : integer;
  1205. b : Boolean;
  1206. Q : TPQCursor;
  1207. FD : TSQLDBFieldDef;
  1208. FB : PFieldBinding;
  1209. begin
  1210. B:=False;
  1211. Q:=cursor as TPQCursor;
  1212. with Q do
  1213. begin
  1214. nFields := PQnfields(Res);
  1215. setlength(FieldBinding,nFields);
  1216. for i := 0 to nFields-1 do
  1217. begin
  1218. fieldtype := TranslateFldType(Res, i, asize, aoid );
  1219. FD := AddFieldDef(FieldDefs, i+1, PQfname(Res, i), fieldtype, asize, -1, False, False, False) as TSQLDBFieldDef;
  1220. With FD do
  1221. begin
  1222. SQLDBData:=@FieldBinding[i];
  1223. FieldBinding[i].Index:=i;
  1224. FieldBinding[i].FieldDef:=FD;
  1225. FieldBinding[i].TypeOID:=aOID;
  1226. B:=B or (aOID>0);
  1227. end;
  1228. end;
  1229. CurTuple := -1;
  1230. end;
  1231. if B then
  1232. begin
  1233. // get all information in 1 go.
  1234. GetExtendedFieldInfo(Q,Q.FieldBinding);
  1235. For I:=0 to Length(Q.FieldBinding)-1 do
  1236. begin
  1237. FB:[email protected][i];
  1238. if (FB^.TypeOID>0) then
  1239. begin
  1240. FD:=FB^.FieldDef;
  1241. Case FB^.ExtendedFieldType of
  1242. eftEnum :
  1243. begin
  1244. FD.DataType:=ftString;
  1245. FD.Size:=64;
  1246. //FD.Attributes:=FD.Attributes+[faReadonly];
  1247. end;
  1248. eftCitext:
  1249. begin
  1250. FD.DataType:=ftMemo;
  1251. end
  1252. else
  1253. if ErrorOnUnknownType then
  1254. DatabaseError('Unhandled field type :'+FB^.TypeName,Self);
  1255. end;
  1256. end;
  1257. end;
  1258. end;
  1259. end;
  1260. function TPQConnection.GetHandle: pointer;
  1261. var
  1262. i:integer;
  1263. L : TList;
  1264. T : TPGHandle;
  1265. begin
  1266. result:=nil;
  1267. if not Connected then
  1268. exit;
  1269. //Get any handle that is (still) connected
  1270. L:=FHandlePool.LockList;
  1271. try
  1272. I:=L.Count-1;
  1273. While (I>=0) and (Result=Nil) do
  1274. begin
  1275. T:=TPGHandle(L[i]);
  1276. if T.Connected and T.CheckConnectionStatus(False) then
  1277. Result:=T;
  1278. Dec(I);
  1279. end;
  1280. finally
  1281. FHandlePool.UnLockList;
  1282. end;
  1283. if Result<>Nil then
  1284. exit;
  1285. //Nothing connected!! Reconnect
  1286. // T is element 0 after loop
  1287. if Not Assigned(T) then
  1288. begin
  1289. T:=TPGHandle.Create(Self,DatabaseName);
  1290. AddHandle(T);
  1291. end;
  1292. T.Connect;
  1293. Result:=T;
  1294. end;
  1295. function TPQConnection.Fetch(cursor : TSQLCursor) : boolean;
  1296. begin
  1297. with cursor as TPQCursor do
  1298. begin
  1299. inc(CurTuple);
  1300. Result := (PQntuples(res)>CurTuple);
  1301. end;
  1302. end;
  1303. function TPQConnection.LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  1304. const NBASE=10000;
  1305. DAYS_PER_MONTH=30;
  1306. type TNumericRecord = record
  1307. Digits : SmallInt;
  1308. Weight : SmallInt;
  1309. Sign : SmallInt;
  1310. Scale : Smallint;
  1311. end;
  1312. TIntervalRec = packed record
  1313. time : int64;
  1314. day : longint;
  1315. month : longint;
  1316. end;
  1317. TMacAddrRec = packed record
  1318. a, b, c, d, e, f: byte;
  1319. end;
  1320. TInetRec = packed record
  1321. family : byte;
  1322. bits : byte;
  1323. is_cidr: byte;
  1324. nb : byte;
  1325. ipaddr : array[1..16] of byte;
  1326. end;
  1327. var
  1328. x,i : integer;
  1329. s : string;
  1330. li : Longint;
  1331. CurrBuff : pchar;
  1332. dbl : pdouble;
  1333. cur : currency;
  1334. NumericRecord : ^TNumericRecord;
  1335. guid : TGUID;
  1336. bcd : TBCD;
  1337. macaddr : ^TMacAddrRec;
  1338. inet : ^TInetRec;
  1339. begin
  1340. Createblob := False;
  1341. with cursor as TPQCursor do
  1342. begin
  1343. x := GetFieldBinding(FieldDef)^.Index;
  1344. // Joost, 5 jan 2006: I disabled the following, since it's useful for
  1345. // debugging, but it also slows things down. In principle things can only go
  1346. // wrong when FieldDefs is changed while the dataset is opened. A user just
  1347. // shoudn't do that. ;) (The same is done in IBConnection)
  1348. //if PQfname(Res, x) <> FieldDef.Name then
  1349. // DatabaseErrorFmt(SFieldNotFound,[FieldDef.Name],self);
  1350. if pqgetisnull(res,CurTuple,x)=1 then
  1351. result := false
  1352. else
  1353. begin
  1354. CurrBuff := pqgetvalue(res,CurTuple,x);
  1355. result := true;
  1356. case FieldDef.DataType of
  1357. ftInteger, ftSmallint, ftLargeInt :
  1358. case PQfsize(res, x) of // postgres returns big-endian numbers
  1359. sizeof(int64) : pint64(buffer)^ := BEtoN(pint64(CurrBuff)^); // INT8
  1360. sizeof(integer) : pinteger(buffer)^ := BEtoN(pinteger(CurrBuff)^); // INT4
  1361. sizeof(smallint) : psmallint(buffer)^ := BEtoN(psmallint(CurrBuff)^); // INT2
  1362. end; {case}
  1363. ftFloat :
  1364. case PQfsize(res, x) of // postgres returns big-endian numbers
  1365. sizeof(int64) : // FLOAT8
  1366. pint64(buffer)^ := BEtoN(pint64(CurrBuff)^);
  1367. sizeof(integer) : // FLOAT4
  1368. begin
  1369. li := BEtoN(pinteger(CurrBuff)^);
  1370. pdouble(buffer)^ := psingle(@li)^
  1371. end;
  1372. end; {case}
  1373. ftString, ftFixedChar :
  1374. begin
  1375. case PQftype(res, x) of
  1376. Oid_MacAddr:
  1377. begin
  1378. macaddr := Pointer(CurrBuff);
  1379. li := FormatBuf(Buffer^, FieldDef.Size, '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x', 29,
  1380. [macaddr^.a,macaddr^.b,macaddr^.c,macaddr^.d,macaddr^.e,macaddr^.f]);
  1381. end;
  1382. Oid_Inet:
  1383. begin
  1384. inet := Pointer(CurrBuff);
  1385. if inet^.nb = 4 then
  1386. li := FormatBuf(Buffer^, FieldDef.Size, '%d.%d.%d.%d', 11,
  1387. [inet^.ipaddr[1],inet^.ipaddr[2],inet^.ipaddr[3],inet^.ipaddr[4]])
  1388. else if inet^.nb = 16 then
  1389. li := FormatBuf(Buffer^, FieldDef.Size, '%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x:%x%.2x', 55,
  1390. [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]])
  1391. else
  1392. li := 0;
  1393. end
  1394. else
  1395. begin
  1396. li := pqgetlength(res,curtuple,x);
  1397. if li > FieldDef.Size then li := FieldDef.Size;
  1398. Move(CurrBuff^, Buffer^, li);
  1399. end;
  1400. end;
  1401. pchar(Buffer + li)^ := #0;
  1402. end;
  1403. ftBlob, ftMemo, ftVarBytes :
  1404. CreateBlob := True;
  1405. ftDate :
  1406. begin
  1407. dbl := pointer(buffer);
  1408. dbl^ := BEtoN(plongint(CurrBuff)^) + 36526;
  1409. end;
  1410. ftDateTime, ftTime :
  1411. begin
  1412. dbl := pointer(buffer);
  1413. if FIntegerDateTimes then
  1414. dbl^ := BEtoN(pint64(CurrBuff)^) / 1000000
  1415. else
  1416. pint64(dbl)^ := BEtoN(pint64(CurrBuff)^);
  1417. case PQftype(res, x) of
  1418. Oid_Timestamp, Oid_TimestampTZ:
  1419. dbl^ := dbl^ + 3.1558464E+009; // postgres counts seconds elapsed since 1-1-2000
  1420. Oid_Interval:
  1421. dbl^ := dbl^ + BEtoN(plongint(CurrBuff+ 8)^) * SecsPerDay
  1422. + BEtoN(plongint(CurrBuff+12)^) * SecsPerDay * DAYS_PER_MONTH;
  1423. end;
  1424. dbl^ := dbl^ / SecsPerDay;
  1425. // Now convert the mathematically-correct datetime to the
  1426. // illogical windows/delphi/fpc TDateTime:
  1427. if (dbl^ <= 0) and (frac(dbl^) < 0) then
  1428. dbl^ := trunc(dbl^)-2-frac(dbl^);
  1429. end;
  1430. ftBCD, ftFmtBCD:
  1431. begin
  1432. NumericRecord := pointer(CurrBuff);
  1433. NumericRecord^.Digits := BEtoN(NumericRecord^.Digits);
  1434. NumericRecord^.Weight := BEtoN(NumericRecord^.Weight);
  1435. NumericRecord^.Sign := BEtoN(NumericRecord^.Sign);
  1436. NumericRecord^.Scale := BEtoN(NumericRecord^.Scale);
  1437. inc(pointer(currbuff),sizeof(TNumericRecord));
  1438. if (NumericRecord^.Digits = 0) and (NumericRecord^.Scale = 0) then // = NaN, which is not supported by Currency-type, so we return NULL
  1439. result := false
  1440. else if FieldDef.DataType = ftBCD then
  1441. begin
  1442. cur := 0;
  1443. for i := 0 to NumericRecord^.Digits-1 do
  1444. begin
  1445. cur := cur + beton(pword(CurrBuff)^) * intpower(NBASE, NumericRecord^.weight-i);
  1446. inc(pointer(CurrBuff),2);
  1447. end;
  1448. if NumericRecord^.Sign <> 0 then cur := -cur;
  1449. Move(Cur, Buffer^, sizeof(currency));
  1450. end
  1451. else //ftFmtBCD
  1452. begin
  1453. bcd := 0;
  1454. for i := 0 to NumericRecord^.Digits-1 do
  1455. begin
  1456. BCDAdd(bcd, beton(pword(CurrBuff)^) * intpower(NBASE, NumericRecord^.weight-i), bcd);
  1457. inc(pointer(CurrBuff),2);
  1458. end;
  1459. if NumericRecord^.Sign <> 0 then BCDNegate(bcd);
  1460. Move(bcd, Buffer^, sizeof(bcd));
  1461. end;
  1462. end;
  1463. ftCurrency :
  1464. begin
  1465. dbl := pointer(buffer);
  1466. dbl^ := BEtoN(PInt64(CurrBuff)^) / 100;
  1467. end;
  1468. ftBoolean:
  1469. pchar(buffer)[0] := CurrBuff[0];
  1470. ftGuid:
  1471. begin
  1472. Move(CurrBuff^, guid, sizeof(guid));
  1473. guid.D1:=BEtoN(guid.D1);
  1474. guid.D2:=BEtoN(guid.D2);
  1475. guid.D3:=BEtoN(guid.D3);
  1476. s:=GUIDToString(guid);
  1477. StrPLCopy(PChar(Buffer), s, FieldDef.Size);
  1478. end
  1479. else
  1480. result := false;
  1481. end;
  1482. end;
  1483. end;
  1484. end;
  1485. {$IFNDEF VER3_2}
  1486. function TPQConnection.PortParamName: string;
  1487. begin
  1488. Result := 'port';
  1489. end;
  1490. {$ENDIF}
  1491. procedure TPQConnection.UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string);
  1492. var qry : TSQLQuery;
  1493. relname : string;
  1494. begin
  1495. if not assigned(Transaction) then
  1496. DatabaseError(SErrConnTransactionnSet);
  1497. if (length(TableName)>2) and (TableName[1]='"') and (TableName[length(TableName)]='"') then
  1498. relname := QuotedStr(AnsiDequotedStr(TableName, '"'))
  1499. else
  1500. relname := 'lower(' + QuotedStr(TableName) + ')'; // unquoted names are stored lower case in PostgreSQL which is incompatible with the SQL standard
  1501. qry := tsqlquery.Create(nil);
  1502. qry.transaction := Transaction;
  1503. qry.database := Self;
  1504. with qry do
  1505. begin
  1506. ReadOnly := True;
  1507. sql.clear;
  1508. sql.add('select '+
  1509. 'ic.relname as indexname, '+
  1510. 'tc.relname as tablename, '+
  1511. 'ia.attname, '+
  1512. 'i.indisprimary, '+
  1513. 'i.indisunique '+
  1514. 'from '+
  1515. 'pg_attribute ta, '+
  1516. 'pg_attribute ia, '+
  1517. 'pg_class tc, '+
  1518. 'pg_class ic, '+
  1519. 'pg_index i '+
  1520. 'where '+
  1521. '(i.indrelid = tc.oid) and '+
  1522. '(ta.attrelid = tc.oid) and '+
  1523. '(ia.attrelid = i.indexrelid) and '+
  1524. '(ic.oid = i.indexrelid) and '+
  1525. '(ta.attnum = i.indkey[ia.attnum-1]) and '+
  1526. '(tc.relname = ' + relname + ') '+
  1527. 'order by '+
  1528. 'ic.relname;');
  1529. open;
  1530. end;
  1531. while not qry.eof do with IndexDefs.AddIndexDef do
  1532. begin
  1533. Name := trim(qry.fields[0].asstring);
  1534. Fields := trim(qry.Fields[2].asstring);
  1535. If qry.fields[3].asboolean then options := options + [ixPrimary];
  1536. If qry.fields[4].asboolean then options := options + [ixUnique];
  1537. qry.next;
  1538. while (name = qry.fields[0].asstring) and (not qry.eof) do
  1539. begin
  1540. Fields := Fields + ';' + trim(qry.Fields[2].asstring);
  1541. qry.next;
  1542. end;
  1543. end;
  1544. qry.close;
  1545. qry.free;
  1546. end;
  1547. function TPQConnection.GetSchemaInfoSQL(SchemaType: TSchemaType;
  1548. SchemaObjectName, SchemaPattern: string): string;
  1549. var s : string;
  1550. begin
  1551. // select * from information_schema.tables with
  1552. // where table_schema [not] in ('pg_catalog','information_schema') may be better.
  1553. // But the following should work:
  1554. case SchemaType of
  1555. stTables : s := 'select '+
  1556. 'relfilenode as recno, '+
  1557. 'current_database() as catalog_name, '+
  1558. 'nspname as schema_name, '+
  1559. 'relname as table_name, '+
  1560. '0 as table_type '+
  1561. 'from pg_class c '+
  1562. 'left join pg_namespace n on c.relnamespace=n.oid '+
  1563. 'where (relkind=''r'') and not (nspname in (''pg_catalog'',''information_schema''))' +
  1564. 'order by relname';
  1565. stSysTables : s := 'select '+
  1566. 'relfilenode as recno, '+
  1567. 'current_database() as catalog_name, '+
  1568. 'nspname as schema_name, '+
  1569. 'relname as table_name, '+
  1570. '0 as table_type '+
  1571. 'from pg_class c '+
  1572. 'left join pg_namespace n on c.relnamespace=n.oid '+
  1573. 'where (relkind=''r'') and nspname in ((''pg_catalog'',''information_schema'')) ' + // only system tables
  1574. 'order by relname';
  1575. stColumns : s := 'select '+
  1576. 'a.attnum as recno, '+
  1577. 'current_database() as catalog_name, '+
  1578. 'nspname as schema_name, '+
  1579. 'c.relname as table_name, '+
  1580. 'a.attname as column_name, '+
  1581. 'a.attnum as column_position, '+
  1582. '0 as column_type, '+
  1583. 'a.atttypid as column_datatype, '+
  1584. 't.typname as column_typename, '+
  1585. '0 as column_subtype, '+
  1586. '0 as column_precision, '+
  1587. '0 as column_scale, '+
  1588. 'a.atttypmod as column_length, '+
  1589. 'not a.attnotnull as column_nullable '+
  1590. 'from pg_class c '+
  1591. 'join pg_attribute a on c.oid=a.attrelid '+
  1592. 'join pg_type t on t.oid=a.atttypid '+
  1593. 'left join pg_namespace n on c.relnamespace=n.oid '+
  1594. // This can lead to problems when case-sensitive tablenames are used.
  1595. 'where (a.attnum>0) and (not a.attisdropped) and (upper(c.relname)=''' + Uppercase(SchemaObjectName) + ''') '+
  1596. 'order by a.attname';
  1597. else
  1598. s := inherited;
  1599. end; {case}
  1600. result := s;
  1601. end;
  1602. function TPQConnection.GetNextValueSQL(const SequenceName: string; IncrementBy: Integer): string;
  1603. begin
  1604. Result := Format('SELECT nextval(''%s'')', [SequenceName]);
  1605. end;
  1606. procedure TPQConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef;
  1607. ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  1608. var
  1609. x : integer;
  1610. li : Longint;
  1611. begin
  1612. with cursor as TPQCursor do
  1613. begin
  1614. x := FieldBinding[FieldDef.FieldNo-1].Index;
  1615. li := pqgetlength(res,curtuple,x);
  1616. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer,li);
  1617. Move(pqgetvalue(res,CurTuple,x)^, ABlobBuf^.BlobBuffer^.Buffer^, li);
  1618. ABlobBuf^.BlobBuffer^.Size := li;
  1619. end;
  1620. end;
  1621. function TPQConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  1622. begin
  1623. if assigned(cursor) and assigned((cursor as TPQCursor).res) then
  1624. Result := StrToIntDef(PQcmdTuples((cursor as TPQCursor).res),-1)
  1625. else
  1626. Result := -1;
  1627. end;
  1628. function TPQConnection.GetConnectionInfo(InfoType: TConnInfoType): string;
  1629. begin
  1630. Result:='';
  1631. try
  1632. {$IFDEF LinkDynamically}
  1633. InitialisePostgres3;
  1634. {$ENDIF}
  1635. case InfoType of
  1636. citServerType:
  1637. Result:=TPQConnectionDef.TypeName;
  1638. citServerVersion,
  1639. citServerVersionString:
  1640. if Connected then
  1641. Result:=format('%6.6d', [PQserverVersion(GetHandle)]);
  1642. citClientName:
  1643. Result:=TPQConnectionDef.LoadedLibraryName;
  1644. else
  1645. Result:=inherited GetConnectionInfo(InfoType);
  1646. end;
  1647. finally
  1648. {$IFDEF LinkDynamically}
  1649. ReleasePostgres3;
  1650. {$ENDIF}
  1651. end;
  1652. end;
  1653. { TPQConnectionDef }
  1654. class function TPQConnectionDef.TypeName: String;
  1655. begin
  1656. Result:='PostgreSQL';
  1657. end;
  1658. class function TPQConnectionDef.ConnectionClass: TSQLConnectionClass;
  1659. begin
  1660. Result:=TPQConnection;
  1661. end;
  1662. class function TPQConnectionDef.Description: String;
  1663. begin
  1664. Result:='Connect to a PostgreSQL database directly via the client library';
  1665. end;
  1666. class function TPQConnectionDef.DefaultLibraryName: String;
  1667. begin
  1668. {$IfDef LinkDynamically}
  1669. Result:=pqlib;
  1670. {$else}
  1671. Result:='';
  1672. {$endif}
  1673. end;
  1674. class function TPQConnectionDef.LoadFunction: TLibraryLoadFunction;
  1675. begin
  1676. {$IfDef LinkDynamically}
  1677. Result:=@InitialisePostgres3;
  1678. {$else}
  1679. Result:=Nil;
  1680. {$endif}
  1681. end;
  1682. class function TPQConnectionDef.UnLoadFunction: TLibraryUnLoadFunction;
  1683. begin
  1684. {$IfDef LinkDynamically}
  1685. Result:=@ReleasePostgres3;
  1686. {$else}
  1687. Result:=Nil;
  1688. {$endif}
  1689. end;
  1690. class function TPQConnectionDef.LoadedLibraryName: string;
  1691. begin
  1692. {$IfDef LinkDynamically}
  1693. Result:=Postgres3LoadedLibrary;
  1694. {$else}
  1695. Result:='';
  1696. {$endif}
  1697. end;
  1698. initialization
  1699. RegisterConnection(TPQConnectionDef);
  1700. finalization
  1701. UnRegisterConnection(TPQConnectionDef);
  1702. end.