sqldb.pp 15 KB

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