1 {*********************************************************}
3 { Zeos Database Objects }
4 { Unidatabase SQLProcessor component }
6 { Originally written by Sergey Seroukhov }
8 {*********************************************************}
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
13 { License Agreement: }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
46 { http://www.sourceforge.net/projects/zeoslib. }
49 { Zeos Development Group. }
50 {********************************************************@}
58 uses Types, Classes, SysUtils, {$IFDEF MSEgui}mclasses, mdb{$ELSE}DB{$ENDIF},
59 ZDbcIntfs, ZAbstractConnection, ZScriptParser, ZSqlStrings, ZCompatibility;
63 {** Forward definition of TZSQLProcessor. }
64 TZSQLProcessor = class;
66 {** Defines an error handle action. }
67 TZErrorHandleAction = (eaFail, eaAbort, eaSkip, eaRetry);
69 {** Defines an Processor notification event. }
70 TZProcessorNotifyEvent = procedure(Processor: TZSQLProcessor;
71 StatementIndex: Integer) of object;
73 {** Defines an Processor error handling event. }
74 TZProcessorErrorEvent = procedure(Processor: TZSQLProcessor;
75 StatementIndex: Integer; E: Exception;
76 var ErrorHandleAction: TZErrorHandleAction) of object;
79 Implements a unidatabase component which parses and executes SQL Scripts.
84 TZSQLProcessor = class (TComponent)
87 FScript: TZSQLStrings;
88 FScriptParser: TZSQLScriptParser;
89 FConnection: TZAbstractConnection;
90 FBeforeExecute: TZProcessorNotifyEvent;
91 FAfterExecute: TZProcessorNotifyEvent;
92 FOnError: TZProcessorErrorEvent;
94 procedure SetParams(Value: TParams);
95 function GetScript: TStrings;
96 procedure SetScript(Value: TStrings);
97 function GetStatementCount: Integer;
98 function GetStatement(Index: Integer): string;
99 procedure SetConnection(Value: TZAbstractConnection);
100 function GetDelimiterType: TZDelimiterType;
101 procedure SetDelimiterType(Value: TZDelimiterType);
102 function GetDelimiter: string;
103 procedure SetDelimiter(const Value: string);
104 function GetCleanupStatements: Boolean;
105 procedure SetCleanupStatements(const Value: Boolean);
107 function GetParamCheck: Boolean;
108 procedure SetParamCheck(Value: Boolean);
109 function GetParamChar: Char;
110 procedure SetParamChar(Value: Char);
111 procedure UpdateSQLStrings(Sender: TObject);
113 procedure CheckConnected;
114 function DoOnError(StatementIndex: Integer; E: Exception):
116 procedure DoBeforeExecute(StatementIndex: Integer);
117 procedure DoAfterExecute(StatementIndex: Integer);
119 function CreateStatement(const SQL: string; Properties: TStrings):
120 IZPreparedStatement; virtual;
121 procedure SetStatementParams(Statement: IZPreparedStatement;
122 const ParamNames: TStringDynArray; Params: TParams); virtual;
124 constructor Create(AOwner: TComponent); override;
125 destructor Destroy; override;
127 procedure LoadFromStream(Stream: TStream);
128 procedure LoadFromFile(const FileName: string);
134 function ParamByName(const Value: string): TParam;
136 property StatementCount: Integer read GetStatementCount;
137 property Statements[Index: Integer]: string read GetStatement;
139 property ParamCheck: Boolean read GetParamCheck write SetParamCheck
141 property ParamChar: Char read GetParamChar write SetParamChar
143 property Params: TParams read FParams write SetParams;
144 property Script: TStrings read GetScript write SetScript;
145 property Connection: TZAbstractConnection read FConnection write SetConnection;
146 property DelimiterType: TZDelimiterType read GetDelimiterType
147 write SetDelimiterType default dtDefault;
148 property Delimiter: string read GetDelimiter write SetDelimiter;
149 property CleanupStatements: Boolean read GetCleanupStatements
150 write SetCleanupStatements default False;
151 property OnError: TZProcessorErrorEvent read FOnError write FOnError;
152 property AfterExecute: TZProcessorNotifyEvent read FAfterExecute write FAfterExecute;
153 property BeforeExecute: TZProcessorNotifyEvent read FBeforeExecute write FBeforeExecute;
158 uses ZMessages, ZDbcUtils, ZAbstractRODataset, ZDatasetUtils;
163 Creates this Processor component and assignes the main properties.
164 @param AOwner an owner component.
166 constructor TZSQLProcessor.Create(AOwner: TComponent);
168 inherited Create(AOwner);
170 FParams := TParams.Create(Self);
171 FScript := TZSQLStrings.Create;
172 FScript.Dataset := Self;
173 FScript.OnChange := UpdateSQLStrings;
174 FScriptParser := TZSQLScriptParser.Create;
175 FScriptParser.DelimiterType := dtDefault;
176 FScriptParser.Delimiter := ';';
177 FScriptParser.CleanupStatements := False;
181 Destroys this component and cleanups the memory.
183 destructor TZSQLProcessor.Destroy;
187 FreeAndNil(FScriptParser);
193 Gets a parsed statement by it's index.
194 @return a SQL statement.
196 function TZSQLProcessor.GetStatement(Index: Integer): string;
198 if (FScriptParser.UncompletedStatement <> '')
199 and (Index = FScriptParser.StatementCount) then
200 Result := FScriptParser.UncompletedStatement
201 else Result := FScriptParser.Statements[Index];
205 Gets a statements count.
206 @return a number of parsed statements.
208 function TZSQLProcessor.GetStatementCount: Integer;
210 Result := FScriptParser.StatementCount;
211 if FScriptParser.UncompletedStatement <> '' then
216 Sets a new SQL connection component.
217 @param Value am SQL connection component.
219 procedure TZSQLProcessor.SetConnection(Value: TZAbstractConnection);
221 if FConnection <> Value then
223 FConnection := Value;
224 FScriptParser.ClearUncompleted;
229 Gets a script delimiter type;
231 function TZSQLProcessor.GetDelimiterType: TZDelimiterType;
233 Result := FScriptParser.DelimiterType;
237 Sets a new Processor delimiter type.
238 @param Value a new Processor delimiter type.
240 procedure TZSQLProcessor.SetDelimiterType(Value: TZDelimiterType);
242 if FScriptParser.DelimiterType <> Value then
244 FScriptParser.DelimiterType := Value;
245 FScriptParser.ClearUncompleted;
250 Gets a script delimiter;
252 function TZSQLProcessor.GetDelimiter: string;
254 Result := FScriptParser.Delimiter;
258 Sets a new Processor delimiter.
259 @param Value a new Processor delimiter.
261 procedure TZSQLProcessor.SetDelimiter(const Value: string);
263 if FScriptParser.Delimiter <> Value then
265 FScriptParser.Delimiter := Value;
266 FScriptParser.ClearUncompleted;
271 Sets a new set of parameters.
272 @param Value a set of parameters.
274 procedure TZSQLProcessor.SetParams(Value: TParams);
276 FParams.AssignValues(Value);
280 Sets a new SQL script.
281 @param Value a new SQL script.
283 procedure TZSQLProcessor.SetScript(Value: TStrings);
285 FScript.Assign(Value);
286 FScriptParser.ClearUncompleted;
290 Checks is the database connection assignes and tries to connect.
292 procedure TZSQLProcessor.CheckConnected;
294 if Connection = nil then
295 raise EZDatabaseError.Create(SConnectionIsNotAssigned);
300 Clears Processor contents and all parsed statements.
302 procedure TZSQLProcessor.Clear;
305 FScriptParser.ClearUncompleted;
309 Performs OnError Event and returns an error handle action.
310 @param StatementIndex an index of the statement which failt.
311 @param E an exception object.
312 @return an error handle action.
314 function TZSQLProcessor.DoOnError(StatementIndex: Integer;
315 E: Exception): TZErrorHandleAction;
318 if Assigned(FOnError) then
319 FOnError(Self, StatementIndex, E, Result);
323 Performs an action before execute a statement.
324 @param StatementIndex an index of the executing statement.
326 procedure TZSQLProcessor.DoBeforeExecute(StatementIndex: Integer);
328 if Assigned(FBeforeExecute) then
329 FBeforeExecute(Self, StatementIndex);
333 Performs an action action execute a statement.
334 @param StatementIndex an index of the executing statement.
336 procedure TZSQLProcessor.DoAfterExecute(StatementIndex: Integer);
338 if Assigned(FAfterExecute) then
339 FAfterExecute(Self, StatementIndex);
343 Loads a SQL Processor from the local file.
344 @param FileName a name of the file.
346 procedure TZSQLProcessor.LoadFromFile(const FileName: string);
348 FScript.LoadFromFile(FileName);
352 Loads a SQL Processor from the stream.
353 @param Stream a stream object.
355 procedure TZSQLProcessor.LoadFromStream(Stream: TStream);
357 FScript.LoadFromStream(Stream);
361 Executes a parsed SQL Processor.
363 procedure TZSQLProcessor.Execute;
366 Statement: IZPreparedStatement;
367 Action: TZErrorHandleAction;
370 if Connection = nil then
371 raise EZDatabaseError.Create(SConnectionIsNotAssigned);
373 FConnection.ShowSQLHourGlass;
375 SQL := TZSQLStrings.Create;
377 SQL.ParamCheck := FScript.ParamCheck;
378 SQL.MultiStatements := False;
381 for I := 0 to Pred(StatementCount) do
387 SQL.Text := GetStatement(I);
388 {http://zeos.firmos.at/viewtopic.php?t=2885&start=0&postdays=0&postorder=asc&highlight=}
389 if SQL.StatementCount > 0 then
391 Statement := CreateStatement(SQL.Statements[0].SQL, nil);
392 SetStatementParams(Statement, SQL.Statements[0].ParamNamesArray,
394 Statement.ExecuteUpdatePrepared;
400 if Assigned(Statement) then
402 Action := DoOnError(I, E);
403 if Action = eaFail then
405 else if Action = eaAbort then
409 until Action <> eaRetry;
415 Connection.HideSQLHourGlass;
420 Gets a SQL parameter by its name.
421 @param Value a parameter name.
422 @return a found parameter object.
424 function TZSQLProcessor.ParamByName(const Value: string): TParam;
426 Result := FParams.ParamByName(Value);
430 Parses the loaded SQL Processor.
432 procedure TZSQLProcessor.Parse;
435 FScriptParser.Tokenizer := Connection.DbcDriver.GetTokenizer;
436 // mdaems 20060429 : Clear would reset the delimiter of the scriptparser
437 // FScriptParser.Clear;
438 FScriptParser.ClearUncompleted;
439 FScriptParser.ParseText(FScript.Text);
443 Creates a DBC statement for the query.
444 @param SQL an SQL query.
445 @param Properties a statement specific properties.
446 @returns a created DBC statement.
448 function TZSQLProcessor.CreateStatement(const SQL: string;
449 Properties: TStrings): IZPreparedStatement;
453 Temp := TStringList.Create;
455 if Assigned(Properties) then
456 Temp.AddStrings(Properties);
458 Result := FConnection.DbcConnection.PrepareStatementWithParams(SQL, Temp);
465 Fill prepared statement with parameters.
466 @param Statement a prepared SQL statement.
467 @param ParamNames an array of parameter names.
468 @param Params a collection of SQL parameters.
470 procedure TZSQLProcessor.SetStatementParams(Statement: IZPreparedStatement;
471 const ParamNames: TStringDynArray; Params: TParams);
474 TempParam, Param: TParam;
476 TempParam := TParam.Create(nil);
479 for I := Low(ParamNames) to High(ParamNames) do
481 Param := Params.FindParam(ParamNames[I]);
482 if not Assigned(Param) or (Param.ParamType in [ptOutput, ptResult]) then
484 SetStatementParam(I+1, Statement, Param);
493 @return the SQL script strings.
495 function TZSQLProcessor.GetScript: TStrings;
501 Updates parameters from SQL statement.
502 @param Sender an event sender object.
504 procedure TZSQLProcessor.UpdateSQLStrings(Sender: TObject);
509 OldParams := TParams.Create;
510 OldParams.Assign(FParams);
514 for I := 0 to FScript.ParamCount - 1 do
515 FParams.CreateParam(ftUnknown, FScript.ParamNames[I], ptUnknown);
516 FParams.AssignValues(OldParams);
523 Gets a parameters check value.
524 @return a parameters check value.
526 function TZSQLProcessor.GetParamCheck: Boolean;
528 Result := FScript.ParamCheck;
532 Sets a new parameters check value.
533 @param Value a parameters check value.
535 procedure TZSQLProcessor.SetParamCheck(Value: Boolean);
537 FScript.ParamCheck := Value;
538 UpdateSQLStrings(Self);
542 Gets a parameters marker.
543 @return a parameter marker.
545 function TZSQLProcessor.GetParamChar: Char;
547 Result := FScript.ParamChar;
551 Sets a new parameter marker.
552 @param Value a parameter marker.
554 procedure TZSQLProcessor.SetParamChar(Value: Char);
556 FScript.ParamChar := Value;
557 UpdateSQLStrings(Self);
560 function TZSQLProcessor.GetCleanupStatements: Boolean;
562 Result := FScriptParser.CleanupStatements;
565 procedure TZSQLProcessor.SetCleanupStatements(const Value: Boolean);
567 if FScriptParser.CleanupStatements <> Value then
569 FScriptParser.CleanupStatements := Value;
570 FScriptParser.ClearUncompleted;