interbase.pp 31 KB

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