sqldb.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  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 StartTransaction(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. procedure SetActive(Value : boolean);
  91. protected
  92. function GetHandle : Pointer; virtual;
  93. public
  94. procedure EndTransaction; override;
  95. procedure Commit; virtual;
  96. procedure CommitRetaining; virtual;
  97. procedure Rollback; virtual;
  98. procedure RollbackRetaining; virtual;
  99. procedure StartTransaction;
  100. constructor Create(AOwner : TComponent); override;
  101. destructor Destroy; override;
  102. property Handle: Pointer read GetHandle;
  103. published
  104. property Action : TCommitRollbackAction read FAction write FAction;
  105. property Active : boolean read FActive write SetActive;
  106. property Database;
  107. end;
  108. { TSQLQuery }
  109. TSQLQuery = class (Tbufdataset)
  110. private
  111. FCursor : TSQLHandle;
  112. FOpen : Boolean;
  113. FTransaction : TSQLTransaction;
  114. FSQL : TStrings;
  115. FIsEOF : boolean;
  116. FLoadingFieldDefs : boolean;
  117. FRecordSize : Integer;
  118. procedure SetTransaction(Value : TSQLTransaction);
  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 : TSQLTransaction read FTransaction write SetTransaction;
  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. if Connected then
  207. Close;
  208. end;
  209. procedure TSQLConnection.DoInternalDisconnect;
  210. begin
  211. end;
  212. destructor TSQLConnection.Destroy;
  213. begin
  214. if FTransaction <> nil then
  215. begin
  216. FTransaction.Active := False;
  217. FTransaction.Database := nil;
  218. end;
  219. inherited Destroy;
  220. end;
  221. function TSQLConnection.GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean;
  222. var
  223. x : longint;
  224. begin
  225. Result := False;
  226. for x := 0 to FieldDefs.count-1 do
  227. begin
  228. if (Field.FieldName = FieldDefs[x].Name) then
  229. begin
  230. Move(CurrBuff^, Buffer^, Field.Size);
  231. Result := True;
  232. Break;
  233. end
  234. else Inc(CurrBuff, FieldDefs[x].Size);
  235. end;
  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. end;
  315. end;
  316. procedure TSQLQuery.FreeStatement;
  317. begin
  318. (Database as tsqlconnection).FreeStatement(FCursor);
  319. end;
  320. procedure TSQLQuery.PrepareStatement;
  321. var
  322. Buf : string;
  323. x : integer;
  324. db : tsqlconnection;
  325. begin
  326. db := (Database as tsqlconnection);
  327. if Db = nil then
  328. DatabaseError(SErrDatabasenAssigned);
  329. if not Db.Connected then
  330. db.Open;
  331. if FTransaction = nil then
  332. DatabaseError(SErrTransactionnSet);
  333. if not FTransaction.Active then
  334. FTransaction.StartTransaction;
  335. if assigned(fcursor) then FCursor.free;
  336. FCursor := Db.AllocateCursorHandle;
  337. for x := 0 to FSQL.Count - 1 do
  338. Buf := Buf + FSQL[x] + ' ';
  339. if Buf='' then
  340. begin
  341. DatabaseError(SErrNoStatement);
  342. exit;
  343. end;
  344. FCursor.StatementType := GetSQLStatementType(buf);
  345. Db.PrepareStatement(Fcursor,FTransaction,buf);
  346. end;
  347. procedure TSQLQuery.FreeFldBuffers;
  348. begin
  349. (Database as tsqlconnection).FreeFldBuffers(FCursor);
  350. end;
  351. procedure TSQLQuery.Fetch;
  352. begin
  353. if not (Fcursor.StatementType in [stSelect]) then
  354. Exit;
  355. FIsEof := not (Database as tsqlconnection).Fetch(Fcursor);
  356. end;
  357. function TSQLQuery.LoadBuffer(Buffer : PChar): TGetResult;
  358. begin
  359. Fetch;
  360. if FIsEOF then
  361. begin
  362. Result := grEOF;
  363. Exit;
  364. end;
  365. (Database as tsqlconnection).LoadFieldsFromBuffer(FCursor,buffer);
  366. Result := grOK;
  367. end;
  368. procedure TSQLQuery.SetFieldSizes;
  369. begin
  370. FRecordSize := (Database as tsqlconnection).GetfieldSizes(Fcursor);
  371. end;
  372. procedure TSQLQuery.Execute;
  373. begin
  374. (Database as tsqlconnection).execute(Fcursor,FTransaction);
  375. end;
  376. function TSQLQuery.AllocRecord(ExtraSize : integer): PChar;
  377. begin
  378. Result := AllocMem(FRecordSize+ExtraSize);
  379. end;
  380. procedure TSQLQuery.FreeRecord(var Buffer: PChar);
  381. begin
  382. if Assigned(@Buffer) then
  383. FreeMem(Buffer);
  384. end;
  385. function TSQLQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  386. begin
  387. result := (Database as tsqlconnection).GetFieldData(Fcursor,Field,FieldDefs,buffer,activebuffer);
  388. end;
  389. function TSQLQuery.GetNextRecord(Buffer: PChar): TGetResult;
  390. begin
  391. if FIsEOF then
  392. Result := grEof
  393. else
  394. Result := LoadBuffer(Buffer);
  395. end;
  396. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  397. begin
  398. // not implemented - sql dataset
  399. end;
  400. procedure TSQLQuery.InternalClose;
  401. begin
  402. FreeFldBuffers;
  403. FreeStatement;
  404. if DefaultFields then
  405. DestroyFields;
  406. FIsEOF := False;
  407. FRecordSize := 0;
  408. FOpen:=False;
  409. FCursor.free;
  410. inherited internalclose;
  411. end;
  412. procedure TSQLQuery.InternalDelete;
  413. begin
  414. // not implemented - sql dataset
  415. end;
  416. procedure TSQLQuery.InternalHandleException;
  417. begin
  418. end;
  419. procedure TSQLQuery.InternalInitFieldDefs;
  420. begin
  421. if FLoadingFieldDefs then
  422. Exit;
  423. FLoadingFieldDefs := True;
  424. try
  425. FieldDefs.Clear;
  426. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  427. finally
  428. FLoadingFieldDefs := False;
  429. end;
  430. end;
  431. procedure TSQLQuery.InternalInitRecord(Buffer: PChar);
  432. begin
  433. FillChar(Buffer^, FRecordSize, #0);
  434. end;
  435. procedure TSQLQuery.InternalOpen;
  436. begin
  437. try
  438. PrepareStatement;
  439. if Fcursor.StatementType in [stSelect] then
  440. begin
  441. Execute;
  442. FOpen:=True;
  443. InternalInitFieldDefs;
  444. if DefaultFields then
  445. CreateFields;
  446. SetFieldSizes;
  447. BindFields(True);
  448. end
  449. else
  450. DatabaseError(SErrNoSelectStatement,Self);
  451. except
  452. on E:Exception do
  453. raise;
  454. end;
  455. inherited InternalOpen;
  456. end;
  457. procedure TSQLQuery.InternalPost;
  458. begin
  459. // not implemented - sql dataset
  460. end;
  461. function TSQLQuery.IsCursorOpen: Boolean;
  462. begin
  463. Result := FOpen;
  464. end;
  465. procedure TSQLQuery.SetFieldData(Field: TField; Buffer: Pointer);
  466. begin
  467. end;
  468. // public part
  469. procedure TSQLQuery.ExecSQL;
  470. begin
  471. try
  472. PrepareStatement;
  473. Execute;
  474. finally
  475. FreeStatement;
  476. end;
  477. end;
  478. constructor TSQLQuery.Create(AOwner : TComponent);
  479. begin
  480. inherited Create(AOwner);
  481. FSQL := TStringList.Create;
  482. end;
  483. destructor TSQLQuery.Destroy;
  484. begin
  485. if Active then Close;
  486. // if assigned(FCursor) then FCursor.destroy;
  487. FSQL.Free;
  488. inherited Destroy;
  489. end;
  490. Function TSQLQuery.GetSQLStatementType(SQL : String) : TStatementType;
  491. Var
  492. L : Integer;
  493. cmt : boolean;
  494. P,PE,PP : PChar;
  495. S : string;
  496. T : TStatementType;
  497. begin
  498. Result:=stNone;
  499. L:=Length(SQL);
  500. If (L=0) then
  501. Exit;
  502. P:=Pchar(SQL);
  503. PP:=P;
  504. Cmt:=False;
  505. While ((P-PP)<L) do
  506. begin
  507. if not (P^ in [' ',#13,#10,#9]) then
  508. begin
  509. if not Cmt then
  510. begin
  511. // Check for comment.
  512. Cmt:=(P^='/') and (((P-PP)<=L) and (P[1]='*'));
  513. if not (cmt) then
  514. Break;
  515. end
  516. else
  517. begin
  518. // Check for end of comment.
  519. Cmt:=Not( (P^='*') and (((P-PP)<=L) and (P[1]='/')) );
  520. If not cmt then
  521. Inc(p);
  522. end;
  523. end;
  524. inc(P);
  525. end;
  526. PE:=P+1;
  527. While ((PE-PP)<L) and (PE^ in ['0'..'9','a'..'z','A'..'Z','_']) do
  528. Inc(PE);
  529. Setlength(S,PE-P);
  530. Move(P^,S[1],(PE-P));
  531. S:=Lowercase(s);
  532. For t:=stselect to strollback do
  533. if (S=StatementTokens[t]) then
  534. Exit(t);
  535. end;
  536. function TSQLQuery.getrecordsize : Word;
  537. begin
  538. result := FRecordSize;
  539. end;
  540. end.
  541. {
  542. $Log$
  543. Revision 1.5 2004-10-10 14:45:52 michael
  544. + Use of dbconst for resource strings
  545. Revision 1.4 2004/10/10 14:24:22 michael
  546. * Large patch from Joost Van der Sluis.
  547. * Float fix in interbase
  548. + Commit and commitretaining for pqconnection
  549. + Preparestatement and prepareselect joined.
  550. + Freestatement and FreeSelect joined
  551. + TSQLQuery.GetSQLStatementType implemented
  552. + TBufDataset.AllocBuffer now no longer does a realloc
  553. + Fetch=True means succesfully got data. False means end of data.
  554. + Default implementation of GetFieldData implemented/
  555. Revision 1.3 2004/10/02 14:52:25 michael
  556. + Added mysql connection
  557. Revision 1.2 2004/09/26 16:56:32 michael
  558. + Further fixes from Joost van der sluis for Postgresql
  559. Revision 1.1 2004/08/31 09:49:47 michael
  560. + initial implementation of TSQLQuery
  561. }