sqldb.pp 26 KB

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