sqldb.pp 32 KB

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