sqldb.pp 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  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. FreeAndNil(FIndexDefs);
  650. inherited Destroy;
  651. end;
  652. procedure TSQLQuery.SetReadOnly(AValue : Boolean);
  653. begin
  654. if not Active then FReadOnly := AValue
  655. else
  656. begin
  657. // Just temporary, this should be possible in the future
  658. DatabaseError(SActiveDataset);
  659. end;
  660. end;
  661. procedure TSQLQuery.SetUsePrimaryKeyAsKey(AValue : Boolean);
  662. begin
  663. if not Active then FusePrimaryKeyAsKey := AValue
  664. else
  665. begin
  666. // Just temporary, this should be possible in the future
  667. DatabaseError(SActiveDataset);
  668. end;
  669. end;
  670. Procedure TSQLQuery.UpdateIndexDefs;
  671. begin
  672. if assigned(DataBase) then
  673. (DataBase as TSQLConnection).UpdateIndexDefs(FIndexDefs,FTableName);
  674. end;
  675. function TSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean;
  676. var
  677. sql_tables : string;
  678. s : string;
  679. procedure UpdateWherePart(var sql_where : string;x : integer);
  680. begin
  681. if (pfInKey in Fields[x].ProviderFlags) or
  682. ((FUpdateMode = upWhereAll) and (pfInWhere in Fields[x].ProviderFlags)) or
  683. ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (fields[x].value <> fields[x].oldvalue)) then
  684. begin
  685. // This should be converted to something like GetAsSQLText, but better wait until variants (oldvalue) are working for strings
  686. s := fields[x].oldvalue; // This directly int the line below raises a variant-error
  687. sql_where := sql_where + '(' + fields[x].FieldName + '=' + s + ') and ';
  688. end;
  689. end;
  690. function ModifyRecQuery : string;
  691. var x : integer;
  692. sql_set : string;
  693. sql_where : string;
  694. begin
  695. sql_tables := FTableName;
  696. sql_set := '';
  697. sql_where := '';
  698. for x := 0 to Fields.Count -1 do
  699. begin
  700. UpdateWherePart(sql_where,x);
  701. if (pfInUpdate in Fields[x].ProviderFlags) then
  702. if fields[x].IsNull then // check for null
  703. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(nil) + ','
  704. else
  705. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  706. end;
  707. setlength(sql_set,length(sql_set)-1);
  708. setlength(sql_where,length(sql_where)-5);
  709. result := 'update ' + sql_tables + ' set ' + sql_set + ' where ' + sql_where;
  710. end;
  711. function InsertRecQuery : string;
  712. var x : integer;
  713. sql_fields : string;
  714. sql_values : string;
  715. begin
  716. sql_tables := FTableName;
  717. sql_fields := '';
  718. sql_values := '';
  719. for x := 0 to Fields.Count -1 do
  720. begin
  721. if not fields[x].IsNull then
  722. begin
  723. sql_fields := sql_fields + fields[x].DisplayName + ',';
  724. sql_values := sql_values + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  725. end;
  726. end;
  727. setlength(sql_fields,length(sql_fields)-1);
  728. setlength(sql_values,length(sql_values)-1);
  729. result := 'insert into ' + sql_tables + ' (' + sql_fields + ') values (' + sql_values + ')';
  730. end;
  731. function DeleteRecQuery : string;
  732. var x : integer;
  733. sql_where : string;
  734. begin
  735. sql_tables := FTableName;
  736. sql_where := '';
  737. for x := 0 to Fields.Count -1 do
  738. UpdateWherePart(sql_where,x);
  739. setlength(sql_where,length(sql_where)-5);
  740. result := 'delete from ' + sql_tables + ' where ' + sql_where;
  741. end;
  742. begin
  743. Result := False;
  744. with tsqlquery.Create(nil) do
  745. begin
  746. DataBase := self.Database;
  747. transaction := self.transaction;
  748. sql.clear;
  749. case UpdateKind of
  750. ukModify : s := ModifyRecQuery;
  751. ukInsert : s := InsertRecQuery;
  752. ukDelete : s := DeleteRecQuery;
  753. end; {case}
  754. sql.add(s);
  755. ExecSQL;
  756. Result := true;
  757. Free;
  758. end;
  759. end;
  760. Function TSQLQuery.GetCanModify: Boolean;
  761. begin
  762. if FCursor.FStatementType = stSelect then
  763. Result:= Active and FUpdateable and (not FReadOnly)
  764. else
  765. Result := False;
  766. end;
  767. function TSQLQuery.GetIndexDefs : TIndexDefs;
  768. begin
  769. Result := FIndexDefs;
  770. end;
  771. procedure TSQLQuery.SetIndexDefs(AValue : TIndexDefs);
  772. begin
  773. FIndexDefs := AValue;
  774. end;
  775. procedure TSQLQuery.SetUpdateMode(AValue : TUpdateMode);
  776. begin
  777. FUpdateMode := AValue;
  778. end;
  779. procedure TSQLQuery.SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string);
  780. begin
  781. SQL.Clear;
  782. SQL.Add((DataBase as tsqlconnection).GetSchemaInfoSQL(SchemaType, SchemaObjectName, SchemaPattern));
  783. end;
  784. function TSQLQuery.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
  785. begin
  786. result := (DataBase as tsqlconnection).CreateBlobStream(Field, Mode);
  787. end;
  788. function TSQLQuery.GetStatementType : TStatementType;
  789. begin
  790. if assigned(FCursor) then Result := FCursor.FStatementType
  791. else Result := stNone;
  792. end;
  793. end.
  794. {
  795. $Log$
  796. Revision 1.17 2005-04-13 22:10:26 joost
  797. - TSQLQuery now frees FIndexDefs
  798. Revision 1.16 2005/04/10 18:29:26 joost
  799. - implemented parameter support for sqldb
  800. - Added TSQLConnection.ConnOptions
  801. - renamed TSQLQuery.FreeStatement to TSQLQuery.CloseStatement
  802. - renamed TSQLQuery.PrepareStatement to TSQLQuery.Prepare
  803. - added TSQLQuery.UnPrepare
  804. Revision 1.15 2005/03/23 08:17:51 michael
  805. + Several patches from Jose A. Rimon
  806. # Prevents "field not found" error, when use a query without the primary key
  807. Set SQLlen of different data types
  808. Use AliasName instead of SQLname to avoid "duplicate field name" error, for
  809. example when using "coalesce" more than once
  810. use SQLScale in ftLargeInt to get actual values
  811. Send query to server with different lines. Provides line info in sqlErrors
  812. and allows single line comments
  813. Revision 1.14 2005/02/14 17:13:12 peter
  814. * truncate log
  815. Revision 1.13 2005/02/07 11:23:41 joost
  816. - implemented TSQLQuery.SetSchemaInfo
  817. - added support for delete and insert
  818. Revision 1.12 2005/01/24 10:52:43 michael
  819. * Patch from Joost van der Sluis
  820. - Made it possible to run 'show' queries for MySQL
  821. Revision 1.11 2005/01/12 10:30:33 michael
  822. * Patch from Joost Van der Sluis:
  823. - implemented TSQLQuery.UpdateIndexDefs
  824. - implemented TSQLQuery.ReadOnly
  825. - implemented TSQLQuery.IndexDefs
  826. - implemented TSQLQuery.UpdateMode
  827. - implemented TSQLQuery.UsePrimaryKeyAsKey (Set pfInKey in the
  828. providerflags
  829. of fields that are in the primary index of the underlying table)
  830. - Added support for updates on date-fields
  831. }