sqldb.pp 28 KB

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