sqldb.pp 30 KB

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