interbase.pp 31 KB

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