sqldb.pp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  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. SQLDelimiterCharacters = [';',',',' ','(',')',#13,#10,#9];
  41. { TSQLConnection }
  42. type
  43. { TSQLConnection }
  44. TSQLConnection = class (TDatabase)
  45. private
  46. FPassword : string;
  47. FTransaction : TSQLTransaction;
  48. FUserName : string;
  49. FHostName : string;
  50. FCharSet : string;
  51. FRole : String;
  52. procedure SetTransaction(Value : TSQLTransaction);
  53. procedure GetDBInfo(const SchemaType : TSchemaType; const SchemaObjectName, ReturnField : string; List: TStrings);
  54. protected
  55. FConnOptions : TConnOptions;
  56. function StrToStatementType(s : string) : TStatementType; virtual;
  57. procedure DoInternalConnect; override;
  58. procedure DoInternalDisconnect; override;
  59. function GetAsSQLText(Field : TField) : string; virtual;
  60. function GetHandle : pointer; virtual; abstract;
  61. Function AllocateCursorHandle : TSQLCursor; virtual; abstract;
  62. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); virtual; abstract;
  63. Function AllocateTransactionHandle : TSQLHandle; virtual; abstract;
  64. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); virtual; abstract;
  65. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); virtual; abstract;
  66. function Fetch(cursor : TSQLCursor) : boolean; virtual; abstract;
  67. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); virtual; abstract;
  68. procedure UnPrepareStatement(cursor : TSQLCursor); virtual; abstract;
  69. procedure FreeFldBuffers(cursor : TSQLCursor); virtual; abstract;
  70. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer) : boolean; virtual; abstract;
  71. function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
  72. function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
  73. function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
  74. function StartdbTransaction(trans : TSQLHandle; aParams : string) : boolean; virtual; abstract;
  75. procedure CommitRetaining(trans : TSQLHandle); virtual; abstract;
  76. procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract;
  77. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); virtual;
  78. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; virtual;
  79. function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; virtual;abstract;
  80. public
  81. property Handle: Pointer read GetHandle;
  82. destructor Destroy; override;
  83. property ConnOptions: TConnOptions read FConnOptions;
  84. procedure ExecuteDirect(SQL : String); overload; virtual;
  85. procedure ExecuteDirect(SQL : String; Transaction : TSQLTransaction); overload; virtual;
  86. procedure GetTableNames(List : TStrings; SystemTables : Boolean = false); virtual;
  87. procedure GetProcedureNames(List : TStrings); virtual;
  88. procedure GetFieldNames(const TableName : string; List : TStrings); virtual;
  89. published
  90. property Password : string read FPassword write FPassword;
  91. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  92. property UserName : string read FUserName write FUserName;
  93. property CharSet : string read FCharSet write FCharSet;
  94. property HostName : string Read FHostName Write FHostName;
  95. property Connected;
  96. Property Role : String read FRole write FRole;
  97. property DatabaseName;
  98. property KeepConnection;
  99. property LoginPrompt;
  100. property Params;
  101. property OnLogin;
  102. end;
  103. { TSQLTransaction }
  104. TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback,
  105. caRollbackRetaining);
  106. TSQLTransaction = class (TDBTransaction)
  107. private
  108. FTrans : TSQLHandle;
  109. FAction : TCommitRollbackAction;
  110. FParams : TStringList;
  111. protected
  112. function GetHandle : Pointer; virtual;
  113. Procedure SetDatabase (Value : TDatabase); override;
  114. public
  115. procedure Commit; virtual;
  116. procedure CommitRetaining; virtual;
  117. procedure Rollback; virtual;
  118. procedure RollbackRetaining; virtual;
  119. procedure StartTransaction; override;
  120. constructor Create(AOwner : TComponent); override;
  121. destructor Destroy; override;
  122. property Handle: Pointer read GetHandle;
  123. procedure EndTransaction; override;
  124. published
  125. property Action : TCommitRollbackAction read FAction write FAction;
  126. property Database;
  127. property Params : TStringList read FParams write FParams;
  128. end;
  129. { TSQLQuery }
  130. TSQLQuery = class (Tbufdataset)
  131. private
  132. FCursor : TSQLCursor;
  133. FUpdateable : boolean;
  134. FTableName : string;
  135. FSQL : TStringList;
  136. FIsEOF : boolean;
  137. FLoadingFieldDefs : boolean;
  138. FIndexDefs : TIndexDefs;
  139. FReadOnly : boolean;
  140. FUpdateMode : TUpdateMode;
  141. FParams : TParams;
  142. FusePrimaryKeyAsKey : Boolean;
  143. FSQLBuf : String;
  144. FFromPart : String;
  145. FWhereStartPos : integer;
  146. FWhereStopPos : integer;
  147. FParseSQL : boolean;
  148. // FSchemaInfo : TSchemaInfo;
  149. procedure FreeFldBuffers;
  150. procedure InitUpdates(SQL : string);
  151. function GetIndexDefs : TIndexDefs;
  152. function GetStatementType : TStatementType;
  153. procedure SetIndexDefs(AValue : TIndexDefs);
  154. procedure SetReadOnly(AValue : Boolean);
  155. procedure SetParseSQL(AValue : Boolean);
  156. procedure SetUsePrimaryKeyAsKey(AValue : Boolean);
  157. procedure SetUpdateMode(AValue : TUpdateMode);
  158. procedure OnChangeSQL(Sender : TObject);
  159. procedure Execute;
  160. Procedure SQLParser(var SQL : string);
  161. procedure ApplyFilter;
  162. Function AddFilter(SQLstr : string) : string;
  163. protected
  164. // abstract & virtual methods of TBufDataset
  165. function Fetch : boolean; override;
  166. function LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean; override;
  167. // abstract & virtual methods of TDataset
  168. procedure UpdateIndexDefs; override;
  169. procedure SetDatabase(Value : TDatabase); override;
  170. Procedure SetTransaction(Value : TDBTransaction); override;
  171. procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
  172. procedure InternalClose; override;
  173. procedure InternalInitFieldDefs; override;
  174. procedure InternalOpen; override;
  175. function GetCanModify: Boolean; override;
  176. function ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean; override;
  177. Function IsPrepared : Boolean; virtual;
  178. procedure SetFiltered(Value: Boolean); override;
  179. procedure SetFilterText(const Value: string); override;
  180. public
  181. procedure Prepare; virtual;
  182. procedure UnPrepare; virtual;
  183. procedure ExecSQL; virtual;
  184. constructor Create(AOwner : TComponent); override;
  185. destructor Destroy; override;
  186. procedure SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string); virtual;
  187. function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
  188. property Prepared : boolean read IsPrepared;
  189. published
  190. // redeclared data set properties
  191. property Active;
  192. property Filter;
  193. property Filtered;
  194. // property FilterOptions;
  195. property BeforeOpen;
  196. property AfterOpen;
  197. property BeforeClose;
  198. property AfterClose;
  199. property BeforeInsert;
  200. property AfterInsert;
  201. property BeforeEdit;
  202. property AfterEdit;
  203. property BeforePost;
  204. property AfterPost;
  205. property BeforeCancel;
  206. property AfterCancel;
  207. property BeforeDelete;
  208. property AfterDelete;
  209. property BeforeScroll;
  210. property AfterScroll;
  211. property OnCalcFields;
  212. property OnDeleteError;
  213. property OnEditError;
  214. property OnFilterRecord;
  215. property OnNewRecord;
  216. property OnPostError;
  217. property AutoCalcFields;
  218. property Database;
  219. property Transaction;
  220. property ReadOnly : Boolean read FReadOnly write SetReadOnly;
  221. property SQL : TStringlist read FSQL write FSQL;
  222. property IndexDefs : TIndexDefs read GetIndexDefs;
  223. property Params : TParams read FParams write FParams;
  224. property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode;
  225. property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey;
  226. property StatementType : TStatementType read GetStatementType;
  227. property ParseSQL : Boolean read FParseSQL write SetParseSQL;
  228. // property SchemaInfo : TSchemaInfo read FSchemaInfo default stNoSchema;
  229. end;
  230. implementation
  231. uses dbconst, strutils;
  232. { TSQLConnection }
  233. function TSQLConnection.StrToStatementType(s : string) : TStatementType;
  234. var T : TStatementType;
  235. begin
  236. S:=Lowercase(s);
  237. For t:=stselect to strollback do
  238. if (S=StatementTokens[t]) then
  239. Exit(t);
  240. end;
  241. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  242. begin
  243. if FTransaction<>value then
  244. begin
  245. if Assigned(FTransaction) and FTransaction.Active then
  246. DatabaseError(SErrAssTransaction);
  247. if Assigned(Value) then
  248. Value.Database := Self;
  249. FTransaction := Value;
  250. end;
  251. end;
  252. procedure TSQLConnection.UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string);
  253. begin
  254. // Empty abstract
  255. end;
  256. procedure TSQLConnection.DoInternalConnect;
  257. begin
  258. // Empty abstract
  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; Transaction : TSQLTransaction);
  272. var Cursor : TSQLCursor;
  273. begin
  274. if not assigned(Transaction) then
  275. DatabaseError(SErrTransactionnSet);
  276. if not Connected then Open;
  277. if not Transaction.Active then Transaction.StartTransaction;
  278. try
  279. Cursor := AllocateCursorHandle;
  280. SQL := TrimRight(SQL);
  281. if SQL = '' then
  282. DatabaseError(SErrNoStatement);
  283. Cursor.FStatementType := stNone;
  284. PrepareStatement(cursor,Transaction,SQL,Nil);
  285. execute(cursor,Transaction, 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 assigned(FParams) then FParams.Clear;
  430. s := FSQL.Text;
  431. i := posex(':',s);
  432. while i > 0 do
  433. begin
  434. inc(i);
  435. p := @s[i];
  436. repeat
  437. inc(p);
  438. until (p^ in SQLDelimiterCharacters);
  439. if not assigned(FParams) then FParams := TParams.create(self);
  440. ParamName := copy(s,i,p-@s[i]);
  441. if FParams.FindParam(ParamName) = nil then
  442. FParams.CreateParam(ftUnknown, ParamName, ptInput);
  443. i := posex(':',s,i);
  444. end;
  445. end
  446. end;
  447. Procedure TSQLQuery.SetTransaction(Value : TDBTransaction);
  448. begin
  449. UnPrepare;
  450. inherited;
  451. end;
  452. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  453. var db : tsqlconnection;
  454. begin
  455. if (Database <> Value) then
  456. begin
  457. UnPrepare;
  458. if assigned(FCursor) then (Database as TSQLConnection).DeAllocateCursorHandle(FCursor);
  459. db := value as tsqlconnection;
  460. inherited setdatabase(value);
  461. if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
  462. transaction := Db.Transaction;
  463. end;
  464. end;
  465. Function TSQLQuery.IsPrepared : Boolean;
  466. begin
  467. Result := Assigned(FCursor) and FCursor.FPrepared;
  468. end;
  469. Function TSQLQuery.AddFilter(SQLstr : string) : string;
  470. begin
  471. if FWhereStartPos = 0 then
  472. SQLstr := SQLstr + ' where (' + Filter + ')'
  473. else if FWhereStopPos > 0 then
  474. system.insert(' and ('+Filter+') ',SQLstr,FWhereStopPos+1)
  475. else
  476. system.insert(' where ('+Filter+') ',SQLstr,FWhereStartPos);
  477. Result := SQLstr;
  478. end;
  479. procedure TSQLQuery.ApplyFilter;
  480. var S : String;
  481. begin
  482. FreeFldBuffers;
  483. (Database as tsqlconnection).UnPrepareStatement(FCursor);
  484. FIsEOF := False;
  485. inherited internalclose;
  486. s := FSQLBuf;
  487. if Filtered then s := AddFilter(s);
  488. (Database as tsqlconnection).PrepareStatement(Fcursor,(transaction as tsqltransaction),S,FParams);
  489. Execute;
  490. inherited InternalOpen;
  491. First;
  492. end;
  493. procedure TSQLQuery.SetFiltered(Value: Boolean);
  494. begin
  495. if Value and not FParseSQL then DatabaseErrorFmt(SNoParseSQL,['Filtering ']);
  496. if (Filtered <> Value) then
  497. begin
  498. inherited setfiltered(Value);
  499. if active then ApplyFilter;
  500. end;
  501. end;
  502. procedure TSQLQuery.SetFilterText(const Value: string);
  503. begin
  504. if Value <> Filter then
  505. begin
  506. inherited SetFilterText(Value);
  507. if active then ApplyFilter;
  508. end;
  509. end;
  510. procedure TSQLQuery.Prepare;
  511. var
  512. db : tsqlconnection;
  513. sqltr : tsqltransaction;
  514. begin
  515. if not IsPrepared then
  516. begin
  517. db := (Database as tsqlconnection);
  518. sqltr := (transaction as tsqltransaction);
  519. if not assigned(Db) then
  520. DatabaseError(SErrDatabasenAssigned);
  521. if not assigned(sqltr) then
  522. DatabaseError(SErrTransactionnSet);
  523. if not Db.Connected then db.Open;
  524. if not sqltr.Active then sqltr.StartTransaction;
  525. // if assigned(fcursor) then FreeAndNil(fcursor);
  526. if not assigned(fcursor) then
  527. FCursor := Db.AllocateCursorHandle;
  528. FSQLBuf := TrimRight(FSQL.Text);
  529. if FSQLBuf = '' then
  530. DatabaseError(SErrNoStatement);
  531. SQLParser(FSQLBuf);
  532. if filtered then
  533. Db.PrepareStatement(Fcursor,sqltr,AddFilter(FSQLBuf),FParams)
  534. else
  535. Db.PrepareStatement(Fcursor,sqltr,FSQLBuf,FParams);
  536. if (FCursor.FStatementType = stSelect) then
  537. begin
  538. FCursor.FInitFieldDef := True;
  539. if not ReadOnly then InitUpdates(FSQLBuf);
  540. end;
  541. end;
  542. end;
  543. procedure TSQLQuery.UnPrepare;
  544. begin
  545. CheckInactive;
  546. if IsPrepared then with Database as TSQLConnection do
  547. UnPrepareStatement(FCursor);
  548. end;
  549. procedure TSQLQuery.FreeFldBuffers;
  550. begin
  551. if assigned(FCursor) then (Database as tsqlconnection).FreeFldBuffers(FCursor);
  552. end;
  553. function TSQLQuery.Fetch : boolean;
  554. begin
  555. if not (Fcursor.FStatementType in [stSelect]) then
  556. Exit;
  557. if not FIsEof then FIsEOF := not (Database as tsqlconnection).Fetch(Fcursor);
  558. Result := not FIsEOF;
  559. end;
  560. procedure TSQLQuery.Execute;
  561. begin
  562. (Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction, FParams);
  563. end;
  564. function TSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean;
  565. begin
  566. result := (Database as tSQLConnection).LoadField(FCursor,FieldDef,buffer)
  567. end;
  568. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  569. begin
  570. // not implemented - sql dataset
  571. end;
  572. procedure TSQLQuery.InternalClose;
  573. begin
  574. if StatementType = stSelect then FreeFldBuffers;
  575. if not IsPrepared then (database as TSQLconnection).UnPrepareStatement(FCursor);
  576. if DefaultFields then
  577. DestroyFields;
  578. FIsEOF := False;
  579. // FRecordSize := 0;
  580. inherited internalclose;
  581. end;
  582. procedure TSQLQuery.InternalInitFieldDefs;
  583. begin
  584. if FLoadingFieldDefs then
  585. Exit;
  586. FLoadingFieldDefs := True;
  587. try
  588. FieldDefs.Clear;
  589. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  590. finally
  591. FLoadingFieldDefs := False;
  592. end;
  593. end;
  594. procedure TSQLQuery.SQLParser(var SQL : string);
  595. type TParsePart = (ppStart,ppSelect,ppWhere,ppFrom,ppOrder,ppComment,ppBogus);
  596. Var
  597. PSQL,CurrentP,
  598. PhraseP, PStatementPart : pchar;
  599. S : string;
  600. ParsePart : TParsePart;
  601. StrLength : Integer;
  602. begin
  603. PSQL:=Pchar(SQL);
  604. ParsePart := ppStart;
  605. CurrentP := PSQL-1;
  606. PhraseP := PSQL;
  607. FWhereStartPos := 0;
  608. FWhereStopPos := 0;
  609. repeat
  610. begin
  611. inc(CurrentP);
  612. if CurrentP^ in [' ',#13,#10,#9,#0,'(',')',';'] then
  613. begin
  614. if (CurrentP-PhraseP > 0) or (CurrentP^ in [';',#0]) then
  615. begin
  616. strLength := CurrentP-PhraseP;
  617. Setlength(S,strLength);
  618. if strLength > 0 then Move(PhraseP^,S[1],(strLength));
  619. s := uppercase(s);
  620. case ParsePart of
  621. ppStart : begin
  622. FCursor.FStatementType := (Database as tsqlconnection).StrToStatementType(s);
  623. if FCursor.FStatementType = stSelect then ParsePart := ppSelect
  624. else break;
  625. if not FParseSQL then break;
  626. PStatementPart := CurrentP;
  627. end;
  628. ppSelect : begin
  629. if s = 'FROM' then
  630. begin
  631. ParsePart := ppFrom;
  632. PhraseP := CurrentP;
  633. PStatementPart := CurrentP;
  634. end;
  635. end;
  636. ppFrom : begin
  637. if (s = 'WHERE') or (s = 'ORDER') or (CurrentP^=#0) or (CurrentP^=';') then
  638. begin
  639. if (s = 'WHERE') then
  640. begin
  641. ParsePart := ppWhere;
  642. StrLength := PhraseP-PStatementPart;
  643. end
  644. else if (s = 'ORDER') then
  645. begin
  646. ParsePart := ppOrder;
  647. StrLength := PhraseP-PStatementPart
  648. end
  649. else
  650. begin
  651. ParsePart := ppBogus;
  652. StrLength := CurrentP-PStatementPart;
  653. end;
  654. Setlength(FFromPart,StrLength);
  655. Move(PStatementPart^,FFromPart[1],(StrLength));
  656. FFrompart := trim(FFrompart);
  657. FWhereStartPos := PStatementPart-PSQL+StrLength+1;
  658. PStatementPart := CurrentP;
  659. end;
  660. end;
  661. ppWhere : begin
  662. if (s = 'ORDER') or (CurrentP^=#0) or (CurrentP^=';') then
  663. begin
  664. ParsePart := ppBogus;
  665. FWhereStartPos := PStatementPart-PSQL;
  666. if s = 'ORDER' then
  667. FWhereStopPos := PhraseP-PSQL+1
  668. else
  669. FWhereStopPos := CurrentP-PSQL+1;
  670. end;
  671. end;
  672. end; {case}
  673. end;
  674. PhraseP := CurrentP+1;
  675. end
  676. end;
  677. until CurrentP^=#0;
  678. if (FWhereStartPos > 0) and (FWhereStopPos > 0) then
  679. begin
  680. system.insert('(',SQL,FWhereStartPos+1);
  681. inc(FWhereStopPos);
  682. system.insert(')',SQL,FWhereStopPos);
  683. end
  684. end;
  685. procedure TSQLQuery.InitUpdates(SQL : string);
  686. begin
  687. if pos(',',FFromPart) > 0 then
  688. FUpdateable := False // select-statements from more then one table are not updateable
  689. else
  690. begin
  691. FUpdateable := True;
  692. FTableName := FFromPart;
  693. end;
  694. end;
  695. procedure TSQLQuery.InternalOpen;
  696. var tel : integer;
  697. f : TField;
  698. s : string;
  699. begin
  700. try
  701. Prepare;
  702. if FCursor.FStatementType in [stSelect] then
  703. begin
  704. Execute;
  705. if FCursor.FInitFieldDef then InternalInitFieldDefs;
  706. if DefaultFields then
  707. begin
  708. CreateFields;
  709. if FUpdateable and FusePrimaryKeyAsKey then
  710. begin
  711. UpdateIndexDefs;
  712. for tel := 0 to indexdefs.count-1 do {with indexdefs[tel] do}
  713. begin
  714. if ixPrimary in indexdefs[tel].options then
  715. begin
  716. // Todo: If there is more then one field in the key, that must be parsed
  717. s := indexdefs[tel].fields;
  718. F := Findfield(s);
  719. if F <> nil then
  720. F.ProviderFlags := F.ProviderFlags + [pfInKey];
  721. end;
  722. end;
  723. end;
  724. end;
  725. end
  726. else
  727. DatabaseError(SErrNoSelectStatement,Self);
  728. except
  729. on E:Exception do
  730. raise;
  731. end;
  732. inherited InternalOpen;
  733. end;
  734. // public part
  735. procedure TSQLQuery.ExecSQL;
  736. begin
  737. try
  738. Prepare;
  739. Execute;
  740. finally
  741. if not IsPrepared then (database as TSQLConnection).UnPrepareStatement(Fcursor);
  742. end;
  743. end;
  744. constructor TSQLQuery.Create(AOwner : TComponent);
  745. begin
  746. inherited Create(AOwner);
  747. FSQL := TStringList.Create;
  748. FSQL.OnChange := @OnChangeSQL;
  749. FIndexDefs := TIndexDefs.Create(Self);
  750. FReadOnly := false;
  751. FParseSQL := True;
  752. // Delphi has upWhereAll as default, but since strings and oldvalue's don't work yet
  753. // (variants) set it to upWhereKeyOnly
  754. FUpdateMode := upWhereKeyOnly;
  755. FUsePrimaryKeyAsKey := True;
  756. end;
  757. destructor TSQLQuery.Destroy;
  758. begin
  759. if Active then Close;
  760. UnPrepare;
  761. if assigned(FCursor) then (Database as TSQLConnection).DeAllocateCursorHandle(FCursor);
  762. FreeAndNil(FParams);
  763. FreeAndNil(FSQL);
  764. FreeAndNil(FIndexDefs);
  765. inherited Destroy;
  766. end;
  767. procedure TSQLQuery.SetReadOnly(AValue : Boolean);
  768. begin
  769. CheckInactive;
  770. if not AValue then
  771. begin
  772. if FParseSQL then FReadOnly := False
  773. else DatabaseErrorFmt(SNoParseSQL,['Updating ']);
  774. end
  775. else FReadOnly := True;
  776. end;
  777. procedure TSQLQuery.SetParseSQL(AValue : Boolean);
  778. begin
  779. CheckInactive;
  780. if not AValue then
  781. begin
  782. FReadOnly := True;
  783. Filtered := False;
  784. FParseSQL := False;
  785. end
  786. else
  787. FParseSQL := True;
  788. end;
  789. procedure TSQLQuery.SetUsePrimaryKeyAsKey(AValue : Boolean);
  790. begin
  791. if not Active then FusePrimaryKeyAsKey := AValue
  792. else
  793. begin
  794. // Just temporary, this should be possible in the future
  795. DatabaseError(SActiveDataset);
  796. end;
  797. end;
  798. Procedure TSQLQuery.UpdateIndexDefs;
  799. begin
  800. if assigned(DataBase) then
  801. (DataBase as TSQLConnection).UpdateIndexDefs(FIndexDefs,FTableName);
  802. end;
  803. function TSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean;
  804. var
  805. s : string;
  806. procedure UpdateWherePart(var sql_where : string;x : integer);
  807. begin
  808. if (pfInKey in Fields[x].ProviderFlags) or
  809. ((FUpdateMode = upWhereAll) and (pfInWhere in Fields[x].ProviderFlags)) or
  810. ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (fields[x].value <> fields[x].oldvalue)) then
  811. begin
  812. // This should be converted to something like GetAsSQLText, but better wait until variants (oldvalue) are working for strings
  813. s := fields[x].oldvalue; // This directly int the line below raises a variant-error
  814. sql_where := sql_where + '(' + fields[x].FieldName + '=' + s + ') and ';
  815. end;
  816. end;
  817. function ModifyRecQuery : string;
  818. var x : integer;
  819. sql_set : string;
  820. sql_where : string;
  821. begin
  822. sql_set := '';
  823. sql_where := '';
  824. for x := 0 to Fields.Count -1 do
  825. begin
  826. UpdateWherePart(sql_where,x);
  827. if (pfInUpdate in Fields[x].ProviderFlags) then
  828. if fields[x].IsNull then // check for null
  829. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(nil) + ','
  830. else
  831. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  832. end;
  833. setlength(sql_set,length(sql_set)-1);
  834. setlength(sql_where,length(sql_where)-5);
  835. result := 'update ' + FTableName + ' set ' + sql_set + ' where ' + sql_where;
  836. end;
  837. function InsertRecQuery : string;
  838. var x : integer;
  839. sql_fields : string;
  840. sql_values : string;
  841. begin
  842. sql_fields := '';
  843. sql_values := '';
  844. for x := 0 to Fields.Count -1 do
  845. begin
  846. if not fields[x].IsNull then
  847. begin
  848. sql_fields := sql_fields + fields[x].DisplayName + ',';
  849. sql_values := sql_values + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  850. end;
  851. end;
  852. setlength(sql_fields,length(sql_fields)-1);
  853. setlength(sql_values,length(sql_values)-1);
  854. result := 'insert into ' + FTableName + ' (' + sql_fields + ') values (' + sql_values + ')';
  855. end;
  856. function DeleteRecQuery : string;
  857. var x : integer;
  858. sql_where : string;
  859. begin
  860. sql_where := '';
  861. for x := 0 to Fields.Count -1 do
  862. UpdateWherePart(sql_where,x);
  863. setlength(sql_where,length(sql_where)-5);
  864. result := 'delete from ' + FTableName + ' where ' + sql_where;
  865. end;
  866. begin
  867. Result := True;
  868. case UpdateKind of
  869. ukModify : s := ModifyRecQuery;
  870. ukInsert : s := InsertRecQuery;
  871. ukDelete : s := DeleteRecQuery;
  872. end; {case}
  873. try
  874. (Database as TSQLConnection).ExecuteDirect(s,Transaction as TSQLTransaction);
  875. except
  876. on EDatabaseError do Result := False
  877. else
  878. raise;
  879. end;
  880. end;
  881. Function TSQLQuery.GetCanModify: Boolean;
  882. begin
  883. if FCursor.FStatementType = stSelect then
  884. Result:= Active and FUpdateable and (not FReadOnly)
  885. else
  886. Result := False;
  887. end;
  888. function TSQLQuery.GetIndexDefs : TIndexDefs;
  889. begin
  890. Result := FIndexDefs;
  891. end;
  892. procedure TSQLQuery.SetIndexDefs(AValue : TIndexDefs);
  893. begin
  894. FIndexDefs := AValue;
  895. end;
  896. procedure TSQLQuery.SetUpdateMode(AValue : TUpdateMode);
  897. begin
  898. FUpdateMode := AValue;
  899. end;
  900. procedure TSQLQuery.SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string);
  901. begin
  902. ReadOnly := True;
  903. SQL.Clear;
  904. SQL.Add((DataBase as tsqlconnection).GetSchemaInfoSQL(SchemaType, SchemaObjectName, SchemaPattern));
  905. end;
  906. function TSQLQuery.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
  907. begin
  908. result := (DataBase as tsqlconnection).CreateBlobStream(Field, Mode);
  909. end;
  910. function TSQLQuery.GetStatementType : TStatementType;
  911. begin
  912. if assigned(FCursor) then Result := FCursor.FStatementType
  913. else Result := stNone;
  914. end;
  915. end.