sqldb.pp 31 KB

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