sqldb.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. { $Id$
  2. Copyright (c) 2004 by Joost van der Sluis
  3. SQL 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 sqldb;
  11. {$mode objfpc}
  12. {$H+}
  13. {$M+} // ### remove this!!!
  14. interface
  15. uses SysUtils, Classes, DB;
  16. type
  17. TSQLConnection = class;
  18. TSQLTransaction = class;
  19. TSQLQuery = class;
  20. ESQLdbError = class(Exception);
  21. TStatementType = (stNone, stSelect, stInsert, stUpdate, stDelete,
  22. stDDL, stGetSegment, stPutSegment, stExecProcedure,
  23. stStartTrans, stCommit, stRollback, stSelectForUpd);
  24. TSQLHandle = Class(TObject)
  25. // Procedure FreeHandle ; Virtual; Abstract;
  26. end;
  27. { TSQLConnection }
  28. TSQLConnection = class (TDatabase)
  29. private
  30. FPassword : string;
  31. FTransaction : TSQLTransaction;
  32. FUserName : string;
  33. FCharSet : string;
  34. FRole : String;
  35. procedure SetTransaction(Value : TSQLTransaction);
  36. protected
  37. procedure DoInternalConnect; override;
  38. procedure DoInternalDisconnect; override;
  39. function GetHandle : pointer; virtual; abstract;
  40. Function AllocateCursorHandle : TSQLHandle; virtual; abstract;
  41. Function AllocateTransactionHandle : TSQLHandle; virtual; abstract;
  42. { function GetCursor : pointer; virtual; abstract;
  43. procedure FreeCursor(cursor : pointer); virtual; abstract;
  44. function GetTrans : pointer; virtual; abstract;
  45. procedure FreeTrans(trans : pointer); virtual; abstract;}
  46. procedure FreeStatement(cursor : TSQLHandle); virtual; abstract;
  47. procedure FreeSelect(cursor : TSQLHandle); virtual; abstract;
  48. procedure PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string); virtual; abstract;
  49. procedure PrepareSelect(cursor : TSQLHandle); virtual; abstract;
  50. procedure FreeFldBuffers(cursor : TSQLHandle); virtual; abstract;
  51. procedure Execute(cursor: TSQLHandle;atransaction:tSQLtransaction); virtual; abstract;
  52. procedure AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs); virtual; abstract;
  53. function GetFieldSizes(cursor : TSQLHandle) : integer; virtual; abstract;
  54. function Fetch(cursor : TSQLHandle) : boolean; virtual; abstract;
  55. procedure LoadFieldsFromBuffer(cursor : TSQLHandle;buffer : pchar); virtual; abstract;
  56. function GetFieldData(cursor : TSQLHandle; Field: TField; Buffer: Pointer;currbuff : pchar): Boolean; virtual; abstract;
  57. function GetStatementType(cursor : TSQLHandle) : tStatementType; virtual; abstract;
  58. function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
  59. function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
  60. function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
  61. function StartTransaction(trans : TSQLHandle) : boolean; virtual; abstract;
  62. procedure CommitRetaining(trans : TSQLHandle); virtual; abstract;
  63. procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract;
  64. public
  65. destructor Destroy; override;
  66. property Handle: Pointer read GetHandle;
  67. published
  68. property Password : string read FPassword write FPassword;
  69. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  70. property UserName : string read FUserName write FUserName;
  71. property CharSet : string read FCharSet write FCharSet;
  72. property Connected;
  73. Property Role : String read FRole write FRole;
  74. property DatabaseName;
  75. property KeepConnection;
  76. property LoginPrompt;
  77. property Params;
  78. property OnLogin;
  79. end;
  80. { TSQLTransaction }
  81. TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback,
  82. caRollbackRetaining);
  83. TSQLTransaction = class (TDBTransaction)
  84. private
  85. FTrans : TSQLHandle;
  86. FAction : TCommitRollbackAction;
  87. FActive : boolean;
  88. // FDatabase : TSQLConnection;
  89. procedure SetActive(Value : boolean);
  90. protected
  91. function GetHandle : Pointer; virtual;
  92. public
  93. procedure EndTransaction; override;
  94. procedure Commit; virtual;
  95. procedure CommitRetaining; virtual;
  96. procedure Rollback; virtual;
  97. procedure RollbackRetaining; virtual;
  98. procedure StartTransaction;
  99. constructor Create(AOwner : TComponent); override;
  100. destructor Destroy; override;
  101. property Handle: Pointer read GetHandle;
  102. published
  103. property Action : TCommitRollbackAction read FAction write FAction;
  104. property Active : boolean read FActive write SetActive;
  105. // property Database : TSQLConnection read FDatabase write FDatabase;
  106. property Database;
  107. end;
  108. { TSQLQuery }
  109. TSQLQuery = class (Tbufdataset)
  110. private
  111. FCursor : TSQLHandle;
  112. FOpen : Boolean;
  113. FTransaction : TSQLTransaction;
  114. // FDatabase : TSQLConnection;
  115. FSQL : TStrings;
  116. FIsEOF : boolean;
  117. FStatementType : TStatementType;
  118. FLoadingFieldDefs : boolean;
  119. FRecordSize : Integer;
  120. procedure SetTransaction(Value : TSQLTransaction);
  121. procedure FreeStatement;
  122. procedure FreeSelect;
  123. procedure PrepareStatement;
  124. procedure PrepareSelect;
  125. procedure FreeFldBuffers;
  126. procedure Fetch;
  127. function LoadBuffer(Buffer : PChar): TGetResult;
  128. procedure GetStatementType;
  129. procedure SetFieldSizes;
  130. procedure ExecuteImmediate;
  131. procedure ExecuteParams;
  132. procedure Execute;
  133. protected
  134. // abstract & virual methods of TDataset
  135. procedure SetDatabase(Value : TDatabase); override;
  136. function AllocRecord: PChar; override;
  137. procedure FreeRecord(var Buffer: PChar); override;
  138. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  139. function GetNextRecord(Buffer : pchar) : TGetResult; override;
  140. function GetRecordSize: Word; override;
  141. procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
  142. procedure InternalClose; override;
  143. procedure InternalDelete; override;
  144. procedure InternalHandleException; override;
  145. procedure InternalInitFieldDefs; override;
  146. procedure InternalInitRecord(Buffer: PChar); override;
  147. procedure InternalOpen; override;
  148. procedure InternalPost; override;
  149. function IsCursorOpen: Boolean; override;
  150. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  151. public
  152. procedure ExecSQL; virtual;
  153. constructor Create(AOwner : TComponent); override;
  154. destructor Destroy; override;
  155. published
  156. // redeclared data set properties
  157. property Active;
  158. // property FieldDefs stored FieldDefsStored;
  159. property Filter;
  160. property Filtered;
  161. property FilterOptions;
  162. property BeforeOpen;
  163. property AfterOpen;
  164. property BeforeClose;
  165. property AfterClose;
  166. property BeforeInsert;
  167. property AfterInsert;
  168. property BeforeEdit;
  169. property AfterEdit;
  170. property BeforePost;
  171. property AfterPost;
  172. property BeforeCancel;
  173. property AfterCancel;
  174. property BeforeDelete;
  175. property AfterDelete;
  176. property BeforeScroll;
  177. property AfterScroll;
  178. property OnCalcFields;
  179. property OnDeleteError;
  180. property OnEditError;
  181. property OnFilterRecord;
  182. property OnNewRecord;
  183. property OnPostError;
  184. property AutoCalcFields;
  185. property Database;
  186. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  187. // property Database : TSQLConnection read FDatabase write SetDatabase;
  188. property SQL : TStrings read FSQL write FSQL;
  189. end;
  190. implementation
  191. ResourceString
  192. SErrAssTransaction = 'Cannot assign transaction while old transaction active!';
  193. SErrDatabasenAssigned = 'Database not assigned!';
  194. SErrTransactionnSet = 'Transaction not set';
  195. SErrNoStatement = 'SQL statement not set';
  196. { TSQLConnection }
  197. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  198. begin
  199. if FTransaction = nil then
  200. begin
  201. FTransaction := Value;
  202. if Assigned(FTransaction) then
  203. FTransaction.Database := Self;
  204. exit;
  205. end;
  206. if (Value <> FTransaction) and (Value <> nil) then
  207. if (not FTransaction.Active) then
  208. begin
  209. FTransaction := Value;
  210. FTransaction.Database := Self;
  211. end
  212. else
  213. DatabaseError(SErrAssTransaction);
  214. end;
  215. procedure TSQLConnection.DoInternalConnect;
  216. begin
  217. if Connected then
  218. Close;
  219. end;
  220. procedure TSQLQuery.GetStatementType;
  221. begin
  222. FStatementType := (Database as tsqlconnection).GetStatementType(Fcursor);
  223. end;
  224. procedure TSQLConnection.DoInternalDisconnect;
  225. begin
  226. end;
  227. destructor TSQLConnection.Destroy;
  228. begin
  229. if FTransaction <> nil then
  230. begin
  231. FTransaction.Active := False;
  232. FTransaction.Database := nil;
  233. end;
  234. inherited Destroy;
  235. end;
  236. { TSQLTransaction }
  237. procedure TSQLTransaction.SetActive(Value : boolean);
  238. begin
  239. if FActive and (not Value) then
  240. Rollback
  241. else if (not FActive) and Value then
  242. StartTransaction;
  243. end;
  244. function TSQLTransaction.GetHandle: pointer;
  245. begin
  246. Result := (Database as tsqlconnection).GetTransactionHandle(FTrans);
  247. end;
  248. procedure TSQLTransaction.Commit;
  249. begin
  250. if not FActive then Exit;
  251. if (Database as tsqlconnection).commit(FTrans) then FActive := false;
  252. FTrans.free;
  253. end;
  254. procedure TSQLTransaction.CommitRetaining;
  255. begin
  256. if not FActive then Exit;
  257. (Database as tsqlconnection).commitRetaining(FTrans);
  258. end;
  259. procedure TSQLTransaction.Rollback;
  260. begin
  261. if not FActive then Exit;
  262. if (Database as tsqlconnection).RollBack(FTrans) then FActive := false;
  263. FTrans.free;
  264. end;
  265. procedure TSQLTransaction.EndTransaction;
  266. begin
  267. Rollback;
  268. end;
  269. procedure TSQLTransaction.RollbackRetaining;
  270. begin
  271. if not FActive then Exit;
  272. (Database as tsqlconnection).RollBackRetaining(FTrans);
  273. end;
  274. procedure TSQLTransaction.StartTransaction;
  275. var db : TSQLConnection;
  276. begin
  277. if Active then Active := False;
  278. db := (Database as tsqlconnection);
  279. if Db = nil then
  280. DatabaseError(SErrDatabasenAssigned);
  281. if not Db.Connected then
  282. Db.Open;
  283. if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle;
  284. if Db.StartTransaction(FTrans) then FActive := true;
  285. end;
  286. constructor TSQLTransaction.Create(AOwner : TComponent);
  287. begin
  288. inherited Create(AOwner);
  289. end;
  290. destructor TSQLTransaction.Destroy;
  291. begin
  292. // This will also do a Rollback, if the transaction is currently active
  293. Active := False;
  294. // Database.Transaction := nil;
  295. inherited Destroy;
  296. end;
  297. { TSQLQuery }
  298. procedure TSQLQuery.SetTransaction(Value : TSQLTransaction);
  299. begin
  300. CheckInactive;
  301. if (FTransaction <> Value) then
  302. FTransaction := Value;
  303. end;
  304. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  305. var db : tsqlconnection;
  306. begin
  307. if (Database <> Value) then
  308. begin
  309. db := value as tsqlconnection;
  310. inherited setdatabase(value);
  311. if (FTransaction = nil) and (Assigned(Db.Transaction)) then
  312. SetTransaction(Db.Transaction);
  313. { if assigned(fcursor) then freemem(FCursor);
  314. FCursor := Db.AllocateCursorHandle;}
  315. end;
  316. end;
  317. procedure TSQLQuery.FreeStatement;
  318. begin
  319. (Database as tsqlconnection).FreeStatement(FCursor);
  320. end;
  321. procedure TSQLQuery.FreeSelect;
  322. begin
  323. (Database as tsqlconnection).FreeSelect(FCursor);
  324. end;
  325. procedure TSQLQuery.PrepareStatement;
  326. var
  327. Buf : string;
  328. x : integer;
  329. db : tsqlconnection;
  330. begin
  331. db := (Database as tsqlconnection);
  332. if Db = nil then
  333. DatabaseError(SErrDatabasenAssigned);
  334. if not Db.Connected then
  335. db.Open;
  336. if FTransaction = nil then
  337. DatabaseError(SErrTransactionnSet);
  338. if not FTransaction.Active then
  339. FTransaction.StartTransaction;
  340. if assigned(fcursor) then FCursor.free;
  341. FCursor := Db.AllocateCursorHandle;
  342. for x := 0 to FSQL.Count - 1 do
  343. Buf := Buf + FSQL[x] + ' ';
  344. if Buf='' then
  345. begin
  346. DatabaseError(SErrNoStatement);
  347. exit;
  348. end;
  349. Db.PrepareStatement(Fcursor,FTransaction,buf);
  350. end;
  351. procedure TSQLQuery.PrepareSelect;
  352. begin
  353. (Database as tsqlconnection).PrepareSelect(FCursor);
  354. end;
  355. procedure TSQLQuery.FreeFldBuffers;
  356. begin
  357. (Database as tsqlconnection).FreeFldBuffers(FCursor);
  358. end;
  359. procedure TSQLQuery.Fetch;
  360. begin
  361. if not (FStatementType in [stSelect]) then
  362. Exit;
  363. FIsEof := (Database as tsqlconnection).Fetch(Fcursor);
  364. end;
  365. function TSQLQuery.LoadBuffer(Buffer : PChar): TGetResult;
  366. begin
  367. Fetch;
  368. if FIsEOF then
  369. begin
  370. Result := grEOF;
  371. Exit;
  372. end;
  373. (Database as tsqlconnection).LoadFieldsFromBuffer(FCursor,buffer);
  374. Result := grOK;
  375. end;
  376. procedure TSQLQuery.SetFieldSizes;
  377. begin
  378. FRecordSize := (Database as tsqlconnection).GetfieldSizes(Fcursor);
  379. end;
  380. procedure TSQLQuery.ExecuteImmediate;
  381. begin
  382. end;
  383. procedure TSQLQuery.ExecuteParams;
  384. begin
  385. //!! to be implemented
  386. end;
  387. procedure TSQLQuery.Execute;
  388. begin
  389. if FTransaction = nil then
  390. DatabaseError(SErrTransactionnSet);
  391. if not FTransaction.Active then
  392. FTransaction.StartTransaction;
  393. (Database as tsqlconnection).execute(Fcursor,FTransaction);
  394. end;
  395. function TSQLQuery.AllocRecord: PChar;
  396. begin
  397. // writeln('AllocRecord, Recordsize:' + inttostr(FRecordSize));
  398. Result := AllocMem(FRecordSize);
  399. end;
  400. procedure TSQLQuery.FreeRecord(var Buffer: PChar);
  401. begin
  402. if Assigned(@Buffer) then
  403. FreeMem(Buffer);
  404. end;
  405. function TSQLQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  406. begin
  407. result := (Database as tsqlconnection).GetFieldData(Fcursor,Field,buffer,activebuffer);
  408. end;
  409. function TSQLQuery.GetNextRecord(Buffer: PChar): TGetResult;
  410. begin
  411. if FIsEOF then
  412. Result := grEof
  413. else
  414. Result := LoadBuffer(Buffer);
  415. end;
  416. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  417. begin
  418. // not implemented - sql dataset
  419. end;
  420. procedure TSQLQuery.InternalClose;
  421. begin
  422. if FStatementType in [stSelect] then
  423. begin
  424. FreeFldBuffers;
  425. FreeSelect;
  426. end;
  427. FreeStatement;
  428. if DefaultFields then
  429. DestroyFields;
  430. FIsEOF := False;
  431. FRecordSize := 0;
  432. FOpen:=False;
  433. FCursor.free;
  434. inherited internalclose;
  435. end;
  436. procedure TSQLQuery.InternalDelete;
  437. begin
  438. // not implemented - sql dataset
  439. end;
  440. procedure TSQLQuery.InternalHandleException;
  441. begin
  442. end;
  443. procedure TSQLQuery.InternalInitFieldDefs;
  444. begin
  445. if FLoadingFieldDefs then
  446. Exit;
  447. FLoadingFieldDefs := True;
  448. try
  449. FieldDefs.Clear;
  450. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  451. finally
  452. FLoadingFieldDefs := False;
  453. end;
  454. end;
  455. procedure TSQLQuery.InternalInitRecord(Buffer: PChar);
  456. begin
  457. FillChar(Buffer^, FRecordSize, #0);
  458. end;
  459. procedure TSQLQuery.InternalOpen;
  460. begin
  461. try
  462. PrepareStatement;
  463. GetStatementType;
  464. if FStatementType in [stSelect] then
  465. begin
  466. PrepareSelect;
  467. Execute;
  468. FOpen:=True;
  469. InternalInitFieldDefs;
  470. if DefaultFields then
  471. CreateFields;
  472. SetFieldSizes;
  473. BindFields(True);
  474. end
  475. else Execute;
  476. except
  477. on E:Exception do
  478. raise;
  479. end;
  480. inherited InternalOpen;
  481. end;
  482. procedure TSQLQuery.InternalPost;
  483. begin
  484. // not implemented - sql dataset
  485. end;
  486. function TSQLQuery.IsCursorOpen: Boolean;
  487. begin
  488. Result := FOpen;
  489. end;
  490. procedure TSQLQuery.SetFieldData(Field: TField; Buffer: Pointer);
  491. begin
  492. end;
  493. // public part
  494. procedure TSQLQuery.ExecSQL;
  495. begin
  496. try
  497. PrepareStatement;
  498. GetStatementType;
  499. Execute;
  500. finally
  501. FreeStatement;
  502. end;
  503. end;
  504. constructor TSQLQuery.Create(AOwner : TComponent);
  505. begin
  506. inherited Create(AOwner);
  507. FSQL := TStringList.Create;
  508. end;
  509. destructor TSQLQuery.Destroy;
  510. begin
  511. if Active then Close;
  512. // if assigned(FCursor) then FCursor.destroy;
  513. FSQL.Free;
  514. inherited Destroy;
  515. end;
  516. function TSQLQuery.getrecordsize : Word;
  517. begin
  518. result := FRecordSize;
  519. end;
  520. end.
  521. {
  522. $Log$
  523. Revision 1.2 2004-09-26 16:56:32 michael
  524. + Further fixes from Joost van der sluis for Postgresql
  525. Revision 1.1 2004/08/31 09:49:47 michael
  526. + initial implementation of TSQLQuery
  527. }