sqldb.pp 28 KB

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