sqldb.pp 17 KB

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