sqldb.pp 34 KB

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