interbase.pp 34 KB

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