pqconnection.pp 55 KB

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