interbase.pp 31 KB

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