interbase.pp 34 KB

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