interbase.pp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  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. FreeMem(Buffer);
  798. end;
  799. procedure TIBQuery.GetBookmarkData(Buffer: PChar; Data: Pointer);
  800. begin
  801. PInteger(Data)^ := PIBBookmark(Buffer + FRecordSize)^.BookmarkData;
  802. end;
  803. function TIBQuery.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  804. begin
  805. Result := PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag;
  806. end;
  807. function TIBQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  808. var
  809. x : longint;
  810. b : longint;
  811. CurrBuff : PChar;
  812. begin
  813. Result := False;
  814. CurrBuff := ActiveBuffer;
  815. for x := 0 to FSQLDA^.SQLD - 1 do
  816. begin
  817. {$R-}
  818. if (Field.FieldName = FSQLDA^.SQLVar[x].SQLName) then
  819. begin
  820. case Field.DataType of
  821. ftInteger :
  822. begin
  823. b := 0;
  824. Move(b, Buffer^, 4);
  825. Move(CurrBuff^, Buffer^, Field.Size);
  826. end;
  827. ftDate, ftTime, ftDateTime:
  828. GetDateTime(CurrBuff, Buffer, FSQLDA^.SQLVar[x].SQLType);
  829. ftString :
  830. begin
  831. Move(CurrBuff^, Buffer^, Field.Size);
  832. PChar(Buffer + Field.Size)^ := #0;
  833. end;
  834. ftFloat :
  835. GetFloat(CurrBuff, Buffer, Field);
  836. end;
  837. Result := True;
  838. Break;
  839. end
  840. else Inc(CurrBuff, FSQLDA^.SQLVar[x].SQLLen);
  841. {$R+}
  842. end;
  843. end;
  844. function TIBQuery.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  845. begin
  846. if FStatementType <> stSelect then
  847. begin
  848. Result := grEOF;
  849. Exit;
  850. end;
  851. if FIsEOF then
  852. Result := grEOF
  853. else begin
  854. Result := grOK;
  855. case GetMode of
  856. gmPrior :
  857. if FCurrentRecord <= 0 then
  858. begin
  859. Result := grBOF;
  860. FCurrentRecord := -1;
  861. end
  862. else Dec(FCurrentRecord);
  863. gmCurrent :
  864. if (FCurrentRecord < 0) or (FCurrentRecord >= RecordCount) then
  865. Result := grError;
  866. gmNext :
  867. if FCurrentRecord >= (RecordCount - 1) then
  868. begin
  869. Result := LoadBufferFromSQLDA(Buffer);
  870. if Result = grOK then
  871. begin
  872. Inc(FCurrentRecord);
  873. Inc(FRecordCount);
  874. end;
  875. end
  876. else Inc(FCurrentRecord);
  877. end;
  878. end;
  879. if Result = grOK then
  880. begin
  881. with PIBBookmark(Buffer + FRecordSize)^ do
  882. begin
  883. BookmarkData := FCurrentRecord;
  884. BookmarkFlag := bfCurrent;
  885. end;
  886. end
  887. else if (Result = grError) then
  888. DatabaseError('No record');
  889. end;
  890. function TIBQuery.GetRecordCount: integer;
  891. begin
  892. Result := FRecordCount;
  893. end;
  894. function TIBQuery.GetRecordSize: Word;
  895. begin
  896. Result := FRecordSize;
  897. end;
  898. procedure TIBQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  899. begin
  900. // not implemented - sql dataset
  901. end;
  902. procedure TIBQuery.InternalClose;
  903. begin
  904. FreeFldBuffers;
  905. FreeStatement;
  906. if DefaultFields then
  907. DestroyFields;
  908. FIsEOF := False;
  909. FCurrentRecord := -1;
  910. FBufferSize := 0;
  911. FRecordSize := 0;
  912. FRecordCount:= 0;
  913. FOpen:=False;
  914. end;
  915. procedure TIBQuery.InternalDelete;
  916. begin
  917. // not implemented - sql dataset
  918. end;
  919. procedure TIBQuery.InternalFirst;
  920. begin
  921. FCurrentRecord := -1;
  922. end;
  923. procedure TIBQuery.InternalGotoBookmark(ABookmark: Pointer);
  924. begin
  925. FCurrentRecord := PInteger(ABookmark)^;
  926. end;
  927. procedure TIBQuery.InternalHandleException;
  928. begin
  929. end;
  930. procedure TIBQuery.InternalInitFieldDefs;
  931. var
  932. x : integer;
  933. lenset : boolean;
  934. TransLen : word;
  935. TransType : TFieldType;
  936. begin
  937. if FLoadingFieldDefs then
  938. Exit;
  939. FLoadingFieldDefs := True;
  940. try
  941. FieldDefs.Clear;
  942. {$R-}
  943. for x := 0 to FSQLDA^.SQLD - 1 do
  944. begin
  945. TranslateFldType(FSQLDA^.SQLVar[x].SQLType, FSQLDA^.SQLVar[x].SQLLen, lenset,
  946. TransType, TransLen);
  947. TFieldDef.Create(FieldDefs, FSQLDA^.SQLVar[x].SQLName, TransType,
  948. TransLen, False, (x + 1));
  949. end;
  950. {$R+}
  951. finally
  952. FLoadingFieldDefs := False;
  953. end;
  954. end;
  955. procedure TIBQuery.InternalInitRecord(Buffer: PChar);
  956. begin
  957. FillChar(Buffer^, FBufferSize, #0);
  958. end;
  959. procedure TIBQuery.InternalLast;
  960. begin
  961. FCurrentRecord := RecordCount;
  962. end;
  963. procedure TIBQuery.InternalOpen;
  964. begin
  965. try
  966. AllocStatement;
  967. PrepareStatement;
  968. GetStatementType;
  969. if FStatementType in [stSelect] then
  970. begin
  971. DescribeStatement;
  972. AllocFldBuffers;
  973. Execute;
  974. FOpen:=True;
  975. InternalInitFieldDefs;
  976. if DefaultFields then
  977. CreateFields;
  978. SetFieldSizes;
  979. BindFields(True);
  980. end
  981. else Execute;
  982. except
  983. on E:Exception do
  984. raise;
  985. end;
  986. end;
  987. procedure TIBQuery.InternalPost;
  988. begin
  989. // not implemented - sql dataset
  990. end;
  991. procedure TIBQuery.InternalSetToRecord(Buffer: PChar);
  992. begin
  993. FCurrentRecord := PIBBookmark(Buffer + FRecordSize)^.BookmarkData;
  994. end;
  995. function TIBQuery.IsCursorOpen: Boolean;
  996. begin
  997. Result := FOpen;
  998. end;
  999. procedure TIBQuery.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  1000. begin
  1001. PIBBookmark(Buffer + FRecordSize)^.BookmarkFlag := Value;
  1002. end;
  1003. procedure TIBQuery.SetBookmarkData(Buffer: PChar; Data: Pointer);
  1004. begin
  1005. PIBBookmark(Buffer + FRecordSize)^.BookmarkData := PInteger(Data)^;
  1006. end;
  1007. procedure TIBQuery.SetFieldData(Field: TField; Buffer: Pointer);
  1008. begin
  1009. end;
  1010. // public part
  1011. procedure TIBQuery.ExecSQL;
  1012. begin
  1013. AllocStatement;
  1014. try
  1015. PrepareStatement;
  1016. GetStatementType;
  1017. Execute;
  1018. finally
  1019. FreeStatement;
  1020. end;
  1021. end;
  1022. constructor TIBQuery.Create(AOwner : TComponent);
  1023. begin
  1024. inherited Create(AOwner);
  1025. FSQL := TStringList.Create;
  1026. FCurrentRecord := -1;
  1027. AllocSQLDA(10);
  1028. end;
  1029. destructor TIBQuery.Destroy;
  1030. begin
  1031. if Active then Close;
  1032. FSQL.Free;
  1033. inherited Destroy;
  1034. FreeMem(FSQLDA);
  1035. end;
  1036. { TIBStoredProc }
  1037. end.