1 {*********************************************************}
3 { Zeos Database Objects }
4 { SQL Query Strings 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 {********************************************************@}
59 Types, Classes, SysUtils, {$IFDEF MSEgui}mclasses,{$ENDIF}Contnrs,
60 ZDbcIntfs, ZTokenizer, ZGenericSqlToken, ZCompatibility;
63 {** Represents a SQL statement description object. }
64 TZSQLStatement = class (TObject)
67 FParamIndices: TIntegerDynArray;
70 function GetParamCount: Integer;
71 function GetParamName(Index: Integer): string;
72 function GetParamNamesArray: TStringDynArray;
74 constructor Create(const SQL: string; const ParamIndices: TIntegerDynArray;
76 property SQL: string read FSQL;
77 property ParamCount: Integer read GetParamCount;
78 property ParamNames[Index: Integer]: string read GetParamName;
79 property ParamIndices: TIntegerDynArray read FParamIndices;
80 property ParamNamesArray: TStringDynArray read GetParamNamesArray;
83 {** Imlements a string list with SQL statements. }
87 TZSQLStrings = class (TStringList)
91 FStatements: TObjectList;
93 FMultiStatements: Boolean;
96 function GetParamCount: Integer;
97 function GetParamName(Index: Integer): string;
98 function GetStatement(Index: Integer): TZSQLStatement;
99 function GetStatementCount: Integer;
100 function GetTokenizer: IZTokenizer;
101 procedure SetDataset(Value: TObject);
102 procedure SetParamCheck(Value: Boolean);
103 procedure SetParamChar(Value: Char);
104 procedure SetMultiStatements(Value: Boolean);
106 procedure Changed; override;
107 function FindParam(const ParamName: string): Integer;
108 procedure RebuildAll;
111 destructor Destroy; override;
113 property Dataset: TObject read FDataset write SetDataset;
114 property ParamCheck: Boolean read FParamCheck write SetParamCheck;
115 property ParamCount: Integer read GetParamCount;
116 property ParamChar: Char read FParamChar write SetParamChar;
117 property ParamNames[Index: Integer]: string read GetParamName;
118 property StatementCount: Integer read GetStatementCount;
119 property Statements[Index: Integer]: TZSQLStatement read GetStatement;
120 property MultiStatements: Boolean read FMultiStatements
121 write SetMultiStatements;
126 uses ZMessages, ZAbstractRODataset, ZDatasetUtils, ZSqlProcessor;
131 Creates a SQL statement object and assignes the main properties.
132 @param SQL a SQL statement.
133 @param ParamIndices a parameter indices.
134 @param Params a list with all parameter names.
136 constructor TZSQLStatement.Create(const SQL: string;
137 const ParamIndices: TIntegerDynArray; Params: TStrings);
140 FParamIndices := ParamIndices;
145 Gets a parameters count for this statement.
146 @return a parameters count.
148 function TZSQLStatement.GetParamCount: Integer;
150 if Assigned(FParamIndices) then
151 Result := High(FParamIndices) - Low(FParamIndices) + 1
156 Gets a parameter name by it's index inside the statement.
157 @return a parameter name.
159 function TZSQLStatement.GetParamName(Index: Integer): string;
161 if Assigned(FParamIndices) then
162 Result := FParams[FParamIndices[Index + Low(FParamIndices)]]
167 Gets an array of parameter names.
168 @return an array of parameter names.
170 function TZSQLStatement.GetParamNamesArray: TStringDynArray;
174 SetLength(Result, High(FParamIndices) - Low(FParamIndices) + 1);
175 for I := Low(Result) to High(Result) do
176 Result[I] := FParams[FParamIndices[I + Low(FParamIndices)]];
182 Creates a SQL strings object and assigns the main properties.
184 constructor TZSQLStrings.Create;
186 inherited Create; { -> needed to run the TestSuite else Inheritance(Self).Methods fails}
187 FParams := TStringList.Create;
189 FStatements := TObjectList.Create;
190 FMultiStatements := True;
195 Destroys this object and cleanups the memory.
197 destructor TZSQLStrings.Destroy;
200 FreeAndNil(FStatements);
206 Gets a parameter count.
207 @return a count of SQL parameters.
209 function TZSQLStrings.GetParamCount: Integer;
211 Result := FParams.Count;
215 Gets parameter name by it's index.
216 @param Index a parameter index.
217 @return a parameter name.
219 function TZSQLStrings.GetParamName(Index: Integer): string;
221 Result := FParams[Index];
225 Gets a SQL statements count.
226 @return a SQL statements count.
228 function TZSQLStrings.GetStatementCount: Integer;
230 Result := FStatements.Count;
233 function TZSQLStrings.GetTokenizer: IZTokenizer;
237 { Defines a SQL specific tokenizer object. }
239 if FDataset is TZAbstractRODataset then
241 if Assigned(TZAbstractRODataset(FDataset).Connection) then
243 Driver := TZAbstractRODataset(FDataset).Connection.DbcDriver;
244 if Assigned(Driver) then
245 Result := Driver.GetTokenizer;
248 else if FDataset is TZSQLProcessor then
249 if Assigned(TZSQLProcessor(FDataset).Connection) then
251 Driver := TZSQLProcessor(FDataset).Connection.DbcDriver;
252 if Assigned(Driver) then
253 Result := Driver.GetTokenizer;
256 Result := TZGenericSQLTokenizer.Create; { thread save! Allways return a new Tokenizer! }
260 Gets a SQL statement by it's index.
261 @param Index a SQL statement index.
262 @return a SQL statement object.
264 function TZSQLStrings.GetStatement(Index: Integer): TZSQLStatement;
266 Result := TZSQLStatement(FStatements[Index]);
270 Sets a new ParamCheck value.
271 @param Value a new ParamCheck value.
273 procedure TZSQLStrings.SetParamCheck(Value: Boolean);
275 if FParamCheck <> Value then
277 FParamCheck := Value;
283 Sets a new ParamChar value.
284 @param Value a new ParamCheck value.
286 procedure TZSQLStrings.SetParamChar(Value: Char);
288 if FParamChar <> Value then
290 If not(GetTokenizer.GetCharacterState(Value) is TZSymbolstate) Then
291 raise EZDatabaseError.Create('Ongeldige ParamChar waarde : '+Value);
298 Sets a new MultiStatements value.
299 @param Value a new MultiStatements value.
301 procedure TZSQLStrings.SetMultiStatements(Value: Boolean);
303 if FMultiStatements <> Value then
305 FMultiStatements := Value;
311 Sets a new correspondent dataset object.
312 @param Value a new dataset object.
314 procedure TZSQLStrings.SetDataset(Value: TObject);
316 if FDataset <> Value then
324 Finds a parameter by it's name.
325 @param ParamName a parameter name.
326 @return an index of found parameters or -1 if nothing was found.
328 function TZSQLStrings.FindParam(const ParamName: string): Integer;
330 FParams.CaseSensitive := False;
331 Result := FParams.IndexOf(ParamName);
335 Rebuilds all SQL statements.
337 procedure TZSQLStrings.RebuildAll;
341 TokenType: TZTokenType;
344 ParamIndices: TIntegerDynArray;
345 ParamIndexCount: Integer;
346 ParamName, SQL: string;
347 Tokenizer: IZTokenizer;
351 TokenType := TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
352 Tokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF});
353 TokenValue := Tokens[TokenIndex];
358 if not (Assigned(FParams) and Assigned(FStatements)) then exit; //Alexs
363 ParamIndexCount := 0;
364 SetLength(ParamIndices, ParamIndexCount);
366 { Optimization for empty query. }
367 If Length(Trim(Text)) = 0 then
370 { Optimization for single query without parameters. }
371 if (not FParamCheck or (Pos(FParamChar, Text) = 0))
372 and (not FMultiStatements or (Pos(';', Text) = 0)) then
374 FStatements.Add(TZSQLStatement.Create(Text, ParamIndices, FParams));
378 Tokenizer:=GetTokenizer;
379 Tokens := Tokenizer.TokenizeBufferToList(Text,
380 [toSkipComments, toUnifyWhitespaces]);
385 { Processes parameters. }
386 if ParamCheck and (TokenValue = FParamChar) then
389 if (TokenType <> ttEOF) and (TokenValue <> FParamChar) then
391 { Check for correct parameter type. }
392 if not (TokenType in [ttWord, ttQuoted, ttQuotedIdentifier, ttKeyWord]) then
393 raise EZDatabaseError.Create(SIncorrectToken);
397 ParamName := TokenValue;
398 if (ParamName <> '') and CharInSet(ParamName[1], [#39, '`', '"', '[']) then
400 ParamName := Tokenizer.GetQuoteState.
401 DecodeString(ParamName, ParamName[1]);
404 ParamIndex := FindParam(ParamName);
405 if ParamIndex < 0 then
406 ParamIndex := FParams.Add(ParamName);
408 Inc(ParamIndexCount);
409 SetLength(ParamIndices, ParamIndexCount);
410 ParamIndices[ParamIndexCount - 1] := ParamIndex;
416 { Adds a DML statement. }
417 if (TokenType = ttEOF) or (FMultiStatements and (TokenValue = ';')) then
421 FStatements.Add(TZSQLStatement.Create(SQL, ParamIndices, FParams));
424 ParamIndexCount := 0;
425 SetLength(ParamIndices, ParamIndexCount);
427 { Adds a default token. }
429 SQL := SQL + TokenValue;
430 until TokenType = ttEOF;
437 Performs action when the content of this string list is changed.
439 procedure TZSQLStrings.Changed;
441 if UpdateCount = 0 then