sqldb.pp 30 KB

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