sqldb.pp 30 KB

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