ibconnection.pp 57 KB

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