interbase.pp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  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. // Publish TDataset properties.
  247. property Active;
  248. property AutoCalcFields;
  249. property BeforeOpen;
  250. property AfterOpen;
  251. property BeforeClose;
  252. property AfterClose;
  253. property BeforeInsert;
  254. property AfterInsert;
  255. property BeforeEdit;
  256. property AfterEdit;
  257. property BeforePost;
  258. property AfterPost;
  259. property BeforeCancel;
  260. property AfterCancel;
  261. property BeforeDelete;
  262. property AfterDelete;
  263. property BeforeScroll;
  264. property AfterScroll;
  265. property OnCalcFields;
  266. property OnDeleteError;
  267. property OnEditError;
  268. property OnFilterRecord;
  269. property OnNewRecord;
  270. property OnPostError;
  271. end;
  272. { TIBStoredProc - not implemented - yet :-/}
  273. TIBStoredProc = class (TDataset)
  274. private
  275. protected
  276. public
  277. published
  278. end;
  279. implementation
  280. type
  281. TTm = packed record
  282. tm_sec : longint;
  283. tm_min : longint;
  284. tm_hour : longint;
  285. tm_mday : longint;
  286. tm_mon : longint;
  287. tm_year : longint;
  288. tm_wday : longint;
  289. tm_yday : longint;
  290. tm_isdst : longint;
  291. __tm_gmtoff : longint;
  292. __tm_zone : Pchar;
  293. end;
  294. procedure CheckError(ProcName : string; Status : array of ISC_STATUS);
  295. var
  296. buf : array [0..1024] of char;
  297. p : pointer;
  298. Msg : string;
  299. begin
  300. if ((Status[0] = 1) and (Status[1] <> 0)) then
  301. begin
  302. p := @Status;
  303. while isc_interprete(Buf, @p) > 0 do
  304. Msg := Msg + #10' -' + StrPas(Buf);
  305. raise EInterBaseError.Create(ProcName + ': ' + Msg);
  306. end;
  307. end;
  308. { TIBDatabase }
  309. procedure TIBDatabase.SetDBDialect;
  310. var
  311. x : integer;
  312. Len : integer;
  313. Buffer : string;
  314. ResBuf : array [0..39] of byte;
  315. begin
  316. Buffer := Chr(isc_info_db_sql_dialect) + Chr(isc_info_end);
  317. if isc_database_info(@FStatus, @FIBDatabaseHandle, Length(Buffer),
  318. @Buffer[1], SizeOf(ResBuf), @ResBuf) <> 0 then
  319. CheckError('TIBDatabse.SetDBDialect', FStatus);
  320. x := 0;
  321. while x < 40 do
  322. case ResBuf[x] of
  323. isc_info_db_sql_dialect :
  324. begin
  325. Inc(x);
  326. Len := isc_vax_integer(@ResBuf[x], 2);
  327. Inc(x, 2);
  328. FDialect := isc_vax_integer(@ResBuf[x], Len);
  329. Inc(x, Len);
  330. end;
  331. isc_info_end : Break;
  332. end;
  333. end;
  334. procedure TIBDatabase.SetTransaction(Value : TIBTransaction);
  335. begin
  336. if FTransaction = nil then
  337. begin
  338. FTransaction := Value;
  339. if Assigned(FTransaction) then
  340. FTransaction.Database := Self;
  341. exit;
  342. end;
  343. if (Value <> FTransaction) and (Value <> nil) then
  344. if (not FTransaction.Active) then
  345. begin
  346. FTransaction := Value;
  347. FTransaction.Database := Self;
  348. end
  349. else
  350. raise EInterBaseError.Create('Cannot assign transaction while old transaction active!');
  351. end;
  352. function TIBDatabase.GetHandle: pointer;
  353. begin
  354. Result := FIBDatabaseHandle;
  355. end;
  356. procedure TIBDatabase.DoInternalConnect;
  357. var
  358. DPB : string;
  359. begin
  360. if Connected then
  361. Close;
  362. DPB := chr(isc_dpb_version1);
  363. if (FUserName <> '') then
  364. begin
  365. DPB := DPB + chr(isc_dpb_user_name) + chr(Length(FUserName)) + FUserName;
  366. if (FPassword <> '') then
  367. DPB := DPB + chr(isc_dpb_password) + chr(Length(FPassword)) + FPassword;
  368. end;
  369. if (FRole <> '') then
  370. DPB := DPB + chr(isc_dpb_sql_role_name) + chr(Length(FRole)) + FRole;
  371. if Length(CharSet) > 0 then
  372. DPB := DPB + Chr(isc_dpb_lc_ctype) + Chr(Length(CharSet)) + CharSet;
  373. if (DatabaseName = '') then
  374. raise EInterBaseError.Create('TIBDatabase.Open: Database connect string not filled in!');
  375. FIBDatabaseHandle := nil;
  376. if isc_attach_database(@FStatus, Length(DatabaseName), @DatabaseName[1], @FIBDatabaseHandle,
  377. Length(DPB), @DPB[1]) <> 0 then
  378. CheckError('TIBDatabase.Open', FStatus);
  379. SetDBDialect;
  380. end;
  381. procedure TIBDatabase.DoInternalDisconnect;
  382. begin
  383. if not Connected then
  384. begin
  385. FIBDatabaseHandle := nil;
  386. Exit;
  387. end;
  388. isc_detach_database(@FStatus[0], @FIBDatabaseHandle);
  389. CheckError('TIBDatabase.Close', FStatus);
  390. end;
  391. procedure TIBDatabase.StartTransaction;
  392. begin
  393. if FTransaction = nil then
  394. raise EDatabaseError.Create('TIBDatabase.StartTransaction: Transaction not set');
  395. FTransaction.Active := True;
  396. end;
  397. procedure TIBDatabase.EndTransaction;
  398. begin
  399. if FTransaction = nil then
  400. raise EDatabaseError.Create('TIBDatabase.EndTransaction: Transaction not set');
  401. FTransaction.Active := False;
  402. end;
  403. destructor TIBDatabase.Destroy;
  404. begin
  405. if FTransaction <> nil then
  406. begin
  407. FTransaction.Active := False;
  408. FTransaction.Database := nil;
  409. end;
  410. inherited Destroy;
  411. end;
  412. { TIBTransaction }
  413. procedure TIBTransaction.SetActive(Value : boolean);
  414. begin
  415. if FActive and (not Value) then
  416. Rollback
  417. else if (not FActive) and Value then
  418. StartTransaction;
  419. end;
  420. procedure TIBTransaction.SetTPB;
  421. begin
  422. FTPB := chr(isc_tpb_version3);
  423. case FAccessMode of
  424. amReadWrite : FTPB := FTPB + chr(isc_tpb_write);
  425. amReadOnly : FTPB := FTPB + chr(isc_tpb_read);
  426. end;
  427. case FIsolationLevel of
  428. ilConsistent : FTPB := FTPB + chr(isc_tpb_consistency);
  429. ilConcurrent : FTPB := FTPB + chr(isc_tpb_concurrency);
  430. ilReadCommittedRecV : FTPB := FTPB + chr(isc_tpb_read_committed) +
  431. chr(isc_tpb_rec_version);
  432. ilReadCommitted : FTPB := FTPB + chr(isc_tpb_read_committed) +
  433. chr(isc_tpb_no_rec_version);
  434. end;
  435. case FLockResolution of
  436. lrWait : FTPB := FTPB + chr(isc_tpb_wait);
  437. lrNoWait : FTPB := FTPB + chr(isc_tpb_nowait);
  438. end;
  439. case FTableReservation of
  440. trSharedLockRead : FTPB := FTPB + chr(isc_tpb_shared) +
  441. chr(isc_tpb_lock_read);
  442. trSharedLockWrite : FTPB := FTPB + chr(isc_tpb_shared) +
  443. chr(isc_tpb_lock_write);
  444. trProtectedLockRead : FTPB := FTPB + chr(isc_tpb_protected) +
  445. chr(isc_tpb_lock_read);
  446. trProtectedLockWrite : FTPB := FTPB + chr(isc_tpb_protected) +
  447. chr(isc_tpb_lock_write);
  448. end;
  449. end;
  450. function TIBTransaction.GetHandle: pointer;
  451. begin
  452. Result := FTransactionHandle;
  453. end;
  454. procedure TIBTransaction.Commit;
  455. begin
  456. if not FActive then Exit;
  457. if isc_commit_transaction(@FStatus, @FTransactionHandle) <> 0 then
  458. CheckError('TIBTransaction.Commit', FStatus)
  459. else FActive := False;
  460. end;
  461. procedure TIBTransaction.CommitRetaining;
  462. begin
  463. if not FActive then Exit;
  464. if isc_commit_retaining(@FStatus, @FTransactionHandle) <> 0 then
  465. CheckError('TIBTransaction.CommitRetaining', FStatus);
  466. end;
  467. procedure TIBTransaction.Rollback;
  468. begin
  469. if not FActive then Exit;
  470. if isc_rollback_transaction(@FStatus, @FTransactionHandle) <> 0 then
  471. CheckError('TIBTransaction.Rollback', FStatus)
  472. else FActive := False;
  473. end;
  474. procedure TIBTransaction.RollbackRetaining;
  475. begin
  476. if not FActive then Exit;
  477. if isc_rollback_retaining(@FStatus, @FTransactionHandle) <> 0 then
  478. CheckError('TIBTransaction.RollbackRetaining', FStatus);
  479. end;
  480. procedure TIBTransaction.StartTransaction;
  481. var
  482. DBHandle : pointer;
  483. begin
  484. if Active then Active := False;
  485. if FDatabase = nil then
  486. raise EInterBaseError.Create('TIBTransaction.StartTransaction: Database not assigned!');
  487. if not Database.Connected then
  488. Database.Open;
  489. DBHandle := Database.GetHandle;
  490. SetTPB;
  491. FTransactionHandle := nil;
  492. if isc_start_transaction(@FStatus, @FTransactionHandle, 1,
  493. [@DBHandle, Length(FTPB), @FTPB[1]]) <> 0 then
  494. CheckError('TIBTransaction.StartTransaction',FStatus)
  495. else FActive := True;
  496. end;
  497. constructor TIBTransaction.Create(AOwner : TComponent);
  498. begin
  499. inherited Create(AOwner);
  500. FIsolationLevel := ilReadCommitted;
  501. end;
  502. destructor TIBTransaction.Destroy;
  503. begin
  504. // This will also do a Rollback, if the transaction is currently active
  505. Active := False;
  506. if Database <> nil then
  507. Database.Transaction := nil;
  508. inherited Destroy;
  509. end;
  510. { TIBQuery }
  511. procedure TIBQuery.SetTransaction(Value : TIBTransaction);
  512. begin
  513. CheckInactive;
  514. if (FTransaction <> Value) then
  515. FTransaction := Value;
  516. end;
  517. procedure TIBQuery.SetDatabase(Value : TIBDatabase);
  518. begin
  519. CheckInactive;
  520. if (FDatabase <> Value) then
  521. begin
  522. FDatabase := Value;
  523. if (FTransaction = nil) and (Assigned(FDatabase.Transaction)) then
  524. SetTransaction(FDatabase.Transaction);
  525. end;
  526. end;
  527. procedure TIBQuery.AllocSQLDA(Count : integer);
  528. begin
  529. if FSQLDAAllocated > 0 then
  530. FreeMem(FSQLDA);
  531. GetMem(FSQLDA, XSQLDA_Length(Count));
  532. { Zero out the memory block to avoid problems with exceptions within the
  533. constructor of this class. }
  534. FillChar(FSQLDA^, XSQLDA_Length(Count), 0);
  535. FSQLDAAllocated := Count;
  536. FSQLDA^.Version := sqlda_version1;
  537. FSQLDA^.SQLN := Count;
  538. end;
  539. procedure TIBQuery.AllocStatement;
  540. var
  541. dh : pointer;
  542. begin
  543. if not FDatabase.Connected then
  544. FDatabase.Open;
  545. dh := FDatabase.GetHandle;
  546. if isc_dsql_allocate_statement(@FStatus, @dh, @FStatement) <> 0 then
  547. CheckError('TIBQuery.AllocStatement', FStatus);
  548. end;
  549. procedure TIBQuery.FreeStatement;
  550. begin
  551. if isc_dsql_free_statement(@FStatus, @FStatement, DSQL_Drop) <> 0 then
  552. CheckError('TIBQuery.FreeStatement', FStatus);
  553. FStatement := nil;
  554. end;
  555. procedure TIBQuery.PrepareStatement;
  556. var
  557. Buf : string;
  558. x : integer;
  559. tr : pointer;
  560. begin
  561. tr := FTransaction.GetHandle;
  562. for x := 0 to FSQL.Count - 1 do
  563. Buf := Buf + FSQL[x] + ' ';
  564. if isc_dsql_prepare(@FStatus, @tr, @FStatement, 0, @Buf[1], Database.Dialect, nil) <> 0 then
  565. CheckError('TIBQuery.PrepareStatement', FStatus);
  566. end;
  567. procedure TIBQuery.DescribeStatement;
  568. begin
  569. if isc_dsql_describe(@FStatus, @FStatement, 1, FSQLDA) <> 0 then
  570. CheckError('TIBQuery.DescribeStatement', FStatus);
  571. if FSQLDA^.SQLD > FSQLDA^.SQLN then
  572. begin
  573. AllocSQLDA(FSQLDA^.SQLD);
  574. if isc_dsql_describe(@FStatus, @FStatement, 1, FSQLDA) <> 0 then
  575. CheckError('TIBQuery.DescribeStatement', FStatus);
  576. end;
  577. end;
  578. procedure TIBQuery.SetUpSQLVars;
  579. var
  580. x : integer;
  581. begin
  582. for x := 0 to FSQLDA^.SQLN - 1 do
  583. begin
  584. case FSQLDA^.SQLVar[x].SQLType of
  585. sql_varying + 1:
  586. FSQLDA^.SQLVar[x].SQLType := sql_varying;
  587. sql_text + 1 :
  588. FSQLDA^.SQLVar[x].SQLType := sql_text;
  589. sql_short, sql_short + 1, sql_long + 1:
  590. FSQLDA^.SQLVar[x].SQLType := sql_long;
  591. sql_float + 1 :
  592. FSQLDA^.SQLVar[x].SQLType := sql_float;
  593. sql_double + 1 :
  594. FSQLDA^.SQLVar[x].SQLType := sql_double;
  595. sql_blob + 1 :
  596. FSQLDA^.SQLVar[x].SQLType := sql_blob;
  597. sql_type_time + 1 :
  598. FSQLDA^.SQLVar[x].SQLType := sql_type_time;
  599. sql_timestamp + 1:
  600. FSQLDA^.SQLVar[x].SQLType := sql_timestamp;
  601. end;
  602. end;
  603. end;
  604. procedure TIBQuery.AllocFldBuffers;
  605. var
  606. x : shortint;
  607. begin
  608. {$R-}
  609. for x := 0 to FSQLDA^.SQLD - 1 do
  610. begin
  611. FSQLDA^.SQLVar[x].SQLData := AllocMem(FSQLDA^.SQLVar[x].SQLLen);
  612. FSQLDA^.SQLVar[x].SQLInd := @FFieldFlag[x];
  613. end;
  614. {$R+}
  615. end;
  616. procedure TIBQuery.FreeFldBuffers;
  617. var
  618. x : integer;
  619. begin
  620. {$R-}
  621. for x := 0 to FSQLDA^.SQLD - 1 do
  622. begin
  623. if FSQLDA^.SQLVar[x].SQLData <> nil then
  624. begin
  625. FreeMem(FSQLDA^.SQLVar[x].SQLData);
  626. FSQLDA^.SQLVar[x].SQLData := nil;
  627. end;
  628. end;
  629. {$R+}
  630. end;
  631. procedure TIBQuery.Fetch;
  632. var
  633. retcode : integer;
  634. begin
  635. if not (FStatementType in [stSelect]) then
  636. Exit;
  637. retcode := isc_dsql_fetch(@FStatus, @FStatement, 1, FSQLDA);
  638. if (retcode <> 0) and (retcode <> 100) then
  639. CheckError('TIBQuery.Fetch', FStatus);
  640. FIsEOF := (retcode = 100);
  641. end;
  642. function TIBQuery.LoadBufferFromSQLDA(Buffer : PChar): TGetResult;
  643. var
  644. x : integer;
  645. VarcharLen : word;
  646. begin
  647. Fetch;
  648. if FIsEOF then
  649. begin
  650. Result := grEOF;
  651. Exit;
  652. end;
  653. {$R-}
  654. for x := 0 to FSQLDA^.SQLD - 1 do
  655. begin
  656. with FSQLDA^.SQLVar[x] do
  657. begin
  658. if ((SQLType and not 1) = SQL_VARYING) then
  659. begin
  660. Move(SQLData^, VarcharLen, 2);
  661. Move((SQLData + 2)^, Buffer^, VarcharLen);
  662. PChar(Buffer + VarcharLen)^ := #0;
  663. end
  664. else Move(SQLData^, Buffer^, SQLLen);
  665. Inc(Buffer, SQLLen);
  666. end;
  667. end;
  668. {$R+}
  669. Result := grOK;
  670. end;
  671. procedure TIBQuery.GetStatementType;
  672. var
  673. x : integer;
  674. ResBuf : array [0..7] of char;
  675. begin
  676. FStatementType := stNone;
  677. x := isc_info_sql_stmt_type;
  678. if isc_dsql_sql_info(@FStatus, @FStatement, SizeOf(X),
  679. @x, SizeOf(ResBuf), @ResBuf) <> 0 then
  680. CheckError('TIBQuery.GetStatementType', FStatus);
  681. if Ord(ResBuf[0]) = isc_info_sql_stmt_type then
  682. begin
  683. x := isc_vax_integer(@ResBuf[1], 2);
  684. FStatementType := TStatementType(isc_vax_integer(@ResBuf[3], x));
  685. end;
  686. end;
  687. procedure TIBQuery.SetFieldSizes;
  688. var
  689. x : integer;
  690. begin
  691. FRecordSize := 0;
  692. FBufferSize := 0;
  693. {$R-}
  694. for x := 0 to FSQLDA^.SQLD - 1 do
  695. Inc(FRecordSize, FSQLDA^.SQLVar[x].SQLLen);
  696. {$R+}
  697. FBufferSize := FRecordSize + SizeOf(TIBBookmark);
  698. end;
  699. procedure TIBQuery.TranslateFldType(SQLType, SQLLen : integer; var LensSet : boolean;
  700. var TrType : TFieldType; var TrLen : word);
  701. begin
  702. LensSet := False;
  703. case (SQLType and not 1) of
  704. SQL_VARYING :
  705. begin
  706. LensSet := True;
  707. TrType := ftString;
  708. TrLen := SQLLen;
  709. end;
  710. SQL_TEXT :
  711. begin
  712. LensSet := True;
  713. TrType := ftString;
  714. TrLen := SQLLen;
  715. end;
  716. SQL_TYPE_DATE :
  717. TrType := ftDateTime;
  718. SQL_TYPE_TIME :
  719. TrType := ftDateTime;
  720. SQL_TIMESTAMP :
  721. TrType := ftDateTime;
  722. SQL_ARRAY :
  723. begin
  724. end;
  725. SQL_BLOB :
  726. begin
  727. end;
  728. SQL_SHORT :
  729. begin
  730. LensSet := True;
  731. TrLen := SQLLen;
  732. TrType := ftInteger;
  733. end;
  734. SQL_LONG :
  735. begin
  736. LensSet := True;
  737. TrLen := SQLLen;
  738. TrType := ftInteger;
  739. end;
  740. SQL_INT64 :
  741. {TrType := ftInt64};
  742. SQL_DOUBLE :
  743. begin
  744. LensSet := True;
  745. TrLen := SQLLen;
  746. TrType := ftFloat;
  747. end;
  748. SQL_FLOAT :
  749. begin
  750. LensSet := True;
  751. TrLen := SQLLen;
  752. TrType := ftFloat;
  753. end;
  754. end;
  755. end;
  756. procedure TIBQuery.ExecuteImmediate;
  757. begin
  758. end;
  759. procedure TIBQuery.ExecuteParams;
  760. begin
  761. //!! to be implemented
  762. end;
  763. procedure TIBQuery.Execute;
  764. var
  765. tr : pointer;
  766. begin
  767. tr := FTransaction.GetHandle;
  768. if isc_dsql_execute(@FStatus, @tr, @FStatement, 1, nil) <> 0 then
  769. CheckError('TIBQuery.Execute', FStatus);
  770. end;
  771. procedure TIBQuery.GetDateTime(CurrBuff, Buffer : pointer; AType : integer);
  772. var
  773. CTime : TTm; // C struct time
  774. STime : TSystemTime; // System time
  775. PTime : TDateTime; // Pascal time
  776. begin
  777. case (AType and not 1) of
  778. SQL_TYPE_DATE :
  779. isc_decode_sql_date(PISC_DATE(CurrBuff), @CTime);
  780. SQL_TYPE_TIME :
  781. isc_decode_sql_time(PISC_TIME(CurrBuff), @CTime);
  782. SQL_TIMESTAMP :
  783. isc_decode_timestamp(PISC_TIMESTAMP(CurrBuff), @CTime);
  784. end;
  785. STime.Year := CTime.tm_year + 1900;
  786. STime.Month := CTime.tm_mon + 1;
  787. STime.Day := CTime.tm_mday;
  788. STime.Hour := CTime.tm_hour;
  789. STime.Minute := CTime.tm_min;
  790. STime.Second := CTime.tm_sec;
  791. STime.Millisecond := 0;
  792. PTime := SystemTimeToDateTime(STime);
  793. Move(PTime, Buffer^, SizeOf(PTime));
  794. end;
  795. procedure TIBQuery.GetFloat(CurrBuff, Buffer : pointer; Field : TField);
  796. var
  797. Ext : extended;
  798. Dbl : double;
  799. Sin : single;
  800. begin
  801. case Field.Size of
  802. 4 :
  803. begin
  804. Move(CurrBuff^, Sin, 4);
  805. Ext := Sin;
  806. end;
  807. 8 :
  808. begin
  809. Move(CurrBuff^, Dbl, 8);
  810. Ext := Dbl;
  811. end;
  812. 10: Move(CurrBuff^, Ext, 10);
  813. end;
  814. Move(Ext, Buffer^, 10);
  815. end;
  816. function TIBQuery.AllocRecordBuffer: PChar;
  817. begin
  818. Result := AllocMem(FBufferSize);
  819. end;
  820. procedure TIBQuery.FreeRecordBuffer(var Buffer: PChar);
  821. begin
  822. if Assigned(@Buffer) then
  823. FreeMem(Buffer);
  824. end;
  825. procedure TIBQuery.GetBookmarkData(Buffer: PChar; Data: Pointer);
  826. begin
  827. PInteger(Data)^ := PIBBookmark(Buffer + FRecordSize)^.BookmarkData;
  828. end;
  829. function TIBQuery.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  830. begin
  831. Result := PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag;
  832. end;
  833. function TIBQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  834. var
  835. x : longint;
  836. b : longint;
  837. CurrBuff : PChar;
  838. begin
  839. Result := False;
  840. CurrBuff := ActiveBuffer;
  841. for x := 0 to FSQLDA^.SQLD - 1 do
  842. begin
  843. {$R-}
  844. if (Field.FieldName = FSQLDA^.SQLVar[x].SQLName) then
  845. begin
  846. case Field.DataType of
  847. ftInteger :
  848. begin
  849. b := 0;
  850. Move(b, Buffer^, 4);
  851. Move(CurrBuff^, Buffer^, Field.Size);
  852. end;
  853. ftDate, ftTime, ftDateTime:
  854. GetDateTime(CurrBuff, Buffer, FSQLDA^.SQLVar[x].SQLType);
  855. ftString :
  856. begin
  857. Move(CurrBuff^, Buffer^, Field.Size);
  858. PChar(Buffer + Field.Size)^ := #0;
  859. end;
  860. ftFloat :
  861. GetFloat(CurrBuff, Buffer, Field);
  862. end;
  863. Result := True;
  864. Break;
  865. end
  866. else Inc(CurrBuff, FSQLDA^.SQLVar[x].SQLLen);
  867. {$R+}
  868. end;
  869. end;
  870. function TIBQuery.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  871. begin
  872. if FStatementType <> stSelect then
  873. begin
  874. Result := grEOF;
  875. Exit;
  876. end;
  877. if FIsEOF then
  878. Result := grEOF
  879. else begin
  880. Result := grOK;
  881. case GetMode of
  882. gmPrior :
  883. if FCurrentRecord <= 0 then
  884. begin
  885. Result := grBOF;
  886. FCurrentRecord := -1;
  887. end
  888. else Dec(FCurrentRecord);
  889. gmCurrent :
  890. if (FCurrentRecord < 0) or (FCurrentRecord >= RecordCount) then
  891. Result := grError;
  892. gmNext :
  893. if FCurrentRecord >= (RecordCount - 1) then
  894. begin
  895. Result := LoadBufferFromSQLDA(Buffer);
  896. if Result = grOK then
  897. begin
  898. Inc(FCurrentRecord);
  899. Inc(FRecordCount);
  900. end;
  901. end
  902. else Inc(FCurrentRecord);
  903. end;
  904. end;
  905. if Result = grOK then
  906. begin
  907. with PIBBookmark(Buffer + FRecordSize)^ do
  908. begin
  909. BookmarkData := FCurrentRecord;
  910. BookmarkFlag := bfCurrent;
  911. end;
  912. end
  913. else if (Result = grError) then
  914. DatabaseError('No record');
  915. end;
  916. function TIBQuery.GetRecordCount: integer;
  917. begin
  918. Result := FRecordCount;
  919. end;
  920. function TIBQuery.GetRecordSize: Word;
  921. begin
  922. Result := FRecordSize;
  923. end;
  924. procedure TIBQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  925. begin
  926. // not implemented - sql dataset
  927. end;
  928. procedure TIBQuery.InternalClose;
  929. begin
  930. FreeFldBuffers;
  931. FreeStatement;
  932. if DefaultFields then
  933. DestroyFields;
  934. FIsEOF := False;
  935. FCurrentRecord := -1;
  936. FBufferSize := 0;
  937. FRecordSize := 0;
  938. FRecordCount:= 0;
  939. FOpen:=False;
  940. end;
  941. procedure TIBQuery.InternalDelete;
  942. begin
  943. // not implemented - sql dataset
  944. end;
  945. procedure TIBQuery.InternalFirst;
  946. begin
  947. FCurrentRecord := -1;
  948. end;
  949. procedure TIBQuery.InternalGotoBookmark(ABookmark: Pointer);
  950. begin
  951. FCurrentRecord := PInteger(ABookmark)^;
  952. end;
  953. procedure TIBQuery.InternalHandleException;
  954. begin
  955. end;
  956. procedure TIBQuery.InternalInitFieldDefs;
  957. var
  958. x : integer;
  959. lenset : boolean;
  960. TransLen : word;
  961. TransType : TFieldType;
  962. begin
  963. if FLoadingFieldDefs then
  964. Exit;
  965. FLoadingFieldDefs := True;
  966. try
  967. FieldDefs.Clear;
  968. {$R-}
  969. for x := 0 to FSQLDA^.SQLD - 1 do
  970. begin
  971. TranslateFldType(FSQLDA^.SQLVar[x].SQLType, FSQLDA^.SQLVar[x].SQLLen, lenset,
  972. TransType, TransLen);
  973. TFieldDef.Create(FieldDefs, FSQLDA^.SQLVar[x].SQLName, TransType,
  974. TransLen, False, (x + 1));
  975. end;
  976. {$R+}
  977. finally
  978. FLoadingFieldDefs := False;
  979. end;
  980. end;
  981. procedure TIBQuery.InternalInitRecord(Buffer: PChar);
  982. begin
  983. FillChar(Buffer^, FBufferSize, #0);
  984. end;
  985. procedure TIBQuery.InternalLast;
  986. begin
  987. FCurrentRecord := RecordCount;
  988. end;
  989. procedure TIBQuery.InternalOpen;
  990. begin
  991. try
  992. AllocStatement;
  993. PrepareStatement;
  994. GetStatementType;
  995. if FStatementType in [stSelect] then
  996. begin
  997. DescribeStatement;
  998. AllocFldBuffers;
  999. Execute;
  1000. FOpen:=True;
  1001. InternalInitFieldDefs;
  1002. if DefaultFields then
  1003. CreateFields;
  1004. SetFieldSizes;
  1005. BindFields(True);
  1006. end
  1007. else Execute;
  1008. except
  1009. on E:Exception do
  1010. raise;
  1011. end;
  1012. end;
  1013. procedure TIBQuery.InternalPost;
  1014. begin
  1015. // not implemented - sql dataset
  1016. end;
  1017. procedure TIBQuery.InternalSetToRecord(Buffer: PChar);
  1018. begin
  1019. FCurrentRecord := PIBBookmark(Buffer + FRecordSize)^.BookmarkData;
  1020. end;
  1021. function TIBQuery.IsCursorOpen: Boolean;
  1022. begin
  1023. Result := FOpen;
  1024. end;
  1025. procedure TIBQuery.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  1026. begin
  1027. PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
  1028. end;
  1029. procedure TIBQuery.SetBookmarkData(Buffer: PChar; Data: Pointer);
  1030. begin
  1031. PIBBookmark(Buffer + FRecordSize)^.BookmarkData := PInteger(Data)^;
  1032. end;
  1033. procedure TIBQuery.SetFieldData(Field: TField; Buffer: Pointer);
  1034. begin
  1035. end;
  1036. // public part
  1037. procedure TIBQuery.ExecSQL;
  1038. begin
  1039. AllocStatement;
  1040. try
  1041. PrepareStatement;
  1042. GetStatementType;
  1043. Execute;
  1044. finally
  1045. FreeStatement;
  1046. end;
  1047. end;
  1048. constructor TIBQuery.Create(AOwner : TComponent);
  1049. begin
  1050. inherited Create(AOwner);
  1051. FSQL := TStringList.Create;
  1052. FCurrentRecord := -1;
  1053. AllocSQLDA(10);
  1054. end;
  1055. destructor TIBQuery.Destroy;
  1056. begin
  1057. if Active then Close;
  1058. FSQL.Free;
  1059. inherited Destroy;
  1060. FreeMem(FSQLDA);
  1061. end;
  1062. { TIBStoredProc }
  1063. end.
  1064. {
  1065. $Log$
  1066. Revision 1.12 2004-05-01 23:56:59 michael
  1067. + Published TDataset properties
  1068. Revision 1.11 2003/12/07 23:13:34 sg
  1069. * Added Log entries to end of file
  1070. }