interbase.pp 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175
  1. { $Id$
  2. Copyright (c) 2000 by Pavel Stingl
  3. Interbase database & dataset
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. unit Interbase;
  11. {$mode objfpc}
  12. {$H+}
  13. {$M+} // ### remove this!!!
  14. interface
  15. uses SysUtils, Classes, IBase60, DB;
  16. type
  17. PInteger = ^integer;
  18. PSmallInt= ^smallint;
  19. TIBDatabase = class;
  20. TIBTransaction = class;
  21. TIBQuery = class;
  22. TIBStoredProc = class;
  23. EInterBaseError = class(Exception);
  24. { TIBDatabase }
  25. TIBDatabase = class (TDatabase)
  26. private
  27. FIBDatabaseHandle : pointer;
  28. FPassword : string;
  29. FStatus : array [0..19] of ISC_STATUS;
  30. FTransaction : TIBTransaction;
  31. FUserName : string;
  32. FCharSet : string;
  33. FDialect : integer;
  34. FRole : String;
  35. procedure SetDBDialect;
  36. procedure SetTransaction(Value : TIBTransaction);
  37. protected
  38. function GetHandle : pointer; virtual;
  39. { This procedure makes connection to Interbase server internally.
  40. Is visible only by descendants, in application programming
  41. will be invisible. Connection you must establish by setting
  42. @link(Connected) property to true, or by call of Open method.
  43. }
  44. procedure DoInternalConnect; override;
  45. { This procedure disconnects object from IB server internally.
  46. Is visible only by descendants, in application programming
  47. will be invisible. Disconnection you must make by setting
  48. @link(Connected) property to false, or by call of Close method.
  49. }
  50. procedure DoInternalDisconnect; override;
  51. public
  52. procedure StartTransaction; override;
  53. procedure EndTransaction; override;
  54. destructor Destroy; override;
  55. property Handle: Pointer read GetHandle;
  56. published
  57. { On connect, TIBDatabase object retrieve SQL dialect of database file,
  58. and sets this property to responding value }
  59. property Dialect : integer read FDialect write FDialect;
  60. { Before firing Open method you must set @link(Password),@link(DatabaseName),
  61. @link(UserName) properties in order of successfull connect to database }
  62. property Password : string read FPassword write FPassword;
  63. { This property holds default transaction for database. You must assign it by hand
  64. now, default assignment becomes handy, in next release, with transaction
  65. handling and evidence }
  66. property Transaction : TIBTransaction read FTransaction write SetTransaction;
  67. { Before firing Open method you must set @link(Password),@link(DatabaseName),
  68. @link(UserName) properties in order of successfull connect to database }
  69. property UserName : string read FUserName write FUserName;
  70. { The character set used in SQL statements }
  71. property CharSet : string read FCharSet write FCharSet;
  72. { Identifies, if connection to Interbase server is established, or not.
  73. Instead of calling Open, Close methods you can connect or disconnect
  74. by setting this property to true or false.
  75. }
  76. property Connected;
  77. { This property holds database connect string. On local server it will be
  78. absolute path to the db file, if you wanna connect over network, this
  79. path looks like this: <server_name>:<path_on_server>, where server_name
  80. is absolute IP address, or name of server in DNS or hosts file, path_on_server
  81. is absolute path to the file again }
  82. Property Role : String read FRole write FRole;
  83. property DatabaseName;
  84. property KeepConnection;
  85. property LoginPrompt;
  86. property Params;
  87. property OnLogin;
  88. end;
  89. { TIBTransaction }
  90. {
  91. Interbase has two modes for commit and rollback transactions,
  92. the difference is simple. If you execute Commit or Rollback,
  93. current transaction ends, and you must create new one.
  94. If you, on other side, need only commit or rollback data
  95. without transaction closing, execute with CommitRetaining or
  96. RollbackRetaining. Transaction handle, environment etc. will be
  97. as same as before action. Possible values are : caNone, caCommit, caCommitRetaining, caRollback,
  98. caRollbackRetaining
  99. }
  100. TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback,
  101. caRollbackRetaining);
  102. TAccessMode = (amReadWrite, amReadOnly);
  103. TIsolationLevel = (ilConcurrent, ilConsistent, ilReadCommittedRecV,
  104. ilReadCommitted);
  105. TLockResolution = (lrWait, lrNoWait);
  106. TTableReservation = (trNone, trSharedLockRead, trSharedLockWrite,
  107. trProtectedLockRead, trProtectedLockWrite);
  108. TIBTransaction = class (TComponent)
  109. private
  110. FTransactionHandle : pointer; // Transaction handle
  111. FAction : TCommitRollbackAction;
  112. FActive : boolean;
  113. FTPB : string; // Transaction parameter buffer
  114. FDatabase : TIBDatabase;
  115. FAccessMode : TAccessMode;
  116. FIsolationLevel : TIsolationLevel;
  117. FLockResolution : TLockResolution;
  118. FTableReservation : TTableReservation;
  119. FStatus : array [0..19] of ISC_STATUS;
  120. procedure SetActive(Value : boolean);
  121. procedure SetTPB;
  122. protected
  123. function GetHandle : pointer; virtual;
  124. public
  125. { Commits all actions, which was made in transaction, and closes transaction}
  126. procedure Commit; virtual;
  127. { Commits all actions, closes transaction, and creates new one }
  128. procedure CommitRetaining; virtual;
  129. { Rollbacks all actions made in transaction, and closes transaction }
  130. procedure Rollback; virtual;
  131. { Rollbacks all actions made in transaction, closes trans. and creates new one }
  132. procedure RollbackRetaining; virtual;
  133. { Creates new transaction. If transaction is active, closes it and make new one.
  134. Action taken while closing responds to @link(Action) property settings }
  135. procedure StartTransaction;
  136. constructor Create(AOwner : TComponent); override;
  137. destructor Destroy; override;
  138. property Handle: Pointer read GetHandle;
  139. published
  140. { Default action while closing transaction by setting
  141. @link(Active) property. For details see @link(TCommitRollbackAction)}
  142. property Action : TCommitRollbackAction read FAction write FAction;
  143. { Is set to true while transaction is active, false if not.
  144. If you set it manually to true, object executes
  145. @link(StartTransaction) method, if transaction is
  146. active, and you set Active to false, object executes
  147. one of @link(Commit), @link(CommitRetaining), @link(Rollback),
  148. @link(RollbackRetaining) methods, depending on @link(Action) property
  149. setting.
  150. }
  151. property Active : boolean read FActive write SetActive;
  152. { Transaction must be assigned to some database session, for which purpose
  153. you must use this property}
  154. property Database : TIBDatabase read FDatabase write FDatabase;
  155. end;
  156. { TIBQuery }
  157. PIBBookmark = ^TIBBookmark;
  158. TIBBookmark = record
  159. BookmarkData : integer;
  160. BookmarkFlag : TBookmarkFlag;
  161. end;
  162. TStatementType = (stNone, stSelect, stInsert, stUpdate, stDelete,
  163. stDDL, stGetSegment, stPutSegment, stExecProcedure,
  164. stStartTrans, stCommit, stRollback, stSelectForUpd);
  165. TIBQuery = class (TDBDataset)
  166. private
  167. FOpen : Boolean;
  168. FTransaction : TIBTransaction;
  169. FDatabase : TIBDatabase;
  170. FStatus : array [0..19] of ISC_STATUS;
  171. FFieldFlag : array [0..1023] of shortint;
  172. FBufferSize : integer;
  173. FSQLDA : PXSQLDA;
  174. FSQLDAAllocated : integer;
  175. FStatement : pointer;
  176. FRecordCount : integer;
  177. FRecordSize : word;
  178. FCurrentRecord : integer;
  179. FSQL : TStrings;
  180. FPrepared : boolean;
  181. FIsEOF : boolean;
  182. FStatementType : TStatementType;
  183. FLoadingFieldDefs : boolean;
  184. procedure SetDatabase(Value : TIBDatabase);
  185. procedure SetTransaction(Value : TIBTransaction);
  186. procedure AllocSQLDA(Count : integer);
  187. procedure AllocStatement;
  188. procedure FreeStatement;
  189. procedure PrepareStatement;
  190. procedure DescribeStatement;
  191. procedure SetUpSQLVars;
  192. procedure AllocFldBuffers;
  193. procedure FreeFldBuffers;
  194. procedure Fetch;
  195. function LoadBufferFromSQLDA(Buffer : PChar): TGetResult;
  196. procedure GetStatementType;
  197. procedure SetFieldSizes;
  198. procedure TranslateFldType(SQLType, SQLLen : integer; var LensSet : boolean;
  199. var TrType : TFieldType; var TrLen : word);
  200. procedure ExecuteImmediate;
  201. procedure ExecuteParams;
  202. procedure Execute;
  203. // conversion methods
  204. procedure GetDateTime(CurrBuff, Buffer : pointer; AType : integer);
  205. procedure GetFloat(CurrBuff, Buffer : pointer; Field : TField);
  206. protected
  207. // abstract & virual methods of TDataset
  208. function AllocRecordBuffer: PChar; override;
  209. procedure FreeRecordBuffer(var Buffer: PChar); override;
  210. procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  211. function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  212. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  213. function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  214. function GetRecordCount: integer; override;
  215. function GetRecordSize: Word; override;
  216. procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
  217. procedure InternalClose; override;
  218. procedure InternalDelete; override;
  219. procedure InternalFirst; override;
  220. procedure InternalGotoBookmark(ABookmark: Pointer); override;
  221. procedure InternalHandleException; override;
  222. procedure InternalInitFieldDefs; override;
  223. procedure InternalInitRecord(Buffer: PChar); override;
  224. procedure InternalLast; override;
  225. procedure InternalOpen; override;
  226. procedure InternalPost; override;
  227. procedure InternalSetToRecord(Buffer: PChar); override;
  228. function IsCursorOpen: Boolean; override;
  229. procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  230. procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  231. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  232. public
  233. { This method is used for executing sql statements, which
  234. doesn't return any rows. (insert,delete,update, and DDL commands) }
  235. procedure ExecSQL; virtual;
  236. constructor Create(AOwner : TComponent); override;
  237. destructor Destroy; override;
  238. published
  239. { Query must have transaction assigned. If transaction is not assigned, and database
  240. is, object looks, if database have default transaction, and assigns it }
  241. property Transaction : TIBTransaction read FTransaction write SetTransaction;
  242. { Use this property to determine, which database session can query use }
  243. property Database : TIBDatabase read FDatabase write SetDatabase;
  244. { This property holds SQL command, which you want to execute }
  245. property SQL : TStrings read FSQL write FSQL;
  246. Property Database;
  247. end;
  248. { TIBStoredProc - not implemented - yet :-/}
  249. TIBStoredProc = class (TDataset)
  250. private
  251. protected
  252. public
  253. published
  254. end;
  255. implementation
  256. type
  257. TTm = packed record
  258. tm_sec : longint;
  259. tm_min : longint;
  260. tm_hour : longint;
  261. tm_mday : longint;
  262. tm_mon : longint;
  263. tm_year : longint;
  264. tm_wday : longint;
  265. tm_yday : longint;
  266. tm_isdst : longint;
  267. __tm_gmtoff : longint;
  268. __tm_zone : Pchar;
  269. end;
  270. procedure CheckError(ProcName : string; Status : array of ISC_STATUS);
  271. var
  272. buf : array [0..1024] of char;
  273. p : pointer;
  274. Msg : string;
  275. begin
  276. if ((Status[0] = 1) and (Status[1] <> 0)) then
  277. begin
  278. p := @Status;
  279. while isc_interprete(Buf, @p) > 0 do
  280. Msg := Msg + #10' -' + StrPas(Buf);
  281. raise EInterBaseError.Create(ProcName + ': ' + Msg);
  282. end;
  283. end;
  284. { TIBDatabase }
  285. procedure TIBDatabase.SetDBDialect;
  286. var
  287. x : integer;
  288. Len : integer;
  289. Buffer : string;
  290. ResBuf : array [0..39] of byte;
  291. begin
  292. Buffer := Chr(isc_info_db_sql_dialect) + Chr(isc_info_end);
  293. if isc_database_info(@FStatus, @FIBDatabaseHandle, Length(Buffer),
  294. @Buffer[1], SizeOf(ResBuf), @ResBuf) <> 0 then
  295. CheckError('TIBDatabse.SetDBDialect', FStatus);
  296. x := 0;
  297. while x < 40 do
  298. case ResBuf[x] of
  299. isc_info_db_sql_dialect :
  300. begin
  301. Inc(x);
  302. Len := isc_vax_integer(@ResBuf[x], 2);
  303. Inc(x, 2);
  304. FDialect := isc_vax_integer(@ResBuf[x], Len);
  305. Inc(x, Len);
  306. end;
  307. isc_info_end : Break;
  308. end;
  309. end;
  310. procedure TIBDatabase.SetTransaction(Value : TIBTransaction);
  311. begin
  312. if FTransaction = nil then
  313. begin
  314. FTransaction := Value;
  315. if Assigned(FTransaction) then
  316. FTransaction.Database := Self;
  317. exit;
  318. end;
  319. if (Value <> FTransaction) and (Value <> nil) then
  320. if (not FTransaction.Active) then
  321. begin
  322. FTransaction := Value;
  323. FTransaction.Database := Self;
  324. end
  325. else
  326. raise EInterBaseError.Create('Cannot assign transaction while old transaction active!');
  327. end;
  328. function TIBDatabase.GetHandle: pointer;
  329. begin
  330. Result := FIBDatabaseHandle;
  331. end;
  332. procedure TIBDatabase.DoInternalConnect;
  333. var
  334. DPB : string;
  335. begin
  336. if Connected then
  337. Close;
  338. DPB := chr(isc_dpb_version1);
  339. if (FUserName <> '') then
  340. begin
  341. DPB := DPB + chr(isc_dpb_user_name) + chr(Length(FUserName)) + FUserName;
  342. if (FPassword <> '') then
  343. DPB := DPB + chr(isc_dpb_password) + chr(Length(FPassword)) + FPassword;
  344. end;
  345. if (FRole <> '') then
  346. DPB := DPB + chr(isc_dpb_sql_role_name) + chr(Length(FRole)) + FRole;
  347. if Length(CharSet) > 0 then
  348. DPB := DPB + Chr(isc_dpb_lc_ctype) + Chr(Length(CharSet)) + CharSet;
  349. if (DatabaseName = '') then
  350. raise EInterBaseError.Create('TIBDatabase.Open: Database connect string not filled in!');
  351. FIBDatabaseHandle := nil;
  352. if isc_attach_database(@FStatus, Length(DatabaseName), @DatabaseName[1], @FIBDatabaseHandle,
  353. Length(DPB), @DPB[1]) <> 0 then
  354. CheckError('TIBDatabase.Open', FStatus);
  355. SetDBDialect;
  356. end;
  357. procedure TIBDatabase.DoInternalDisconnect;
  358. begin
  359. if not Connected then
  360. begin
  361. FIBDatabaseHandle := nil;
  362. Exit;
  363. end;
  364. isc_detach_database(@FStatus[0], @FIBDatabaseHandle);
  365. CheckError('TIBDatabase.Close', FStatus);
  366. end;
  367. procedure TIBDatabase.StartTransaction;
  368. begin
  369. if FTransaction = nil then
  370. raise EDatabaseError.Create('TIBDatabase.StartTransaction: Transaction not set');
  371. FTransaction.Active := True;
  372. end;
  373. procedure TIBDatabase.EndTransaction;
  374. begin
  375. if FTransaction = nil then
  376. raise EDatabaseError.Create('TIBDatabase.EndTransaction: Transaction not set');
  377. FTransaction.Active := False;
  378. end;
  379. destructor TIBDatabase.Destroy;
  380. begin
  381. if FTransaction <> nil then
  382. begin
  383. FTransaction.Active := False;
  384. FTransaction.Database := nil;
  385. end;
  386. inherited Destroy;
  387. end;
  388. { TIBTransaction }
  389. procedure TIBTransaction.SetActive(Value : boolean);
  390. begin
  391. if FActive and (not Value) then
  392. Rollback
  393. else if (not FActive) and Value then
  394. StartTransaction;
  395. end;
  396. procedure TIBTransaction.SetTPB;
  397. begin
  398. FTPB := chr(isc_tpb_version3);
  399. case FAccessMode of
  400. amReadWrite : FTPB := FTPB + chr(isc_tpb_write);
  401. amReadOnly : FTPB := FTPB + chr(isc_tpb_read);
  402. end;
  403. case FIsolationLevel of
  404. ilConsistent : FTPB := FTPB + chr(isc_tpb_consistency);
  405. ilConcurrent : FTPB := FTPB + chr(isc_tpb_concurrency);
  406. ilReadCommittedRecV : FTPB := FTPB + chr(isc_tpb_read_committed) +
  407. chr(isc_tpb_rec_version);
  408. ilReadCommitted : FTPB := FTPB + chr(isc_tpb_read_committed) +
  409. chr(isc_tpb_no_rec_version);
  410. end;
  411. case FLockResolution of
  412. lrWait : FTPB := FTPB + chr(isc_tpb_wait);
  413. lrNoWait : FTPB := FTPB + chr(isc_tpb_nowait);
  414. end;
  415. case FTableReservation of
  416. trSharedLockRead : FTPB := FTPB + chr(isc_tpb_shared) +
  417. chr(isc_tpb_lock_read);
  418. trSharedLockWrite : FTPB := FTPB + chr(isc_tpb_shared) +
  419. chr(isc_tpb_lock_write);
  420. trProtectedLockRead : FTPB := FTPB + chr(isc_tpb_protected) +
  421. chr(isc_tpb_lock_read);
  422. trProtectedLockWrite : FTPB := FTPB + chr(isc_tpb_protected) +
  423. chr(isc_tpb_lock_write);
  424. end;
  425. end;
  426. function TIBTransaction.GetHandle: pointer;
  427. begin
  428. Result := FTransactionHandle;
  429. end;
  430. procedure TIBTransaction.Commit;
  431. begin
  432. if not FActive then Exit;
  433. if isc_commit_transaction(@FStatus, @FTransactionHandle) <> 0 then
  434. CheckError('TIBTransaction.Commit', FStatus)
  435. else FActive := False;
  436. end;
  437. procedure TIBTransaction.CommitRetaining;
  438. begin
  439. if not FActive then Exit;
  440. if isc_commit_retaining(@FStatus, @FTransactionHandle) <> 0 then
  441. CheckError('TIBTransaction.CommitRetaining', FStatus);
  442. end;
  443. procedure TIBTransaction.Rollback;
  444. begin
  445. if not FActive then Exit;
  446. if isc_rollback_transaction(@FStatus, @FTransactionHandle) <> 0 then
  447. CheckError('TIBTransaction.Rollback', FStatus)
  448. else FActive := False;
  449. end;
  450. procedure TIBTransaction.RollbackRetaining;
  451. begin
  452. if not FActive then Exit;
  453. if isc_rollback_retaining(@FStatus, @FTransactionHandle) <> 0 then
  454. CheckError('TIBTransaction.RollbackRetaining', FStatus);
  455. end;
  456. procedure TIBTransaction.StartTransaction;
  457. var
  458. DBHandle : pointer;
  459. begin
  460. if Active then Active := False;
  461. if FDatabase = nil then
  462. raise EInterBaseError.Create('TIBTransaction.StartTransaction: Database not assigned!');
  463. if not Database.Connected then
  464. Database.Open;
  465. DBHandle := Database.GetHandle;
  466. SetTPB;
  467. FTransactionHandle := nil;
  468. if isc_start_transaction(@FStatus, @FTransactionHandle, 1,
  469. [@DBHandle, Length(FTPB), @FTPB[1]]) <> 0 then
  470. CheckError('TIBTransaction.StartTransaction',FStatus)
  471. else FActive := True;
  472. end;
  473. constructor TIBTransaction.Create(AOwner : TComponent);
  474. begin
  475. inherited Create(AOwner);
  476. FIsolationLevel := ilReadCommitted;
  477. end;
  478. destructor TIBTransaction.Destroy;
  479. begin
  480. // This will also do a Rollback, if the transaction is currently active
  481. Active := False;
  482. if Database <> nil then
  483. Database.Transaction := nil;
  484. inherited Destroy;
  485. end;
  486. { TIBQuery }
  487. procedure TIBQuery.SetTransaction(Value : TIBTransaction);
  488. begin
  489. CheckInactive;
  490. if (FTransaction <> Value) then
  491. FTransaction := Value;
  492. end;
  493. procedure TIBQuery.SetDatabase(Value : TIBDatabase);
  494. begin
  495. CheckInactive;
  496. if (FDatabase <> Value) then
  497. begin
  498. FDatabase := Value;
  499. if (FTransaction = nil) and (Assigned(FDatabase.Transaction)) then
  500. SetTransaction(FDatabase.Transaction);
  501. end;
  502. end;
  503. procedure TIBQuery.AllocSQLDA(Count : integer);
  504. begin
  505. if FSQLDAAllocated > 0 then
  506. FreeMem(FSQLDA);
  507. GetMem(FSQLDA, XSQLDA_Length(Count));
  508. { Zero out the memory block to avoid problems with exceptions within the
  509. constructor of this class. }
  510. FillChar(FSQLDA^, XSQLDA_Length(Count), 0);
  511. FSQLDAAllocated := Count;
  512. FSQLDA^.Version := sqlda_version1;
  513. FSQLDA^.SQLN := Count;
  514. end;
  515. procedure TIBQuery.AllocStatement;
  516. var
  517. dh : pointer;
  518. begin
  519. if not FDatabase.Connected then
  520. FDatabase.Open;
  521. dh := FDatabase.GetHandle;
  522. if isc_dsql_allocate_statement(@FStatus, @dh, @FStatement) <> 0 then
  523. CheckError('TIBQuery.AllocStatement', FStatus);
  524. end;
  525. procedure TIBQuery.FreeStatement;
  526. begin
  527. if isc_dsql_free_statement(@FStatus, @FStatement, DSQL_Drop) <> 0 then
  528. CheckError('TIBQuery.FreeStatement', FStatus);
  529. FStatement := nil;
  530. end;
  531. procedure TIBQuery.PrepareStatement;
  532. var
  533. Buf : string;
  534. x : integer;
  535. tr : pointer;
  536. begin
  537. tr := FTransaction.GetHandle;
  538. for x := 0 to FSQL.Count - 1 do
  539. Buf := Buf + FSQL[x] + ' ';
  540. if isc_dsql_prepare(@FStatus, @tr, @FStatement, 0, @Buf[1], Database.Dialect, nil) <> 0 then
  541. CheckError('TIBQuery.PrepareStatement', FStatus);
  542. end;
  543. procedure TIBQuery.DescribeStatement;
  544. begin
  545. if isc_dsql_describe(@FStatus, @FStatement, 1, FSQLDA) <> 0 then
  546. CheckError('TIBQuery.DescribeStatement', FStatus);
  547. if FSQLDA^.SQLD > FSQLDA^.SQLN then
  548. begin
  549. AllocSQLDA(FSQLDA^.SQLD);
  550. if isc_dsql_describe(@FStatus, @FStatement, 1, FSQLDA) <> 0 then
  551. CheckError('TIBQuery.DescribeStatement', FStatus);
  552. end;
  553. end;
  554. procedure TIBQuery.SetUpSQLVars;
  555. var
  556. x : integer;
  557. begin
  558. for x := 0 to FSQLDA^.SQLN - 1 do
  559. begin
  560. case FSQLDA^.SQLVar[x].SQLType of
  561. sql_varying + 1:
  562. FSQLDA^.SQLVar[x].SQLType := sql_varying;
  563. sql_text + 1 :
  564. FSQLDA^.SQLVar[x].SQLType := sql_text;
  565. sql_short, sql_short + 1, sql_long + 1:
  566. FSQLDA^.SQLVar[x].SQLType := sql_long;
  567. sql_float + 1 :
  568. FSQLDA^.SQLVar[x].SQLType := sql_float;
  569. sql_double + 1 :
  570. FSQLDA^.SQLVar[x].SQLType := sql_double;
  571. sql_blob + 1 :
  572. FSQLDA^.SQLVar[x].SQLType := sql_blob;
  573. sql_type_time + 1 :
  574. FSQLDA^.SQLVar[x].SQLType := sql_type_time;
  575. sql_timestamp + 1:
  576. FSQLDA^.SQLVar[x].SQLType := sql_timestamp;
  577. end;
  578. end;
  579. end;
  580. procedure TIBQuery.AllocFldBuffers;
  581. var
  582. x : shortint;
  583. begin
  584. {$R-}
  585. for x := 0 to FSQLDA^.SQLD - 1 do
  586. begin
  587. FSQLDA^.SQLVar[x].SQLData := AllocMem(FSQLDA^.SQLVar[x].SQLLen);
  588. FSQLDA^.SQLVar[x].SQLInd := @FFieldFlag[x];
  589. end;
  590. {$R+}
  591. end;
  592. procedure TIBQuery.FreeFldBuffers;
  593. var
  594. x : integer;
  595. begin
  596. {$R-}
  597. for x := 0 to FSQLDA^.SQLD - 1 do
  598. begin
  599. if FSQLDA^.SQLVar[x].SQLData <> nil then
  600. begin
  601. FreeMem(FSQLDA^.SQLVar[x].SQLData);
  602. FSQLDA^.SQLVar[x].SQLData := nil;
  603. end;
  604. end;
  605. {$R+}
  606. end;
  607. procedure TIBQuery.Fetch;
  608. var
  609. retcode : integer;
  610. begin
  611. if not (FStatementType in [stSelect]) then
  612. Exit;
  613. retcode := isc_dsql_fetch(@FStatus, @FStatement, 1, FSQLDA);
  614. if (retcode <> 0) and (retcode <> 100) then
  615. CheckError('TIBQuery.Fetch', FStatus);
  616. FIsEOF := (retcode = 100);
  617. end;
  618. function TIBQuery.LoadBufferFromSQLDA(Buffer : PChar): TGetResult;
  619. var
  620. x : integer;
  621. VarcharLen : word;
  622. begin
  623. Fetch;
  624. if FIsEOF then
  625. begin
  626. Result := grEOF;
  627. Exit;
  628. end;
  629. {$R-}
  630. for x := 0 to FSQLDA^.SQLD - 1 do
  631. begin
  632. with FSQLDA^.SQLVar[x] do
  633. begin
  634. if ((SQLType and not 1) = SQL_VARYING) then
  635. begin
  636. Move(SQLData^, VarcharLen, 2);
  637. Move((SQLData + 2)^, Buffer^, VarcharLen);
  638. PChar(Buffer + VarcharLen)^ := #0;
  639. end
  640. else Move(SQLData^, Buffer^, SQLLen);
  641. Inc(Buffer, SQLLen);
  642. end;
  643. end;
  644. {$R+}
  645. Result := grOK;
  646. end;
  647. procedure TIBQuery.GetStatementType;
  648. var
  649. x : integer;
  650. ResBuf : array [0..7] of char;
  651. begin
  652. FStatementType := stNone;
  653. x := isc_info_sql_stmt_type;
  654. if isc_dsql_sql_info(@FStatus, @FStatement, SizeOf(X),
  655. @x, SizeOf(ResBuf), @ResBuf) <> 0 then
  656. CheckError('TIBQuery.GetStatementType', FStatus);
  657. if Ord(ResBuf[0]) = isc_info_sql_stmt_type then
  658. begin
  659. x := isc_vax_integer(@ResBuf[1], 2);
  660. FStatementType := TStatementType(isc_vax_integer(@ResBuf[3], x));
  661. end;
  662. end;
  663. procedure TIBQuery.SetFieldSizes;
  664. var
  665. x : integer;
  666. begin
  667. FRecordSize := 0;
  668. FBufferSize := 0;
  669. {$R-}
  670. for x := 0 to FSQLDA^.SQLD - 1 do
  671. Inc(FRecordSize, FSQLDA^.SQLVar[x].SQLLen);
  672. {$R+}
  673. FBufferSize := FRecordSize + SizeOf(TIBBookmark);
  674. end;
  675. procedure TIBQuery.TranslateFldType(SQLType, SQLLen : integer; var LensSet : boolean;
  676. var TrType : TFieldType; var TrLen : word);
  677. begin
  678. LensSet := False;
  679. case (SQLType and not 1) of
  680. SQL_VARYING :
  681. begin
  682. LensSet := True;
  683. TrType := ftString;
  684. TrLen := SQLLen;
  685. end;
  686. SQL_TEXT :
  687. begin
  688. LensSet := True;
  689. TrType := ftString;
  690. TrLen := SQLLen;
  691. end;
  692. SQL_TYPE_DATE :
  693. TrType := ftDateTime;
  694. SQL_TYPE_TIME :
  695. TrType := ftDateTime;
  696. SQL_TIMESTAMP :
  697. TrType := ftDateTime;
  698. SQL_ARRAY :
  699. begin
  700. end;
  701. SQL_BLOB :
  702. begin
  703. end;
  704. SQL_SHORT :
  705. begin
  706. LensSet := True;
  707. TrLen := SQLLen;
  708. TrType := ftInteger;
  709. end;
  710. SQL_LONG :
  711. begin
  712. LensSet := True;
  713. TrLen := SQLLen;
  714. TrType := ftInteger;
  715. end;
  716. SQL_INT64 :
  717. {TrType := ftInt64};
  718. SQL_DOUBLE :
  719. begin
  720. LensSet := True;
  721. TrLen := SQLLen;
  722. TrType := ftFloat;
  723. end;
  724. SQL_FLOAT :
  725. begin
  726. LensSet := True;
  727. TrLen := SQLLen;
  728. TrType := ftFloat;
  729. end;
  730. end;
  731. end;
  732. procedure TIBQuery.ExecuteImmediate;
  733. begin
  734. end;
  735. procedure TIBQuery.ExecuteParams;
  736. begin
  737. //!! to be implemented
  738. end;
  739. procedure TIBQuery.Execute;
  740. var
  741. tr : pointer;
  742. begin
  743. tr := FTransaction.GetHandle;
  744. if isc_dsql_execute(@FStatus, @tr, @FStatement, 1, nil) <> 0 then
  745. CheckError('TIBQuery.Execute', FStatus);
  746. end;
  747. procedure TIBQuery.GetDateTime(CurrBuff, Buffer : pointer; AType : integer);
  748. var
  749. CTime : TTm; // C struct time
  750. STime : TSystemTime; // System time
  751. PTime : TDateTime; // Pascal time
  752. begin
  753. case (AType and not 1) of
  754. SQL_TYPE_DATE :
  755. isc_decode_sql_date(PISC_DATE(CurrBuff), @CTime);
  756. SQL_TYPE_TIME :
  757. isc_decode_sql_time(PISC_TIME(CurrBuff), @CTime);
  758. SQL_TIMESTAMP :
  759. isc_decode_timestamp(PISC_TIMESTAMP(CurrBuff), @CTime);
  760. end;
  761. STime.Year := CTime.tm_year + 1900;
  762. STime.Month := CTime.tm_mon + 1;
  763. STime.Day := CTime.tm_mday;
  764. STime.Hour := CTime.tm_hour;
  765. STime.Minute := CTime.tm_min;
  766. STime.Second := CTime.tm_sec;
  767. STime.Millisecond := 0;
  768. PTime := SystemTimeToDateTime(STime);
  769. Move(PTime, Buffer^, SizeOf(PTime));
  770. end;
  771. procedure TIBQuery.GetFloat(CurrBuff, Buffer : pointer; Field : TField);
  772. var
  773. Ext : extended;
  774. Dbl : double;
  775. Sin : single;
  776. begin
  777. case Field.Size of
  778. 4 :
  779. begin
  780. Move(CurrBuff^, Sin, 4);
  781. Ext := Sin;
  782. end;
  783. 8 :
  784. begin
  785. Move(CurrBuff^, Dbl, 8);
  786. Ext := Dbl;
  787. end;
  788. 10: Move(CurrBuff^, Ext, 10);
  789. end;
  790. Move(Ext, Buffer^, 10);
  791. end;
  792. function TIBQuery.AllocRecordBuffer: PChar;
  793. begin
  794. Result := AllocMem(FBufferSize);
  795. end;
  796. procedure TIBQuery.FreeRecordBuffer(var Buffer: PChar);
  797. begin
  798. FreeMem(Buffer);
  799. end;
  800. procedure TIBQuery.GetBookmarkData(Buffer: PChar; Data: Pointer);
  801. begin
  802. PInteger(Data)^ := PIBBookmark(Buffer + FRecordSize)^.BookmarkData;
  803. end;
  804. function TIBQuery.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  805. begin
  806. Result := PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag;
  807. end;
  808. function TIBQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  809. var
  810. x : longint;
  811. b : longint;
  812. CurrBuff : PChar;
  813. begin
  814. Result := False;
  815. CurrBuff := ActiveBuffer;
  816. for x := 0 to FSQLDA^.SQLD - 1 do
  817. begin
  818. {$R-}
  819. if (Field.FieldName = FSQLDA^.SQLVar[x].SQLName) then
  820. begin
  821. case Field.DataType of
  822. ftInteger :
  823. begin
  824. b := 0;
  825. Move(b, Buffer^, 4);
  826. Move(CurrBuff^, Buffer^, Field.Size);
  827. end;
  828. ftDate, ftTime, ftDateTime:
  829. GetDateTime(CurrBuff, Buffer, FSQLDA^.SQLVar[x].SQLType);
  830. ftString :
  831. begin
  832. Move(CurrBuff^, Buffer^, Field.Size);
  833. PChar(Buffer + Field.Size)^ := #0;
  834. end;
  835. ftFloat :
  836. GetFloat(CurrBuff, Buffer, Field);
  837. end;
  838. Result := True;
  839. Break;
  840. end
  841. else Inc(CurrBuff, FSQLDA^.SQLVar[x].SQLLen);
  842. {$R+}
  843. end;
  844. end;
  845. function TIBQuery.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  846. begin
  847. if FStatementType <> stSelect then
  848. begin
  849. Result := grEOF;
  850. Exit;
  851. end;
  852. if FIsEOF then
  853. Result := grEOF
  854. else begin
  855. Result := grOK;
  856. case GetMode of
  857. gmPrior :
  858. if FCurrentRecord <= 0 then
  859. begin
  860. Result := grBOF;
  861. FCurrentRecord := -1;
  862. end
  863. else Dec(FCurrentRecord);
  864. gmCurrent :
  865. if (FCurrentRecord < 0) or (FCurrentRecord >= RecordCount) then
  866. Result := grError;
  867. gmNext :
  868. if FCurrentRecord >= (RecordCount - 1) then
  869. begin
  870. Result := LoadBufferFromSQLDA(Buffer);
  871. if Result = grOK then
  872. begin
  873. Inc(FCurrentRecord);
  874. Inc(FRecordCount);
  875. end;
  876. end
  877. else Inc(FCurrentRecord);
  878. end;
  879. end;
  880. if Result = grOK then
  881. begin
  882. with PIBBookmark(Buffer + FRecordSize)^ do
  883. begin
  884. BookmarkData := FCurrentRecord;
  885. BookmarkFlag := bfCurrent;
  886. end;
  887. end
  888. else if (Result = grError) then
  889. DatabaseError('No record');
  890. end;
  891. function TIBQuery.GetRecordCount: integer;
  892. begin
  893. Result := FRecordCount;
  894. end;
  895. function TIBQuery.GetRecordSize: Word;
  896. begin
  897. Result := FRecordSize;
  898. end;
  899. procedure TIBQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  900. begin
  901. // not implemented - sql dataset
  902. end;
  903. procedure TIBQuery.InternalClose;
  904. begin
  905. FreeFldBuffers;
  906. FreeStatement;
  907. if DefaultFields then
  908. DestroyFields;
  909. FIsEOF := False;
  910. FCurrentRecord := -1;
  911. FBufferSize := 0;
  912. FRecordSize := 0;
  913. FRecordCount:= 0;
  914. FOpen:=False;
  915. end;
  916. procedure TIBQuery.InternalDelete;
  917. begin
  918. // not implemented - sql dataset
  919. end;
  920. procedure TIBQuery.InternalFirst;
  921. begin
  922. FCurrentRecord := -1;
  923. end;
  924. procedure TIBQuery.InternalGotoBookmark(ABookmark: Pointer);
  925. begin
  926. FCurrentRecord := PInteger(ABookmark)^;
  927. end;
  928. procedure TIBQuery.InternalHandleException;
  929. begin
  930. end;
  931. procedure TIBQuery.InternalInitFieldDefs;
  932. var
  933. x : integer;
  934. lenset : boolean;
  935. TransLen : word;
  936. TransType : TFieldType;
  937. begin
  938. if FLoadingFieldDefs then
  939. Exit;
  940. FLoadingFieldDefs := True;
  941. try
  942. FieldDefs.Clear;
  943. {$R-}
  944. for x := 0 to FSQLDA^.SQLD - 1 do
  945. begin
  946. TranslateFldType(FSQLDA^.SQLVar[x].SQLType, FSQLDA^.SQLVar[x].SQLLen, lenset,
  947. TransType, TransLen);
  948. TFieldDef.Create(FieldDefs, FSQLDA^.SQLVar[x].SQLName, TransType,
  949. TransLen, False, (x + 1));
  950. end;
  951. {$R+}
  952. finally
  953. FLoadingFieldDefs := False;
  954. end;
  955. end;
  956. procedure TIBQuery.InternalInitRecord(Buffer: PChar);
  957. begin
  958. FillChar(Buffer^, FBufferSize, #0);
  959. end;
  960. procedure TIBQuery.InternalLast;
  961. begin
  962. FCurrentRecord := RecordCount;
  963. end;
  964. procedure TIBQuery.InternalOpen;
  965. begin
  966. try
  967. AllocStatement;
  968. PrepareStatement;
  969. GetStatementType;
  970. if FStatementType in [stSelect] then
  971. begin
  972. DescribeStatement;
  973. AllocFldBuffers;
  974. Execute;
  975. FOpen:=True;
  976. InternalInitFieldDefs;
  977. if DefaultFields then
  978. CreateFields;
  979. SetFieldSizes;
  980. BindFields(True);
  981. end
  982. else Execute;
  983. except
  984. on E:Exception do
  985. raise;
  986. end;
  987. end;
  988. procedure TIBQuery.InternalPost;
  989. begin
  990. // not implemented - sql dataset
  991. end;
  992. procedure TIBQuery.InternalSetToRecord(Buffer: PChar);
  993. begin
  994. FCurrentRecord := PIBBookmark(Buffer + FRecordSize)^.BookmarkData;
  995. end;
  996. function TIBQuery.IsCursorOpen: Boolean;
  997. begin
  998. Result := FOpen;
  999. end;
  1000. procedure TIBQuery.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  1001. begin
  1002. PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
  1003. end;
  1004. procedure TIBQuery.SetBookmarkData(Buffer: PChar; Data: Pointer);
  1005. begin
  1006. PIBBookmark(Buffer + FRecordSize)^.BookmarkData := PInteger(Data)^;
  1007. end;
  1008. procedure TIBQuery.SetFieldData(Field: TField; Buffer: Pointer);
  1009. begin
  1010. end;
  1011. // public part
  1012. procedure TIBQuery.ExecSQL;
  1013. begin
  1014. AllocStatement;
  1015. try
  1016. PrepareStatement;
  1017. GetStatementType;
  1018. Execute;
  1019. finally
  1020. FreeStatement;
  1021. end;
  1022. end;
  1023. constructor TIBQuery.Create(AOwner : TComponent);
  1024. begin
  1025. inherited Create(AOwner);
  1026. FSQL := TStringList.Create;
  1027. FCurrentRecord := -1;
  1028. AllocSQLDA(10);
  1029. end;
  1030. destructor TIBQuery.Destroy;
  1031. begin
  1032. if Active then Close;
  1033. FSQL.Free;
  1034. inherited Destroy;
  1035. FreeMem(FSQLDA);
  1036. end;
  1037. { TIBStoredProc }
  1038. end.