sqldb.pp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  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 TSchemaType = (stNoSchema, stTables, stSysTables, stProcedures, stColumns, stProcedureParams, stIndexes, stPackages);
  17. type
  18. TSQLConnection = class;
  19. TSQLTransaction = class;
  20. TSQLQuery = class;
  21. TStatementType = (stNone, stSelect, stInsert, stUpdate, stDelete,
  22. stDDL, stGetSegment, stPutSegment, stExecProcedure,
  23. stStartTrans, stCommit, stRollback, stSelectForUpd);
  24. TSQLHandle = Class(TObject)
  25. protected
  26. StatementType : TStatementType;
  27. end;
  28. const
  29. StatementTokens : Array[TStatementType] of string = ('(none)', 'select',
  30. 'insert', 'update', 'delete',
  31. 'create', 'get', 'put', 'execute',
  32. 'start','commit','rollback', '?'
  33. );
  34. { TSQLConnection }
  35. type
  36. TSQLConnection = class (TDatabase)
  37. private
  38. FPassword : string;
  39. FTransaction : TSQLTransaction;
  40. FUserName : string;
  41. FHostName : string;
  42. FCharSet : string;
  43. FRole : String;
  44. procedure SetTransaction(Value : TSQLTransaction);
  45. protected
  46. function StrToStatementType(s : string) : TStatementType; virtual;
  47. procedure DoInternalConnect; override;
  48. procedure DoInternalDisconnect; override;
  49. function GetAsSQLText(Field : TField) : string; virtual;
  50. function GetHandle : pointer; virtual; abstract;
  51. Function AllocateCursorHandle : TSQLHandle; virtual; abstract;
  52. Function AllocateTransactionHandle : TSQLHandle; virtual; abstract;
  53. procedure FreeStatement(cursor : TSQLHandle); virtual; abstract;
  54. procedure PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string); virtual; abstract;
  55. procedure FreeFldBuffers(cursor : TSQLHandle); virtual; abstract;
  56. procedure Execute(cursor: TSQLHandle;atransaction:tSQLtransaction); virtual; abstract;
  57. procedure AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs); virtual; abstract;
  58. function Fetch(cursor : TSQLHandle) : boolean; virtual; abstract;
  59. function LoadField(cursor : TSQLHandle;FieldDef : TfieldDef;buffer : pointer) : boolean; virtual; abstract;
  60. function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
  61. function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
  62. function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
  63. function StartdbTransaction(trans : TSQLHandle) : boolean; virtual; abstract;
  64. procedure CommitRetaining(trans : TSQLHandle); virtual; abstract;
  65. procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract;
  66. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); virtual;
  67. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; virtual;
  68. public
  69. property Handle: Pointer read GetHandle;
  70. destructor Destroy; override;
  71. published
  72. property Password : string read FPassword write FPassword;
  73. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  74. property UserName : string read FUserName write FUserName;
  75. property CharSet : string read FCharSet write FCharSet;
  76. property HostName : string Read FHostName Write FHostName;
  77. property Connected;
  78. Property Role : String read FRole write FRole;
  79. property DatabaseName;
  80. property KeepConnection;
  81. property LoginPrompt;
  82. property Params;
  83. property OnLogin;
  84. end;
  85. { TSQLTransaction }
  86. TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback,
  87. caRollbackRetaining);
  88. TSQLTransaction = class (TDBTransaction)
  89. private
  90. FTrans : TSQLHandle;
  91. FAction : TCommitRollbackAction;
  92. protected
  93. function GetHandle : Pointer; virtual;
  94. public
  95. procedure Commit; virtual;
  96. procedure CommitRetaining; virtual;
  97. procedure Rollback; virtual;
  98. procedure RollbackRetaining; virtual;
  99. procedure StartTransaction; override;
  100. constructor Create(AOwner : TComponent); override;
  101. destructor Destroy; override;
  102. property Handle: Pointer read GetHandle;
  103. procedure EndTransaction; override;
  104. published
  105. property Action : TCommitRollbackAction read FAction write FAction;
  106. property Database;
  107. end;
  108. { TSQLQuery }
  109. TSQLQuery = class (Tbufdataset)
  110. private
  111. FCursor : TSQLHandle;
  112. FUpdateable : boolean;
  113. FTableName : string;
  114. FSQL : TStrings;
  115. FIsEOF : boolean;
  116. FLoadingFieldDefs : boolean;
  117. FIndexDefs : TIndexDefs;
  118. FReadOnly : boolean;
  119. FUpdateMode : TUpdateMode;
  120. FusePrimaryKeyAsKey : Boolean;
  121. // FSchemaInfo : TSchemaInfo;
  122. procedure FreeStatement;
  123. procedure PrepareStatement;
  124. procedure FreeFldBuffers;
  125. procedure InitUpdates(SQL : string);
  126. function GetIndexDefs : TIndexDefs;
  127. procedure SetIndexDefs(AValue : TIndexDefs);
  128. procedure SetReadOnly(AValue : Boolean);
  129. procedure SetUsePrimaryKeyAsKey(AValue : Boolean);
  130. procedure SetUpdateMode(AValue : TUpdateMode);
  131. procedure Execute;
  132. protected
  133. // abstract & virtual methods of TBufDataset
  134. function Fetch : boolean; override;
  135. function LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean; override;
  136. // abstract & virtual methods of TDataset
  137. procedure UpdateIndexDefs; override;
  138. procedure SetDatabase(Value : TDatabase); override;
  139. procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
  140. procedure InternalClose; override;
  141. procedure InternalHandleException; override;
  142. procedure InternalInitFieldDefs; override;
  143. procedure InternalOpen; override;
  144. function GetCanModify: Boolean; override;
  145. Function GetSQLStatementType(SQL : String) : TStatementType; virtual;
  146. function ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean; override;
  147. public
  148. procedure ExecSQL; virtual;
  149. constructor Create(AOwner : TComponent); override;
  150. destructor Destroy; override;
  151. procedure SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string); virtual;
  152. published
  153. // redeclared data set properties
  154. property Active;
  155. // property Filter;
  156. // property Filtered;
  157. // property FilterOptions;
  158. property BeforeOpen;
  159. property AfterOpen;
  160. property BeforeClose;
  161. property AfterClose;
  162. property BeforeInsert;
  163. property AfterInsert;
  164. property BeforeEdit;
  165. property AfterEdit;
  166. property BeforePost;
  167. property AfterPost;
  168. property BeforeCancel;
  169. property AfterCancel;
  170. property BeforeDelete;
  171. property AfterDelete;
  172. property BeforeScroll;
  173. property AfterScroll;
  174. property OnCalcFields;
  175. property OnDeleteError;
  176. property OnEditError;
  177. property OnFilterRecord;
  178. property OnNewRecord;
  179. property OnPostError;
  180. property AutoCalcFields;
  181. property Database;
  182. property Transaction;
  183. property ReadOnly : Boolean read FReadOnly write SetReadOnly;
  184. property SQL : TStrings read FSQL write FSQL;
  185. property IndexDefs : TIndexDefs read GetIndexDefs;
  186. property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode;
  187. property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey;
  188. // property SchemaInfo : TSchemaInfo read FSchemaInfo default stNoSchema;
  189. end;
  190. implementation
  191. uses dbconst;
  192. { TSQLConnection }
  193. function TSQLConnection.StrToStatementType(s : string) : TStatementType;
  194. var T : TStatementType;
  195. begin
  196. S:=Lowercase(s);
  197. For t:=stselect to strollback do
  198. if (S=StatementTokens[t]) then
  199. Exit(t);
  200. end;
  201. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  202. begin
  203. if FTransaction = nil then
  204. begin
  205. FTransaction := Value;
  206. if Assigned(FTransaction) then
  207. FTransaction.Database := Self;
  208. exit;
  209. end;
  210. if (Value <> FTransaction) and (Value <> nil) then
  211. if (not FTransaction.Active) then
  212. begin
  213. FTransaction := Value;
  214. FTransaction.Database := Self;
  215. end
  216. else
  217. DatabaseError(SErrAssTransaction);
  218. end;
  219. procedure TSQLConnection.UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string);
  220. begin
  221. // Empty abstract
  222. end;
  223. procedure TSQLConnection.DoInternalConnect;
  224. begin
  225. // Empty abstract
  226. end;
  227. procedure TSQLConnection.DoInternalDisconnect;
  228. begin
  229. end;
  230. destructor TSQLConnection.Destroy;
  231. begin
  232. inherited Destroy;
  233. end;
  234. function TSQLConnection.GetAsSQLText(Field : TField) : string;
  235. begin
  236. if not assigned(field) then Result := 'Null'
  237. else case field.DataType of
  238. ftString : Result := '''' + field.asstring + '''';
  239. ftDate : Result := '''' + FormatDateTime('yyyy-mm-dd',Field.AsDateTime) + '''';
  240. ftDateTime : Result := '''' + FormatDateTime('yyyy-mm-dd hh:mm:ss',Field.AsDateTime) + ''''
  241. else
  242. Result := field.asstring;
  243. end; {case}
  244. end;
  245. function TSQLConnection.GetSchemaInfoSQL( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string;
  246. begin
  247. DatabaseError(SMetadataUnavailable);
  248. end;
  249. { TSQLTransaction }
  250. procedure TSQLTransaction.EndTransaction;
  251. begin
  252. rollback;
  253. end;
  254. function TSQLTransaction.GetHandle: pointer;
  255. begin
  256. Result := (Database as tsqlconnection).GetTransactionHandle(FTrans);
  257. end;
  258. procedure TSQLTransaction.Commit;
  259. begin
  260. if active then
  261. begin
  262. closedatasets;
  263. if (Database as tsqlconnection).commit(FTrans) then
  264. begin
  265. closeTrans;
  266. FreeAndNil(FTrans);
  267. end;
  268. end;
  269. end;
  270. procedure TSQLTransaction.CommitRetaining;
  271. begin
  272. if active then
  273. (Database as tsqlconnection).commitRetaining(FTrans);
  274. end;
  275. procedure TSQLTransaction.Rollback;
  276. begin
  277. if active then
  278. begin
  279. closedatasets;
  280. if (Database as tsqlconnection).RollBack(FTrans) then
  281. begin
  282. CloseTrans;
  283. FreeAndNil(FTrans);
  284. end;
  285. end;
  286. end;
  287. procedure TSQLTransaction.RollbackRetaining;
  288. begin
  289. if active then
  290. (Database as tsqlconnection).RollBackRetaining(FTrans);
  291. end;
  292. procedure TSQLTransaction.StartTransaction;
  293. var db : TSQLConnection;
  294. begin
  295. if Active then
  296. DatabaseError(SErrTransAlreadyActive);
  297. db := (Database as tsqlconnection);
  298. if Db = nil then
  299. DatabaseError(SErrDatabasenAssigned);
  300. if not Db.Connected then
  301. Db.Open;
  302. if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle;
  303. if Db.StartdbTransaction(FTrans) then OpenTrans;
  304. end;
  305. constructor TSQLTransaction.Create(AOwner : TComponent);
  306. begin
  307. inherited Create(AOwner);
  308. end;
  309. destructor TSQLTransaction.Destroy;
  310. begin
  311. Rollback;
  312. inherited Destroy;
  313. end;
  314. { TSQLQuery }
  315. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  316. var db : tsqlconnection;
  317. begin
  318. if (Database <> Value) then
  319. begin
  320. db := value as tsqlconnection;
  321. inherited setdatabase(value);
  322. if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
  323. transaction := Db.Transaction;
  324. end;
  325. end;
  326. procedure TSQLQuery.FreeStatement;
  327. begin
  328. if assigned(FCursor) then
  329. begin
  330. (Database as tsqlconnection).FreeStatement(FCursor);
  331. // FreeAndNil(FCursor);
  332. end;
  333. end;
  334. procedure TSQLQuery.PrepareStatement;
  335. var
  336. Buf : string;
  337. x : integer;
  338. db : tsqlconnection;
  339. sqltr : tsqltransaction;
  340. begin
  341. db := (Database as tsqlconnection);
  342. if not assigned(Db) then
  343. DatabaseError(SErrDatabasenAssigned);
  344. if not Db.Connected then
  345. db.Open;
  346. if not assigned(Transaction) then
  347. DatabaseError(SErrTransactionnSet);
  348. sqltr := (transaction as tsqltransaction);
  349. if not sqltr.Active then sqltr.StartTransaction;
  350. if assigned(fcursor) then FreeAndNil(fcursor);
  351. FCursor := Db.AllocateCursorHandle;
  352. Buf := '';
  353. for x := 0 to FSQL.Count - 1 do
  354. Buf := Buf + FSQL[x] + ' ';
  355. if Buf='' then
  356. begin
  357. DatabaseError(SErrNoStatement);
  358. exit;
  359. end;
  360. FCursor.StatementType := GetSQLStatementType(buf);
  361. if (FCursor.StatementType = stSelect) and not ReadOnly then InitUpdates(Buf);
  362. Db.PrepareStatement(Fcursor,sqltr,buf);
  363. end;
  364. procedure TSQLQuery.FreeFldBuffers;
  365. begin
  366. if assigned(FCursor) then (Database as tsqlconnection).FreeFldBuffers(FCursor);
  367. end;
  368. function TSQLQuery.Fetch : boolean;
  369. begin
  370. if not (Fcursor.StatementType in [stSelect]) then
  371. Exit;
  372. if not FIsEof then FIsEOF := not (Database as tsqlconnection).Fetch(Fcursor);
  373. Result := not FIsEOF;
  374. end;
  375. procedure TSQLQuery.Execute;
  376. begin
  377. (Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction);
  378. end;
  379. function TSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean;
  380. begin
  381. result := (Database as tSQLConnection).LoadField(FCursor,FieldDef,buffer)
  382. end;
  383. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  384. begin
  385. // not implemented - sql dataset
  386. end;
  387. procedure TSQLQuery.InternalClose;
  388. begin
  389. FreeFldBuffers;
  390. FreeStatement;
  391. if DefaultFields then
  392. DestroyFields;
  393. FIsEOF := False;
  394. // FRecordSize := 0;
  395. inherited internalclose;
  396. end;
  397. procedure TSQLQuery.InternalHandleException;
  398. begin
  399. end;
  400. procedure TSQLQuery.InternalInitFieldDefs;
  401. begin
  402. if FLoadingFieldDefs then
  403. Exit;
  404. FLoadingFieldDefs := True;
  405. try
  406. FieldDefs.Clear;
  407. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  408. finally
  409. FLoadingFieldDefs := False;
  410. end;
  411. end;
  412. procedure TSQLQuery.InitUpdates(SQL : string);
  413. Var
  414. L : Integer;
  415. P,PP : PChar;
  416. PS: PChar;
  417. S : string;
  418. function GetStatement(var StartP : PChar) : PChar;
  419. var p : pchar;
  420. Cmt, Stm : boolean;
  421. begin
  422. p := StartP;
  423. Cmt := false;
  424. Stm := False;
  425. While ((P-PP)<L) do
  426. begin
  427. if Cmt then
  428. begin
  429. end
  430. else if (p^ in [',',' ','(',')',#13,#10,#9]) then
  431. begin
  432. if stm then break;
  433. end
  434. else if not stm then
  435. begin
  436. StartP := p;
  437. stm := true;
  438. end;
  439. inc(p);
  440. end;
  441. Result := P;
  442. end;
  443. begin
  444. FUpdateable := False;
  445. L:=Length(SQL);
  446. PP:=Pchar(SQL);
  447. P := pp;
  448. PS := pp;
  449. // select-keyword
  450. P := GetStatement(PS);
  451. Setlength(S,P-PS);
  452. Move(PS^,S[1],(P-PS));
  453. S:=Lowercase(S);
  454. if (S) <> 'select' then exit;
  455. // select-part
  456. While ((P-PP)<L) and (S <> 'from') do
  457. begin
  458. repeat
  459. PS := P;
  460. P := GetStatement(PS);
  461. until P^ <> ',';
  462. Setlength(S,P-PS);
  463. Move(PS^,S[1],(P-PS));
  464. S:=Lowercase(S);
  465. end;
  466. // from-part
  467. PS := P;
  468. P := GetStatement(PS);
  469. Setlength(FTableName,P-PS);
  470. Move(PS^,FTableName[1],(P-PS));
  471. While ((P-PP)<L) do
  472. begin
  473. PS := P;
  474. P := GetStatement(PS);
  475. if P^ = ',' then exit; // select-statements from more then one table are not updateable
  476. Setlength(S,P-PS);
  477. Move(PS^,S[1],(P-PS));
  478. S:=Lowercase(S);
  479. if (s = 'where') or (s='order') then break;
  480. end;
  481. FUpdateable := True;
  482. end;
  483. procedure TSQLQuery.InternalOpen;
  484. var tel : integer;
  485. f : TField;
  486. s : string;
  487. begin
  488. try
  489. PrepareStatement;
  490. if Fcursor.StatementType in [stSelect] then
  491. begin
  492. Execute;
  493. InternalInitFieldDefs;
  494. if DefaultFields then
  495. begin
  496. CreateFields;
  497. if FUpdateable and FusePrimaryKeyAsKey then
  498. begin
  499. UpdateIndexDefs;
  500. for tel := 0 to indexdefs.count-1 do {with indexdefs[tel] do}
  501. begin
  502. if ixPrimary in indexdefs[tel].options then
  503. begin
  504. // Todo: If there is more then one field in the key, that must be parsed
  505. s := indexdefs[tel].fields;
  506. F := fieldbyname(s);
  507. F.ProviderFlags := F.ProviderFlags + [pfInKey];
  508. end;
  509. end;
  510. end;
  511. end;
  512. end
  513. else
  514. DatabaseError(SErrNoSelectStatement,Self);
  515. except
  516. on E:Exception do
  517. raise;
  518. end;
  519. inherited InternalOpen;
  520. end;
  521. // public part
  522. procedure TSQLQuery.ExecSQL;
  523. begin
  524. try
  525. PrepareStatement;
  526. Execute;
  527. finally
  528. FreeStatement;
  529. end;
  530. end;
  531. constructor TSQLQuery.Create(AOwner : TComponent);
  532. begin
  533. inherited Create(AOwner);
  534. FSQL := TStringList.Create;
  535. FIndexDefs := TIndexDefs.Create(Self);
  536. FReadOnly := false;
  537. // Delphi has upWhereAll as default, but since strings and oldvalue's don't work yet
  538. // (variants) set it to upWhereKeyOnly
  539. FUpdateMode := upWhereKeyOnly;
  540. FUsePrimaryKeyAsKey := True;
  541. end;
  542. destructor TSQLQuery.Destroy;
  543. begin
  544. if Active then Close;
  545. // if assigned(FCursor) then FCursor.destroy;
  546. FreeAndNil(FSQL);
  547. inherited Destroy;
  548. end;
  549. Function TSQLQuery.GetSQLStatementType(SQL : String) : TStatementType;
  550. Var
  551. L : Integer;
  552. cmt : boolean;
  553. P,PE,PP : PChar;
  554. S : string;
  555. begin
  556. Result:=stNone;
  557. L:=Length(SQL);
  558. If (L=0) then
  559. Exit;
  560. P:=Pchar(SQL);
  561. PP:=P;
  562. Cmt:=False;
  563. While ((P-PP)<L) do
  564. begin
  565. if not (P^ in [' ',#13,#10,#9]) then
  566. begin
  567. if not Cmt then
  568. begin
  569. // Check for comment.
  570. Cmt:=(P^='/') and (((P-PP)<=L) and (P[1]='*'));
  571. if not (cmt) then
  572. Break;
  573. end
  574. else
  575. begin
  576. // Check for end of comment.
  577. Cmt:=Not( (P^='*') and (((P-PP)<=L) and (P[1]='/')) );
  578. If not cmt then
  579. Inc(p);
  580. end;
  581. end;
  582. inc(P);
  583. end;
  584. PE:=P+1;
  585. While ((PE-PP)<L) and (PE^ in ['0'..'9','a'..'z','A'..'Z','_']) do
  586. Inc(PE);
  587. Setlength(S,PE-P);
  588. Move(P^,S[1],(PE-P));
  589. result := (DataBase as TSQLConnection).StrToStatementType(s);
  590. end;
  591. procedure TSQLQuery.SetReadOnly(AValue : Boolean);
  592. begin
  593. if not Active then FReadOnly := AValue
  594. else
  595. begin
  596. // Just temporary, this should be possible in the future
  597. DatabaseError(SActiveDataset);
  598. end;
  599. end;
  600. procedure TSQLQuery.SetUsePrimaryKeyAsKey(AValue : Boolean);
  601. begin
  602. if not Active then FusePrimaryKeyAsKey := AValue
  603. else
  604. begin
  605. // Just temporary, this should be possible in the future
  606. DatabaseError(SActiveDataset);
  607. end;
  608. end;
  609. Procedure TSQLQuery.UpdateIndexDefs;
  610. begin
  611. if assigned(DataBase) then
  612. (DataBase as TSQLConnection).UpdateIndexDefs(FIndexDefs,FTableName);
  613. end;
  614. function TSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean;
  615. var
  616. sql_tables : string;
  617. s : string;
  618. procedure UpdateWherePart(var sql_where : string;x : integer);
  619. begin
  620. if (pfInKey in Fields[x].ProviderFlags) or
  621. ((FUpdateMode = upWhereAll) and (pfInWhere in Fields[x].ProviderFlags)) or
  622. ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (fields[x].value <> fields[x].oldvalue)) then
  623. begin
  624. // This should be converted to something like GetAsSQLText, but better wait until variants (oldvalue) are working for strings
  625. s := fields[x].oldvalue; // This directly int the line below raises a variant-error
  626. sql_where := sql_where + '(' + fields[x].FieldName + '=' + s + ') and ';
  627. end;
  628. end;
  629. function ModifyRecQuery : string;
  630. var x : integer;
  631. sql_set : string;
  632. sql_where : string;
  633. begin
  634. sql_tables := FTableName;
  635. sql_set := '';
  636. sql_where := '';
  637. for x := 0 to Fields.Count -1 do
  638. begin
  639. UpdateWherePart(sql_where,x);
  640. if (pfInUpdate in Fields[x].ProviderFlags) then
  641. if fields[x].IsNull then // check for null
  642. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(nil) + ','
  643. else
  644. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  645. end;
  646. setlength(sql_set,length(sql_set)-1);
  647. setlength(sql_where,length(sql_where)-5);
  648. result := 'update ' + sql_tables + ' set ' + sql_set + ' where ' + sql_where;
  649. end;
  650. function InsertRecQuery : string;
  651. var x : integer;
  652. sql_fields : string;
  653. sql_values : string;
  654. begin
  655. sql_tables := FTableName;
  656. sql_fields := '';
  657. sql_values := '';
  658. for x := 0 to Fields.Count -1 do
  659. begin
  660. if not fields[x].IsNull then
  661. begin
  662. sql_fields := sql_fields + fields[x].DisplayName + ',';
  663. sql_values := sql_values + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  664. end;
  665. end;
  666. setlength(sql_fields,length(sql_fields)-1);
  667. setlength(sql_values,length(sql_values)-1);
  668. result := 'insert into ' + sql_tables + ' (' + sql_fields + ') values (' + sql_values + ')';
  669. end;
  670. function DeleteRecQuery : string;
  671. var x : integer;
  672. sql_where : string;
  673. begin
  674. sql_tables := FTableName;
  675. sql_where := '';
  676. for x := 0 to Fields.Count -1 do
  677. UpdateWherePart(sql_where,x);
  678. setlength(sql_where,length(sql_where)-5);
  679. result := 'delete from ' + sql_tables + ' where ' + sql_where;
  680. end;
  681. begin
  682. Result := False;
  683. with tsqlquery.Create(nil) do
  684. begin
  685. DataBase := self.Database;
  686. transaction := self.transaction;
  687. sql.clear;
  688. case UpdateKind of
  689. ukModify : s := ModifyRecQuery;
  690. ukInsert : s := InsertRecQuery;
  691. ukDelete : s := DeleteRecQuery;
  692. end; {case}
  693. sql.add(s);
  694. ExecSQL;
  695. Result := true;
  696. Free;
  697. end;
  698. end;
  699. Function TSQLQuery.GetCanModify: Boolean;
  700. begin
  701. if FCursor.StatementType = stSelect then
  702. Result:= Active and FUpdateable and (not FReadOnly)
  703. else
  704. Result := False;
  705. end;
  706. function TSQLQuery.GetIndexDefs : TIndexDefs;
  707. begin
  708. Result := FIndexDefs;
  709. end;
  710. procedure TSQLQuery.SetIndexDefs(AValue : TIndexDefs);
  711. begin
  712. FIndexDefs := AValue;
  713. end;
  714. procedure TSQLQuery.SetUpdateMode(AValue : TUpdateMode);
  715. begin
  716. FUpdateMode := AValue;
  717. end;
  718. procedure TSQLQuery.SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string);
  719. begin
  720. SQL.Clear;
  721. SQL.Add((DataBase as tsqlconnection).GetSchemaInfoSQL(SchemaType, SchemaObjectName, SchemaPattern));
  722. end;
  723. end.
  724. {
  725. $Log$
  726. Revision 1.13 2005-02-07 11:23:41 joost
  727. - implemented TSQLQuery.SetSchemaInfo
  728. - added support for delete and insert
  729. Revision 1.12 2005/01/24 10:52:43 michael
  730. * Patch from Joost van der Sluis
  731. - Made it possible to run 'show' queries for MySQL
  732. Revision 1.11 2005/01/12 10:30:33 michael
  733. * Patch from Joost Van der Sluis:
  734. - implemented TSQLQuery.UpdateIndexDefs
  735. - implemented TSQLQuery.ReadOnly
  736. - implemented TSQLQuery.IndexDefs
  737. - implemented TSQLQuery.UpdateMode
  738. - implemented TSQLQuery.UsePrimaryKeyAsKey (Set pfInKey in the
  739. providerflags
  740. of fields that are in the primary index of the underlying table)
  741. - Added support for updates on date-fields
  742. Revision 1.10 2004/12/29 14:31:27 michael
  743. + Patch from Joost van der Sluis:
  744. - implemented support for modifying queries, with a simple parser
  745. - implemented ApplyRecUpdate
  746. Revision 1.9 2004/12/13 19:22:16 michael
  747. * Ptahc from Joost van der Sluis
  748. - moved IsCursorOpen from TSQLQuery to tbufdataset
  749. - moved SetFieldData from TSQLQuery to TBufDataset
  750. - very first start for support of cached updates
  751. Revision 1.8 2004/12/04 22:43:38 michael
  752. * Patch from Joost van der Sluis
  753. - replaced checkactive in commit and rollback for 'if active'
  754. - fixed a warning
  755. - adapted for the changes in TBuffDataset
  756. Revision 1.7 2004/11/05 08:32:02 michael
  757. TBufDataset.inc:
  758. - replaced Freemem by Reallocmem, Free by FreeAndNil
  759. Database.inc:
  760. - Moved Active property from TSQLTransaction to TDBTransaction
  761. - Gives an error if the database of an active transaction is changed
  762. Dataset.inc
  763. - Don't distribute events if FDisableControlsCount > 0
  764. - Replaced FActive by FState<>dsInactive
  765. - Set EOF after append
  766. db.pp:
  767. - Removed duplicate definition of TAlignment
  768. - Moved Active property from TSQLTransaction to TDBTransaction
  769. - Replaced FActive by FState<>dsInactive
  770. - Gives an error if the database of an active transaction is changed
  771. sqldb:
  772. - Moved Active property from TSQLTransaction to TDBTransaction
  773. - replaced Freemem by Reallocmem, Free by FreeAndNil
  774. IBConnection:
  775. - Moved FSQLDAAllocated to the cursor
  776. PQConnection:
  777. - Don't try to free the statement if a fatal error occured
  778. Revision 1.6 2004/10/27 07:23:13 michael
  779. + Patch from Joost Van der Sluis to fix transactions
  780. Revision 1.5 2004/10/10 14:45:52 michael
  781. + Use of dbconst for resource strings
  782. Revision 1.4 2004/10/10 14:24:22 michael
  783. * Large patch from Joost Van der Sluis.
  784. * Float fix in interbase
  785. + Commit and commitretaining for pqconnection
  786. + Preparestatement and prepareselect joined.
  787. + Freestatement and FreeSelect joined
  788. + TSQLQuery.GetSQLStatementType implemented
  789. + TBufDataset.AllocBuffer now no longer does a realloc
  790. + Fetch=True means succesfully got data. False means end of data.
  791. + Default implementation of GetFieldData implemented/
  792. Revision 1.3 2004/10/02 14:52:25 michael
  793. + Added mysql connection
  794. Revision 1.2 2004/09/26 16:56:32 michael
  795. + Further fixes from Joost van der sluis for Postgresql
  796. Revision 1.1 2004/08/31 09:49:47 michael
  797. + initial implementation of TSQLQuery
  798. }