interbase.pp 33 KB

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