sqldb.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  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. TStatementType = (stNone, stSelect, stInsert, stUpdate, stDelete,
  21. stDDL, stGetSegment, stPutSegment, stExecProcedure,
  22. stStartTrans, stCommit, stRollback, stSelectForUpd);
  23. TSQLHandle = Class(TObject)
  24. protected
  25. StatementType : TStatementType;
  26. end;
  27. const
  28. StatementTokens : Array[TStatementType] of string = ('(none)', 'select',
  29. 'insert', 'update', 'delete',
  30. 'create', 'get', 'put', 'execute',
  31. 'start','commit','rollback', '?'
  32. );
  33. { TSQLConnection }
  34. type
  35. TSQLConnection = class (TDatabase)
  36. private
  37. FPassword : string;
  38. FTransaction : TSQLTransaction;
  39. FUserName : string;
  40. FHostName : string;
  41. FCharSet : string;
  42. FRole : String;
  43. procedure SetTransaction(Value : TSQLTransaction);
  44. protected
  45. procedure DoInternalConnect; override;
  46. procedure DoInternalDisconnect; override;
  47. function GetHandle : pointer; virtual; abstract;
  48. Function AllocateCursorHandle : TSQLHandle; virtual; abstract;
  49. Function AllocateTransactionHandle : TSQLHandle; virtual; abstract;
  50. procedure FreeStatement(cursor : TSQLHandle); virtual; abstract;
  51. procedure PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string); virtual; abstract;
  52. procedure FreeFldBuffers(cursor : TSQLHandle); virtual; abstract;
  53. procedure Execute(cursor: TSQLHandle;atransaction:tSQLtransaction); virtual; abstract;
  54. procedure AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs); virtual; abstract;
  55. function Fetch(cursor : TSQLHandle) : boolean; virtual; abstract;
  56. function LoadField(cursor : TSQLHandle;FieldDef : TfieldDef;buffer : pointer) : boolean; virtual; abstract;
  57. function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
  58. function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
  59. function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
  60. function StartdbTransaction(trans : TSQLHandle) : boolean; virtual; abstract;
  61. procedure CommitRetaining(trans : TSQLHandle); virtual; abstract;
  62. procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract;
  63. public
  64. destructor Destroy; override;
  65. property Handle: Pointer read GetHandle;
  66. published
  67. property Password : string read FPassword write FPassword;
  68. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  69. property UserName : string read FUserName write FUserName;
  70. property CharSet : string read FCharSet write FCharSet;
  71. property HostName : string Read FHostName Write FHostName;
  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. protected
  88. function GetHandle : Pointer; virtual;
  89. public
  90. procedure Commit; virtual;
  91. procedure CommitRetaining; virtual;
  92. procedure Rollback; virtual;
  93. procedure RollbackRetaining; virtual;
  94. procedure StartTransaction; override;
  95. constructor Create(AOwner : TComponent); override;
  96. destructor Destroy; override;
  97. property Handle: Pointer read GetHandle;
  98. procedure EndTransaction; override;
  99. published
  100. property Action : TCommitRollbackAction read FAction write FAction;
  101. property Database;
  102. end;
  103. { TSQLQuery }
  104. TSQLQuery = class (Tbufdataset)
  105. private
  106. FCursor : TSQLHandle;
  107. FSQL : TStrings;
  108. FIsEOF : boolean;
  109. FLoadingFieldDefs : boolean;
  110. procedure FreeStatement;
  111. procedure PrepareStatement;
  112. procedure FreeFldBuffers;
  113. procedure Execute;
  114. protected
  115. // abstract & virual methods of TBufDataset
  116. function Fetch : boolean; override;
  117. function LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean; override;
  118. // abstract & virual methods of TDataset
  119. procedure SetDatabase(Value : TDatabase); override;
  120. procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
  121. procedure InternalClose; override;
  122. procedure InternalDelete; override;
  123. procedure InternalHandleException; override;
  124. procedure InternalInitFieldDefs; override;
  125. procedure InternalOpen; override;
  126. procedure InternalPost; override;
  127. function GetCanModify: Boolean; override;
  128. Function GetSQLStatementType(SQL : String) : TStatementType; virtual;
  129. public
  130. procedure ExecSQL; virtual;
  131. constructor Create(AOwner : TComponent); override;
  132. destructor Destroy; override;
  133. published
  134. // redeclared data set properties
  135. property Active;
  136. // property Filter;
  137. // property Filtered;
  138. // property FilterOptions;
  139. property BeforeOpen;
  140. property AfterOpen;
  141. property BeforeClose;
  142. property AfterClose;
  143. property BeforeInsert;
  144. property AfterInsert;
  145. property BeforeEdit;
  146. property AfterEdit;
  147. property BeforePost;
  148. property AfterPost;
  149. property BeforeCancel;
  150. property AfterCancel;
  151. property BeforeDelete;
  152. property AfterDelete;
  153. property BeforeScroll;
  154. property AfterScroll;
  155. property OnCalcFields;
  156. property OnDeleteError;
  157. property OnEditError;
  158. property OnFilterRecord;
  159. property OnNewRecord;
  160. property OnPostError;
  161. property AutoCalcFields;
  162. property Database;
  163. property Transaction;
  164. property SQL : TStrings read FSQL write FSQL;
  165. end;
  166. implementation
  167. uses dbconst;
  168. { TSQLConnection }
  169. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  170. begin
  171. if FTransaction = nil then
  172. begin
  173. FTransaction := Value;
  174. if Assigned(FTransaction) then
  175. FTransaction.Database := Self;
  176. exit;
  177. end;
  178. if (Value <> FTransaction) and (Value <> nil) then
  179. if (not FTransaction.Active) then
  180. begin
  181. FTransaction := Value;
  182. FTransaction.Database := Self;
  183. end
  184. else
  185. DatabaseError(SErrAssTransaction);
  186. end;
  187. procedure TSQLConnection.DoInternalConnect;
  188. begin
  189. // Where is this for?!?!
  190. // if Connected then
  191. // Close;
  192. end;
  193. procedure TSQLConnection.DoInternalDisconnect;
  194. begin
  195. end;
  196. destructor TSQLConnection.Destroy;
  197. begin
  198. inherited Destroy;
  199. end;
  200. { TSQLTransaction }
  201. procedure TSQLTransaction.EndTransaction;
  202. begin
  203. rollback;
  204. end;
  205. function TSQLTransaction.GetHandle: pointer;
  206. begin
  207. Result := (Database as tsqlconnection).GetTransactionHandle(FTrans);
  208. end;
  209. procedure TSQLTransaction.Commit;
  210. begin
  211. if active then
  212. begin
  213. closedatasets;
  214. if (Database as tsqlconnection).commit(FTrans) then
  215. begin
  216. closeTrans;
  217. FreeAndNil(FTrans);
  218. end;
  219. end;
  220. end;
  221. procedure TSQLTransaction.CommitRetaining;
  222. begin
  223. if active then
  224. (Database as tsqlconnection).commitRetaining(FTrans);
  225. end;
  226. procedure TSQLTransaction.Rollback;
  227. begin
  228. if active then
  229. begin
  230. closedatasets;
  231. if (Database as tsqlconnection).RollBack(FTrans) then
  232. begin
  233. CloseTrans;
  234. FreeAndNil(FTrans);
  235. end;
  236. end;
  237. end;
  238. procedure TSQLTransaction.RollbackRetaining;
  239. begin
  240. if active then
  241. (Database as tsqlconnection).RollBackRetaining(FTrans);
  242. end;
  243. procedure TSQLTransaction.StartTransaction;
  244. var db : TSQLConnection;
  245. begin
  246. if Active then
  247. DatabaseError(SErrTransAlreadyActive);
  248. db := (Database as tsqlconnection);
  249. if Db = nil then
  250. DatabaseError(SErrDatabasenAssigned);
  251. if not Db.Connected then
  252. Db.Open;
  253. if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle;
  254. if Db.StartdbTransaction(FTrans) then OpenTrans;
  255. end;
  256. constructor TSQLTransaction.Create(AOwner : TComponent);
  257. begin
  258. inherited Create(AOwner);
  259. end;
  260. destructor TSQLTransaction.Destroy;
  261. begin
  262. Rollback;
  263. inherited Destroy;
  264. end;
  265. { TSQLQuery }
  266. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  267. var db : tsqlconnection;
  268. begin
  269. if (Database <> Value) then
  270. begin
  271. db := value as tsqlconnection;
  272. inherited setdatabase(value);
  273. if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
  274. transaction := Db.Transaction;
  275. end;
  276. end;
  277. procedure TSQLQuery.FreeStatement;
  278. begin
  279. if assigned(FCursor) then
  280. begin
  281. (Database as tsqlconnection).FreeStatement(FCursor);
  282. FreeAndNil(FCursor);
  283. end;
  284. end;
  285. procedure TSQLQuery.PrepareStatement;
  286. var
  287. Buf : string;
  288. x : integer;
  289. db : tsqlconnection;
  290. sqltr : tsqltransaction;
  291. begin
  292. db := (Database as tsqlconnection);
  293. if Db = nil then
  294. DatabaseError(SErrDatabasenAssigned);
  295. if not Db.Connected then
  296. db.Open;
  297. if Transaction = nil then
  298. DatabaseError(SErrTransactionnSet);
  299. sqltr := (transaction as tsqltransaction);
  300. if not sqltr.Active then sqltr.StartTransaction;
  301. if assigned(fcursor) then FreeAndNil(fcursor);
  302. FCursor := Db.AllocateCursorHandle;
  303. Buf := '';
  304. for x := 0 to FSQL.Count - 1 do
  305. Buf := Buf + FSQL[x] + ' ';
  306. if Buf='' then
  307. begin
  308. DatabaseError(SErrNoStatement);
  309. exit;
  310. end;
  311. FCursor.StatementType := GetSQLStatementType(buf);
  312. Db.PrepareStatement(Fcursor,sqltr,buf);
  313. end;
  314. procedure TSQLQuery.FreeFldBuffers;
  315. begin
  316. if assigned(FCursor) then (Database as tsqlconnection).FreeFldBuffers(FCursor);
  317. end;
  318. function TSQLQuery.Fetch : boolean;
  319. begin
  320. if not (Fcursor.StatementType in [stSelect]) then
  321. Exit;
  322. if not FIsEof then FIsEOF := not (Database as tsqlconnection).Fetch(Fcursor);
  323. Result := not FIsEOF;
  324. end;
  325. procedure TSQLQuery.Execute;
  326. begin
  327. (Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction);
  328. end;
  329. function TSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean;
  330. begin
  331. result := (Database as tSQLConnection).LoadField(FCursor,FieldDef,buffer)
  332. end;
  333. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  334. begin
  335. // not implemented - sql dataset
  336. end;
  337. procedure TSQLQuery.InternalClose;
  338. begin
  339. FreeFldBuffers;
  340. FreeStatement;
  341. if DefaultFields then
  342. DestroyFields;
  343. FIsEOF := False;
  344. // FRecordSize := 0;
  345. inherited internalclose;
  346. end;
  347. procedure TSQLQuery.InternalDelete;
  348. begin
  349. // not implemented - sql dataset
  350. end;
  351. procedure TSQLQuery.InternalHandleException;
  352. begin
  353. end;
  354. procedure TSQLQuery.InternalInitFieldDefs;
  355. begin
  356. if FLoadingFieldDefs then
  357. Exit;
  358. FLoadingFieldDefs := True;
  359. try
  360. FieldDefs.Clear;
  361. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  362. finally
  363. FLoadingFieldDefs := False;
  364. end;
  365. end;
  366. procedure TSQLQuery.InternalOpen;
  367. begin
  368. try
  369. PrepareStatement;
  370. if Fcursor.StatementType in [stSelect] then
  371. begin
  372. Execute;
  373. InternalInitFieldDefs;
  374. if DefaultFields then
  375. CreateFields;
  376. end
  377. else
  378. DatabaseError(SErrNoSelectStatement,Self);
  379. except
  380. on E:Exception do
  381. raise;
  382. end;
  383. inherited InternalOpen;
  384. end;
  385. procedure TSQLQuery.InternalPost;
  386. begin
  387. // not implemented - sql dataset
  388. end;
  389. // public part
  390. procedure TSQLQuery.ExecSQL;
  391. begin
  392. try
  393. PrepareStatement;
  394. Execute;
  395. finally
  396. FreeStatement;
  397. end;
  398. end;
  399. constructor TSQLQuery.Create(AOwner : TComponent);
  400. begin
  401. inherited Create(AOwner);
  402. FSQL := TStringList.Create;
  403. end;
  404. destructor TSQLQuery.Destroy;
  405. begin
  406. if Active then Close;
  407. // if assigned(FCursor) then FCursor.destroy;
  408. FreeAndNil(FSQL);
  409. inherited Destroy;
  410. end;
  411. Function TSQLQuery.GetSQLStatementType(SQL : String) : TStatementType;
  412. Var
  413. L : Integer;
  414. cmt : boolean;
  415. P,PE,PP : PChar;
  416. S : string;
  417. T : TStatementType;
  418. begin
  419. Result:=stNone;
  420. L:=Length(SQL);
  421. If (L=0) then
  422. Exit;
  423. P:=Pchar(SQL);
  424. PP:=P;
  425. Cmt:=False;
  426. While ((P-PP)<L) do
  427. begin
  428. if not (P^ in [' ',#13,#10,#9]) then
  429. begin
  430. if not Cmt then
  431. begin
  432. // Check for comment.
  433. Cmt:=(P^='/') and (((P-PP)<=L) and (P[1]='*'));
  434. if not (cmt) then
  435. Break;
  436. end
  437. else
  438. begin
  439. // Check for end of comment.
  440. Cmt:=Not( (P^='*') and (((P-PP)<=L) and (P[1]='/')) );
  441. If not cmt then
  442. Inc(p);
  443. end;
  444. end;
  445. inc(P);
  446. end;
  447. PE:=P+1;
  448. While ((PE-PP)<L) and (PE^ in ['0'..'9','a'..'z','A'..'Z','_']) do
  449. Inc(PE);
  450. Setlength(S,PE-P);
  451. Move(P^,S[1],(PE-P));
  452. S:=Lowercase(s);
  453. For t:=stselect to strollback do
  454. if (S=StatementTokens[t]) then
  455. Exit(t);
  456. end;
  457. Function TSQLQuery.GetCanModify: Boolean;
  458. begin
  459. Result:= False;
  460. end;
  461. end.
  462. {
  463. $Log$
  464. Revision 1.9 2004-12-13 19:22:16 michael
  465. * Ptahc from Joost van der Sluis
  466. - moved IsCursorOpen from TSQLQuery to tbufdataset
  467. - moved SetFieldData from TSQLQuery to TBufDataset
  468. - very first start for support of cached updates
  469. Revision 1.8 2004/12/04 22:43:38 michael
  470. * Patch from Joost van der Sluis
  471. - replaced checkactive in commit and rollback for 'if active'
  472. - fixed a warning
  473. - adapted for the changes in TBuffDataset
  474. Revision 1.7 2004/11/05 08:32:02 michael
  475. TBufDataset.inc:
  476. - replaced Freemem by Reallocmem, Free by FreeAndNil
  477. Database.inc:
  478. - Moved Active property from TSQLTransaction to TDBTransaction
  479. - Gives an error if the database of an active transaction is changed
  480. Dataset.inc
  481. - Don't distribute events if FDisableControlsCount > 0
  482. - Replaced FActive by FState<>dsInactive
  483. - Set EOF after append
  484. db.pp:
  485. - Removed duplicate definition of TAlignment
  486. - Moved Active property from TSQLTransaction to TDBTransaction
  487. - Replaced FActive by FState<>dsInactive
  488. - Gives an error if the database of an active transaction is changed
  489. sqldb:
  490. - Moved Active property from TSQLTransaction to TDBTransaction
  491. - replaced Freemem by Reallocmem, Free by FreeAndNil
  492. IBConnection:
  493. - Moved FSQLDAAllocated to the cursor
  494. PQConnection:
  495. - Don't try to free the statement if a fatal error occured
  496. Revision 1.6 2004/10/27 07:23:13 michael
  497. + Patch from Joost Van der Sluis to fix transactions
  498. Revision 1.5 2004/10/10 14:45:52 michael
  499. + Use of dbconst for resource strings
  500. Revision 1.4 2004/10/10 14:24:22 michael
  501. * Large patch from Joost Van der Sluis.
  502. * Float fix in interbase
  503. + Commit and commitretaining for pqconnection
  504. + Preparestatement and prepareselect joined.
  505. + Freestatement and FreeSelect joined
  506. + TSQLQuery.GetSQLStatementType implemented
  507. + TBufDataset.AllocBuffer now no longer does a realloc
  508. + Fetch=True means succesfully got data. False means end of data.
  509. + Default implementation of GetFieldData implemented/
  510. Revision 1.3 2004/10/02 14:52:25 michael
  511. + Added mysql connection
  512. Revision 1.2 2004/09/26 16:56:32 michael
  513. + Further fixes from Joost van der sluis for Postgresql
  514. Revision 1.1 2004/08/31 09:49:47 michael
  515. + initial implementation of TSQLQuery
  516. }