sqldb.pp 28 KB

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