sqldb.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  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. ResourceString
  185. SErrAssTransaction = 'Cannot assign transaction while old transaction active!';
  186. SErrDatabasenAssigned = 'Database not assigned!';
  187. SErrTransactionnSet = 'Transaction not set';
  188. SErrNoStatement = 'SQL statement not set';
  189. SErrNoSelectStatement = 'Cannot open a non-select statement';
  190. { TSQLConnection }
  191. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  192. begin
  193. if FTransaction = nil then
  194. begin
  195. FTransaction := Value;
  196. if Assigned(FTransaction) then
  197. FTransaction.Database := Self;
  198. exit;
  199. end;
  200. if (Value <> FTransaction) and (Value <> nil) then
  201. if (not FTransaction.Active) then
  202. begin
  203. FTransaction := Value;
  204. FTransaction.Database := Self;
  205. end
  206. else
  207. DatabaseError(SErrAssTransaction);
  208. end;
  209. procedure TSQLConnection.DoInternalConnect;
  210. begin
  211. if Connected then
  212. Close;
  213. end;
  214. procedure TSQLConnection.DoInternalDisconnect;
  215. begin
  216. end;
  217. destructor TSQLConnection.Destroy;
  218. begin
  219. if FTransaction <> nil then
  220. begin
  221. FTransaction.Active := False;
  222. FTransaction.Database := nil;
  223. end;
  224. inherited Destroy;
  225. end;
  226. function TSQLConnection.GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean;
  227. var
  228. x : longint;
  229. begin
  230. Result := False;
  231. for x := 0 to FieldDefs.count-1 do
  232. begin
  233. if (Field.FieldName = FieldDefs[x].Name) then
  234. begin
  235. Move(CurrBuff^, Buffer^, Field.Size);
  236. Result := True;
  237. Break;
  238. end
  239. else Inc(CurrBuff, FieldDefs[x].Size);
  240. end;
  241. end;
  242. { TSQLTransaction }
  243. procedure TSQLTransaction.SetActive(Value : boolean);
  244. begin
  245. if FActive and (not Value) then
  246. Rollback
  247. else if (not FActive) and Value then
  248. StartTransaction;
  249. end;
  250. function TSQLTransaction.GetHandle: pointer;
  251. begin
  252. Result := (Database as tsqlconnection).GetTransactionHandle(FTrans);
  253. end;
  254. procedure TSQLTransaction.Commit;
  255. begin
  256. if not FActive then Exit;
  257. if (Database as tsqlconnection).commit(FTrans) then FActive := false;
  258. FTrans.free;
  259. end;
  260. procedure TSQLTransaction.CommitRetaining;
  261. begin
  262. if not FActive then Exit;
  263. (Database as tsqlconnection).commitRetaining(FTrans);
  264. end;
  265. procedure TSQLTransaction.Rollback;
  266. begin
  267. if not FActive then Exit;
  268. if (Database as tsqlconnection).RollBack(FTrans) then FActive := false;
  269. FTrans.free;
  270. end;
  271. procedure TSQLTransaction.EndTransaction;
  272. begin
  273. Rollback;
  274. end;
  275. procedure TSQLTransaction.RollbackRetaining;
  276. begin
  277. if not FActive then Exit;
  278. (Database as tsqlconnection).RollBackRetaining(FTrans);
  279. end;
  280. procedure TSQLTransaction.StartTransaction;
  281. var db : TSQLConnection;
  282. begin
  283. if Active then Active := False;
  284. db := (Database as tsqlconnection);
  285. if Db = nil then
  286. DatabaseError(SErrDatabasenAssigned);
  287. if not Db.Connected then
  288. Db.Open;
  289. if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle;
  290. if Db.StartTransaction(FTrans) then FActive := true;
  291. end;
  292. constructor TSQLTransaction.Create(AOwner : TComponent);
  293. begin
  294. inherited Create(AOwner);
  295. end;
  296. destructor TSQLTransaction.Destroy;
  297. begin
  298. // This will also do a Rollback, if the transaction is currently active
  299. Active := False;
  300. // Database.Transaction := nil;
  301. inherited Destroy;
  302. end;
  303. { TSQLQuery }
  304. procedure TSQLQuery.SetTransaction(Value : TSQLTransaction);
  305. begin
  306. CheckInactive;
  307. if (FTransaction <> Value) then
  308. FTransaction := Value;
  309. end;
  310. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  311. var db : tsqlconnection;
  312. begin
  313. if (Database <> Value) then
  314. begin
  315. db := value as tsqlconnection;
  316. inherited setdatabase(value);
  317. if (FTransaction = nil) and (Assigned(Db.Transaction)) then
  318. SetTransaction(Db.Transaction);
  319. end;
  320. end;
  321. procedure TSQLQuery.FreeStatement;
  322. begin
  323. (Database as tsqlconnection).FreeStatement(FCursor);
  324. end;
  325. procedure TSQLQuery.PrepareStatement;
  326. var
  327. Buf : string;
  328. x : integer;
  329. db : tsqlconnection;
  330. begin
  331. db := (Database as tsqlconnection);
  332. if Db = nil then
  333. DatabaseError(SErrDatabasenAssigned);
  334. if not Db.Connected then
  335. db.Open;
  336. if FTransaction = nil then
  337. DatabaseError(SErrTransactionnSet);
  338. if not FTransaction.Active then
  339. FTransaction.StartTransaction;
  340. if assigned(fcursor) then FCursor.free;
  341. FCursor := Db.AllocateCursorHandle;
  342. for x := 0 to FSQL.Count - 1 do
  343. Buf := Buf + FSQL[x] + ' ';
  344. if Buf='' then
  345. begin
  346. DatabaseError(SErrNoStatement);
  347. exit;
  348. end;
  349. FCursor.StatementType := GetSQLStatementType(buf);
  350. Db.PrepareStatement(Fcursor,FTransaction,buf);
  351. end;
  352. procedure TSQLQuery.FreeFldBuffers;
  353. begin
  354. (Database as tsqlconnection).FreeFldBuffers(FCursor);
  355. end;
  356. procedure TSQLQuery.Fetch;
  357. begin
  358. if not (Fcursor.StatementType in [stSelect]) then
  359. Exit;
  360. FIsEof := not (Database as tsqlconnection).Fetch(Fcursor);
  361. end;
  362. function TSQLQuery.LoadBuffer(Buffer : PChar): TGetResult;
  363. begin
  364. Fetch;
  365. if FIsEOF then
  366. begin
  367. Result := grEOF;
  368. Exit;
  369. end;
  370. (Database as tsqlconnection).LoadFieldsFromBuffer(FCursor,buffer);
  371. Result := grOK;
  372. end;
  373. procedure TSQLQuery.SetFieldSizes;
  374. begin
  375. FRecordSize := (Database as tsqlconnection).GetfieldSizes(Fcursor);
  376. end;
  377. procedure TSQLQuery.Execute;
  378. begin
  379. (Database as tsqlconnection).execute(Fcursor,FTransaction);
  380. end;
  381. function TSQLQuery.AllocRecord(ExtraSize : integer): PChar;
  382. begin
  383. Result := AllocMem(FRecordSize+ExtraSize);
  384. end;
  385. procedure TSQLQuery.FreeRecord(var Buffer: PChar);
  386. begin
  387. if Assigned(@Buffer) then
  388. FreeMem(Buffer);
  389. end;
  390. function TSQLQuery.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  391. begin
  392. result := (Database as tsqlconnection).GetFieldData(Fcursor,Field,FieldDefs,buffer,activebuffer);
  393. end;
  394. function TSQLQuery.GetNextRecord(Buffer: PChar): TGetResult;
  395. begin
  396. if FIsEOF then
  397. Result := grEof
  398. else
  399. Result := LoadBuffer(Buffer);
  400. end;
  401. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  402. begin
  403. // not implemented - sql dataset
  404. end;
  405. procedure TSQLQuery.InternalClose;
  406. begin
  407. FreeFldBuffers;
  408. FreeStatement;
  409. if DefaultFields then
  410. DestroyFields;
  411. FIsEOF := False;
  412. FRecordSize := 0;
  413. FOpen:=False;
  414. FCursor.free;
  415. inherited internalclose;
  416. end;
  417. procedure TSQLQuery.InternalDelete;
  418. begin
  419. // not implemented - sql dataset
  420. end;
  421. procedure TSQLQuery.InternalHandleException;
  422. begin
  423. end;
  424. procedure TSQLQuery.InternalInitFieldDefs;
  425. begin
  426. if FLoadingFieldDefs then
  427. Exit;
  428. FLoadingFieldDefs := True;
  429. try
  430. FieldDefs.Clear;
  431. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  432. finally
  433. FLoadingFieldDefs := False;
  434. end;
  435. end;
  436. procedure TSQLQuery.InternalInitRecord(Buffer: PChar);
  437. begin
  438. FillChar(Buffer^, FRecordSize, #0);
  439. end;
  440. procedure TSQLQuery.InternalOpen;
  441. begin
  442. try
  443. PrepareStatement;
  444. if Fcursor.StatementType in [stSelect] then
  445. begin
  446. Execute;
  447. FOpen:=True;
  448. InternalInitFieldDefs;
  449. if DefaultFields then
  450. CreateFields;
  451. SetFieldSizes;
  452. BindFields(True);
  453. end
  454. else
  455. DatabaseError(SErrNoSelectStatement,Self);
  456. except
  457. on E:Exception do
  458. raise;
  459. end;
  460. inherited InternalOpen;
  461. end;
  462. procedure TSQLQuery.InternalPost;
  463. begin
  464. // not implemented - sql dataset
  465. end;
  466. function TSQLQuery.IsCursorOpen: Boolean;
  467. begin
  468. Result := FOpen;
  469. end;
  470. procedure TSQLQuery.SetFieldData(Field: TField; Buffer: Pointer);
  471. begin
  472. end;
  473. // public part
  474. procedure TSQLQuery.ExecSQL;
  475. begin
  476. try
  477. PrepareStatement;
  478. Execute;
  479. finally
  480. FreeStatement;
  481. end;
  482. end;
  483. constructor TSQLQuery.Create(AOwner : TComponent);
  484. begin
  485. inherited Create(AOwner);
  486. FSQL := TStringList.Create;
  487. end;
  488. destructor TSQLQuery.Destroy;
  489. begin
  490. if Active then Close;
  491. // if assigned(FCursor) then FCursor.destroy;
  492. FSQL.Free;
  493. inherited Destroy;
  494. end;
  495. Function TSQLQuery.GetSQLStatementType(SQL : String) : TStatementType;
  496. Var
  497. L : Integer;
  498. cmt : boolean;
  499. P,PE,PP : PChar;
  500. S : string;
  501. T : TStatementType;
  502. begin
  503. Result:=stNone;
  504. L:=Length(SQL);
  505. If (L=0) then
  506. Exit;
  507. P:=Pchar(SQL);
  508. PP:=P;
  509. Cmt:=False;
  510. While ((P-PP)<L) do
  511. begin
  512. if not (P^ in [' ',#13,#10,#9]) then
  513. begin
  514. if not Cmt then
  515. begin
  516. // Check for comment.
  517. Cmt:=(P^='/') and (((P-PP)<=L) and (P[1]='*'));
  518. if not (cmt) then
  519. Break;
  520. end
  521. else
  522. begin
  523. // Check for end of comment.
  524. Cmt:=Not( (P^='*') and (((P-PP)<=L) and (P[1]='/')) );
  525. If not cmt then
  526. Inc(p);
  527. end;
  528. end;
  529. inc(P);
  530. end;
  531. PE:=P+1;
  532. While ((PE-PP)<L) and (PE^ in ['0'..'9','a'..'z','A'..'Z','_']) do
  533. Inc(PE);
  534. Setlength(S,PE-P);
  535. Move(P^,S[1],(PE-P));
  536. S:=Lowercase(s);
  537. For t:=stselect to strollback do
  538. if (S=StatementTokens[t]) then
  539. Exit(t);
  540. end;
  541. function TSQLQuery.getrecordsize : Word;
  542. begin
  543. result := FRecordSize;
  544. end;
  545. end.
  546. {
  547. $Log$
  548. Revision 1.4 2004-10-10 14:24:22 michael
  549. * Large patch from Joost Van der Sluis.
  550. * Float fix in interbase
  551. + Commit and commitretaining for pqconnection
  552. + Preparestatement and prepareselect joined.
  553. + Freestatement and FreeSelect joined
  554. + TSQLQuery.GetSQLStatementType implemented
  555. + TBufDataset.AllocBuffer now no longer does a realloc
  556. + Fetch=True means succesfully got data. False means end of data.
  557. + Default implementation of GetFieldData implemented/
  558. Revision 1.3 2004/10/02 14:52:25 michael
  559. + Added mysql connection
  560. Revision 1.2 2004/09/26 16:56:32 michael
  561. + Further fixes from Joost van der sluis for Postgresql
  562. Revision 1.1 2004/08/31 09:49:47 michael
  563. + initial implementation of TSQLQuery
  564. }