sqldb.pp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  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 }
  29. TSQLCursor = Class(TSQLHandle)
  30. public
  31. FPrepared : Boolean;
  32. FInitFieldDef : Boolean;
  33. FStatementType : TStatementType;
  34. FBlobStrings : TStringList; // list of strings in which the blob-fields are stored
  35. public
  36. constructor Create;
  37. destructor Destroy; override;
  38. end;
  39. const
  40. StatementTokens : Array[TStatementType] of string = ('(none)', 'select',
  41. 'insert', 'update', 'delete',
  42. 'create', 'get', 'put', 'execute',
  43. 'start','commit','rollback', '?'
  44. );
  45. { TSQLConnection }
  46. type
  47. { TSQLConnection }
  48. TSQLConnection = class (TDatabase)
  49. private
  50. FPassword : string;
  51. FTransaction : TSQLTransaction;
  52. FUserName : string;
  53. FHostName : string;
  54. FCharSet : string;
  55. FRole : String;
  56. procedure SetTransaction(Value : TSQLTransaction);
  57. procedure GetDBInfo(const SchemaType : TSchemaType; const SchemaObjectName, ReturnField : string; List: TStrings);
  58. protected
  59. FConnOptions : TConnOptions;
  60. function StrToStatementType(s : string) : TStatementType; virtual;
  61. procedure DoInternalConnect; override;
  62. procedure DoInternalDisconnect; override;
  63. function GetAsSQLText(Field : TField) : string; overload; virtual;
  64. function GetAsSQLText(Param : TParam) : string; overload; virtual;
  65. function GetHandle : pointer; virtual; virtual;
  66. Function AllocateCursorHandle : TSQLCursor; virtual; abstract;
  67. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); virtual; abstract;
  68. Function AllocateTransactionHandle : TSQLHandle; virtual; abstract;
  69. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); virtual; abstract;
  70. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction; AParams : TParams); virtual; abstract;
  71. function Fetch(cursor : TSQLCursor) : boolean; virtual; abstract;
  72. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); virtual; abstract;
  73. procedure UnPrepareStatement(cursor : TSQLCursor); virtual; abstract;
  74. procedure FreeFldBuffers(cursor : TSQLCursor); virtual;
  75. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer) : boolean; virtual; abstract;
  76. function GetTransactionHandle(trans : TSQLHandle): pointer; virtual; abstract;
  77. function Commit(trans : TSQLHandle) : boolean; virtual; abstract;
  78. function RollBack(trans : TSQLHandle) : boolean; virtual; abstract;
  79. function StartdbTransaction(trans : TSQLHandle; aParams : string) : boolean; virtual; abstract;
  80. procedure CommitRetaining(trans : TSQLHandle); virtual; abstract;
  81. procedure RollBackRetaining(trans : TSQLHandle); virtual; abstract;
  82. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); virtual;
  83. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; virtual;
  84. procedure LoadBlobIntoStream(Field: TField;AStream: TMemoryStream;cursor: TSQLCursor;ATransaction : TSQLTransaction); virtual;
  85. public
  86. property Handle: Pointer read GetHandle;
  87. destructor Destroy; override;
  88. procedure StartTransaction; override;
  89. procedure EndTransaction; override;
  90. property ConnOptions: TConnOptions read FConnOptions;
  91. procedure ExecuteDirect(SQL : String); overload; virtual;
  92. procedure ExecuteDirect(SQL : String; ATransaction : TSQLTransaction); overload; virtual;
  93. procedure GetTableNames(List : TStrings; SystemTables : Boolean = false); virtual;
  94. procedure GetProcedureNames(List : TStrings); virtual;
  95. procedure GetFieldNames(const TableName : string; List : TStrings); virtual;
  96. published
  97. property Password : string read FPassword write FPassword;
  98. property Transaction : TSQLTransaction read FTransaction write SetTransaction;
  99. property UserName : string read FUserName write FUserName;
  100. property CharSet : string read FCharSet write FCharSet;
  101. property HostName : string Read FHostName Write FHostName;
  102. property Connected;
  103. Property Role : String read FRole write FRole;
  104. property DatabaseName;
  105. property KeepConnection;
  106. property LoginPrompt;
  107. property Params;
  108. property OnLogin;
  109. end;
  110. { TSQLTransaction }
  111. TCommitRollbackAction = (caNone, caCommit, caCommitRetaining, caRollback,
  112. caRollbackRetaining);
  113. TSQLTransaction = class (TDBTransaction)
  114. private
  115. FTrans : TSQLHandle;
  116. FAction : TCommitRollbackAction;
  117. FParams : TStringList;
  118. protected
  119. function GetHandle : Pointer; virtual;
  120. Procedure SetDatabase (Value : TDatabase); override;
  121. public
  122. procedure Commit; virtual;
  123. procedure CommitRetaining; virtual;
  124. procedure Rollback; virtual;
  125. procedure RollbackRetaining; virtual;
  126. procedure StartTransaction; override;
  127. constructor Create(AOwner : TComponent); override;
  128. destructor Destroy; override;
  129. property Handle: Pointer read GetHandle;
  130. procedure EndTransaction; override;
  131. published
  132. property Action : TCommitRollbackAction read FAction write FAction;
  133. property Database;
  134. property Params : TStringList read FParams write FParams;
  135. end;
  136. { TSQLQuery }
  137. TSQLQuery = class (Tbufdataset)
  138. private
  139. FCursor : TSQLCursor;
  140. FUpdateable : boolean;
  141. FTableName : string;
  142. FSQL : TStringList;
  143. FUpdateSQL,
  144. FInsertSQL,
  145. FDeleteSQL : TStringList;
  146. FIsEOF : boolean;
  147. FLoadingFieldDefs : boolean;
  148. FIndexDefs : TIndexDefs;
  149. FReadOnly : boolean;
  150. FUpdateMode : TUpdateMode;
  151. FParams : TParams;
  152. FusePrimaryKeyAsKey : Boolean;
  153. FSQLBuf : String;
  154. FFromPart : String;
  155. FWhereStartPos : integer;
  156. FWhereStopPos : integer;
  157. FParseSQL : boolean;
  158. FMasterLink : TMasterParamsDatalink;
  159. // FSchemaInfo : TSchemaInfo;
  160. FUpdateQry,
  161. FDeleteQry,
  162. FInsertQry : TSQLQuery;
  163. procedure FreeFldBuffers;
  164. procedure InitUpdates(ASQL : string);
  165. function GetIndexDefs : TIndexDefs;
  166. function GetStatementType : TStatementType;
  167. procedure SetIndexDefs(AValue : TIndexDefs);
  168. procedure SetReadOnly(AValue : Boolean);
  169. procedure SetParseSQL(AValue : Boolean);
  170. procedure SetUsePrimaryKeyAsKey(AValue : Boolean);
  171. procedure SetUpdateMode(AValue : TUpdateMode);
  172. procedure OnChangeSQL(Sender : TObject);
  173. procedure OnChangeModifySQL(Sender : TObject);
  174. procedure Execute;
  175. Procedure SQLParser(var ASQL : string);
  176. procedure ApplyFilter;
  177. Function AddFilter(SQLstr : string) : string;
  178. protected
  179. // abstract & virtual methods of TBufDataset
  180. function Fetch : boolean; override;
  181. function LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean; override;
  182. // abstract & virtual methods of TDataset
  183. procedure UpdateIndexDefs; override;
  184. procedure SetDatabase(Value : TDatabase); override;
  185. Procedure SetTransaction(Value : TDBTransaction); override;
  186. procedure InternalAddRecord(Buffer: Pointer; AAppend: Boolean); override;
  187. procedure InternalClose; override;
  188. procedure InternalInitFieldDefs; override;
  189. procedure InternalOpen; override;
  190. function GetCanModify: Boolean; override;
  191. procedure ApplyRecUpdate(UpdateKind : TUpdateKind); override;
  192. Function IsPrepared : Boolean; virtual;
  193. Procedure SetActive (Value : Boolean); override;
  194. procedure SetFiltered(Value: Boolean); override;
  195. procedure SetFilterText(const Value: string); override;
  196. Function GetDataSource : TDatasource; override;
  197. Procedure SetDataSource(AValue : TDatasource);
  198. procedure LoadBlobIntoStream(Field: TField;AStream: TMemoryStream); override;
  199. public
  200. procedure Prepare; virtual;
  201. procedure UnPrepare; virtual;
  202. procedure ExecSQL; virtual;
  203. constructor Create(AOwner : TComponent); override;
  204. destructor Destroy; override;
  205. procedure SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string); virtual;
  206. property Prepared : boolean read IsPrepared;
  207. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  208. published
  209. // redeclared data set properties
  210. property Active;
  211. property Filter;
  212. property Filtered;
  213. // property FilterOptions;
  214. property BeforeOpen;
  215. property AfterOpen;
  216. property BeforeClose;
  217. property AfterClose;
  218. property BeforeInsert;
  219. property AfterInsert;
  220. property BeforeEdit;
  221. property AfterEdit;
  222. property BeforePost;
  223. property AfterPost;
  224. property BeforeCancel;
  225. property AfterCancel;
  226. property BeforeDelete;
  227. property AfterDelete;
  228. property BeforeScroll;
  229. property AfterScroll;
  230. property OnCalcFields;
  231. property OnDeleteError;
  232. property OnEditError;
  233. property OnFilterRecord;
  234. property OnNewRecord;
  235. property OnPostError;
  236. property AutoCalcFields;
  237. property Database;
  238. property Transaction;
  239. property ReadOnly : Boolean read FReadOnly write SetReadOnly;
  240. property SQL : TStringlist read FSQL write FSQL;
  241. property UpdateSQL : TStringlist read FUpdateSQL write FUpdateSQL;
  242. property InsertSQL : TStringlist read FInsertSQL write FInsertSQL;
  243. property DeleteSQL : TStringlist read FDeleteSQL write FDeleteSQL;
  244. property IndexDefs : TIndexDefs read GetIndexDefs;
  245. property Params : TParams read FParams write FParams;
  246. property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode;
  247. property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey;
  248. property StatementType : TStatementType read GetStatementType;
  249. property ParseSQL : Boolean read FParseSQL write SetParseSQL;
  250. Property DataSource : TDatasource Read GetDataSource Write SetDatasource;
  251. // property SchemaInfo : TSchemaInfo read FSchemaInfo default stNoSchema;
  252. end;
  253. implementation
  254. uses dbconst, strutils;
  255. { TSQLConnection }
  256. function TSQLConnection.StrToStatementType(s : string) : TStatementType;
  257. var T : TStatementType;
  258. begin
  259. S:=Lowercase(s);
  260. For t:=stselect to strollback do
  261. if (S=StatementTokens[t]) then
  262. Exit(t);
  263. end;
  264. procedure TSQLConnection.SetTransaction(Value : TSQLTransaction);
  265. begin
  266. if FTransaction<>value then
  267. begin
  268. if Assigned(FTransaction) and FTransaction.Active then
  269. DatabaseError(SErrAssTransaction);
  270. if Assigned(Value) then
  271. Value.Database := Self;
  272. FTransaction := Value;
  273. end;
  274. end;
  275. procedure TSQLConnection.UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string);
  276. begin
  277. // Empty abstract
  278. end;
  279. procedure TSQLConnection.DoInternalConnect;
  280. begin
  281. if (DatabaseName = '') then
  282. DatabaseError(SErrNoDatabaseName,self);
  283. end;
  284. procedure TSQLConnection.DoInternalDisconnect;
  285. begin
  286. end;
  287. destructor TSQLConnection.Destroy;
  288. begin
  289. inherited Destroy;
  290. end;
  291. procedure TSQLConnection.StartTransaction;
  292. begin
  293. if not assigned(Transaction) then
  294. DatabaseError(SErrConnTransactionnSet)
  295. else
  296. Transaction.StartTransaction;
  297. end;
  298. procedure TSQLConnection.EndTransaction;
  299. begin
  300. if not assigned(Transaction) then
  301. DatabaseError(SErrConnTransactionnSet)
  302. else
  303. Transaction.EndTransaction;
  304. end;
  305. Procedure TSQLConnection.ExecuteDirect(SQL: String);
  306. begin
  307. ExecuteDirect(SQL,FTransaction);
  308. end;
  309. Procedure TSQLConnection.ExecuteDirect(SQL: String; ATransaction : TSQLTransaction);
  310. var Cursor : TSQLCursor;
  311. begin
  312. if not assigned(ATransaction) then
  313. DatabaseError(SErrTransactionnSet);
  314. if not Connected then Open;
  315. if not ATransaction.Active then ATransaction.StartTransaction;
  316. try
  317. Cursor := AllocateCursorHandle;
  318. SQL := TrimRight(SQL);
  319. if SQL = '' then
  320. DatabaseError(SErrNoStatement);
  321. Cursor.FStatementType := stNone;
  322. PrepareStatement(cursor,ATransaction,SQL,Nil);
  323. execute(cursor,ATransaction, Nil);
  324. UnPrepareStatement(Cursor);
  325. finally;
  326. DeAllocateCursorHandle(Cursor);
  327. end;
  328. end;
  329. procedure TSQLConnection.GetDBInfo(const SchemaType : TSchemaType; const SchemaObjectName, ReturnField : string; List: TStrings);
  330. var qry : TSQLQuery;
  331. begin
  332. if not assigned(Transaction) then
  333. DatabaseError(SErrConnTransactionnSet);
  334. qry := tsqlquery.Create(nil);
  335. qry.transaction := Transaction;
  336. qry.database := Self;
  337. with qry do
  338. begin
  339. ParseSQL := False;
  340. SetSchemaInfo(SchemaType,SchemaObjectName,'');
  341. open;
  342. List.Clear;
  343. while not eof do
  344. begin
  345. List.Append(fieldbyname(ReturnField).asstring);
  346. Next;
  347. end;
  348. end;
  349. qry.free;
  350. end;
  351. procedure TSQLConnection.GetTableNames(List: TStrings; SystemTables: Boolean);
  352. begin
  353. if not systemtables then GetDBInfo(stTables,'','table_name',List)
  354. else GetDBInfo(stSysTables,'','table_name',List);
  355. end;
  356. procedure TSQLConnection.GetProcedureNames(List: TStrings);
  357. begin
  358. GetDBInfo(stProcedures,'','proc_name',List);
  359. end;
  360. procedure TSQLConnection.GetFieldNames(const TableName: string; List: TStrings);
  361. begin
  362. GetDBInfo(stColumns,TableName,'column_name',List);
  363. end;
  364. function TSQLConnection.GetAsSQLText(Field : TField) : string;
  365. begin
  366. if (not assigned(field)) or field.IsNull then Result := 'Null'
  367. else case field.DataType of
  368. ftString : Result := '''' + field.asstring + '''';
  369. ftDate : Result := '''' + FormatDateTime('yyyy-mm-dd',Field.AsDateTime) + '''';
  370. ftDateTime : Result := '''' + FormatDateTime('yyyy-mm-dd hh:mm:ss',Field.AsDateTime) + ''''
  371. else
  372. Result := field.asstring;
  373. end; {case}
  374. end;
  375. function TSQLConnection.GetAsSQLText(Param: TParam) : string;
  376. begin
  377. if (not assigned(param)) or param.IsNull then Result := 'Null'
  378. else case param.DataType of
  379. ftString : Result := '''' + param.asstring + '''';
  380. ftDate : Result := '''' + FormatDateTime('yyyy-mm-dd',Param.AsDateTime) + '''';
  381. ftDateTime : Result := '''' + FormatDateTime('yyyy-mm-dd hh:mm:ss',Param.AsDateTime) + ''''
  382. else
  383. Result := Param.asstring;
  384. end; {case}
  385. end;
  386. function TSQLConnection.GetHandle: pointer;
  387. begin
  388. Result := nil;
  389. end;
  390. procedure TSQLConnection.FreeFldBuffers(cursor: TSQLCursor);
  391. begin
  392. cursor.FBlobStrings.Clear;
  393. end;
  394. function TSQLConnection.GetSchemaInfoSQL( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string;
  395. begin
  396. DatabaseError(SMetadataUnavailable);
  397. end;
  398. procedure TSQLConnection.LoadBlobIntoStream(Field: TField;AStream: TMemoryStream; cursor: TSQLCursor;ATransaction : TSQLTransaction);
  399. var blobId : pinteger;
  400. BlobBuf : TBufBlobField;
  401. s : string;
  402. begin
  403. if not field.getData(@BlobBuf) then
  404. exit;
  405. blobId := @BlobBuf.BufBlobId;
  406. s := cursor.FBlobStrings.Strings[blobid^];
  407. AStream.WriteBuffer(s[1],length(s));
  408. AStream.seek(0,soFromBeginning);
  409. end;
  410. { TSQLTransaction }
  411. procedure TSQLTransaction.EndTransaction;
  412. begin
  413. rollback;
  414. end;
  415. function TSQLTransaction.GetHandle: pointer;
  416. begin
  417. Result := (Database as tsqlconnection).GetTransactionHandle(FTrans);
  418. end;
  419. procedure TSQLTransaction.Commit;
  420. begin
  421. if active then
  422. begin
  423. closedatasets;
  424. if (Database as tsqlconnection).commit(FTrans) then
  425. begin
  426. closeTrans;
  427. FreeAndNil(FTrans);
  428. end;
  429. end;
  430. end;
  431. procedure TSQLTransaction.CommitRetaining;
  432. begin
  433. if active then
  434. (Database as tsqlconnection).commitRetaining(FTrans);
  435. end;
  436. procedure TSQLTransaction.Rollback;
  437. begin
  438. if active then
  439. begin
  440. closedatasets;
  441. if (Database as tsqlconnection).RollBack(FTrans) then
  442. begin
  443. CloseTrans;
  444. FreeAndNil(FTrans);
  445. end;
  446. end;
  447. end;
  448. procedure TSQLTransaction.RollbackRetaining;
  449. begin
  450. if active then
  451. (Database as tsqlconnection).RollBackRetaining(FTrans);
  452. end;
  453. procedure TSQLTransaction.StartTransaction;
  454. var db : TSQLConnection;
  455. begin
  456. if Active then
  457. DatabaseError(SErrTransAlreadyActive);
  458. db := (Database as tsqlconnection);
  459. if Db = nil then
  460. DatabaseError(SErrDatabasenAssigned);
  461. if not Db.Connected then
  462. Db.Open;
  463. if not assigned(FTrans) then FTrans := Db.AllocateTransactionHandle;
  464. if Db.StartdbTransaction(FTrans,FParams.CommaText) then OpenTrans;
  465. end;
  466. constructor TSQLTransaction.Create(AOwner : TComponent);
  467. begin
  468. inherited Create(AOwner);
  469. FParams := TStringList.Create;
  470. end;
  471. destructor TSQLTransaction.Destroy;
  472. begin
  473. Rollback;
  474. FreeAndNil(FParams);
  475. inherited Destroy;
  476. end;
  477. Procedure TSQLTransaction.SetDatabase(Value : TDatabase);
  478. begin
  479. If Value<>Database then
  480. begin
  481. CheckInactive;
  482. If Assigned(Database) then
  483. with Database as TSqlConnection do
  484. if Transaction = self then Transaction := nil;
  485. inherited SetDatabase(Value);
  486. end;
  487. end;
  488. { TSQLQuery }
  489. procedure TSQLQuery.OnChangeSQL(Sender : TObject);
  490. begin
  491. UnPrepare;
  492. if (FSQL <> nil) then
  493. begin
  494. FParams.ParseSQL(FSQL.Text,True);
  495. If Assigned(FMasterLink) then
  496. FMasterLink.RefreshParamNames;
  497. end;
  498. end;
  499. procedure TSQLQuery.OnChangeModifySQL(Sender : TObject);
  500. begin
  501. CheckInactive;
  502. end;
  503. Procedure TSQLQuery.SetTransaction(Value : TDBTransaction);
  504. begin
  505. UnPrepare;
  506. inherited;
  507. end;
  508. procedure TSQLQuery.SetDatabase(Value : TDatabase);
  509. var db : tsqlconnection;
  510. begin
  511. if (Database <> Value) then
  512. begin
  513. UnPrepare;
  514. if assigned(FCursor) then (Database as TSQLConnection).DeAllocateCursorHandle(FCursor);
  515. db := value as tsqlconnection;
  516. inherited setdatabase(value);
  517. if assigned(value) and (Transaction = nil) and (Assigned(db.Transaction)) then
  518. transaction := Db.Transaction;
  519. end;
  520. end;
  521. Function TSQLQuery.IsPrepared : Boolean;
  522. begin
  523. Result := Assigned(FCursor) and FCursor.FPrepared;
  524. end;
  525. Function TSQLQuery.AddFilter(SQLstr : string) : string;
  526. begin
  527. if FWhereStartPos = 0 then
  528. SQLstr := SQLstr + ' where (' + Filter + ')'
  529. else if FWhereStopPos > 0 then
  530. system.insert(' and ('+Filter+') ',SQLstr,FWhereStopPos+1)
  531. else
  532. system.insert(' where ('+Filter+') ',SQLstr,FWhereStartPos);
  533. Result := SQLstr;
  534. end;
  535. procedure TSQLQuery.ApplyFilter;
  536. var S : String;
  537. begin
  538. FreeFldBuffers;
  539. (Database as tsqlconnection).UnPrepareStatement(FCursor);
  540. FIsEOF := False;
  541. inherited internalclose;
  542. s := FSQLBuf;
  543. if Filtered then s := AddFilter(s);
  544. (Database as tsqlconnection).PrepareStatement(Fcursor,(transaction as tsqltransaction),S,FParams);
  545. Execute;
  546. inherited InternalOpen;
  547. First;
  548. end;
  549. Procedure TSQLQuery.SetActive (Value : Boolean);
  550. begin
  551. inherited SetActive(Value);
  552. // The query is UnPrepared, so that if a transaction closes all datasets
  553. // they also get unprepared
  554. if not Value and IsPrepared then UnPrepare;
  555. end;
  556. procedure TSQLQuery.SetFiltered(Value: Boolean);
  557. begin
  558. if Value and not FParseSQL then DatabaseErrorFmt(SNoParseSQL,['Filtering ']);
  559. if (Filtered <> Value) then
  560. begin
  561. inherited setfiltered(Value);
  562. if active then ApplyFilter;
  563. end;
  564. end;
  565. procedure TSQLQuery.SetFilterText(const Value: string);
  566. begin
  567. if Value <> Filter then
  568. begin
  569. inherited SetFilterText(Value);
  570. if active then ApplyFilter;
  571. end;
  572. end;
  573. procedure TSQLQuery.Prepare;
  574. var
  575. db : tsqlconnection;
  576. sqltr : tsqltransaction;
  577. begin
  578. if not IsPrepared then
  579. begin
  580. db := (Database as tsqlconnection);
  581. sqltr := (transaction as tsqltransaction);
  582. if not assigned(Db) then
  583. DatabaseError(SErrDatabasenAssigned);
  584. if not assigned(sqltr) then
  585. DatabaseError(SErrTransactionnSet);
  586. if not Db.Connected then db.Open;
  587. if not sqltr.Active then sqltr.StartTransaction;
  588. // if assigned(fcursor) then FreeAndNil(fcursor);
  589. if not assigned(fcursor) then
  590. FCursor := Db.AllocateCursorHandle;
  591. FSQLBuf := TrimRight(FSQL.Text);
  592. if FSQLBuf = '' then
  593. DatabaseError(SErrNoStatement);
  594. SQLParser(FSQLBuf);
  595. if filtered then
  596. Db.PrepareStatement(Fcursor,sqltr,AddFilter(FSQLBuf),FParams)
  597. else
  598. Db.PrepareStatement(Fcursor,sqltr,FSQLBuf,FParams);
  599. if (FCursor.FStatementType = stSelect) then
  600. begin
  601. FCursor.FInitFieldDef := True;
  602. if not ReadOnly then InitUpdates(FSQLBuf);
  603. end;
  604. end;
  605. end;
  606. procedure TSQLQuery.UnPrepare;
  607. begin
  608. CheckInactive;
  609. if IsPrepared then with Database as TSQLConnection do
  610. UnPrepareStatement(FCursor);
  611. end;
  612. procedure TSQLQuery.FreeFldBuffers;
  613. begin
  614. if assigned(FCursor) then (Database as tsqlconnection).FreeFldBuffers(FCursor);
  615. end;
  616. function TSQLQuery.Fetch : boolean;
  617. begin
  618. if not (Fcursor.FStatementType in [stSelect]) then
  619. Exit;
  620. if not FIsEof then FIsEOF := not (Database as tsqlconnection).Fetch(Fcursor);
  621. Result := not FIsEOF;
  622. end;
  623. procedure TSQLQuery.Execute;
  624. begin
  625. If (FParams.Count>0) and Assigned(FMasterLink) then
  626. FMasterLink.CopyParamsFromMaster(False);
  627. (Database as tsqlconnection).execute(Fcursor,Transaction as tsqltransaction, FParams);
  628. end;
  629. function TSQLQuery.LoadField(FieldDef : TFieldDef;buffer : pointer) : boolean;
  630. begin
  631. result := (Database as tSQLConnection).LoadField(FCursor,FieldDef,buffer)
  632. end;
  633. procedure TSQLQuery.InternalAddRecord(Buffer: Pointer; AAppend: Boolean);
  634. begin
  635. // not implemented - sql dataset
  636. end;
  637. procedure TSQLQuery.InternalClose;
  638. begin
  639. if StatementType = stSelect then FreeFldBuffers;
  640. // Database and FCursor could be nil, for example if the database is not assigned, and .open is called
  641. if (not IsPrepared) and (assigned(database)) and (assigned(FCursor)) then (database as TSQLconnection).UnPrepareStatement(FCursor);
  642. if DefaultFields then
  643. DestroyFields;
  644. FIsEOF := False;
  645. if assigned(FUpdateQry) then FreeAndNil(FUpdateQry);
  646. if assigned(FInsertQry) then FreeAndNil(FInsertQry);
  647. if assigned(FDeleteQry) then FreeAndNil(FDeleteQry);
  648. // FRecordSize := 0;
  649. inherited internalclose;
  650. end;
  651. procedure TSQLQuery.InternalInitFieldDefs;
  652. begin
  653. if FLoadingFieldDefs then
  654. Exit;
  655. FLoadingFieldDefs := True;
  656. try
  657. FieldDefs.Clear;
  658. (Database as tsqlconnection).AddFieldDefs(fcursor,FieldDefs);
  659. finally
  660. FLoadingFieldDefs := False;
  661. end;
  662. end;
  663. procedure TSQLQuery.SQLParser(var ASQL : string);
  664. type TParsePart = (ppStart,ppSelect,ppWhere,ppFrom,ppOrder,ppComment,ppBogus);
  665. Var
  666. PSQL,CurrentP,
  667. PhraseP, PStatementPart : pchar;
  668. S : string;
  669. ParsePart : TParsePart;
  670. StrLength : Integer;
  671. begin
  672. PSQL:=Pchar(ASQL);
  673. ParsePart := ppStart;
  674. CurrentP := PSQL-1;
  675. PhraseP := PSQL;
  676. FWhereStartPos := 0;
  677. FWhereStopPos := 0;
  678. repeat
  679. begin
  680. inc(CurrentP);
  681. if CurrentP^ in [' ',#13,#10,#9,#0,'(',')',';'] then
  682. begin
  683. if (CurrentP-PhraseP > 0) or (CurrentP^ in [';',#0]) then
  684. begin
  685. strLength := CurrentP-PhraseP;
  686. Setlength(S,strLength);
  687. if strLength > 0 then Move(PhraseP^,S[1],(strLength));
  688. s := uppercase(s);
  689. case ParsePart of
  690. ppStart : begin
  691. FCursor.FStatementType := (Database as tsqlconnection).StrToStatementType(s);
  692. if FCursor.FStatementType = stSelect then ParsePart := ppSelect
  693. else break;
  694. if not FParseSQL then break;
  695. PStatementPart := CurrentP;
  696. end;
  697. ppSelect : begin
  698. if s = 'FROM' then
  699. begin
  700. ParsePart := ppFrom;
  701. PhraseP := CurrentP;
  702. PStatementPart := CurrentP;
  703. end;
  704. end;
  705. ppFrom : begin
  706. if (s = 'WHERE') or (s = 'ORDER') or (CurrentP^=#0) or (CurrentP^=';') then
  707. begin
  708. if (s = 'WHERE') then
  709. begin
  710. ParsePart := ppWhere;
  711. StrLength := PhraseP-PStatementPart;
  712. end
  713. else if (s = 'ORDER') then
  714. begin
  715. ParsePart := ppOrder;
  716. StrLength := PhraseP-PStatementPart
  717. end
  718. else
  719. begin
  720. ParsePart := ppBogus;
  721. StrLength := CurrentP-PStatementPart;
  722. end;
  723. Setlength(FFromPart,StrLength);
  724. Move(PStatementPart^,FFromPart[1],(StrLength));
  725. FFrompart := trim(FFrompart);
  726. FWhereStartPos := PStatementPart-PSQL+StrLength+1;
  727. PStatementPart := CurrentP;
  728. end;
  729. end;
  730. ppWhere : begin
  731. if (s = 'ORDER') or (CurrentP^=#0) or (CurrentP^=';') then
  732. begin
  733. ParsePart := ppBogus;
  734. FWhereStartPos := PStatementPart-PSQL;
  735. if s = 'ORDER' then
  736. FWhereStopPos := PhraseP-PSQL+1
  737. else
  738. FWhereStopPos := CurrentP-PSQL+1;
  739. end;
  740. end;
  741. end; {case}
  742. end;
  743. PhraseP := CurrentP+1;
  744. end
  745. end;
  746. until CurrentP^=#0;
  747. if (FWhereStartPos > 0) and (FWhereStopPos > 0) then
  748. begin
  749. system.insert('(',ASQL,FWhereStartPos+1);
  750. inc(FWhereStopPos);
  751. system.insert(')',ASQL,FWhereStopPos);
  752. end
  753. end;
  754. procedure TSQLQuery.InitUpdates(ASQL : string);
  755. begin
  756. if pos(',',FFromPart) > 0 then
  757. FUpdateable := False // select-statements from more then one table are not updateable
  758. else
  759. begin
  760. FUpdateable := True;
  761. FTableName := FFromPart;
  762. end;
  763. end;
  764. procedure TSQLQuery.InternalOpen;
  765. procedure InitialiseModifyQuery(var qry : TSQLQuery; aSQL: TSTringList);
  766. begin
  767. qry := TSQLQuery.Create(nil);
  768. with qry do
  769. begin
  770. ParseSQL := False;
  771. DataBase := Self.DataBase;
  772. Transaction := Self.Transaction;
  773. SQL.Assign(aSQL);
  774. end;
  775. end;
  776. var tel : integer;
  777. f : TField;
  778. s : string;
  779. begin
  780. try
  781. Prepare;
  782. if FCursor.FStatementType in [stSelect] then
  783. begin
  784. Execute;
  785. if FCursor.FInitFieldDef then InternalInitFieldDefs;
  786. if DefaultFields then
  787. begin
  788. CreateFields;
  789. if FUpdateable then
  790. begin
  791. if FusePrimaryKeyAsKey then
  792. begin
  793. UpdateIndexDefs;
  794. for tel := 0 to indexdefs.count-1 do {with indexdefs[tel] do}
  795. begin
  796. if ixPrimary in indexdefs[tel].options then
  797. begin
  798. // Todo: If there is more then one field in the key, that must be parsed
  799. s := indexdefs[tel].fields;
  800. F := Findfield(s);
  801. if F <> nil then
  802. F.ProviderFlags := F.ProviderFlags + [pfInKey];
  803. end;
  804. end;
  805. end;
  806. end;
  807. end;
  808. if FUpdateable then
  809. begin
  810. InitialiseModifyQuery(FDeleteQry,FDeleteSQL);
  811. InitialiseModifyQuery(FUpdateQry,FUpdateSQL);
  812. InitialiseModifyQuery(FInsertQry,FInsertSQL);
  813. end;
  814. end
  815. else
  816. DatabaseError(SErrNoSelectStatement,Self);
  817. except
  818. on E:Exception do
  819. raise;
  820. end;
  821. inherited InternalOpen;
  822. end;
  823. // public part
  824. procedure TSQLQuery.ExecSQL;
  825. begin
  826. try
  827. Prepare;
  828. Execute;
  829. finally
  830. // FCursor has to be assigned, or else the prepare went wrong before PrepareStatment was
  831. // called, so UnPrepareStatement shoudn't be called either
  832. if (not IsPrepared) and (assigned(database)) and (assigned(FCursor)) then (database as TSQLConnection).UnPrepareStatement(Fcursor);
  833. end;
  834. end;
  835. constructor TSQLQuery.Create(AOwner : TComponent);
  836. begin
  837. inherited Create(AOwner);
  838. FParams := TParams.create(self);
  839. FSQL := TStringList.Create;
  840. FSQL.OnChange := @OnChangeSQL;
  841. FUpdateSQL := TStringList.Create;
  842. FUpdateSQL.OnChange := @OnChangeModifySQL;
  843. FInsertSQL := TStringList.Create;
  844. FInsertSQL.OnChange := @OnChangeModifySQL;
  845. FDeleteSQL := TStringList.Create;
  846. FDeleteSQL.OnChange := @OnChangeModifySQL;
  847. FIndexDefs := TIndexDefs.Create(Self);
  848. FReadOnly := false;
  849. FParseSQL := True;
  850. // Delphi has upWhereAll as default, but since strings and oldvalue's don't work yet
  851. // (variants) set it to upWhereKeyOnly
  852. FUpdateMode := upWhereKeyOnly;
  853. FUsePrimaryKeyAsKey := True;
  854. end;
  855. destructor TSQLQuery.Destroy;
  856. begin
  857. if Active then Close;
  858. UnPrepare;
  859. if assigned(FCursor) then (Database as TSQLConnection).DeAllocateCursorHandle(FCursor);
  860. FreeAndNil(FMasterLink);
  861. FreeAndNil(FParams);
  862. FreeAndNil(FSQL);
  863. FreeAndNil(FInsertSQL);
  864. FreeAndNil(FDeleteSQL);
  865. FreeAndNil(FUpdateSQL);
  866. FreeAndNil(FIndexDefs);
  867. inherited Destroy;
  868. end;
  869. procedure TSQLQuery.SetReadOnly(AValue : Boolean);
  870. begin
  871. CheckInactive;
  872. if not AValue then
  873. begin
  874. if FParseSQL then FReadOnly := False
  875. else DatabaseErrorFmt(SNoParseSQL,['Updating ']);
  876. end
  877. else FReadOnly := True;
  878. end;
  879. procedure TSQLQuery.SetParseSQL(AValue : Boolean);
  880. begin
  881. CheckInactive;
  882. if not AValue then
  883. begin
  884. FReadOnly := True;
  885. Filtered := False;
  886. FParseSQL := False;
  887. end
  888. else
  889. FParseSQL := True;
  890. end;
  891. procedure TSQLQuery.SetUsePrimaryKeyAsKey(AValue : Boolean);
  892. begin
  893. if not Active then FusePrimaryKeyAsKey := AValue
  894. else
  895. begin
  896. // Just temporary, this should be possible in the future
  897. DatabaseError(SActiveDataset);
  898. end;
  899. end;
  900. Procedure TSQLQuery.UpdateIndexDefs;
  901. begin
  902. if assigned(DataBase) then
  903. (DataBase as TSQLConnection).UpdateIndexDefs(FIndexDefs,FTableName);
  904. end;
  905. Procedure TSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind);
  906. procedure UpdateWherePart(var sql_where : string;x : integer);
  907. begin
  908. if (pfInKey in Fields[x].ProviderFlags) or
  909. ((FUpdateMode = upWhereAll) and (pfInWhere in Fields[x].ProviderFlags)) or
  910. ((FUpdateMode = UpWhereChanged) and (pfInWhere in Fields[x].ProviderFlags) and (fields[x].value <> fields[x].oldvalue)) then
  911. sql_where := sql_where + '(' + fields[x].FieldName + '= :OLD_' + fields[x].FieldName + ') and ';
  912. end;
  913. function ModifyRecQuery : string;
  914. var x : integer;
  915. sql_set : string;
  916. sql_where : string;
  917. begin
  918. sql_set := '';
  919. sql_where := '';
  920. for x := 0 to Fields.Count -1 do
  921. begin
  922. UpdateWherePart(sql_where,x);
  923. if (pfInUpdate in Fields[x].ProviderFlags) then
  924. sql_set := sql_set + fields[x].FieldName + '=:' + fields[x].FieldName + ',';
  925. end;
  926. setlength(sql_set,length(sql_set)-1);
  927. if length(sql_where) = 0 then DatabaseError(sNoWhereFields,self);
  928. setlength(sql_where,length(sql_where)-5);
  929. result := 'update ' + FTableName + ' set ' + sql_set + ' where ' + sql_where;
  930. end;
  931. function InsertRecQuery : string;
  932. var x : integer;
  933. sql_fields : string;
  934. sql_values : string;
  935. begin
  936. sql_fields := '';
  937. sql_values := '';
  938. for x := 0 to Fields.Count -1 do
  939. begin
  940. if not fields[x].IsNull then
  941. begin
  942. sql_fields := sql_fields + fields[x].FieldName + ',';
  943. sql_values := sql_values + ':' + fields[x].FieldName + ',';
  944. end;
  945. end;
  946. setlength(sql_fields,length(sql_fields)-1);
  947. setlength(sql_values,length(sql_values)-1);
  948. result := 'insert into ' + FTableName + ' (' + sql_fields + ') values (' + sql_values + ')';
  949. end;
  950. function DeleteRecQuery : string;
  951. var x : integer;
  952. sql_where : string;
  953. begin
  954. sql_where := '';
  955. for x := 0 to Fields.Count -1 do
  956. UpdateWherePart(sql_where,x);
  957. if length(sql_where) = 0 then DatabaseError(sNoWhereFields,self);
  958. setlength(sql_where,length(sql_where)-5);
  959. result := 'delete from ' + FTableName + ' where ' + sql_where;
  960. end;
  961. var qry : tsqlquery;
  962. x : integer;
  963. Fld : TField;
  964. begin
  965. case UpdateKind of
  966. ukModify : begin
  967. qry := FUpdateQry;
  968. if trim(qry.sql.Text) = '' then qry.SQL.Add(ModifyRecQuery);
  969. end;
  970. ukInsert : begin
  971. qry := FInsertQry;
  972. if trim(qry.sql.Text) = '' then qry.SQL.Add(InsertRecQuery);
  973. end;
  974. ukDelete : begin
  975. qry := FDeleteQry;
  976. if trim(qry.sql.Text) = '' then qry.SQL.Add(DeleteRecQuery);
  977. end;
  978. end;
  979. with qry do
  980. begin
  981. for x := 0 to Params.Count-1 do with params[x] do if leftstr(name,4)='OLD_' then
  982. begin
  983. Fld := self.FieldByName(copy(name,5,length(name)-4));
  984. AssignFieldValue(Fld,Fld.OldValue);
  985. end
  986. else
  987. begin
  988. Fld := self.FieldByName(name);
  989. AssignFieldValue(Fld,Fld.Value);
  990. end;
  991. execsql;
  992. end;
  993. end;
  994. Function TSQLQuery.GetCanModify: Boolean;
  995. begin
  996. // the test for assigned(FCursor) is needed for the case that the dataset isn't opened
  997. if assigned(FCursor) and (FCursor.FStatementType = stSelect) then
  998. Result:= Active and FUpdateable and (not FReadOnly)
  999. else
  1000. Result := False;
  1001. end;
  1002. function TSQLQuery.GetIndexDefs : TIndexDefs;
  1003. begin
  1004. Result := FIndexDefs;
  1005. end;
  1006. procedure TSQLQuery.SetIndexDefs(AValue : TIndexDefs);
  1007. begin
  1008. FIndexDefs := AValue;
  1009. end;
  1010. procedure TSQLQuery.SetUpdateMode(AValue : TUpdateMode);
  1011. begin
  1012. FUpdateMode := AValue;
  1013. end;
  1014. procedure TSQLQuery.SetSchemaInfo( SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string);
  1015. begin
  1016. ReadOnly := True;
  1017. SQL.Clear;
  1018. SQL.Add((DataBase as tsqlconnection).GetSchemaInfoSQL(SchemaType, SchemaObjectName, SchemaPattern));
  1019. end;
  1020. procedure TSQLQuery.LoadBlobIntoStream(Field: TField;AStream: TMemoryStream);
  1021. begin
  1022. (DataBase as tsqlconnection).LoadBlobIntoStream(Field, AStream, FCursor,(Transaction as tsqltransaction));
  1023. end;
  1024. function TSQLQuery.GetStatementType : TStatementType;
  1025. begin
  1026. if assigned(FCursor) then Result := FCursor.FStatementType
  1027. else Result := stNone;
  1028. end;
  1029. Procedure TSQLQuery.SetDataSource(AVAlue : TDatasource);
  1030. Var
  1031. DS : TDatasource;
  1032. begin
  1033. DS:=DataSource;
  1034. If (AValue<>DS) then
  1035. begin
  1036. If Assigned(DS) then
  1037. DS.RemoveFreeNotification(Self);
  1038. If Assigned(AValue) then
  1039. begin
  1040. AValue.FreeNotification(Self);
  1041. FMasterLink:=TMasterParamsDataLink.Create(Self);
  1042. FMasterLink.Datasource:=AValue;
  1043. end
  1044. else
  1045. FreeAndNil(FMasterLink);
  1046. end;
  1047. end;
  1048. Function TSQLQuery.GetDataSource : TDatasource;
  1049. begin
  1050. If Assigned(FMasterLink) then
  1051. Result:=FMasterLink.DataSource
  1052. else
  1053. Result:=Nil;
  1054. end;
  1055. procedure TSQLQuery.Notification(AComponent: TComponent; Operation: TOperation);
  1056. begin
  1057. Inherited;
  1058. If (Operation=opRemove) and (AComponent=DataSource) then
  1059. DataSource:=Nil;
  1060. end;
  1061. { TSQLCursor }
  1062. constructor TSQLCursor.Create;
  1063. begin
  1064. FBlobStrings := TStringList.Create;
  1065. inherited;
  1066. end;
  1067. destructor TSQLCursor.Destroy;
  1068. begin
  1069. FBlobStrings.Free;
  1070. inherited Destroy;
  1071. end;
  1072. end.