sqldb.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  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 GetFieldSizes(cursor : TSQLHandle) : integer; virtual; abstract;
  56. function Fetch(cursor : TSQLHandle) : boolean; virtual; abstract;
  57. procedure LoadFieldsFromBuffer(cursor : TSQLHandle;buffer : pchar); virtual; abstract;
  58. function GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean; virtual;
  59. function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
  60. function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
  61. function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
  62. function StartdbTransaction(trans : TSQLHandle) : boolean; virtual; abstract;
  63. procedure CommitRetaining(trans : TSQLHandle); virtual; abstract;
  64. procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract;
  65. public
  66. destructor Destroy; override;
  67. property Handle: Pointer read GetHandle;
  68. published
  69. property Password : string read FPassword write FPassword;
  70. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  71. property UserName : string read FUserName write FUserName;
  72. property CharSet : string read FCharSet write FCharSet;
  73. property HostName : string Read FHostName Write FHostName;
  74. property Connected;
  75. Property Role : String read FRole write FRole;
  76. property DatabaseName;
  77. property KeepConnection;
  78. property LoginPrompt;
  79. property Params;
  80. property OnLogin;
  81. end;
  82. { TSQLTransaction }
  83. TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback,
  84. caRollbackRetaining);
  85. TSQLTransaction = class (TDBTransaction)
  86. private
  87. FTrans : TSQLHandle;
  88. FAction : TCommitRollbackAction;
  89. FActive : boolean;
  90. FOpenAfterRead : boolean;
  91. procedure SetActive(Value : boolean);
  92. protected
  93. function GetHandle : Pointer; virtual;
  94. procedure Loaded; override;
  95. public
  96. procedure Commit; virtual;
  97. procedure CommitRetaining; virtual;
  98. procedure Rollback; virtual;
  99. procedure RollbackRetaining; virtual;
  100. procedure StartTransaction; virtual;
  101. constructor Create(AOwner : TComponent); override;
  102. destructor Destroy; override;
  103. property Handle: Pointer read GetHandle;
  104. procedure EndTransaction; override;
  105. published
  106. property Action : TCommitRollbackAction read FAction write FAction;
  107. property Active : boolean read FActive write setactive;
  108. property Database;
  109. end;
  110. { TSQLQuery }
  111. TSQLQuery = class (Tbufdataset)
  112. private
  113. FCursor : TSQLHandle;
  114. FOpen : Boolean;
  115. FSQL : TStrings;
  116. FIsEOF : boolean;
  117. FLoadingFieldDefs : boolean;
  118. FRecordSize : Integer;
  119. procedure FreeStatement;
  120. procedure PrepareStatement;
  121. procedure FreeFldBuffers;
  122. procedure Fetch;
  123. function LoadBuffer(Buffer : PChar): TGetResult;
  124. procedure SetFieldSizes;
  125. procedure Execute;
  126. protected
  127. // abstract & virual methods of TDataset
  128. procedure SetDatabase(Value : TDatabase); override;
  129. function AllocRecord(ExtraSize : integer): PChar; override;
  130. procedure FreeRecord(var Buffer: PChar); override;
  131. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  132. function GetNextRecord(Buffer : pchar) : TGetResult; override;
  133. function GetRecordSize: Word; override;
  134. procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
  135. procedure InternalClose; override;
  136. procedure InternalDelete; override;
  137. procedure InternalHandleException; override;
  138. procedure InternalInitFieldDefs; override;
  139. procedure InternalInitRecord(Buffer: PChar); override;
  140. procedure InternalOpen; override;
  141. procedure InternalPost; override;
  142. function IsCursorOpen: Boolean; override;
  143. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  144. Function GetSQLStatementType(SQL : String) : TStatementType; virtual;
  145. public
  146. procedure ExecSQL; virtual;
  147. constructor Create(AOwner : TComponent); override;
  148. destructor Destroy; override;
  149. published
  150. // redeclared data set properties
  151. property Active;
  152. // property FieldDefs stored FieldDefsStored;
  153. property Filter;
  154. property Filtered;
  155. property FilterOptions;
  156. property BeforeOpen;
  157. property AfterOpen;
  158. property BeforeClose;
  159. property AfterClose;
  160. property BeforeInsert;
  161. property AfterInsert;
  162. property BeforeEdit;
  163. property AfterEdit;
  164. property BeforePost;
  165. property AfterPost;
  166. property BeforeCancel;
  167. property AfterCancel;
  168. property BeforeDelete;
  169. property AfterDelete;
  170. property BeforeScroll;
  171. property AfterScroll;
  172. property OnCalcFields;
  173. property OnDeleteError;
  174. property OnEditError;
  175. property OnFilterRecord;
  176. property OnNewRecord;
  177. property OnPostError;
  178. property AutoCalcFields;
  179. property Database;
  180. property Transaction;
  181. property SQL : TStrings read FSQL write FSQL;
  182. end;
  183. implementation
  184. uses dbconst;
  185. { TSQLConnection }
  186. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  187. begin
  188. if FTransaction = nil then
  189. begin
  190. FTransaction := Value;
  191. if Assigned(FTransaction) then
  192. FTransaction.Database := Self;
  193. exit;
  194. end;
  195. if (Value <> FTransaction) and (Value <> nil) then
  196. if (not FTransaction.Active) then
  197. begin
  198. FTransaction := Value;
  199. FTransaction.Database := Self;
  200. end
  201. else
  202. DatabaseError(SErrAssTransaction);
  203. end;
  204. procedure TSQLConnection.DoInternalConnect;
  205. begin
  206. // Where is this for?!?!
  207. // if Connected then
  208. // Close;
  209. end;
  210. procedure TSQLConnection.DoInternalDisconnect;
  211. begin
  212. end;
  213. destructor TSQLConnection.Destroy;
  214. begin
  215. inherited Destroy;
  216. end;
  217. function TSQLConnection.GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean;
  218. var
  219. x : longint;
  220. begin
  221. Result := False;
  222. for x := 0 to FieldDefs.count-1 do
  223. begin
  224. if (Field.FieldName = FieldDefs[x].Name) then
  225. begin
  226. Move(CurrBuff^, Buffer^, Field.Size);
  227. Result := True;
  228. Break;
  229. end
  230. else Inc(CurrBuff, FieldDefs[x].Size);
  231. end;
  232. end;
  233. { TSQLTransaction }
  234. procedure TSQLTransaction.SetActive(Value : boolean);
  235. begin
  236. if FActive and (not Value) then
  237. EndTransaction
  238. else if (not FActive) and Value then
  239. if csLoading in ComponentState then
  240. begin
  241. FOpenAfterRead := true;
  242. exit;
  243. end
  244. else
  245. StartTransaction;
  246. end;
  247. procedure TSQLTransaction.Loaded;
  248. begin
  249. inherited;
  250. if FOpenAfterRead then SetActive(true);
  251. end;
  252. procedure TSQLTransaction.EndTransaction;
  253. begin
  254. rollback;
  255. end;
  256. function TSQLTransaction.GetHandle: pointer;
  257. begin
  258. Result := (Database as tsqlconnection).GetTransactionHandle(FTrans);
  259. end;
  260. procedure TSQLTransaction.Commit;
  261. begin
  262. if not FActive then Exit;
  263. closedatasets;
  264. if (Database as tsqlconnection).commit(FTrans) then
  265. begin
  266. FActive := false;
  267. FTrans.free;
  268. end;
  269. end;
  270. procedure TSQLTransaction.CommitRetaining;
  271. begin
  272. if not FActive then Exit;
  273. (Database as tsqlconnection).commitRetaining(FTrans);
  274. end;
  275. procedure TSQLTransaction.Rollback;
  276. begin
  277. if not FActive then Exit;
  278. closedatasets;
  279. if (Database as tsqlconnection).RollBack(FTrans) then
  280. begin
  281. FActive := false;
  282. FTrans.free;
  283. end;
  284. end;
  285. procedure TSQLTransaction.RollbackRetaining;
  286. begin
  287. if not FActive then Exit;
  288. (Database as tsqlconnection).RollBackRetaining(FTrans);
  289. end;
  290. procedure TSQLTransaction.StartTransaction;
  291. var db : TSQLConnection;
  292. begin
  293. if Active then
  294. DatabaseError(SErrTransAlreadyActive);
  295. db := (Database as tsqlconnection);
  296. if Db = nil then
  297. DatabaseError(SErrDatabasenAssigned);
  298. if not Db.Connected then
  299. Db.Open;
  300. if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle;
  301. if Db.StartdbTransaction(FTrans) then FActive := true;
  302. end;
  303. constructor TSQLTransaction.Create(AOwner : TComponent);
  304. begin
  305. inherited Create(AOwner);
  306. end;
  307. destructor TSQLTransaction.Destroy;
  308. begin
  309. Rollback;
  310. inherited Destroy;
  311. end;
  312. { TSQLQuery }
  313. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  314. var db : tsqlconnection;
  315. begin
  316. if (Database <> Value) then
  317. begin
  318. db := value as tsqlconnection;
  319. inherited setdatabase(value);
  320. if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
  321. transaction := Db.Transaction;
  322. end;
  323. end;
  324. procedure TSQLQuery.FreeStatement;
  325. begin
  326. if assigned(FCursor) then
  327. begin
  328. (Database as tsqlconnection).FreeStatement(FCursor);
  329. FCursor.free;
  330. end;
  331. end;
  332. procedure TSQLQuery.PrepareStatement;
  333. var
  334. Buf : string;
  335. x : integer;
  336. db : tsqlconnection;
  337. sqltr : tsqltransaction;
  338. begin
  339. db := (Database as tsqlconnection);
  340. if Db = nil then
  341. DatabaseError(SErrDatabasenAssigned);
  342. if not Db.Connected then
  343. db.Open;
  344. if Transaction = nil then
  345. DatabaseError(SErrTransactionnSet);
  346. sqltr := (transaction as tsqltransaction);
  347. if not sqltr.Active then sqltr.StartTransaction;
  348. if assigned(fcursor) then FCursor.free;
  349. FCursor := Db.AllocateCursorHandle;
  350. for x := 0 to FSQL.Count - 1 do
  351. Buf := Buf + FSQL[x] + ' ';
  352. if Buf='' then
  353. begin
  354. DatabaseError(SErrNoStatement);
  355. exit;
  356. end;
  357. FCursor.StatementType := GetSQLStatementType(buf);
  358. Db.PrepareStatement(Fcursor,sqltr,buf);
  359. end;
  360. procedure TSQLQuery.FreeFldBuffers;
  361. begin
  362. if assigned(FCursor) then (Database as tsqlconnection).FreeFldBuffers(FCursor);
  363. end;
  364. procedure TSQLQuery.Fetch;
  365. begin
  366. if not (Fcursor.StatementType in [stSelect]) then
  367. Exit;
  368. FIsEof := not (Database as tsqlconnection).Fetch(Fcursor);
  369. end;
  370. function TSQLQuery.LoadBuffer(Buffer : PChar): TGetResult;
  371. begin
  372. Fetch;
  373. if FIsEOF then
  374. begin
  375. Result := grEOF;
  376. Exit;
  377. end;
  378. (Database as tsqlconnection).LoadFieldsFromBuffer(FCursor,buffer);
  379. Result := grOK;
  380. end;
  381. procedure TSQLQuery.SetFieldSizes;
  382. begin
  383. FRecordSize := (Database as tsqlconnection).GetfieldSizes(Fcursor);
  384. end;
  385. procedure TSQLQuery.Execute;
  386. begin
  387. (Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction);
  388. end;
  389. function TSQLQuery.AllocRecord(ExtraSize : integer): PChar;
  390. begin
  391. Result := AllocMem(FRecordSize+ExtraSize);
  392. end;
  393. procedure TSQLQuery.FreeRecord(var Buffer: PChar);
  394. begin
  395. if Assigned(@Buffer) then
  396. FreeMem(Buffer);
  397. end;
  398. function TSQLQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  399. begin
  400. result := (Database as tsqlconnection).GetFieldData(Fcursor,Field,FieldDefs,buffer,activebuffer);
  401. end;
  402. function TSQLQuery.GetNextRecord(Buffer: PChar): TGetResult;
  403. begin
  404. if FIsEOF then
  405. Result := grEof
  406. else
  407. Result := LoadBuffer(Buffer);
  408. end;
  409. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  410. begin
  411. // not implemented - sql dataset
  412. end;
  413. procedure TSQLQuery.InternalClose;
  414. begin
  415. FreeFldBuffers;
  416. FreeStatement;
  417. if DefaultFields then
  418. DestroyFields;
  419. FIsEOF := False;
  420. FRecordSize := 0;
  421. FOpen:=False;
  422. inherited internalclose;
  423. end;
  424. procedure TSQLQuery.InternalDelete;
  425. begin
  426. // not implemented - sql dataset
  427. end;
  428. procedure TSQLQuery.InternalHandleException;
  429. begin
  430. end;
  431. procedure TSQLQuery.InternalInitFieldDefs;
  432. begin
  433. if FLoadingFieldDefs then
  434. Exit;
  435. FLoadingFieldDefs := True;
  436. try
  437. FieldDefs.Clear;
  438. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  439. finally
  440. FLoadingFieldDefs := False;
  441. end;
  442. end;
  443. procedure TSQLQuery.InternalInitRecord(Buffer: PChar);
  444. begin
  445. FillChar(Buffer^, FRecordSize, #0);
  446. end;
  447. procedure TSQLQuery.InternalOpen;
  448. begin
  449. try
  450. PrepareStatement;
  451. if Fcursor.StatementType in [stSelect] then
  452. begin
  453. Execute;
  454. FOpen:=True;
  455. InternalInitFieldDefs;
  456. if DefaultFields then
  457. CreateFields;
  458. SetFieldSizes;
  459. BindFields(True);
  460. end
  461. else
  462. DatabaseError(SErrNoSelectStatement,Self);
  463. except
  464. on E:Exception do
  465. raise;
  466. end;
  467. inherited InternalOpen;
  468. end;
  469. procedure TSQLQuery.InternalPost;
  470. begin
  471. // not implemented - sql dataset
  472. end;
  473. function TSQLQuery.IsCursorOpen: Boolean;
  474. begin
  475. Result := FOpen;
  476. end;
  477. procedure TSQLQuery.SetFieldData(Field: TField; Buffer: Pointer);
  478. begin
  479. end;
  480. // public part
  481. procedure TSQLQuery.ExecSQL;
  482. begin
  483. try
  484. PrepareStatement;
  485. Execute;
  486. finally
  487. FreeStatement;
  488. end;
  489. end;
  490. constructor TSQLQuery.Create(AOwner : TComponent);
  491. begin
  492. inherited Create(AOwner);
  493. FSQL := TStringList.Create;
  494. end;
  495. destructor TSQLQuery.Destroy;
  496. begin
  497. if Active then Close;
  498. // if assigned(FCursor) then FCursor.destroy;
  499. FSQL.Free;
  500. inherited Destroy;
  501. end;
  502. Function TSQLQuery.GetSQLStatementType(SQL : String) : TStatementType;
  503. Var
  504. L : Integer;
  505. cmt : boolean;
  506. P,PE,PP : PChar;
  507. S : string;
  508. T : TStatementType;
  509. begin
  510. Result:=stNone;
  511. L:=Length(SQL);
  512. If (L=0) then
  513. Exit;
  514. P:=Pchar(SQL);
  515. PP:=P;
  516. Cmt:=False;
  517. While ((P-PP)<L) do
  518. begin
  519. if not (P^ in [' ',#13,#10,#9]) then
  520. begin
  521. if not Cmt then
  522. begin
  523. // Check for comment.
  524. Cmt:=(P^='/') and (((P-PP)<=L) and (P[1]='*'));
  525. if not (cmt) then
  526. Break;
  527. end
  528. else
  529. begin
  530. // Check for end of comment.
  531. Cmt:=Not( (P^='*') and (((P-PP)<=L) and (P[1]='/')) );
  532. If not cmt then
  533. Inc(p);
  534. end;
  535. end;
  536. inc(P);
  537. end;
  538. PE:=P+1;
  539. While ((PE-PP)<L) and (PE^ in ['0'..'9','a'..'z','A'..'Z','_']) do
  540. Inc(PE);
  541. Setlength(S,PE-P);
  542. Move(P^,S[1],(PE-P));
  543. S:=Lowercase(s);
  544. For t:=stselect to strollback do
  545. if (S=StatementTokens[t]) then
  546. Exit(t);
  547. end;
  548. function TSQLQuery.getrecordsize : Word;
  549. begin
  550. result := FRecordSize;
  551. end;
  552. end.
  553. {
  554. $Log$
  555. Revision 1.6 2004-10-27 07:23:13 michael
  556. + Patch from Joost Van der Sluis to fix transactions
  557. Revision 1.5 2004/10/10 14:45:52 michael
  558. + Use of dbconst for resource strings
  559. Revision 1.4 2004/10/10 14:24:22 michael
  560. * Large patch from Joost Van der Sluis.
  561. * Float fix in interbase
  562. + Commit and commitretaining for pqconnection
  563. + Preparestatement and prepareselect joined.
  564. + Freestatement and FreeSelect joined
  565. + TSQLQuery.GetSQLStatementType implemented
  566. + TBufDataset.AllocBuffer now no longer does a realloc
  567. + Fetch=True means succesfully got data. False means end of data.
  568. + Default implementation of GetFieldData implemented/
  569. Revision 1.3 2004/10/02 14:52:25 michael
  570. + Added mysql connection
  571. Revision 1.2 2004/09/26 16:56:32 michael
  572. + Further fixes from Joost van der sluis for Postgresql
  573. Revision 1.1 2004/08/31 09:49:47 michael
  574. + initial implementation of TSQLQuery
  575. }