sqldb.pp 25 KB

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