sqldb.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  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. SErrNoSelectStatement = 'Cannot open a non-select statement';
  197. { TSQLConnection }
  198. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  199. begin
  200. if FTransaction = nil then
  201. begin
  202. FTransaction := Value;
  203. if Assigned(FTransaction) then
  204. FTransaction.Database := Self;
  205. exit;
  206. end;
  207. if (Value <> FTransaction) and (Value <> nil) then
  208. if (not FTransaction.Active) then
  209. begin
  210. FTransaction := Value;
  211. FTransaction.Database := Self;
  212. end
  213. else
  214. DatabaseError(SErrAssTransaction);
  215. end;
  216. procedure TSQLConnection.DoInternalConnect;
  217. begin
  218. if Connected then
  219. Close;
  220. end;
  221. procedure TSQLQuery.GetStatementType;
  222. begin
  223. FStatementType := (Database as tsqlconnection).GetStatementType(Fcursor);
  224. end;
  225. procedure TSQLConnection.DoInternalDisconnect;
  226. begin
  227. end;
  228. destructor TSQLConnection.Destroy;
  229. begin
  230. if FTransaction <> nil then
  231. begin
  232. FTransaction.Active := False;
  233. FTransaction.Database := nil;
  234. end;
  235. inherited Destroy;
  236. end;
  237. { TSQLTransaction }
  238. procedure TSQLTransaction.SetActive(Value : boolean);
  239. begin
  240. if FActive and (not Value) then
  241. Rollback
  242. else if (not FActive) and Value then
  243. StartTransaction;
  244. end;
  245. function TSQLTransaction.GetHandle: pointer;
  246. begin
  247. Result := (Database as tsqlconnection).GetTransactionHandle(FTrans);
  248. end;
  249. procedure TSQLTransaction.Commit;
  250. begin
  251. if not FActive then Exit;
  252. if (Database as tsqlconnection).commit(FTrans) then FActive := false;
  253. FTrans.free;
  254. end;
  255. procedure TSQLTransaction.CommitRetaining;
  256. begin
  257. if not FActive then Exit;
  258. (Database as tsqlconnection).commitRetaining(FTrans);
  259. end;
  260. procedure TSQLTransaction.Rollback;
  261. begin
  262. if not FActive then Exit;
  263. if (Database as tsqlconnection).RollBack(FTrans) then FActive := false;
  264. FTrans.free;
  265. end;
  266. procedure TSQLTransaction.EndTransaction;
  267. begin
  268. Rollback;
  269. end;
  270. procedure TSQLTransaction.RollbackRetaining;
  271. begin
  272. if not FActive then Exit;
  273. (Database as tsqlconnection).RollBackRetaining(FTrans);
  274. end;
  275. procedure TSQLTransaction.StartTransaction;
  276. var db : TSQLConnection;
  277. begin
  278. if Active then Active := False;
  279. db := (Database as tsqlconnection);
  280. if Db = nil then
  281. DatabaseError(SErrDatabasenAssigned);
  282. if not Db.Connected then
  283. Db.Open;
  284. if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle;
  285. if Db.StartTransaction(FTrans) then FActive := true;
  286. end;
  287. constructor TSQLTransaction.Create(AOwner : TComponent);
  288. begin
  289. inherited Create(AOwner);
  290. end;
  291. destructor TSQLTransaction.Destroy;
  292. begin
  293. // This will also do a Rollback, if the transaction is currently active
  294. Active := False;
  295. // Database.Transaction := nil;
  296. inherited Destroy;
  297. end;
  298. { TSQLQuery }
  299. procedure TSQLQuery.SetTransaction(Value : TSQLTransaction);
  300. begin
  301. CheckInactive;
  302. if (FTransaction <> Value) then
  303. FTransaction := Value;
  304. end;
  305. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  306. var db : tsqlconnection;
  307. begin
  308. if (Database <> Value) then
  309. begin
  310. db := value as tsqlconnection;
  311. inherited setdatabase(value);
  312. if (FTransaction = nil) and (Assigned(Db.Transaction)) then
  313. SetTransaction(Db.Transaction);
  314. { if assigned(fcursor) then freemem(FCursor);
  315. FCursor := Db.AllocateCursorHandle;}
  316. end;
  317. end;
  318. procedure TSQLQuery.FreeStatement;
  319. begin
  320. (Database as tsqlconnection).FreeStatement(FCursor);
  321. end;
  322. procedure TSQLQuery.FreeSelect;
  323. begin
  324. (Database as tsqlconnection).FreeSelect(FCursor);
  325. end;
  326. procedure TSQLQuery.PrepareStatement;
  327. var
  328. Buf : string;
  329. x : integer;
  330. db : tsqlconnection;
  331. begin
  332. db := (Database as tsqlconnection);
  333. if Db = nil then
  334. DatabaseError(SErrDatabasenAssigned);
  335. if not Db.Connected then
  336. db.Open;
  337. if FTransaction = nil then
  338. DatabaseError(SErrTransactionnSet);
  339. if not FTransaction.Active then
  340. FTransaction.StartTransaction;
  341. if assigned(fcursor) then FCursor.free;
  342. FCursor := Db.AllocateCursorHandle;
  343. for x := 0 to FSQL.Count - 1 do
  344. Buf := Buf + FSQL[x] + ' ';
  345. if Buf='' then
  346. begin
  347. DatabaseError(SErrNoStatement);
  348. exit;
  349. end;
  350. Db.PrepareStatement(Fcursor,FTransaction,buf);
  351. end;
  352. procedure TSQLQuery.PrepareSelect;
  353. begin
  354. (Database as tsqlconnection).PrepareSelect(FCursor);
  355. end;
  356. procedure TSQLQuery.FreeFldBuffers;
  357. begin
  358. (Database as tsqlconnection).FreeFldBuffers(FCursor);
  359. end;
  360. procedure TSQLQuery.Fetch;
  361. begin
  362. if not (FStatementType in [stSelect]) then
  363. Exit;
  364. FIsEof := (Database as tsqlconnection).Fetch(Fcursor);
  365. end;
  366. function TSQLQuery.LoadBuffer(Buffer : PChar): TGetResult;
  367. begin
  368. Fetch;
  369. if FIsEOF then
  370. begin
  371. Result := grEOF;
  372. Exit;
  373. end;
  374. (Database as tsqlconnection).LoadFieldsFromBuffer(FCursor,buffer);
  375. Result := grOK;
  376. end;
  377. procedure TSQLQuery.SetFieldSizes;
  378. begin
  379. FRecordSize := (Database as tsqlconnection).GetfieldSizes(Fcursor);
  380. end;
  381. procedure TSQLQuery.ExecuteImmediate;
  382. begin
  383. end;
  384. procedure TSQLQuery.ExecuteParams;
  385. begin
  386. //!! to be implemented
  387. end;
  388. procedure TSQLQuery.Execute;
  389. begin
  390. if FTransaction = nil then
  391. DatabaseError(SErrTransactionnSet);
  392. if not FTransaction.Active then
  393. FTransaction.StartTransaction;
  394. (Database as tsqlconnection).execute(Fcursor,FTransaction);
  395. end;
  396. function TSQLQuery.AllocRecord: PChar;
  397. begin
  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
  476. DatabaseError(SErrNoSelectStatement,Self);
  477. except
  478. on E:Exception do
  479. raise;
  480. end;
  481. inherited InternalOpen;
  482. end;
  483. procedure TSQLQuery.InternalPost;
  484. begin
  485. // not implemented - sql dataset
  486. end;
  487. function TSQLQuery.IsCursorOpen: Boolean;
  488. begin
  489. Result := FOpen;
  490. end;
  491. procedure TSQLQuery.SetFieldData(Field: TField; Buffer: Pointer);
  492. begin
  493. end;
  494. // public part
  495. procedure TSQLQuery.ExecSQL;
  496. begin
  497. try
  498. PrepareStatement;
  499. GetStatementType;
  500. Execute;
  501. finally
  502. FreeStatement;
  503. end;
  504. end;
  505. constructor TSQLQuery.Create(AOwner : TComponent);
  506. begin
  507. inherited Create(AOwner);
  508. FSQL := TStringList.Create;
  509. end;
  510. destructor TSQLQuery.Destroy;
  511. begin
  512. if Active then Close;
  513. // if assigned(FCursor) then FCursor.destroy;
  514. FSQL.Free;
  515. inherited Destroy;
  516. end;
  517. function TSQLQuery.getrecordsize : Word;
  518. begin
  519. result := FRecordSize;
  520. end;
  521. end.
  522. {
  523. $Log$
  524. Revision 1.3 2004-10-02 14:52:25 michael
  525. + Added mysql connection
  526. Revision 1.2 2004/09/26 16:56:32 michael
  527. + Further fixes from Joost van der sluis for Postgresql
  528. Revision 1.1 2004/08/31 09:49:47 michael
  529. + initial implementation of TSQLQuery
  530. }