sqldb.pp 27 KB

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