sqldb.pp 30 KB

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