sqldb.pp 15 KB

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