ibconnection.pp 55 KB

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