ibconnection.pp 53 KB

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