interbase.pp 34 KB

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