sqldb.pp 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  1. {
  2. Copyright (c) 2004 by Joost van der Sluis
  3. SQL database & dataset
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. unit sqldb;
  11. {$mode objfpc}
  12. {$H+}
  13. {$M+} // ### remove this!!!
  14. interface
  15. uses SysUtils, Classes, DB;
  16. type TSchemaType = (stNoSchema, stTables, stSysTables, stProcedures, stColumns, stProcedureParams, stIndexes, stPackages);
  17. TConnOption = (sqSupportParams);
  18. TConnOptions= set of TConnOption;
  19. type
  20. TSQLConnection = class;
  21. TSQLTransaction = class;
  22. TSQLQuery = class;
  23. TStatementType = (stNone, stSelect, stInsert, stUpdate, stDelete,
  24. stDDL, stGetSegment, stPutSegment, stExecProcedure,
  25. stStartTrans, stCommit, stRollback, stSelectForUpd);
  26. TSQLHandle = Class(TObject)
  27. end;
  28. TSQLCursor = Class(TSQLHandle)
  29. public
  30. FPrepared : Boolean;
  31. FInitFieldDef : Boolean;
  32. FStatementType : TStatementType;
  33. end;
  34. const
  35. StatementTokens : Array[TStatementType] of string = ('(none)', 'select',
  36. 'insert', 'update', 'delete',
  37. 'create', 'get', 'put', 'execute',
  38. 'start','commit','rollback', '?'
  39. );
  40. { TSQLConnection }
  41. type
  42. { TSQLConnection }
  43. TSQLConnection = class (TDatabase)
  44. private
  45. FPassword : string;
  46. FTransaction : TSQLTransaction;
  47. FUserName : string;
  48. FHostName : string;
  49. FCharSet : string;
  50. FRole : String;
  51. procedure SetTransaction(Value : TSQLTransaction);
  52. procedure GetDBInfo(const SchemaType : TSchemaType; const SchemaObjectName, ReturnField : string; List: TStrings);
  53. protected
  54. FConnOptions : TConnOptions;
  55. function StrToStatementType(s : string) : TStatementType; virtual;
  56. procedure DoInternalConnect; override;
  57. procedure DoInternalDisconnect; override;
  58. function GetAsSQLText(Field : TField) : string; virtual;
  59. function GetHandle : pointer; virtual; abstract;
  60. Function AllocateCursorHandle : TSQLCursor; virtual; abstract;
  61. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); virtual; abstract;
  62. Function AllocateTransactionHandle : TSQLHandle; virtual; abstract;
  63. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); virtual; abstract;
  64. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); virtual; abstract;
  65. function Fetch(cursor : TSQLCursor) : boolean; virtual; abstract;
  66. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); virtual; abstract;
  67. procedure UnPrepareStatement(cursor : TSQLCursor); virtual; abstract;
  68. procedure FreeFldBuffers(cursor : TSQLCursor); virtual; abstract;
  69. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer) : boolean; virtual; abstract;
  70. function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
  71. function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
  72. function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
  73. function StartdbTransaction(trans : TSQLHandle; aParams : string) : boolean; virtual; abstract;
  74. procedure CommitRetaining(trans : TSQLHandle); virtual; abstract;
  75. procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract;
  76. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); virtual;
  77. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; virtual;
  78. function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; virtual;abstract;
  79. public
  80. property Handle: Pointer read GetHandle;
  81. destructor Destroy; override;
  82. property ConnOptions: TConnOptions read FConnOptions;
  83. procedure ExecuteDirect(SQL : String); overload; virtual;
  84. procedure ExecuteDirect(SQL : String; Transaction : TSQLTransaction); overload; virtual;
  85. procedure GetTableNames(List : TStrings; SystemTables : Boolean = false); virtual;
  86. procedure GetProcedureNames(List : TStrings); virtual;
  87. procedure GetFieldNames(const TableName : string; List : TStrings); virtual;
  88. published
  89. property Password : string read FPassword write FPassword;
  90. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  91. property UserName : string read FUserName write FUserName;
  92. property CharSet : string read FCharSet write FCharSet;
  93. property HostName : string Read FHostName Write FHostName;
  94. property Connected;
  95. Property Role : String read FRole write FRole;
  96. property DatabaseName;
  97. property KeepConnection;
  98. property LoginPrompt;
  99. property Params;
  100. property OnLogin;
  101. end;
  102. { TSQLTransaction }
  103. TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback,
  104. caRollbackRetaining);
  105. TSQLTransaction = class (TDBTransaction)
  106. private
  107. FTrans : TSQLHandle;
  108. FAction : TCommitRollbackAction;
  109. FParams : TStringList;
  110. protected
  111. function GetHandle : Pointer; virtual;
  112. Procedure SetDatabase (Value : TDatabase); override;
  113. public
  114. procedure Commit; virtual;
  115. procedure CommitRetaining; virtual;
  116. procedure Rollback; virtual;
  117. procedure RollbackRetaining; virtual;
  118. procedure StartTransaction; override;
  119. constructor Create(AOwner : TComponent); override;
  120. destructor Destroy; override;
  121. property Handle: Pointer read GetHandle;
  122. procedure EndTransaction; override;
  123. published
  124. property Action : TCommitRollbackAction read FAction write FAction;
  125. property Database;
  126. property Params : TStringList read FParams write FParams;
  127. end;
  128. { TSQLQuery }
  129. TSQLQuery = class (Tbufdataset)
  130. private
  131. FCursor : TSQLCursor;
  132. FUpdateable : boolean;
  133. FTableName : string;
  134. FSQL : TStringList;
  135. FIsEOF : boolean;
  136. FLoadingFieldDefs : boolean;
  137. FIndexDefs : TIndexDefs;
  138. FReadOnly : boolean;
  139. FUpdateMode : TUpdateMode;
  140. FParams : TParams;
  141. FusePrimaryKeyAsKey : Boolean;
  142. FSQLBuf : String;
  143. FFromPart : String;
  144. FWhereStartPos : integer;
  145. FWhereStopPos : integer;
  146. FParseSQL : boolean;
  147. // FSchemaInfo : TSchemaInfo;
  148. procedure FreeFldBuffers;
  149. procedure InitUpdates(SQL : string);
  150. function GetIndexDefs : TIndexDefs;
  151. function GetStatementType : TStatementType;
  152. procedure SetIndexDefs(AValue : TIndexDefs);
  153. procedure SetReadOnly(AValue : Boolean);
  154. procedure SetParseSQL(AValue : Boolean);
  155. procedure SetUsePrimaryKeyAsKey(AValue : Boolean);
  156. procedure SetUpdateMode(AValue : TUpdateMode);
  157. procedure OnChangeSQL(Sender : TObject);
  158. procedure Execute;
  159. Procedure SQLParser(var SQL : string);
  160. procedure ApplyFilter;
  161. Function AddFilter(SQLstr : string) : string;
  162. protected
  163. // abstract & virtual methods of TBufDataset
  164. function Fetch : boolean; override;
  165. function LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean; override;
  166. // abstract & virtual methods of TDataset
  167. procedure DataConvert(Field: TField; Source, Dest: Pointer; ToNative: Boolean); override;
  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. if (DatabaseName = '') then
  259. DatabaseError(SErrNoDatabaseName,self);
  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 not assigned(FParams) then FParams := TParams.create(self);
  431. FParams.ParseSQL(FSQL.Text,True);
  432. end;
  433. end;
  434. Procedure TSQLQuery.SetTransaction(Value : TDBTransaction);
  435. begin
  436. UnPrepare;
  437. inherited;
  438. end;
  439. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  440. var db : tsqlconnection;
  441. begin
  442. if (Database <> Value) then
  443. begin
  444. UnPrepare;
  445. if assigned(FCursor) then (Database as TSQLConnection).DeAllocateCursorHandle(FCursor);
  446. db := value as tsqlconnection;
  447. inherited setdatabase(value);
  448. if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
  449. transaction := Db.Transaction;
  450. end;
  451. end;
  452. Function TSQLQuery.IsPrepared : Boolean;
  453. begin
  454. Result := Assigned(FCursor) and FCursor.FPrepared;
  455. end;
  456. Function TSQLQuery.AddFilter(SQLstr : string) : string;
  457. begin
  458. if FWhereStartPos = 0 then
  459. SQLstr := SQLstr + ' where (' + Filter + ')'
  460. else if FWhereStopPos > 0 then
  461. system.insert(' and ('+Filter+') ',SQLstr,FWhereStopPos+1)
  462. else
  463. system.insert(' where ('+Filter+') ',SQLstr,FWhereStartPos);
  464. Result := SQLstr;
  465. end;
  466. procedure TSQLQuery.ApplyFilter;
  467. var S : String;
  468. begin
  469. FreeFldBuffers;
  470. (Database as tsqlconnection).UnPrepareStatement(FCursor);
  471. FIsEOF := False;
  472. inherited internalclose;
  473. s := FSQLBuf;
  474. if Filtered then s := AddFilter(s);
  475. (Database as tsqlconnection).PrepareStatement(Fcursor,(transaction as tsqltransaction),S,FParams);
  476. Execute;
  477. inherited InternalOpen;
  478. First;
  479. end;
  480. procedure TSQLQuery.SetFiltered(Value: Boolean);
  481. begin
  482. if Value and not FParseSQL then DatabaseErrorFmt(SNoParseSQL,['Filtering ']);
  483. if (Filtered <> Value) then
  484. begin
  485. inherited setfiltered(Value);
  486. if active then ApplyFilter;
  487. end;
  488. end;
  489. procedure TSQLQuery.SetFilterText(const Value: string);
  490. begin
  491. if Value <> Filter then
  492. begin
  493. inherited SetFilterText(Value);
  494. if active then ApplyFilter;
  495. end;
  496. end;
  497. procedure TSQLQuery.Prepare;
  498. var
  499. db : tsqlconnection;
  500. sqltr : tsqltransaction;
  501. begin
  502. if not IsPrepared then
  503. begin
  504. db := (Database as tsqlconnection);
  505. sqltr := (transaction as tsqltransaction);
  506. if not assigned(Db) then
  507. DatabaseError(SErrDatabasenAssigned);
  508. if not assigned(sqltr) then
  509. DatabaseError(SErrTransactionnSet);
  510. if not Db.Connected then db.Open;
  511. if not sqltr.Active then sqltr.StartTransaction;
  512. // if assigned(fcursor) then FreeAndNil(fcursor);
  513. if not assigned(fcursor) then
  514. FCursor := Db.AllocateCursorHandle;
  515. FSQLBuf := TrimRight(FSQL.Text);
  516. if FSQLBuf = '' then
  517. DatabaseError(SErrNoStatement);
  518. SQLParser(FSQLBuf);
  519. if filtered then
  520. Db.PrepareStatement(Fcursor,sqltr,AddFilter(FSQLBuf),FParams)
  521. else
  522. Db.PrepareStatement(Fcursor,sqltr,FSQLBuf,FParams);
  523. if (FCursor.FStatementType = stSelect) then
  524. begin
  525. FCursor.FInitFieldDef := True;
  526. if not ReadOnly then InitUpdates(FSQLBuf);
  527. end;
  528. end;
  529. end;
  530. procedure TSQLQuery.UnPrepare;
  531. begin
  532. CheckInactive;
  533. if IsPrepared then with Database as TSQLConnection do
  534. UnPrepareStatement(FCursor);
  535. end;
  536. procedure TSQLQuery.FreeFldBuffers;
  537. begin
  538. if assigned(FCursor) then (Database as tsqlconnection).FreeFldBuffers(FCursor);
  539. end;
  540. function TSQLQuery.Fetch : boolean;
  541. begin
  542. if not (Fcursor.FStatementType in [stSelect]) then
  543. Exit;
  544. if not FIsEof then FIsEOF := not (Database as tsqlconnection).Fetch(Fcursor);
  545. Result := not FIsEOF;
  546. end;
  547. procedure TSQLQuery.Execute;
  548. begin
  549. (Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction, FParams);
  550. end;
  551. function TSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean;
  552. begin
  553. result := (Database as tSQLConnection).LoadField(FCursor,FieldDef,buffer)
  554. end;
  555. procedure TSQLQuery.DataConvert(Field: TField; Source, Dest: Pointer; ToNative: Boolean);
  556. begin
  557. {
  558. all data is in native format for these types, so no conversion is needed.
  559. }
  560. If not (Field.DataType in [ftDate,ftTime,ftDateTime]) then
  561. Inherited DataConvert(Field,Source,Dest,ToNative);
  562. end;
  563. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  564. begin
  565. // not implemented - sql dataset
  566. end;
  567. procedure TSQLQuery.InternalClose;
  568. begin
  569. if StatementType = stSelect then FreeFldBuffers;
  570. if not IsPrepared then (database as TSQLconnection).UnPrepareStatement(FCursor);
  571. if DefaultFields then
  572. DestroyFields;
  573. FIsEOF := False;
  574. // FRecordSize := 0;
  575. inherited internalclose;
  576. end;
  577. procedure TSQLQuery.InternalInitFieldDefs;
  578. begin
  579. if FLoadingFieldDefs then
  580. Exit;
  581. FLoadingFieldDefs := True;
  582. try
  583. FieldDefs.Clear;
  584. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  585. finally
  586. FLoadingFieldDefs := False;
  587. end;
  588. end;
  589. procedure TSQLQuery.SQLParser(var SQL : string);
  590. type TParsePart = (ppStart,ppSelect,ppWhere,ppFrom,ppOrder,ppComment,ppBogus);
  591. Var
  592. PSQL,CurrentP,
  593. PhraseP, PStatementPart : pchar;
  594. S : string;
  595. ParsePart : TParsePart;
  596. StrLength : Integer;
  597. begin
  598. PSQL:=Pchar(SQL);
  599. ParsePart := ppStart;
  600. CurrentP := PSQL-1;
  601. PhraseP := PSQL;
  602. FWhereStartPos := 0;
  603. FWhereStopPos := 0;
  604. repeat
  605. begin
  606. inc(CurrentP);
  607. if CurrentP^ in [' ',#13,#10,#9,#0,'(',')',';'] then
  608. begin
  609. if (CurrentP-PhraseP > 0) or (CurrentP^ in [';',#0]) then
  610. begin
  611. strLength := CurrentP-PhraseP;
  612. Setlength(S,strLength);
  613. if strLength > 0 then Move(PhraseP^,S[1],(strLength));
  614. s := uppercase(s);
  615. case ParsePart of
  616. ppStart : begin
  617. FCursor.FStatementType := (Database as tsqlconnection).StrToStatementType(s);
  618. if FCursor.FStatementType = stSelect then ParsePart := ppSelect
  619. else break;
  620. if not FParseSQL then break;
  621. PStatementPart := CurrentP;
  622. end;
  623. ppSelect : begin
  624. if s = 'FROM' then
  625. begin
  626. ParsePart := ppFrom;
  627. PhraseP := CurrentP;
  628. PStatementPart := CurrentP;
  629. end;
  630. end;
  631. ppFrom : begin
  632. if (s = 'WHERE') or (s = 'ORDER') or (CurrentP^=#0) or (CurrentP^=';') then
  633. begin
  634. if (s = 'WHERE') then
  635. begin
  636. ParsePart := ppWhere;
  637. StrLength := PhraseP-PStatementPart;
  638. end
  639. else if (s = 'ORDER') then
  640. begin
  641. ParsePart := ppOrder;
  642. StrLength := PhraseP-PStatementPart
  643. end
  644. else
  645. begin
  646. ParsePart := ppBogus;
  647. StrLength := CurrentP-PStatementPart;
  648. end;
  649. Setlength(FFromPart,StrLength);
  650. Move(PStatementPart^,FFromPart[1],(StrLength));
  651. FFrompart := trim(FFrompart);
  652. FWhereStartPos := PStatementPart-PSQL+StrLength+1;
  653. PStatementPart := CurrentP;
  654. end;
  655. end;
  656. ppWhere : begin
  657. if (s = 'ORDER') or (CurrentP^=#0) or (CurrentP^=';') then
  658. begin
  659. ParsePart := ppBogus;
  660. FWhereStartPos := PStatementPart-PSQL;
  661. if s = 'ORDER' then
  662. FWhereStopPos := PhraseP-PSQL+1
  663. else
  664. FWhereStopPos := CurrentP-PSQL+1;
  665. end;
  666. end;
  667. end; {case}
  668. end;
  669. PhraseP := CurrentP+1;
  670. end
  671. end;
  672. until CurrentP^=#0;
  673. if (FWhereStartPos > 0) and (FWhereStopPos > 0) then
  674. begin
  675. system.insert('(',SQL,FWhereStartPos+1);
  676. inc(FWhereStopPos);
  677. system.insert(')',SQL,FWhereStopPos);
  678. end
  679. end;
  680. procedure TSQLQuery.InitUpdates(SQL : string);
  681. begin
  682. if pos(',',FFromPart) > 0 then
  683. FUpdateable := False // select-statements from more then one table are not updateable
  684. else
  685. begin
  686. FUpdateable := True;
  687. FTableName := FFromPart;
  688. end;
  689. end;
  690. procedure TSQLQuery.InternalOpen;
  691. var tel : integer;
  692. f : TField;
  693. s : string;
  694. begin
  695. try
  696. Prepare;
  697. if FCursor.FStatementType in [stSelect] then
  698. begin
  699. Execute;
  700. if FCursor.FInitFieldDef then InternalInitFieldDefs;
  701. if DefaultFields then
  702. begin
  703. CreateFields;
  704. if FUpdateable and FusePrimaryKeyAsKey then
  705. begin
  706. UpdateIndexDefs;
  707. for tel := 0 to indexdefs.count-1 do {with indexdefs[tel] do}
  708. begin
  709. if ixPrimary in indexdefs[tel].options then
  710. begin
  711. // Todo: If there is more then one field in the key, that must be parsed
  712. s := indexdefs[tel].fields;
  713. F := Findfield(s);
  714. if F <> nil then
  715. F.ProviderFlags := F.ProviderFlags + [pfInKey];
  716. end;
  717. end;
  718. end;
  719. end;
  720. end
  721. else
  722. DatabaseError(SErrNoSelectStatement,Self);
  723. except
  724. on E:Exception do
  725. raise;
  726. end;
  727. inherited InternalOpen;
  728. end;
  729. // public part
  730. procedure TSQLQuery.ExecSQL;
  731. begin
  732. try
  733. Prepare;
  734. Execute;
  735. finally
  736. if not IsPrepared then (database as TSQLConnection).UnPrepareStatement(Fcursor);
  737. end;
  738. end;
  739. constructor TSQLQuery.Create(AOwner : TComponent);
  740. begin
  741. inherited Create(AOwner);
  742. FSQL := TStringList.Create;
  743. FSQL.OnChange := @OnChangeSQL;
  744. FIndexDefs := TIndexDefs.Create(Self);
  745. FReadOnly := false;
  746. FParseSQL := True;
  747. // Delphi has upWhereAll as default, but since strings and oldvalue's don't work yet
  748. // (variants) set it to upWhereKeyOnly
  749. FUpdateMode := upWhereKeyOnly;
  750. FUsePrimaryKeyAsKey := True;
  751. end;
  752. destructor TSQLQuery.Destroy;
  753. begin
  754. if Active then Close;
  755. UnPrepare;
  756. if assigned(FCursor) then (Database as TSQLConnection).DeAllocateCursorHandle(FCursor);
  757. FreeAndNil(FParams);
  758. FreeAndNil(FSQL);
  759. FreeAndNil(FIndexDefs);
  760. inherited Destroy;
  761. end;
  762. procedure TSQLQuery.SetReadOnly(AValue : Boolean);
  763. begin
  764. CheckInactive;
  765. if not AValue then
  766. begin
  767. if FParseSQL then FReadOnly := False
  768. else DatabaseErrorFmt(SNoParseSQL,['Updating ']);
  769. end
  770. else FReadOnly := True;
  771. end;
  772. procedure TSQLQuery.SetParseSQL(AValue : Boolean);
  773. begin
  774. CheckInactive;
  775. if not AValue then
  776. begin
  777. FReadOnly := True;
  778. Filtered := False;
  779. FParseSQL := False;
  780. end
  781. else
  782. FParseSQL := True;
  783. end;
  784. procedure TSQLQuery.SetUsePrimaryKeyAsKey(AValue : Boolean);
  785. begin
  786. if not Active then FusePrimaryKeyAsKey := AValue
  787. else
  788. begin
  789. // Just temporary, this should be possible in the future
  790. DatabaseError(SActiveDataset);
  791. end;
  792. end;
  793. Procedure TSQLQuery.UpdateIndexDefs;
  794. begin
  795. if assigned(DataBase) then
  796. (DataBase as TSQLConnection).UpdateIndexDefs(FIndexDefs,FTableName);
  797. end;
  798. function TSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind) : boolean;
  799. var
  800. s : string;
  801. procedure UpdateWherePart(var sql_where : string;x : integer);
  802. begin
  803. if (pfInKey in Fields[x].ProviderFlags) or
  804. ((FUpdateMode = upWhereAll) and (pfInWhere in Fields[x].ProviderFlags)) or
  805. ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (fields[x].value <> fields[x].oldvalue)) then
  806. begin
  807. // This should be converted to something like GetAsSQLText, but better wait until variants (oldvalue) are working for strings
  808. s := fields[x].oldvalue; // This directly int the line below raises a variant-error
  809. sql_where := sql_where + '(' + fields[x].FieldName + '=' + s + ') and ';
  810. end;
  811. end;
  812. function ModifyRecQuery : string;
  813. var x : integer;
  814. sql_set : string;
  815. sql_where : string;
  816. begin
  817. sql_set := '';
  818. sql_where := '';
  819. for x := 0 to Fields.Count -1 do
  820. begin
  821. UpdateWherePart(sql_where,x);
  822. if (pfInUpdate in Fields[x].ProviderFlags) then
  823. if fields[x].IsNull then // check for null
  824. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(nil) + ','
  825. else
  826. sql_set := sql_set + fields[x].FieldName + '=' + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  827. end;
  828. setlength(sql_set,length(sql_set)-1);
  829. setlength(sql_where,length(sql_where)-5);
  830. result := 'update ' + FTableName + ' set ' + sql_set + ' where ' + sql_where;
  831. end;
  832. function InsertRecQuery : string;
  833. var x : integer;
  834. sql_fields : string;
  835. sql_values : string;
  836. begin
  837. sql_fields := '';
  838. sql_values := '';
  839. for x := 0 to Fields.Count -1 do
  840. begin
  841. if not fields[x].IsNull then
  842. begin
  843. sql_fields := sql_fields + fields[x].DisplayName + ',';
  844. sql_values := sql_values + (Database as TSQLConnection).GetAsSQLText(fields[x]) + ',';
  845. end;
  846. end;
  847. setlength(sql_fields,length(sql_fields)-1);
  848. setlength(sql_values,length(sql_values)-1);
  849. result := 'insert into ' + FTableName + ' (' + sql_fields + ') values (' + sql_values + ')';
  850. end;
  851. function DeleteRecQuery : string;
  852. var x : integer;
  853. sql_where : string;
  854. begin
  855. sql_where := '';
  856. for x := 0 to Fields.Count -1 do
  857. UpdateWherePart(sql_where,x);
  858. setlength(sql_where,length(sql_where)-5);
  859. result := 'delete from ' + FTableName + ' where ' + sql_where;
  860. end;
  861. begin
  862. Result := True;
  863. case UpdateKind of
  864. ukModify : s := ModifyRecQuery;
  865. ukInsert : s := InsertRecQuery;
  866. ukDelete : s := DeleteRecQuery;
  867. end; {case}
  868. try
  869. (Database as TSQLConnection).ExecuteDirect(s,Transaction as TSQLTransaction);
  870. except
  871. on EDatabaseError do Result := False
  872. else
  873. raise;
  874. end;
  875. end;
  876. Function TSQLQuery.GetCanModify: Boolean;
  877. begin
  878. if FCursor.FStatementType = stSelect then
  879. Result:= Active and FUpdateable and (not FReadOnly)
  880. else
  881. Result := False;
  882. end;
  883. function TSQLQuery.GetIndexDefs : TIndexDefs;
  884. begin
  885. Result := FIndexDefs;
  886. end;
  887. procedure TSQLQuery.SetIndexDefs(AValue : TIndexDefs);
  888. begin
  889. FIndexDefs := AValue;
  890. end;
  891. procedure TSQLQuery.SetUpdateMode(AValue : TUpdateMode);
  892. begin
  893. FUpdateMode := AValue;
  894. end;
  895. procedure TSQLQuery.SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string);
  896. begin
  897. ReadOnly := True;
  898. SQL.Clear;
  899. SQL.Add((DataBase as tsqlconnection).GetSchemaInfoSQL(SchemaType, SchemaObjectName, SchemaPattern));
  900. end;
  901. function TSQLQuery.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
  902. begin
  903. result := (DataBase as tsqlconnection).CreateBlobStream(Field, Mode);
  904. end;
  905. function TSQLQuery.GetStatementType : TStatementType;
  906. begin
  907. if assigned(FCursor) then Result := FCursor.FStatementType
  908. else Result := stNone;
  909. end;
  910. end.