ibconnection.pp 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663
  1. unit IBConnection;
  2. {$mode objfpc}{$H+}
  3. {$Define LinkDynamically}
  4. interface
  5. uses
  6. Classes, SysUtils, sqldb, db, dbconst, bufdataset,
  7. {$IfDef LinkDynamically}
  8. ibase60dyn;
  9. {$Else}
  10. ibase60;
  11. {$EndIf}
  12. const
  13. DEFDIALECT = 3;
  14. MAXBLOBSEGMENTSIZE = 65535; //Maximum number of bytes that fit in a blob segment.
  15. type
  16. TDatabaseInfo = record
  17. Dialect : integer; //Dialect set in database
  18. ODSMajorVersion : integer; //On-Disk Structure version of file
  19. ServerVersion : string; //Representation of major.minor (.build)
  20. ServerVersionString : string; //Complete version string, including name, platform
  21. end;
  22. EIBDatabaseError = class(ESQLDatabaseError)
  23. public
  24. property GDSErrorCode: integer read ErrorCode; deprecated 'Please use ErrorCode instead of GDSErrorCode'; // Nov 2014
  25. end;
  26. { TIBCursor }
  27. TIBCursor = Class(TSQLCursor)
  28. protected
  29. Status : array [0..19] of ISC_STATUS;
  30. Statement : pointer;
  31. SQLDA : PXSQLDA;
  32. in_SQLDA : PXSQLDA;
  33. ParamBinding : array of integer;
  34. FieldBinding : array of integer;
  35. end;
  36. TIBTrans = Class(TSQLHandle)
  37. protected
  38. TransactionHandle : pointer;
  39. TPB : string; // Transaction parameter buffer
  40. Status : array [0..19] of ISC_STATUS;
  41. end;
  42. { TIBConnection }
  43. TIBConnection = class (TSQLConnection)
  44. private
  45. FSQLDatabaseHandle : pointer;
  46. FStatus : array [0..19] of ISC_STATUS;
  47. FDatabaseInfo : TDatabaseInfo;
  48. FDialect : integer;
  49. FBlobSegmentSize : word; //required for backward compatibilty; not used
  50. procedure ConnectFB;
  51. procedure AllocSQLDA(var aSQLDA : PXSQLDA;Count : integer);
  52. // Metadata:
  53. procedure GetDatabaseInfo; //Queries for various information from server once connected
  54. procedure ResetDatabaseInfo; //Useful when disconnecting
  55. function GetDialect: integer;
  56. function GetODSMajorVersion: integer;
  57. function ParseServerVersion(const CompleteVersion: string): string; //Extract version info from complete version identification string
  58. // conversion methods
  59. procedure TranslateFldType(SQLType, SQLSubType, SQLLen, SQLScale : integer;
  60. var TrType : TFieldType; var TrLen : word);
  61. procedure GetDateTime(CurrBuff, Buffer : pointer; AType : integer);
  62. procedure SetDateTime(CurrBuff: pointer; PTime : TDateTime; AType : integer);
  63. procedure GetFloat(CurrBuff, Buffer : pointer; Size : Byte);
  64. procedure SetFloat(CurrBuff: pointer; Dbl: Double; Size: integer);
  65. procedure CheckError(ProcName : string; Status : PISC_STATUS);
  66. procedure SetParameters(cursor : TSQLCursor; aTransation : TSQLTransaction; AParams : TParams);
  67. procedure FreeSQLDABuffer(var aSQLDA : PXSQLDA);
  68. function IsDialectStored: boolean;
  69. protected
  70. procedure DoConnect; override;
  71. procedure DoInternalConnect; override;
  72. procedure DoInternalDisconnect; override;
  73. function GetHandle : pointer; override;
  74. Function AllocateCursorHandle : TSQLCursor; override;
  75. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  76. Function AllocateTransactionHandle : TSQLHandle; override;
  77. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
  78. procedure UnPrepareStatement(cursor : TSQLCursor); override;
  79. procedure FreeFldBuffers(cursor : TSQLCursor); override;
  80. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); override;
  81. procedure AddFieldDefs(cursor: TSQLCursor;FieldDefs : TfieldDefs); override;
  82. function Fetch(cursor : TSQLCursor) : boolean; override;
  83. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean; override;
  84. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  85. function Commit(trans : TSQLHandle) : boolean; override;
  86. function RollBack(trans : TSQLHandle) : boolean; override;
  87. function StartdbTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  88. procedure CommitRetaining(trans : TSQLHandle); override;
  89. procedure RollBackRetaining(trans : TSQLHandle); override;
  90. procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override;
  91. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  92. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction); override;
  93. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  94. public
  95. constructor Create(AOwner : TComponent); override;
  96. function GetConnectionInfo(InfoType:TConnInfoType): string; override;
  97. procedure CreateDB; override;
  98. procedure DropDB; override;
  99. //Segment size is not used in the code; property kept for backward compatibility
  100. property BlobSegmentSize : word read FBlobSegmentSize write FBlobSegmentSize; deprecated;
  101. property ODSMajorVersion : integer read GetODSMajorVersion; //ODS major version number; influences database compatibility/feature level.
  102. published
  103. property DatabaseName;
  104. property Dialect : integer read GetDialect write FDialect stored IsDialectStored default DEFDIALECT;
  105. property KeepConnection;
  106. property LoginPrompt;
  107. property Params;
  108. property OnLogin;
  109. end;
  110. { TIBConnectionDef }
  111. TIBConnectionDef = Class(TConnectionDef)
  112. Class Function TypeName : String; override;
  113. Class Function ConnectionClass : TSQLConnectionClass; override;
  114. Class Function Description : String; override;
  115. Class Function DefaultLibraryName : String; override;
  116. Class Function LoadFunction : TLibraryLoadFunction; override;
  117. Class Function UnLoadFunction : TLibraryUnLoadFunction; override;
  118. Class Function LoadedLibraryName: string; override;
  119. end;
  120. implementation
  121. uses
  122. StrUtils, FmtBCD;
  123. const
  124. SQL_BOOLEAN_INTERBASE = 590;
  125. SQL_BOOLEAN_FIREBIRD = 32764;
  126. INVALID_DATA = -1;
  127. procedure TIBConnection.CheckError(ProcName : string; Status : PISC_STATUS);
  128. var
  129. ErrorCode : longint;
  130. Msg, SQLState : string;
  131. Buf : array [0..1023] of char;
  132. begin
  133. if ((Status[0] = 1) and (Status[1] <> 0)) then
  134. begin
  135. ErrorCode := Status[1];
  136. {$IFDEF LinkDynamically}
  137. if assigned(fb_sqlstate) then // >= Firebird 2.5
  138. begin
  139. fb_sqlstate(Buf, Status);
  140. SQLState := StrPas(Buf);
  141. end;
  142. {$ENDIF}
  143. Msg := '';
  144. while isc_interprete(Buf, @Status) > 0 do
  145. Msg := Msg + LineEnding + ' -' + StrPas(Buf);
  146. raise EIBDatabaseError.CreateFmt('%s : %s', [ProcName,Msg], Self, ErrorCode, SQLState);
  147. end;
  148. end;
  149. constructor TIBConnection.Create(AOwner : TComponent);
  150. begin
  151. inherited;
  152. FConnOptions := FConnOptions + [sqSupportParams] + [sqEscapeRepeat];
  153. FBlobSegmentSize := 65535; //Shows we're using the maximum segment size
  154. FDialect := INVALID_DATA;
  155. ResetDatabaseInfo;
  156. end;
  157. function TIBConnection.GetTransactionHandle(trans : TSQLHandle): pointer;
  158. begin
  159. Result := (trans as TIBtrans).TransactionHandle;
  160. end;
  161. function TIBConnection.Commit(trans : TSQLHandle) : boolean;
  162. begin
  163. result := false;
  164. with (trans as TIBTrans) do
  165. if isc_commit_transaction(@Status[0], @TransactionHandle) <> 0 then
  166. CheckError('Commit', Status)
  167. else result := true;
  168. end;
  169. function TIBConnection.RollBack(trans : TSQLHandle) : boolean;
  170. begin
  171. result := false;
  172. if isc_rollback_transaction(@TIBTrans(trans).Status[0], @TIBTrans(trans).TransactionHandle) <> 0 then
  173. CheckError('Rollback', TIBTrans(trans).Status)
  174. else result := true;
  175. end;
  176. function TIBConnection.StartDBTransaction(trans : TSQLHandle;AParams : String) : boolean;
  177. var
  178. DBHandle : pointer;
  179. tr : TIBTrans;
  180. i : integer;
  181. s : string;
  182. begin
  183. result := false;
  184. DBHandle := GetHandle;
  185. tr := trans as TIBtrans;
  186. with tr do
  187. begin
  188. TPB := chr(isc_tpb_version3);
  189. i := 1;
  190. s := ExtractSubStr(AParams,i,stdWordDelims);
  191. while s <> '' do
  192. begin
  193. if s='isc_tpb_write' then TPB := TPB + chr(isc_tpb_write)
  194. else if s='isc_tpb_read' then TPB := TPB + chr(isc_tpb_read)
  195. else if s='isc_tpb_consistency' then TPB := TPB + chr(isc_tpb_consistency)
  196. else if s='isc_tpb_concurrency' then TPB := TPB + chr(isc_tpb_concurrency)
  197. else if s='isc_tpb_read_committed' then TPB := TPB + chr(isc_tpb_read_committed)
  198. else if s='isc_tpb_rec_version' then TPB := TPB + chr(isc_tpb_rec_version)
  199. else if s='isc_tpb_no_rec_version' then TPB := TPB + chr(isc_tpb_no_rec_version)
  200. else if s='isc_tpb_wait' then TPB := TPB + chr(isc_tpb_wait)
  201. else if s='isc_tpb_nowait' then TPB := TPB + chr(isc_tpb_nowait)
  202. else if s='isc_tpb_shared' then TPB := TPB + chr(isc_tpb_shared)
  203. else if s='isc_tpb_protected' then TPB := TPB + chr(isc_tpb_protected)
  204. else if s='isc_tpb_exclusive' then TPB := TPB + chr(isc_tpb_exclusive)
  205. else if s='isc_tpb_lock_read' then TPB := TPB + chr(isc_tpb_lock_read)
  206. else if s='isc_tpb_lock_write' then TPB := TPB + chr(isc_tpb_lock_write)
  207. else if s='isc_tpb_verb_time' then TPB := TPB + chr(isc_tpb_verb_time)
  208. else if s='isc_tpb_commit_time' then TPB := TPB + chr(isc_tpb_commit_time)
  209. else if s='isc_tpb_ignore_limbo' then TPB := TPB + chr(isc_tpb_ignore_limbo)
  210. else if s='isc_tpb_autocommit' then TPB := TPB + chr(isc_tpb_autocommit)
  211. else if s='isc_tpb_restart_requests' then TPB := TPB + chr(isc_tpb_restart_requests)
  212. else if s='isc_tpb_no_auto_undo' then TPB := TPB + chr(isc_tpb_no_auto_undo);
  213. s := ExtractSubStr(AParams,i,stdWordDelims);
  214. end;
  215. TransactionHandle := nil;
  216. if isc_start_transaction(@Status[0], @TransactionHandle, 1,
  217. [@DBHandle, Length(TPB), @TPB[1]]) <> 0 then
  218. CheckError('StartTransaction',Status)
  219. else Result := True;
  220. end;
  221. end;
  222. procedure TIBConnection.CommitRetaining(trans : TSQLHandle);
  223. begin
  224. with trans as TIBtrans do
  225. if isc_commit_retaining(@Status[0], @TransactionHandle) <> 0 then
  226. CheckError('CommitRetaining', Status);
  227. end;
  228. procedure TIBConnection.RollBackRetaining(trans : TSQLHandle);
  229. begin
  230. with trans as TIBtrans do
  231. if isc_rollback_retaining(@Status[0], @TransactionHandle) <> 0 then
  232. CheckError('RollBackRetaining', Status);
  233. end;
  234. procedure TIBConnection.DropDB;
  235. begin
  236. CheckDisConnected;
  237. {$IfDef LinkDynamically}
  238. InitialiseIBase60;
  239. {$EndIf}
  240. ConnectFB;
  241. if isc_drop_database(@FStatus[0], @FSQLDatabaseHandle) <> 0 then
  242. CheckError('DropDB', FStatus);
  243. {$IfDef LinkDynamically}
  244. ReleaseIBase60;
  245. {$EndIf}
  246. end;
  247. procedure TIBConnection.CreateDB;
  248. var ASQLDatabaseHandle,
  249. ASQLTransactionHandle : pointer;
  250. CreateSQL : String;
  251. pagesize : String;
  252. begin
  253. CheckDisConnected;
  254. {$IfDef LinkDynamically}
  255. InitialiseIBase60;
  256. {$EndIf}
  257. ASQLDatabaseHandle := nil;
  258. ASQLTransactionHandle := nil;
  259. CreateSQL := 'CREATE DATABASE ';
  260. if HostName <> '' then CreateSQL := CreateSQL + ''''+ HostName+':'+DatabaseName + ''''
  261. else CreateSQL := CreateSQL + '''' + DatabaseName + '''';
  262. if UserName <> '' then
  263. CreateSQL := CreateSQL + ' USER ''' + Username + '''';
  264. if Password <> '' then
  265. CreateSQL := CreateSQL + ' PASSWORD ''' + Password + '''';
  266. pagesize := params.Values['PAGE_SIZE'];
  267. if pagesize <> '' then
  268. CreateSQL := CreateSQL + ' PAGE_SIZE '+pagesize;
  269. if CharSet <> '' then
  270. CreateSQL := CreateSQL + ' DEFAULT CHARACTER SET ' + CharSet;
  271. if isc_dsql_execute_immediate(@FStatus[0],@ASQLDatabaseHandle,@ASQLTransactionHandle,length(CreateSQL),@CreateSQL[1],Dialect,nil) <> 0 then
  272. CheckError('CreateDB', FStatus);
  273. if isc_detach_database(@FStatus[0], @ASQLDatabaseHandle) <> 0 then
  274. CheckError('CreateDB', FStatus);
  275. {$IfDef LinkDynamically}
  276. ReleaseIBase60;
  277. {$EndIf}
  278. end;
  279. procedure TIBConnection.DoInternalConnect;
  280. begin
  281. {$IfDef LinkDynamically}
  282. InitialiseIBase60;
  283. {$EndIf}
  284. inherited dointernalconnect;
  285. ConnectFB;
  286. end;
  287. procedure TIBConnection.DoInternalDisconnect;
  288. begin
  289. Inherited;
  290. FDialect := INVALID_DATA;
  291. if not Connected then
  292. begin
  293. ResetDatabaseInfo;
  294. FSQLDatabaseHandle := nil;
  295. Exit;
  296. end;
  297. if isc_detach_database(@FStatus[0], @FSQLDatabaseHandle) <> 0 then
  298. CheckError('Close', FStatus);
  299. {$IfDef LinkDynamically}
  300. ReleaseIBase60;
  301. {$ELSE}
  302. // Shutdown embedded subsystem with timeout 300ms (Firebird 2.5+)
  303. // Required before unloading library; has no effect on non-embedded client
  304. if (pointer(fb_shutdown)<>nil) and (fb_shutdown(300,1)<>0) then
  305. begin
  306. //todo: log error; still try to unload library below as the timeout may have been insufficient
  307. end;
  308. {$EndIf}
  309. end;
  310. function TIBConnection.GetConnectionInfo(InfoType: TConnInfoType): string;
  311. begin
  312. result:='';
  313. {$IFDEF LinkDynamically}
  314. InitialiseIBase60;
  315. {$ENDIF}
  316. try
  317. case InfoType of
  318. citServerType:
  319. // Firebird returns own name in ServerVersion; Interbase 7.5 doesn't.
  320. if Pos('Firebird', FDatabaseInfo.ServerVersionString)=0 then
  321. result := 'Interbase'
  322. else
  323. result := 'Firebird';
  324. citServerVersion:
  325. // Firebird returns major.minor, Interbase major.minor.build
  326. result := FDatabaseInfo.ServerVersion;
  327. citServerVersionString:
  328. result := FDatabaseInfo.ServerVersionString;
  329. citClientName:
  330. result:=TIBConnectionDef.LoadedLibraryName;
  331. else
  332. //including citClientVersion, for which no single IB+FB and Win+*nux solution exists
  333. result:=inherited GetConnectionInfo(InfoType);
  334. end;
  335. finally
  336. {$IFDEF LinkDynamically}
  337. ReleaseIBase60;
  338. {$ENDIF}
  339. end;
  340. end;
  341. procedure TIBConnection.GetDatabaseInfo;
  342. // Asks server for multiple values
  343. const
  344. ResBufHigh = 512; //hopefully enough to include version string as well.
  345. var
  346. x : integer;
  347. Len : integer;
  348. ReqBuf : array [0..3] of byte;
  349. ResBuf : array [0..ResBufHigh] of byte; // should be big enough for version string etc
  350. begin
  351. ResetDatabaseInfo;
  352. if Connected then
  353. begin
  354. ReqBuf[0] := isc_info_ods_version;
  355. ReqBuf[1] := isc_info_version;
  356. ReqBuf[2] := isc_info_db_sql_dialect;
  357. ReqBuf[3] := isc_info_end;
  358. if isc_database_info(@FStatus[0], @FSQLDatabaseHandle, Length(ReqBuf),
  359. pchar(@ReqBuf[0]), SizeOf(ResBuf), pchar(@ResBuf[0])) <> 0 then
  360. CheckError('CacheServerInfo', FStatus);
  361. x := 0;
  362. while x < ResBufHigh+1 do
  363. case ResBuf[x] of
  364. isc_info_db_sql_dialect :
  365. begin
  366. Inc(x);
  367. Len := isc_vax_integer(pchar(@ResBuf[x]), 2);
  368. Inc(x, 2);
  369. FDatabaseInfo.Dialect := isc_vax_integer(pchar(@ResBuf[x]), Len);
  370. Inc(x, Len);
  371. end;
  372. isc_info_ods_version :
  373. begin
  374. Inc(x);
  375. Len := isc_vax_integer(pchar(@ResBuf[x]), 2);
  376. Inc(x, 2);
  377. FDatabaseInfo.ODSMajorVersion := isc_vax_integer(pchar(@ResBuf[x]), Len);
  378. Inc(x, Len);
  379. end;
  380. isc_info_version :
  381. begin
  382. Inc(x);
  383. Len := isc_vax_integer(pchar(@ResBuf[x]), 2);
  384. Inc(x, 2);
  385. SetString(FDatabaseInfo.ServerVersionString, PAnsiChar(@ResBuf[x + 2]), Len-2);
  386. FDatabaseInfo.ServerVersion := ParseServerVersion(FDatabaseInfo.ServerVersionString);
  387. Inc(x, Len);
  388. end;
  389. isc_info_end, isc_info_error : Break;
  390. isc_info_truncated : Break; //result buffer too small; fix your code!
  391. else
  392. inc(x);
  393. end;
  394. end;
  395. end;
  396. procedure TIBConnection.ResetDatabaseInfo;
  397. begin
  398. FDatabaseInfo.Dialect:=0;
  399. FDatabaseInfo.ODSMajorVersion:=0;
  400. FDatabaseInfo.ServerVersion:='';
  401. FDatabaseInfo.ServerVersionString:=''; // don't confuse applications with 'Firebird' or 'Interbase'
  402. end;
  403. function TIBConnection.GetODSMajorVersion: integer;
  404. begin
  405. result:=FDatabaseInfo.ODSMajorVersion;
  406. end;
  407. function TIBConnection.ParseServerVersion(const CompleteVersion: string): string;
  408. // String representation of integer version number derived from
  409. // major.minor.build => should give e.g. 020501
  410. const
  411. Delimiter = '.';
  412. DigitsPerNumber = 2;
  413. MaxNumbers = 3;
  414. var
  415. BeginPos,EndPos,StartLook,i: integer;
  416. NumericPart: string;
  417. begin
  418. result := '';
  419. // Ignore 6.x version number in front of "Firebird"
  420. StartLook := Pos('Firebird', CompleteVersion);
  421. if StartLook = 0 then
  422. StartLook := 1;
  423. BeginPos := 0;
  424. // Catch all numerics + decimal point:
  425. for i := StartLook to Length(CompleteVersion) do
  426. begin
  427. if (BeginPos > 0) and
  428. ((CompleteVersion[i] < '0') or (CompleteVersion[i] > '9')) and (CompleteVersion[i] <> '.') then
  429. begin
  430. EndPos := i - 1;
  431. break;
  432. end;
  433. if (BeginPos = 0) and
  434. (CompleteVersion[i] >= '0') and (CompleteVersion[i] <= '9') then
  435. begin
  436. BeginPos := i;
  437. end;
  438. end;
  439. if BeginPos > 0 then
  440. begin
  441. NumericPart := copy(CompleteVersion, BeginPos, 1+EndPos-BeginPos);
  442. BeginPos := 1;
  443. for i := 1 to MaxNumbers do
  444. begin
  445. EndPos := PosEx(Delimiter,NumericPart,BeginPos);
  446. if EndPos > 0 then
  447. begin
  448. result := result + rightstr(StringOfChar('0',DigitsPerNumber)+copy(NumericPart,BeginPos,EndPos-BeginPos),DigitsPerNumber);
  449. BeginPos := EndPos+1;
  450. end
  451. else
  452. begin
  453. result := result + rightstr(StringOfChar('0',DigitsPerNumber)+copy(NumericPart,BeginPos,Length(NumericPart)),DigitsPerNumber);
  454. break;
  455. end;
  456. end;
  457. result := leftstr(result + StringOfChar('0',DigitsPerNumber * MaxNumbers), DigitsPerNumber * MaxNumbers);
  458. end;
  459. end;
  460. procedure TIBConnection.ConnectFB;
  461. var
  462. ADatabaseName: String;
  463. DPB: string;
  464. begin
  465. DPB := chr(isc_dpb_version1);
  466. if (UserName <> '') then
  467. begin
  468. DPB := DPB + chr(isc_dpb_user_name) + chr(Length(UserName)) + UserName;
  469. if (Password <> '') then
  470. DPB := DPB + chr(isc_dpb_password) + chr(Length(Password)) + Password;
  471. end;
  472. if (Role <> '') then
  473. DPB := DPB + chr(isc_dpb_sql_role_name) + chr(Length(Role)) + Role;
  474. if Length(CharSet) > 0 then
  475. DPB := DPB + Chr(isc_dpb_lc_ctype) + Chr(Length(CharSet)) + CharSet;
  476. FSQLDatabaseHandle := nil;
  477. if HostName <> '' then ADatabaseName := HostName+':'+DatabaseName
  478. else ADatabaseName := DatabaseName;
  479. if isc_attach_database(@FStatus[0], Length(ADatabaseName), @ADatabaseName[1],
  480. @FSQLDatabaseHandle,
  481. Length(DPB), @DPB[1]) <> 0 then
  482. CheckError('DoInternalConnect', FStatus);
  483. end;
  484. function TIBConnection.GetDialect: integer;
  485. begin
  486. if FDialect = INVALID_DATA then
  487. begin
  488. if FDatabaseInfo.Dialect=0 then
  489. Result := DEFDIALECT
  490. else
  491. Result := FDatabaseInfo.Dialect;
  492. end else
  493. Result := FDialect;
  494. end;
  495. procedure TIBConnection.AllocSQLDA(var aSQLDA : PXSQLDA;Count : integer);
  496. begin
  497. FreeSQLDABuffer(aSQLDA);
  498. if count > -1 then
  499. begin
  500. reAllocMem(aSQLDA, XSQLDA_Length(Count));
  501. { Zero out the memory block to avoid problems with exceptions within the
  502. constructor of this class. }
  503. FillChar(aSQLDA^, XSQLDA_Length(Count), 0);
  504. aSQLDA^.Version := sqlda_version1;
  505. aSQLDA^.SQLN := Count;
  506. end
  507. else
  508. reAllocMem(aSQLDA,0);
  509. end;
  510. procedure TIBConnection.TranslateFldType(SQLType, SQLSubType, SQLLen, SQLScale : integer;
  511. var TrType : TFieldType; var TrLen : word);
  512. begin
  513. TrLen := 0;
  514. if SQLScale < 0 then
  515. begin
  516. TrLen := abs(SQLScale);
  517. if (TrLen <= MaxBCDScale) then //Note: NUMERIC(18,3) or (17,2) must be mapped to ftFmtBCD, but we do not know Precision
  518. TrType := ftBCD
  519. else
  520. TrType := ftFMTBcd;
  521. end
  522. else case (SQLType and not 1) of
  523. SQL_VARYING :
  524. begin
  525. TrType := ftString;
  526. TrLen := SQLLen;
  527. end;
  528. SQL_TEXT :
  529. begin
  530. TrType := ftFixedChar;
  531. TrLen := SQLLen;
  532. end;
  533. SQL_TYPE_DATE :
  534. TrType := ftDate;
  535. SQL_TYPE_TIME :
  536. TrType := ftTime;
  537. SQL_TIMESTAMP :
  538. TrType := ftDateTime;
  539. SQL_ARRAY :
  540. begin
  541. TrType := ftArray;
  542. TrLen := SQLLen;
  543. end;
  544. SQL_BLOB :
  545. begin
  546. if SQLSubType = isc_blob_text then
  547. TrType := ftMemo
  548. else
  549. TrType := ftBlob;
  550. TrLen := SQLLen;
  551. end;
  552. SQL_SHORT :
  553. TrType := ftSmallint;
  554. SQL_LONG :
  555. TrType := ftInteger;
  556. SQL_INT64 :
  557. TrType := ftLargeInt;
  558. SQL_DOUBLE :
  559. TrType := ftFloat;
  560. SQL_FLOAT :
  561. TrType := ftFloat;
  562. SQL_BOOLEAN_INTERBASE, SQL_BOOLEAN_FIREBIRD :
  563. TrType := ftBoolean;
  564. else
  565. TrType := ftUnknown;
  566. end;
  567. end;
  568. Function TIBConnection.AllocateCursorHandle : TSQLCursor;
  569. var curs : TIBCursor;
  570. begin
  571. curs := TIBCursor.create;
  572. curs.sqlda := nil;
  573. curs.statement := nil;
  574. curs.FPrepared := False;
  575. AllocSQLDA(curs.SQLDA,0);
  576. result := curs;
  577. end;
  578. procedure TIBConnection.DeAllocateCursorHandle(var cursor : TSQLCursor);
  579. begin
  580. if assigned(cursor) then with cursor as TIBCursor do
  581. begin
  582. AllocSQLDA(SQLDA,-1);
  583. AllocSQLDA(in_SQLDA,-1);
  584. end;
  585. FreeAndNil(cursor);
  586. end;
  587. Function TIBConnection.AllocateTransactionHandle : TSQLHandle;
  588. begin
  589. result := TIBTrans.create;
  590. end;
  591. procedure TIBConnection.PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams);
  592. var dh : pointer;
  593. tr : pointer;
  594. x : Smallint;
  595. info_request : string;
  596. resbuf : array[0..7] of byte;
  597. blockSize : integer;
  598. IBStatementType: integer;
  599. begin
  600. with cursor as TIBcursor do
  601. begin
  602. dh := GetHandle;
  603. if isc_dsql_allocate_statement(@Status[0], @dh, @Statement) <> 0 then
  604. CheckError('PrepareStatement', Status);
  605. tr := aTransaction.Handle;
  606. if assigned(AParams) and (AParams.count > 0) then
  607. buf := AParams.ParseSQL(buf,false,sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions,psInterbase,paramBinding);
  608. if isc_dsql_prepare(@Status[0], @tr, @Statement, 0, @Buf[1], Dialect, nil) <> 0 then
  609. CheckError('PrepareStatement', Status);
  610. if assigned(AParams) and (AParams.count > 0) then
  611. begin
  612. AllocSQLDA(in_SQLDA,Length(ParamBinding));
  613. if isc_dsql_describe_bind(@Status[0], @Statement, 1, in_SQLDA) <> 0 then
  614. CheckError('PrepareStatement', Status);
  615. if in_SQLDA^.SQLD > in_SQLDA^.SQLN then
  616. DatabaseError(SParameterCountIncorrect,self);
  617. {$push}
  618. {$R-}
  619. for x := 0 to in_SQLDA^.SQLD - 1 do with in_SQLDA^.SQLVar[x] do
  620. begin
  621. if ((SQLType and not 1) = SQL_VARYING) then
  622. SQLData := AllocMem(in_SQLDA^.SQLVar[x].SQLLen+2)
  623. else
  624. SQLData := AllocMem(in_SQLDA^.SQLVar[x].SQLLen);
  625. // Always force the creation of slqind for parameters. It could be
  626. // that a database trigger takes care of inserting null values, so
  627. // it should always be possible to pass null parameters. If that fails,
  628. // the database server will generate the appropriate error.
  629. sqltype := sqltype or 1;
  630. new(sqlind);
  631. end;
  632. {$pop}
  633. end
  634. else
  635. AllocSQLDA(in_SQLDA,0);
  636. // Get the statement type from firebird/interbase
  637. info_request := chr(isc_info_sql_stmt_type);
  638. if isc_dsql_sql_info(@Status[0],@Statement,Length(info_request), @info_request[1],sizeof(resbuf),@resbuf) <> 0 then
  639. CheckError('PrepareStatement', Status);
  640. assert(resbuf[0]=isc_info_sql_stmt_type);
  641. BlockSize:=isc_vax_integer(@resbuf[1],2);
  642. IBStatementType:=isc_vax_integer(@resbuf[3],blockSize);
  643. assert(resbuf[3+blockSize]=isc_info_end);
  644. // If the statementtype is isc_info_sql_stmt_exec_procedure then
  645. // override the statement type derived by parsing the query.
  646. // This to recognize statements like 'insert into .. returning' correctly
  647. case IBStatementType of
  648. isc_info_sql_stmt_select: FStatementType := stSelect;
  649. isc_info_sql_stmt_insert: FStatementType := stInsert;
  650. isc_info_sql_stmt_update: FStatementType := stUpdate;
  651. isc_info_sql_stmt_delete: FStatementType := stDelete;
  652. isc_info_sql_stmt_exec_procedure: FStatementType := stExecProcedure;
  653. end;
  654. FSelectable := FStatementType in [stSelect,stExecProcedure];
  655. if FSelectable then
  656. begin
  657. if isc_dsql_describe(@Status[0], @Statement, 1, SQLDA) <> 0 then
  658. CheckError('PrepareSelect', Status);
  659. if SQLDA^.SQLD > SQLDA^.SQLN then
  660. begin
  661. AllocSQLDA(SQLDA,SQLDA^.SQLD);
  662. if isc_dsql_describe(@Status[0], @Statement, 1, SQLDA) <> 0 then
  663. CheckError('PrepareSelect', Status);
  664. end;
  665. {$push}
  666. {$R-}
  667. for x := 0 to SQLDA^.SQLD - 1 do with SQLDA^.SQLVar[x] do
  668. begin
  669. if ((SQLType and not 1) = SQL_VARYING) then
  670. SQLData := AllocMem(SQLDA^.SQLVar[x].SQLLen+2)
  671. else
  672. SQLData := AllocMem(SQLDA^.SQLVar[x].SQLLen);
  673. if (SQLType and 1) = 1 then New(SQLInd);
  674. end;
  675. {$pop}
  676. end;
  677. FPrepared := True;
  678. end;
  679. end;
  680. procedure TIBConnection.UnPrepareStatement(cursor : TSQLCursor);
  681. begin
  682. with cursor as TIBcursor do
  683. if assigned(Statement) Then
  684. begin
  685. if isc_dsql_free_statement(@Status[0], @Statement, DSQL_Drop) <> 0 then
  686. CheckError('FreeStatement', Status);
  687. Statement := nil;
  688. FPrepared := False;
  689. end;
  690. end;
  691. procedure TIBConnection.FreeSQLDABuffer(var aSQLDA : PXSQLDA);
  692. var x : Smallint;
  693. begin
  694. {$push}
  695. {$R-}
  696. if assigned(aSQLDA) then
  697. for x := 0 to aSQLDA^.SQLN - 1 do
  698. begin
  699. reAllocMem(aSQLDA^.SQLVar[x].SQLData,0);
  700. if assigned(aSQLDA^.SQLVar[x].sqlind) then
  701. begin
  702. Dispose(aSQLDA^.SQLVar[x].sqlind);
  703. aSQLDA^.SQLVar[x].sqlind := nil;
  704. end
  705. end;
  706. {$pop}
  707. end;
  708. function TIBConnection.IsDialectStored: boolean;
  709. begin
  710. result := (FDialect<>INVALID_DATA);
  711. end;
  712. procedure TIBConnection.DoConnect;
  713. const NoQuotes: TQuoteChars = (' ',' ');
  714. begin
  715. inherited DoConnect;
  716. GetDatabaseInfo; //Get db dialect, db metadata
  717. if Dialect < 3 then
  718. FieldNameQuoteChars := NoQuotes
  719. else
  720. FieldNameQuoteChars := DoubleQuotes;
  721. end;
  722. procedure TIBConnection.FreeFldBuffers(cursor : TSQLCursor);
  723. begin
  724. with cursor as TIBCursor do
  725. begin
  726. FreeSQLDABuffer(SQLDA);
  727. FreeSQLDABuffer(in_SQLDA);
  728. SetLength(FieldBinding,0);
  729. end;
  730. end;
  731. procedure TIBConnection.Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams);
  732. var tr : pointer;
  733. out_SQLDA : PXSQLDA;
  734. begin
  735. tr := aTransaction.Handle;
  736. if Assigned(APArams) and (AParams.count > 0) then SetParameters(cursor, atransaction, AParams);
  737. with cursor as TIBCursor do
  738. begin
  739. if FStatementType = stExecProcedure then
  740. out_SQLDA := SQLDA
  741. else
  742. out_SQLDA := nil;
  743. if isc_dsql_execute2(@Status[0], @tr, @Statement, 1, in_SQLDA, out_SQLDA) <> 0 then
  744. CheckError('Execute', Status);
  745. end;
  746. end;
  747. procedure TIBConnection.AddFieldDefs(cursor: TSQLCursor;FieldDefs : TfieldDefs);
  748. var
  749. x : integer;
  750. TransLen : word;
  751. TransType : TFieldType;
  752. FD : TFieldDef;
  753. begin
  754. {$push}
  755. {$R-}
  756. with cursor as TIBCursor do
  757. begin
  758. setlength(FieldBinding,SQLDA^.SQLD);
  759. for x := 0 to SQLDA^.SQLD - 1 do
  760. begin
  761. TranslateFldType(SQLDA^.SQLVar[x].SQLType, SQLDA^.SQLVar[x].sqlsubtype, SQLDA^.SQLVar[x].SQLLen, SQLDA^.SQLVar[x].SQLScale,
  762. TransType, TransLen);
  763. FD := FieldDefs.Add(FieldDefs.MakeNameUnique(SQLDA^.SQLVar[x].AliasName), TransType,
  764. TransLen, (SQLDA^.SQLVar[x].sqltype and 1)=0, (x + 1));
  765. if TransType in [ftBCD, ftFmtBCD] then
  766. case (SQLDA^.SQLVar[x].sqltype and not 1) of
  767. SQL_SHORT : FD.precision := 4;
  768. SQL_LONG : FD.precision := 9;
  769. SQL_DOUBLE,
  770. SQL_INT64 : FD.precision := 18;
  771. else FD.precision := SQLDA^.SQLVar[x].SQLLen;
  772. end;
  773. // FD.DisplayName := SQLDA^.SQLVar[x].AliasName;
  774. FieldBinding[FD.FieldNo-1] := x;
  775. end;
  776. end;
  777. {$pop}
  778. end;
  779. function TIBConnection.GetHandle: pointer;
  780. begin
  781. Result := FSQLDatabaseHandle;
  782. end;
  783. function TIBConnection.Fetch(cursor : TSQLCursor) : boolean;
  784. var
  785. retcode : integer;
  786. begin
  787. with cursor as TIBCursor do
  788. begin
  789. if FStatementType = stExecProcedure then
  790. //do not fetch from a non-select statement, i.e. statement which has no cursor
  791. //on Firebird 2.5+ it leads to error 'Invalid cursor reference'
  792. if SQLDA^.SQLD = 0 then
  793. retcode := 100 //no more rows to retrieve
  794. else
  795. begin
  796. retcode := 0;
  797. SQLDA^.SQLD := 0; //hack: mark after first fetch
  798. end
  799. else
  800. retcode := isc_dsql_fetch(@Status[0], @Statement, 1, SQLDA);
  801. if (retcode <> 0) and (retcode <> 100) then
  802. CheckError('Fetch', Status);
  803. end;
  804. Result := (retcode = 0);
  805. end;
  806. function IntPower10(e: integer): double;
  807. const PreComputedPower10: array[0..9] of integer = (1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000);
  808. var n: integer;
  809. begin
  810. n := abs(e); //exponent can't be greater than 18
  811. if n <= 9 then
  812. Result := PreComputedPower10[n]
  813. else
  814. Result := PreComputedPower10[9] * PreComputedPower10[n-9];
  815. if e < 0 then
  816. Result := 1 / Result;
  817. end;
  818. procedure TIBConnection.SetParameters(cursor : TSQLCursor; aTransation : TSQLTransaction; AParams : TParams);
  819. var ParNr,SQLVarNr : integer;
  820. s : string;
  821. i : integer;
  822. si : smallint;
  823. li : LargeInt;
  824. currbuff : pchar;
  825. w : word;
  826. TransactionHandle : pointer;
  827. blobId : ISC_QUAD;
  828. blobHandle : Isc_blob_Handle;
  829. BlobSize,
  830. BlobBytesWritten : longint;
  831. procedure SetBlobParam;
  832. begin
  833. {$push}
  834. {$R-}
  835. with cursor as TIBCursor do
  836. begin
  837. TransactionHandle := aTransation.Handle;
  838. blobhandle := FB_API_NULLHANDLE;
  839. if isc_create_blob(@FStatus[0], @FSQLDatabaseHandle, @TransactionHandle, @blobHandle, @blobId) <> 0 then
  840. CheckError('TIBConnection.CreateBlobStream', FStatus);
  841. s := AParams[ParNr].AsString;
  842. BlobSize := length(s);
  843. BlobBytesWritten := 0;
  844. i := 0;
  845. // Write in segments of MAXBLOBSEGMENTSIZE, as that is the fastest.
  846. // We ignore BlobSegmentSize property.
  847. while BlobBytesWritten < (BlobSize-MAXBLOBSEGMENTSIZE) do
  848. begin
  849. isc_put_segment(@FStatus[0], @blobHandle, MAXBLOBSEGMENTSIZE, @s[(i*MAXBLOBSEGMENTSIZE)+1]);
  850. inc(BlobBytesWritten,MAXBLOBSEGMENTSIZE);
  851. inc(i);
  852. end;
  853. if BlobBytesWritten <> BlobSize then
  854. isc_put_segment(@FStatus[0], @blobHandle, BlobSize-BlobBytesWritten, @s[(i*MAXBLOBSEGMENTSIZE)+1]);
  855. if isc_close_blob(@FStatus[0], @blobHandle) <> 0 then
  856. CheckError('TIBConnection.CreateBlobStream isc_close_blob', FStatus);
  857. Move(blobId, in_sqlda^.SQLvar[SQLVarNr].SQLData^, in_SQLDA^.SQLVar[SQLVarNr].SQLLen);
  858. end;
  859. {$pop}
  860. end;
  861. Const
  862. DateF = 'yyyy-mm-dd';
  863. TimeF = 'hh:nn:ss';
  864. DateTimeF = DateF+' '+TimeF;
  865. var
  866. // This should be a pointer, because the ORIGINAL variables must
  867. // be modified.
  868. VSQLVar: ^XSQLVAR;
  869. P: TParam;
  870. ft : TFieldType;
  871. begin
  872. {$push}
  873. {$R-}
  874. with cursor as TIBCursor do for SQLVarNr := 0 to High(ParamBinding){AParams.count-1} do
  875. begin
  876. ParNr := ParamBinding[SQLVarNr];
  877. VSQLVar := @in_sqlda^.SQLvar[SQLVarNr];
  878. if AParams[ParNr].IsNull then
  879. VSQLVar^.SQLInd^ := -1
  880. else
  881. begin
  882. VSQLVar^.SQLInd^ := 0;
  883. case (VSQLVar^.sqltype and not 1) of
  884. SQL_LONG :
  885. begin
  886. if VSQLVar^.sqlscale = 0 then
  887. i := AParams[ParNr].AsInteger
  888. else
  889. i := Round(BCDToDouble(AParams[ParNr].AsFMTBCD) * IntPower10(-VSQLVar^.sqlscale)); //*any number of digits
  890. Move(i, VSQLVar^.SQLData^, VSQLVar^.SQLLen);
  891. end;
  892. SQL_SHORT, SQL_BOOLEAN_INTERBASE :
  893. begin
  894. if VSQLVar^.sqlscale = 0 then
  895. si := AParams[ParNr].AsSmallint
  896. else
  897. si := Round(AParams[ParNr].AsCurrency * IntPower10(-VSQLVar^.sqlscale));
  898. i := si;
  899. Move(i, VSQLVar^.SQLData^, VSQLVar^.SQLLen);
  900. end;
  901. SQL_BLOB :
  902. SetBlobParam;
  903. SQL_VARYING, SQL_TEXT :
  904. begin
  905. P:=AParams[ParNr];
  906. ft:=P.DataType;
  907. if Not (ft in [ftDate,ftTime,ftDateTime,ftTimeStamp]) then
  908. S:=P.AsString
  909. else
  910. begin
  911. Case ft of
  912. ftDate : S:=DateF;
  913. ftTime : S:=TimeF;
  914. ftDateTime,
  915. ftTimeStamp : S:=DateTimeF;
  916. end;
  917. S:=FormatDateTime(S,P.AsDateTime);
  918. end;
  919. w := length(s); // a word is enough, since the max-length of a string in interbase is 32k
  920. if ((VSQLVar^.SQLType and not 1) = SQL_VARYING) then
  921. begin
  922. VSQLVar^.SQLLen := w;
  923. ReAllocMem(VSQLVar^.SQLData, VSQLVar^.SQLLen+2);
  924. CurrBuff := VSQLVar^.SQLData;
  925. move(w,CurrBuff^,sizeof(w));
  926. inc(CurrBuff,2);
  927. end
  928. else
  929. begin
  930. // The buffer-length is always VSQLVar^.sqllen, nothing more, nothing less
  931. // so fill the complete buffer with valid data. Adding #0 will lead
  932. // to problems, because the #0 will be seen as a part of the (binary) string
  933. CurrBuff := VSQLVar^.SQLData;
  934. w := VSQLVar^.sqllen;
  935. s := PadRight(s,w);
  936. end;
  937. Move(s[1], CurrBuff^, w);
  938. end;
  939. SQL_TYPE_DATE, SQL_TYPE_TIME, SQL_TIMESTAMP :
  940. SetDateTime(VSQLVar^.SQLData, AParams[ParNr].AsDateTime, VSQLVar^.SQLType);
  941. SQL_INT64:
  942. begin
  943. if VSQLVar^.sqlscale = 0 then
  944. li := AParams[ParNr].AsLargeInt
  945. else if AParams[ParNr].DataType = ftFMTBcd then
  946. li := AParams[ParNr].AsFMTBCD * IntPower10(-VSQLVar^.sqlscale)
  947. else
  948. li := Round(AParams[ParNr].AsCurrency * IntPower10(-VSQLVar^.sqlscale));
  949. Move(li, VSQLVar^.SQLData^, VSQLVar^.SQLLen);
  950. end;
  951. SQL_DOUBLE, SQL_FLOAT:
  952. SetFloat(VSQLVar^.SQLData, AParams[ParNr].AsFloat, VSQLVar^.SQLLen);
  953. SQL_BOOLEAN_FIREBIRD:
  954. PByte(VSQLVar^.SQLData)^ := Byte(AParams[ParNr].AsBoolean);
  955. else
  956. DatabaseErrorFmt(SUnsupportedParameter,[Fieldtypenames[AParams[ParNr].DataType]],self);
  957. end {case}
  958. end;
  959. end;
  960. {$pop}
  961. end;
  962. function TIBConnection.LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer; out CreateBlob : boolean) : boolean;
  963. var
  964. VSQLVar : PXSQLVAR;
  965. VarcharLen : word;
  966. CurrBuff : pchar;
  967. c : currency;
  968. AFmtBcd : tBCD;
  969. function BcdDivPower10(Dividend: largeint; e: integer): TBCD;
  970. var d: double;
  971. begin
  972. d := Dividend / IntPower10(e);
  973. Result := StrToBCD( FloatToStr(d) );
  974. end;
  975. begin
  976. CreateBlob := False;
  977. with cursor as TIBCursor do
  978. begin
  979. {$push}
  980. {$R-}
  981. VSQLVar := @SQLDA^.SQLVar[ FieldBinding[FieldDef.FieldNo-1] ];
  982. // Joost, 5 jan 2006: I disabled the following, since it's useful for
  983. // debugging, but it also slows things down. In principle things can only go
  984. // wrong when FieldDefs is changed while the dataset is opened. A user just
  985. // shoudn't do that. ;) (The same is done in PQConnection)
  986. // if VSQLVar^.AliasName <> FieldDef.Name then
  987. // DatabaseErrorFmt(SFieldNotFound,[FieldDef.Name],self);
  988. if assigned(VSQLVar^.SQLInd) and (VSQLVar^.SQLInd^ = -1) then
  989. result := false
  990. else
  991. begin
  992. with VSQLVar^ do
  993. if ((SQLType and not 1) = SQL_VARYING) then
  994. begin
  995. Move(SQLData^, VarcharLen, 2);
  996. CurrBuff := SQLData + 2;
  997. end
  998. else
  999. begin
  1000. CurrBuff := SQLData;
  1001. VarCharLen := FieldDef.Size;
  1002. end;
  1003. Result := true;
  1004. case FieldDef.DataType of
  1005. ftBCD :
  1006. begin
  1007. case VSQLVar^.SQLLen of
  1008. 2 : c := PSmallint(CurrBuff)^ / IntPower10(-VSQLVar^.SQLScale);
  1009. 4 : c := PLongint(CurrBuff)^ / IntPower10(-VSQLVar^.SQLScale);
  1010. 8 : if Dialect < 3 then
  1011. c := PDouble(CurrBuff)^
  1012. else
  1013. c := PLargeint(CurrBuff)^ / IntPower10(-VSQLVar^.SQLScale);
  1014. else
  1015. Result := False; // Just to be sure, in principle this will never happen
  1016. end; {case}
  1017. Move(c, buffer^ , sizeof(c));
  1018. end;
  1019. ftFMTBcd :
  1020. begin
  1021. case VSQLVar^.SQLLen of
  1022. 2 : AFmtBcd := BcdDivPower10(PSmallint(CurrBuff)^, -VSQLVar^.SQLScale);
  1023. 4 : AFmtBcd := BcdDivPower10(PLongint(CurrBuff)^, -VSQLVar^.SQLScale);
  1024. 8 : if Dialect < 3 then
  1025. AFmtBcd := PDouble(CurrBuff)^
  1026. else
  1027. AFmtBcd := BcdDivPower10(PLargeint(CurrBuff)^, -VSQLVar^.SQLScale);
  1028. else
  1029. Result := False; // Just to be sure, in principle this will never happen
  1030. end; {case}
  1031. Move(AFmtBcd, buffer^ , sizeof(AFmtBcd));
  1032. end;
  1033. ftInteger :
  1034. begin
  1035. FillByte(buffer^,sizeof(Longint),0);
  1036. Move(CurrBuff^, Buffer^, VSQLVar^.SQLLen);
  1037. end;
  1038. ftLargeint :
  1039. begin
  1040. FillByte(buffer^,sizeof(LargeInt),0);
  1041. Move(CurrBuff^, Buffer^, VSQLVar^.SQLLen);
  1042. end;
  1043. ftSmallint :
  1044. begin
  1045. FillByte(buffer^,sizeof(Smallint),0);
  1046. Move(CurrBuff^, Buffer^, VSQLVar^.SQLLen);
  1047. end;
  1048. ftDate, ftTime, ftDateTime:
  1049. GetDateTime(CurrBuff, Buffer, VSQLVar^.SQLType);
  1050. ftString, ftFixedChar :
  1051. begin
  1052. Move(CurrBuff^, Buffer^, VarCharLen);
  1053. PChar(Buffer + VarCharLen)^ := #0;
  1054. end;
  1055. ftFloat :
  1056. GetFloat(CurrBuff, Buffer, VSQLVar^.SQLLen);
  1057. ftBlob,
  1058. ftMemo :
  1059. begin // load the BlobIb in field's buffer
  1060. FillByte(buffer^,sizeof(TBufBlobField),0);
  1061. Move(CurrBuff^, Buffer^, VSQLVar^.SQLLen);
  1062. end;
  1063. ftBoolean :
  1064. begin
  1065. case VSQLVar^.SQLLen of
  1066. 1: PWordBool(Buffer)^ := PByte(CurrBuff)^ <> 0; // Firebird
  1067. 2: PWordBool(Buffer)^ := PSmallint(CurrBuff)^ <> 0; // Interbase
  1068. end;
  1069. end
  1070. else
  1071. begin
  1072. result := false;
  1073. databaseerrorfmt(SUnsupportedFieldType, [Fieldtypenames[FieldDef.DataType], Self]);
  1074. end
  1075. end; { case }
  1076. end; { if/else }
  1077. {$pop}
  1078. end; { with cursor }
  1079. end;
  1080. {$DEFINE SUPPORT_MSECS}
  1081. {$IFDEF SUPPORT_MSECS}
  1082. const
  1083. IBDateOffset = 15018; //an offset from 17 Nov 1858.
  1084. IBTimeFractionsPerDay = SecsPerDay * ISC_TIME_SECONDS_PRECISION; //Number of Firebird time fractions per day
  1085. {$ELSE}
  1086. {$PACKRECORDS C}
  1087. type
  1088. TTm = record
  1089. tm_sec : longint;
  1090. tm_min : longint;
  1091. tm_hour : longint;
  1092. tm_mday : longint;
  1093. tm_mon : longint;
  1094. tm_year : longint;
  1095. tm_wday : longint;
  1096. tm_yday : longint;
  1097. tm_isdst: longint;
  1098. __tm_gmtoff : PtrInt; // Seconds east of UTC
  1099. __tm_zone : PAnsiChar; // Timezone abbreviation
  1100. end;
  1101. {$PACKRECORDS DEFAULT}
  1102. {$ENDIF}
  1103. procedure TIBConnection.GetDateTime(CurrBuff, Buffer : pointer; AType : integer);
  1104. var
  1105. {$IFNDEF SUPPORT_MSECS}
  1106. CTime : TTm; // C struct time
  1107. STime : TSystemTime; // System time
  1108. {$ENDIF}
  1109. PTime : TDateTime; // Pascal time
  1110. begin
  1111. case (AType and not 1) of
  1112. SQL_TYPE_DATE :
  1113. {$IFNDEF SUPPORT_MSECS}
  1114. isc_decode_sql_date(PISC_DATE(CurrBuff), @CTime);
  1115. {$ELSE}
  1116. PTime := PISC_DATE(CurrBuff)^ - IBDateOffset;
  1117. {$ENDIF}
  1118. SQL_TYPE_TIME :
  1119. {$IFNDEF SUPPORT_MSECS}
  1120. isc_decode_sql_time(PISC_TIME(CurrBuff), @CTime);
  1121. {$ELSE}
  1122. PTime := PISC_TIME(CurrBuff)^ / IBTimeFractionsPerDay;
  1123. {$ENDIF}
  1124. SQL_TIMESTAMP :
  1125. begin
  1126. {$IFNDEF SUPPORT_MSECS}
  1127. isc_decode_timestamp(PISC_TIMESTAMP(CurrBuff), @CTime);
  1128. {$ELSE}
  1129. PTime := ComposeDateTime(
  1130. PISC_TIMESTAMP(CurrBuff)^.timestamp_date - IBDateOffset,
  1131. PISC_TIMESTAMP(CurrBuff)^.timestamp_time / IBTimeFractionsPerDay
  1132. );
  1133. {$ENDIF}
  1134. end
  1135. else
  1136. Raise EIBDatabaseError.CreateFmt('Invalid parameter type for date Decode : %d',[(AType and not 1)]);
  1137. end;
  1138. {$IFNDEF SUPPORT_MSECS}
  1139. STime.Year := CTime.tm_year + 1900;
  1140. STime.Month := CTime.tm_mon + 1;
  1141. STime.Day := CTime.tm_mday;
  1142. STime.Hour := CTime.tm_hour;
  1143. STime.Minute := CTime.tm_min;
  1144. STime.Second := CTime.tm_sec;
  1145. STime.Millisecond := 0;
  1146. PTime := SystemTimeToDateTime(STime);
  1147. {$ENDIF}
  1148. Move(PTime, Buffer^, SizeOf(PTime));
  1149. end;
  1150. procedure TIBConnection.SetDateTime(CurrBuff: pointer; PTime : TDateTime; AType : integer);
  1151. {$IFNDEF SUPPORT_MSECS}
  1152. var
  1153. CTime : TTm; // C struct time
  1154. STime : TSystemTime; // System time
  1155. {$ENDIF}
  1156. begin
  1157. {$IFNDEF SUPPORT_MSECS}
  1158. DateTimeToSystemTime(PTime,STime);
  1159. CTime.tm_year := STime.Year - 1900;
  1160. CTime.tm_mon := STime.Month -1;
  1161. CTime.tm_mday := STime.Day;
  1162. CTime.tm_hour := STime.Hour;
  1163. CTime.tm_min := STime.Minute;
  1164. CTime.tm_sec := STime.Second;
  1165. {$ENDIF}
  1166. case (AType and not 1) of
  1167. SQL_TYPE_DATE :
  1168. {$IFNDEF SUPPORT_MSECS}
  1169. isc_encode_sql_date(@CTime, PISC_DATE(CurrBuff));
  1170. {$ELSE}
  1171. PISC_DATE(CurrBuff)^ := Trunc(PTime) + IBDateOffset;
  1172. {$ENDIF}
  1173. SQL_TYPE_TIME :
  1174. {$IFNDEF SUPPORT_MSECS}
  1175. isc_encode_sql_time(@CTime, PISC_TIME(CurrBuff));
  1176. {$ELSE}
  1177. PISC_TIME(CurrBuff)^ := Round(abs(Frac(PTime)) * IBTimeFractionsPerDay);
  1178. {$ENDIF}
  1179. SQL_TIMESTAMP :
  1180. begin
  1181. {$IFNDEF SUPPORT_MSECS}
  1182. isc_encode_timestamp(@CTime, PISC_TIMESTAMP(CurrBuff));
  1183. {$ELSE}
  1184. PISC_TIMESTAMP(CurrBuff)^.timestamp_date := Trunc(PTime) + IBDateOffset;
  1185. PISC_TIMESTAMP(CurrBuff)^.timestamp_time := Round(abs(Frac(PTime)) * IBTimeFractionsPerDay);
  1186. {$ENDIF}
  1187. end
  1188. else
  1189. Raise EIBDatabaseError.CreateFmt('Invalid parameter type for date encode : %d',[(AType and not 1)]);
  1190. end;
  1191. end;
  1192. function TIBConnection.GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string;
  1193. var s : string;
  1194. begin
  1195. case SchemaType of
  1196. stTables : s := 'select '+
  1197. 'rdb$relation_id as recno, '+
  1198. '''' + DatabaseName + ''' as catalog_name, '+
  1199. ''''' as schema_name, '+
  1200. 'rdb$relation_name as table_name, '+
  1201. '0 as table_type '+
  1202. 'from '+
  1203. 'rdb$relations '+
  1204. 'where '+
  1205. '(rdb$system_flag = 0 or rdb$system_flag is null) ' + // and rdb$view_blr is null
  1206. 'order by rdb$relation_name';
  1207. stSysTables : s := 'select '+
  1208. 'rdb$relation_id as recno, '+
  1209. '''' + DatabaseName + ''' as catalog_name, '+
  1210. ''''' as schema_name, '+
  1211. 'rdb$relation_name as table_name, '+
  1212. '0 as table_type '+
  1213. 'from '+
  1214. 'rdb$relations '+
  1215. 'where '+
  1216. '(rdb$system_flag > 0) ' + // and rdb$view_blr is null
  1217. 'order by rdb$relation_name';
  1218. stProcedures : s := 'select '+
  1219. 'rdb$procedure_id as recno, '+
  1220. '''' + DatabaseName + ''' as catalog_name, '+
  1221. ''''' as schema_name, '+
  1222. 'rdb$procedure_name as procedure_name, '+
  1223. '0 as procedure_type, '+
  1224. 'rdb$procedure_inputs as in_params, '+
  1225. 'rdb$procedure_outputs as out_params '+
  1226. 'from '+
  1227. 'rdb$procedures '+
  1228. 'WHERE '+
  1229. '(rdb$system_flag = 0 or rdb$system_flag is null)';
  1230. stColumns : s := 'SELECT '+
  1231. 'rdb$field_id as recno, '+
  1232. '''' + DatabaseName + ''' as catalog_name, '+
  1233. ''''' as schema_name, '+
  1234. 'rdb$relation_name as table_name, '+
  1235. 'r.rdb$field_name as column_name, '+
  1236. 'rdb$field_position+1 as column_position, '+
  1237. '0 as column_type, '+
  1238. 'rdb$field_type as column_datatype, '+
  1239. 'rdb$type_name as column_typename, '+
  1240. 'rdb$field_sub_type as column_subtype, '+
  1241. 'rdb$field_precision as column_precision, '+
  1242. '-rdb$field_scale as column_scale, '+
  1243. 'rdb$field_length as column_length, '+
  1244. 'case r.rdb$null_flag when 1 then 0 else 1 end as column_nullable '+
  1245. 'FROM '+
  1246. 'rdb$relation_fields r '+
  1247. 'JOIN rdb$fields f ON r.rdb$field_source=f.rdb$field_name '+
  1248. 'JOIN rdb$types t ON f.rdb$field_type=t.rdb$type AND t.rdb$field_name=''RDB$FIELD_TYPE'' '+
  1249. 'WHERE '+
  1250. '(r.rdb$system_flag = 0 or r.rdb$system_flag is null) and (rdb$relation_name = ''' + Uppercase(SchemaObjectName) + ''') ' +
  1251. 'ORDER BY '+
  1252. 'r.rdb$field_name';
  1253. else
  1254. DatabaseError(SMetadataUnavailable)
  1255. end; {case}
  1256. result := s;
  1257. end;
  1258. procedure TIBConnection.UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string);
  1259. var qry : TSQLQuery;
  1260. begin
  1261. if not assigned(Transaction) then
  1262. DatabaseError(SErrConnTransactionnSet);
  1263. if (length(TableName)>2) and (TableName[1]='"') and (TableName[length(TableName)]='"') then
  1264. TableName := AnsiDequotedStr(TableName, '"')
  1265. else
  1266. TableName := UpperCase(TableName);
  1267. qry := tsqlquery.Create(nil);
  1268. qry.transaction := Transaction;
  1269. qry.database := Self;
  1270. with qry do
  1271. begin
  1272. ReadOnly := True;
  1273. sql.clear;
  1274. sql.add('select '+
  1275. 'ind.rdb$index_name, '+
  1276. 'ind.rdb$relation_name, '+
  1277. 'ind.rdb$unique_flag, '+
  1278. 'ind_seg.rdb$field_name, '+
  1279. 'rel_con.rdb$constraint_type, '+
  1280. 'ind.rdb$index_type '+
  1281. 'from '+
  1282. 'rdb$index_segments ind_seg, '+
  1283. 'rdb$indices ind '+
  1284. 'left outer join '+
  1285. 'rdb$relation_constraints rel_con '+
  1286. 'on '+
  1287. 'rel_con.rdb$index_name = ind.rdb$index_name '+
  1288. 'where '+
  1289. '(ind_seg.rdb$index_name = ind.rdb$index_name) and '+
  1290. '(ind.rdb$relation_name=' + QuotedStr(TableName) + ') '+
  1291. 'order by '+
  1292. 'ind.rdb$index_name;');
  1293. open;
  1294. end;
  1295. while not qry.eof do with IndexDefs.AddIndexDef do
  1296. begin
  1297. Name := trim(qry.fields[0].asstring);
  1298. Fields := trim(qry.Fields[3].asstring);
  1299. If qry.fields[4].asstring = 'PRIMARY KEY' then options := options + [ixPrimary];
  1300. If qry.fields[2].asinteger = 1 then options := options + [ixUnique];
  1301. If qry.fields[5].asInteger = 1 then options:=options+[ixDescending];
  1302. qry.next;
  1303. while (name = trim(qry.fields[0].asstring)) and (not qry.eof) do
  1304. begin
  1305. Fields := Fields + ';' + trim(qry.Fields[3].asstring);
  1306. qry.next;
  1307. end;
  1308. end;
  1309. qry.close;
  1310. qry.free;
  1311. end;
  1312. procedure TIBConnection.SetFloat(CurrBuff: pointer; Dbl: Double; Size: integer);
  1313. var
  1314. Ext : extended;
  1315. Sin : single;
  1316. begin
  1317. case Size of
  1318. 4 :
  1319. begin
  1320. Sin := Dbl;
  1321. Move(Sin, CurrBuff^, 4);
  1322. end;
  1323. 8 :
  1324. begin
  1325. Move(Dbl, CurrBuff^, 8);
  1326. end;
  1327. 10:
  1328. begin
  1329. Ext := Dbl;
  1330. Move(Ext, CurrBuff^, 10);
  1331. end;
  1332. else
  1333. Raise EIBDatabaseError.CreateFmt('Invalid float size for float encode : %d',[Size]);
  1334. end;
  1335. end;
  1336. procedure TIBConnection.GetFloat(CurrBuff, Buffer : pointer; Size : byte);
  1337. var
  1338. Ext : extended;
  1339. Dbl : double;
  1340. Sin : single;
  1341. begin
  1342. case Size of
  1343. 4 :
  1344. begin
  1345. Move(CurrBuff^, Sin, 4);
  1346. Dbl := Sin;
  1347. end;
  1348. 8 :
  1349. begin
  1350. Move(CurrBuff^, Dbl, 8);
  1351. end;
  1352. 10:
  1353. begin
  1354. Move(CurrBuff^, Ext, 10);
  1355. Dbl := double(Ext);
  1356. end;
  1357. else
  1358. Raise EIBDatabaseError.CreateFmt('Invalid float size for float Decode : %d',[Size]);
  1359. end;
  1360. Move(Dbl, Buffer^, 8);
  1361. end;
  1362. procedure TIBConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction);
  1363. const
  1364. isc_segstr_eof = 335544367; // It's not defined in ibase60 but in ibase40. Would it be better to define in ibase60?
  1365. var
  1366. blobHandle : Isc_blob_Handle;
  1367. blobSegment : pointer;
  1368. blobSegLen : word;
  1369. TransactionHandle : pointer;
  1370. blobId : PISC_QUAD;
  1371. ptr : Pointer;
  1372. begin
  1373. blobId := PISC_QUAD(@(ABlobBuf^.ConnBlobBuffer));
  1374. TransactionHandle := Atransaction.Handle;
  1375. blobHandle := FB_API_NULLHANDLE;
  1376. if isc_open_blob(@FStatus[0], @FSQLDatabaseHandle, @TransactionHandle, @blobHandle, blobId) <> 0 then
  1377. CheckError('TIBConnection.CreateBlobStream', FStatus);
  1378. //For performance, read as much as we can, regardless of any segment size set in database.
  1379. blobSegment := AllocMem(MAXBLOBSEGMENTSIZE);
  1380. with ABlobBuf^.BlobBuffer^ do
  1381. begin
  1382. Size := 0;
  1383. while (isc_get_segment(@FStatus[0], @blobHandle, @blobSegLen, MAXBLOBSEGMENTSIZE, blobSegment) = 0) do
  1384. begin
  1385. ReAllocMem(Buffer,Size+blobSegLen);
  1386. ptr := Buffer+Size;
  1387. move(blobSegment^,ptr^,blobSegLen);
  1388. inc(Size,blobSegLen);
  1389. end;
  1390. freemem(blobSegment);
  1391. if FStatus[1] = isc_segstr_eof then
  1392. begin
  1393. if isc_close_blob(@FStatus[0], @blobHandle) <> 0 then
  1394. CheckError('TIBConnection.CreateBlobStream isc_close_blob', FStatus);
  1395. end
  1396. else
  1397. CheckError('TIBConnection.CreateBlobStream isc_get_segment', FStatus);
  1398. end;
  1399. end;
  1400. function TIBConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  1401. var info_request : string;
  1402. resbuf : array[0..63] of byte;
  1403. i : integer;
  1404. BlockSize,
  1405. subBlockSize : integer;
  1406. SelectedRows,
  1407. InsertedRows : integer;
  1408. begin
  1409. SelectedRows:=-1;
  1410. InsertedRows:=-1;
  1411. if assigned(cursor) then with cursor as TIBCursor do
  1412. if assigned(statement) then
  1413. begin
  1414. info_request := chr(isc_info_sql_records);
  1415. if isc_dsql_sql_info(@Status[0],@Statement,Length(info_request), @info_request[1],sizeof(resbuf),@resbuf) <> 0 then
  1416. CheckError('RowsAffected', Status);
  1417. i := 0;
  1418. while not (byte(resbuf[i]) in [isc_info_end,isc_info_truncated]) do
  1419. begin
  1420. BlockSize:=isc_vax_integer(@resbuf[i+1],2);
  1421. if resbuf[i]=isc_info_sql_records then
  1422. begin
  1423. inc(i,3);
  1424. BlockSize:=BlockSize+i;
  1425. while (resbuf[i] <> isc_info_end) and (i < BlockSize) do
  1426. begin
  1427. subBlockSize:=isc_vax_integer(@resbuf[i+1],2);
  1428. if resbuf[i] = isc_info_req_select_count then
  1429. SelectedRows := isc_vax_integer(@resbuf[i+3],subBlockSize)
  1430. else if resbuf[i] = isc_info_req_insert_count then
  1431. InsertedRows := isc_vax_integer(@resbuf[i+3],subBlockSize);
  1432. inc(i,subBlockSize+3);
  1433. end;
  1434. end
  1435. else
  1436. inc(i,BlockSize+3);
  1437. end;
  1438. end;
  1439. if SelectedRows>0 then result:=SelectedRows
  1440. else Result:=InsertedRows;
  1441. end;
  1442. { TIBConnectionDef }
  1443. class function TIBConnectionDef.TypeName: String;
  1444. begin
  1445. Result:='Firebird';
  1446. end;
  1447. class function TIBConnectionDef.ConnectionClass: TSQLConnectionClass;
  1448. begin
  1449. Result:=TIBConnection;
  1450. end;
  1451. class function TIBConnectionDef.Description: String;
  1452. begin
  1453. Result:='Connect to Firebird/Interbase directly via the client library';
  1454. end;
  1455. class function TIBConnectionDef.DefaultLibraryName: String;
  1456. begin
  1457. {$IFDEF LinkDynamically}
  1458. If UseEmbeddedFirebird then
  1459. Result:=fbembedlib
  1460. else
  1461. Result:=fbclib;
  1462. {$ELSE}
  1463. Result:='';
  1464. {$ENDIF}
  1465. end;
  1466. class function TIBConnectionDef.LoadFunction: TLibraryLoadFunction;
  1467. begin
  1468. {$IFDEF LinkDynamically}
  1469. Result:=@InitialiseIBase60;
  1470. {$ELSE}
  1471. Result:=nil;
  1472. {$ENDIF}
  1473. end;
  1474. class function TIBConnectionDef.UnLoadFunction: TLibraryUnLoadFunction;
  1475. begin
  1476. {$IFDEF LinkDynamically}
  1477. Result:=@ReleaseIBase60
  1478. {$ELSE}
  1479. Result:=nil;
  1480. {$ENDIF}
  1481. end;
  1482. class function TIBConnectionDef.LoadedLibraryName: string;
  1483. begin
  1484. {$IFDEF LinkDynamically}
  1485. Result:=IBaseLoadedLibrary;
  1486. {$ELSE}
  1487. Result:='';
  1488. {$ENDIF}
  1489. end;
  1490. initialization
  1491. RegisterConnection(TIBConnectionDef);
  1492. finalization
  1493. UnRegisterConnection(TIBConnectionDef);
  1494. end.