sqldb.pp 34 KB

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