00001 {*********************************************************}
00002 { }
00003 { Zeos Database Objects }
00004 { SQL Statements Analysing classes }
00005 { }
00006 { Originally written by Sergey Seroukhov }
00007 { }
00008 {*********************************************************}
00009
00010 {@********************************************************}
00011 { Copyright (c) 1999-2006 Zeos Development Group }
00012 { }
00013 { License Agreement: }
00014 { }
00015 { This library is distributed in the hope that it will be }
00016 { useful, but WITHOUT ANY WARRANTY; without even the }
00017 { implied warranty of MERCHANTABILITY or FITNESS FOR }
00018 { A PARTICULAR PURPOSE. See the GNU Lesser General }
00019 { Public License for more details. }
00020 { }
00021 { The source code of the ZEOS Libraries and packages are }
00022 { distributed under the Library GNU General Public }
00023 { License (see the file COPYING / COPYING.ZEOS) }
00024 { with the following modification: }
00025 { As a special exception, the copyright holders of this }
00026 { library give you permission to link this library with }
00027 { independent modules to produce an executable, }
00028 { regardless of the license terms of these independent }
00029 { modules, and to copy and distribute the resulting }
00030 { executable under terms of your choice, provided that }
00031 { you also meet, for each linked independent module, }
00032 { the terms and conditions of the license of that module. }
00033 { An independent module is a module which is not derived }
00034 { from or based on this library. If you modify this }
00035 { library, you may extend this exception to your version }
00036 { of the library, but you are not obligated to do so. }
00037 { If you do not wish to do so, delete this exception }
00038 { statement from your version. }
00039 { }
00040 { }
00041 { The project web site is located on: }
00042 { http:
00043 { http:
00044 { svn:
00045 { }
00046 { http:
00047 { http:
00048 { }
00049 { }
00050 { }
00051 { Zeos Development Group. }
00052 {********************************************************@}
00053
00054 unit ZGenericSqlAnalyser;
00055
00056 interface
00057
00058 {$I ZParseSql.inc}
00059
00060 uses Classes, Contnrs, ZClasses, ZTokenizer, ZSelectSchema, ZCompatibility;
00061
00062 type
00063
00064 {** Implements a section of the parsed SQL statement. }
00065 TZStatementSection = class (TObject)
00066 private
00067 FName: string;
00068 FTokens: TStrings;
00069 public
00070 constructor Create(const Name: string; Tokens: TStrings);
00071 destructor Destroy; override;
00072
00073 function Clone: TZStatementSection;
00074
00075 property Name: string read FName write FName;
00076 property Tokens: TStrings read FTokens;
00077 end;
00078
00079 {** Implements a publicly available interface to statement analyser. }
00080 IZStatementAnalyser = interface(IZInterface)
00081 ['{967635B6-411B-4DEF-990C-9C6C01F3DC0A}']
00082
00083 function TokenizeQuery(Tokenizer: IZTokenizer; const SQL: string;
00084 Cleanup: Boolean): TStrings;
00085 function SplitSections(Tokens: TStrings): TObjectList;
00086
00087 function ComposeTokens(Tokens: TStrings): string;
00088 function ComposeSections(Sections: TObjectList): string;
00089
00090 function DefineSelectSchemaFromSections(
00091 Sections: TObjectList): IZSelectSchema;
00092 function DefineSelectSchemaFromQuery(Tokenizer: IZTokenizer;
00093 const SQL: string): IZSelectSchema;
00094 end;
00095
00096 {** Implements an SQL statements analyser. }
00097 TZGenericStatementAnalyser = class (TZAbstractObject, IZStatementAnalyser)
00098 private
00099 FSectionNames: TStrings;
00100 FSelectOptions: TStrings;
00101 FFromJoins: TStrings;
00102 FFromClauses: TStrings;
00103 protected
00104 function ArrayToStrings(const Value: array of string): TStrings;
00105 function CheckForKeyword(Tokens: TStrings; TokenIndex: Integer;
00106 Keywords: TStrings; var Keyword: string; var WordCount: Integer): Boolean;
00107 function FindSectionTokens(Sections: TObjectList; const Name: string): TStrings;
00108
00109 procedure FillFieldRefs(SelectSchema: IZSelectSchema; SelectTokens: TStrings);
00110 procedure FillTableRefs(SelectSchema: IZSelectSchema; FromTokens: TStrings);
00111
00112 function SkipOptionTokens(Tokens: TStrings; var TokenIndex: Integer;
00113 Options: TStrings): Boolean;
00114 function SkipBracketTokens(Tokens: TStrings; var TokenIndex: Integer):
00115 Boolean;
00116
00117 property SectionNames: TStrings read FSectionNames write FSectionNames;
00118 property SelectOptions: TStrings read FSelectOptions write FSelectOptions;
00119 property FromJoins: TStrings read FFromJoins write FFromJoins;
00120 property FromClauses: TStrings read FFromClauses write FFromClauses;
00121 public
00122 constructor Create;
00123 destructor Destroy; override;
00124
00125 function TokenizeQuery(Tokenizer: IZTokenizer; const SQL: string;
00126 Cleanup: Boolean): TStrings;
00127 function SplitSections(Tokens: TStrings): TObjectList;
00128
00129 function ComposeTokens(Tokens: TStrings): string;
00130 function ComposeSections(Sections: TObjectList): string;
00131
00132 function DefineSelectSchemaFromSections(
00133 Sections: TObjectList): IZSelectSchema;
00134 function DefineSelectSchemaFromQuery(Tokenizer: IZTokenizer; const SQL: string):
00135 IZSelectSchema;
00136 end;
00137
00138 implementation
00139
00140 uses SysUtils, ZSysUtils;
00141
00142 { TZStatementSection }
00143
00144 {**
00145 Create SQL statement section object.
00146 }
00147 constructor TZStatementSection.Create(const Name: string; Tokens: TStrings);
00148 begin
00149 FName := Name;
00150 FTokens := Tokens;
00151 end;
00152
00153 {**
00154 Destroys this object and cleanups the memory.
00155 }
00156 destructor TZStatementSection.Destroy;
00157 begin
00158 FTokens.Free;
00159 inherited Destroy;
00160 end;
00161
00162 {**
00163 Clones an object instance.
00164 @return a clonned object instance.
00165 }
00166 function TZStatementSection.Clone: TZStatementSection;
00167 var
00168 Temp: TStrings;
00169 begin
00170 Temp := TStringList.Create;
00171 Temp.AddStrings(FTokens);
00172 Result := TZStatementSection.Create(FName, Temp);
00173 end;
00174
00175 const
00176 {** The generic constants.}
00177 GenericSectionNames: array[0..12] of string = (
00178 'SELECT', 'UPDATE', 'DELETE', 'INSERT', 'FROM',
00179 'WHERE', 'INTO', 'GROUP*BY', 'HAVING', 'ORDER*BY',
00180 'FOR*UPDATE', 'LIMIT', 'OFFSET'
00181 );
00182 GenericSelectOptions: array[0..1] of string = (
00183 'DISTINCT', 'ALL'
00184 );
00185 GenericFromJoins: array[0..5] of string = (
00186 'NATURAL', 'RIGHT', 'LEFT', 'INNER', 'OUTER', 'JOIN'
00187 );
00188 GenericFromClauses: array[0..0] of string = (
00189 'ON'
00190 );
00191
00192 { TZGenericStatementAnalyser }
00193
00194 {**
00195 Creates the object and assignes the main properties.
00196 }
00197 constructor TZGenericStatementAnalyser.Create;
00198 begin
00199 FSectionNames := ArrayToStrings(GenericSectionNames);
00200 FSelectOptions := ArrayToStrings(GenericSelectOptions);
00201 FFromJoins := ArrayToStrings(GenericFromJoins);
00202 FFromClauses := ArrayToStrings(GenericFromClauses);
00203 end;
00204
00205 {**
00206 Destroys this object and cleanups the memory.
00207 }
00208 destructor TZGenericStatementAnalyser.Destroy;
00209 begin
00210 FSectionNames.Free;
00211 FSelectOptions.Free;
00212 FFromJoins.Free;
00213 FFromClauses.Free;
00214 inherited Destroy;
00215 end;
00216
00217 {**
00218 Converts an array of strings into TStrings object.
00219 @param Value an array of strings to be converted.
00220 @return a TStrings object with specified strings.
00221 }
00222 function TZGenericStatementAnalyser.ArrayToStrings(
00223 const Value: array of string): TStrings;
00224 var
00225 I: Integer;
00226 begin
00227 Result := TStringList.Create;
00228 for I := Low(Value) to High(Value) do
00229 Result.Add(Value[I]);
00230 end;
00231
00232 {**
00233 Checks for keyword with one, two or three consisted words in the list
00234 @param Tokens a list or tokens
00235 @param TokenIndex an index of the current token
00236 @param Keywords a list of keywords (in uppers case delimited with '*')
00237 @param Keyword an out parameter with found keyword.
00238 @param WordCount a count of words in the found keyword.
00239 }
00240 function TZGenericStatementAnalyser.CheckForKeyword(Tokens: TStrings;
00241 TokenIndex: Integer; Keywords: TStrings; var Keyword: string;
00242 var WordCount: Integer): Boolean;
00243 var
00244 I: Integer;
00245 begin
00246 WordCount := 0;
00247 Keyword := '';
00248 Result := False;
00249
00250 for I := 1 to 3 do
00251 begin
00252 if (Tokens.Count <= TokenIndex) then
00253 Break;
00254 if TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
00255 Tokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF}) <> ttWord then
00256 Break;
00257 if Keyword <> '' then
00258 Keyword := Keyword + '*';
00259 Keyword := Keyword + AnsiUpperCase(Tokens[TokenIndex]);
00260 Inc(WordCount);
00261 if Keywords.IndexOf(Keyword) >= 0 then
00262 begin
00263 Result := True;
00264 Break;
00265 end;
00266 Inc(TokenIndex);
00267 { Skips whitespaces. }
00268 while Tokens.Count > TokenIndex do
00269 begin
00270 if not (TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
00271 Tokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF})
00272 in [ttWhitespace, ttComment]) then
00273 Break;
00274 Inc(TokenIndex);
00275 Inc(WordCount);
00276 end;
00277 end;
00278
00279 if not Result then
00280 begin
00281 WordCount := 0;
00282 Keyword := '';
00283 end;
00284 end;
00285
00286 {**
00287 Finds a section by it's name.
00288 @param Sections a list of sections.
00289 @param Name a name of the section to be found.
00290 @return a list of section tokens or <code>null</code>
00291 if section is was not found.
00292 }
00293 function TZGenericStatementAnalyser.FindSectionTokens(
00294 Sections: TObjectList; const Name: string): TStrings;
00295 var
00296 I: Integer;
00297 Current: TZStatementSection;
00298 begin
00299 Result := nil;
00300 for I := 0 to Sections.Count - 1 do
00301 begin
00302 Current := TZStatementSection(Sections[I]);
00303 if Current.Name = Name then
00304 begin
00305 Result := Current.Tokens;
00306 Break;
00307 end;
00308 end;
00309 end;
00310
00311 {**
00312 Tokenizes a given SQL query into a list of tokens with tokenizer.
00313 @param Tokenizer a tokenizer object.
00314 @param SQL a SQL query to be tokenized.
00315 @return a list with tokens.
00316 }
00317 function TZGenericStatementAnalyser.TokenizeQuery(
00318 Tokenizer: IZTokenizer; const SQL: string; Cleanup: Boolean): TStrings;
00319 begin
00320 if Cleanup then
00321 begin
00322 Result := Tokenizer.TokenizeBufferToList(SQL,
00323 [toSkipEOF, toSkipComments, toUnifyWhitespaces])
00324 end else
00325 Result := Tokenizer.TokenizeBufferToList(SQL, [toSkipEOF]);
00326 end;
00327
00328 {**
00329 Splits a given list of tokens into the list named sections.
00330 @param Tokens a list of tokens.
00331 @return a list of section names where object property contains
00332 a list of tokens in the section. It initial list is not started
00333 with a section name the first section is unnamed ('').
00334 }
00335 function TZGenericStatementAnalyser.SplitSections(Tokens: TStrings): TObjectList;
00336 var
00337 I: Integer;
00338 Keyword: string;
00339 WordCount: Integer;
00340 TokenIndex: Integer;
00341 Elements: TStrings;
00342 FoundSection: Boolean;
00343 BracketCount: Integer;
00344 begin
00345 Result := TObjectList.Create;
00346 TokenIndex := 0;
00347 FoundSection := True;
00348 Elements := nil;
00349 CheckForKeyword(Tokens, TokenIndex, SectionNames, Keyword, WordCount);
00350
00351 while TokenIndex < Tokens.Count do
00352 begin
00353 if FoundSection then
00354 begin
00355 Elements := TStringList.Create;
00356 for I := 0 to WordCount - 1 do
00357 begin
00358 Elements.AddObject(Tokens[TokenIndex + I],
00359 Tokens.Objects[TokenIndex + I]);
00360 end;
00361 Inc(TokenIndex, WordCount);
00362 Result.Add(TZStatementSection.Create(Keyword, Elements));
00363 end;
00364 FoundSection := CheckForKeyword(Tokens, TokenIndex, SectionNames,
00365 Keyword, WordCount);
00366 if not FoundSection and (TokenIndex < Tokens.Count) then
00367 begin
00368 BracketCount := 0;
00369 repeat
00370 Elements.AddObject(Tokens[TokenIndex], Tokens.Objects[TokenIndex]);
00371 if Tokens[TokenIndex] = '(' then
00372 Inc(BracketCount)
00373 else if Tokens[TokenIndex] = ')' then
00374 Dec(BracketCount);
00375 Inc(TokenIndex);
00376 until (BracketCount <= 0) or (TokenIndex >= Tokens.Count);
00377 end;
00378 end;
00379 end;
00380
00381 {**
00382 Composes a string from the list of tokens.
00383 @param Tokens a list of tokens.
00384 @returns a composes string.
00385 }
00386 function TZGenericStatementAnalyser.ComposeTokens(Tokens: TStrings): string;
00387 begin
00388 Result := ComposeString(Tokens, '');
00389 end;
00390
00391 {**
00392 Composes a string from the list of statement sections.
00393 @param Tokens a list of statement sections.
00394 @returns a composes string.
00395 }
00396 function TZGenericStatementAnalyser.ComposeSections(Sections: TObjectList): string;
00397 var
00398 I: Integer;
00399 begin
00400 Result := '';
00401 for I := 0 to Sections.Count - 1 do
00402 Result := Result + ComposeTokens(TZStatementSection(Sections[I]).Tokens);
00403 end;
00404
00405 {**
00406 Skips tokens inside brackets.
00407 @param Tokens a list of tokens to scan.
00408 @param TokenIndex the index of the current token.
00409 @return <code>true</code> if some tokens were skipped.
00410 }
00411 function TZGenericStatementAnalyser.SkipBracketTokens(Tokens: TStrings;
00412 var TokenIndex: Integer): Boolean;
00413 var
00414 BracketCount: Integer;
00415 Current: string;
00416 begin
00417 { Checks for the start bracket. }
00418 if (TokenIndex < Tokens.Count) and (Tokens[TokenIndex] <> '(') then
00419 begin
00420 Result := False;
00421 Exit;
00422 end;
00423
00424 { Skips the expression in brackets. }
00425 Result := True;
00426 BracketCount := 1;
00427 Inc(TokenIndex);
00428 while (TokenIndex < Tokens.Count) and (BracketCount > 0) do
00429 begin
00430 Current := Tokens[TokenIndex];
00431 if Current = '(' then
00432 Inc(BracketCount)
00433 else if Current = ')' then
00434 Dec(BracketCount);
00435 Inc(TokenIndex);
00436 end;
00437 end;
00438
00439 {**
00440 Skips option tokens specified in the string list.
00441 @param Tokens a list of tokens to scan.
00442 @param TokenIndex the index of the current token.
00443 @param Options a list of option keyword strings in the upper case.
00444 @return <code>true</code> if some tokens were skipped.
00445 }
00446 function TZGenericStatementAnalyser.SkipOptionTokens(Tokens: TStrings;
00447 var TokenIndex: Integer; Options: TStrings): Boolean;
00448 begin
00449 Result := False;
00450 while TokenIndex < Tokens.Count do
00451 begin
00452 if not (TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
00453 Tokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF})
00454 in [ttWhitespace, ttComment])
00455 and (Options.IndexOf(AnsiUpperCase(Tokens[TokenIndex])) < 0) then
00456 begin
00457 Break;
00458 end;
00459 Inc(TokenIndex);
00460 Result := True;
00461 end;
00462 end;
00463
00464 {**
00465 Fills select schema with field references.
00466 @param SelectSchema a select schema object.
00467 @param SelectTokens a list of tokens in select section.
00468 }
00469 procedure TZGenericStatementAnalyser.FillFieldRefs(
00470 SelectSchema: IZSelectSchema; SelectTokens: TStrings);
00471 var
00472 TokenIndex: Integer;
00473 Catalog: string;
00474 Schema: string;
00475 Table: string;
00476 Field: string;
00477 Alias: string;
00478 CurrentValue: string;
00479 CurrentType: TZTokenType;
00480 CurrentUpper: string;
00481 ReadField: Boolean;
00482 HadWhitespace : Boolean;
00483
00484 procedure ClearElements;
00485 begin
00486 Catalog := '';
00487 Schema := '';
00488 Table := '';
00489 Field := '';
00490 Alias := '';
00491 ReadField := True;
00492 end;
00493
00494 begin
00495 TokenIndex := 1;
00496 SkipOptionTokens(SelectTokens, TokenIndex, Self.SelectOptions);
00497
00498 ClearElements;
00499 while TokenIndex < SelectTokens.Count do
00500 begin
00501 CurrentValue := SelectTokens[TokenIndex];
00502 CurrentUpper := AnsiUpperCase(CurrentValue);
00503 CurrentType := TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
00504 SelectTokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF});
00505
00506 { Switches to alias part. }
00507 if (CurrentType = ttWhitespace) or (CurrentUpper = 'AS') then
00508 begin
00509 ReadField := ReadField and (Field = '') and (CurrentUpper <> 'AS');
00510 end
00511 { Reads field. }
00512 else if ReadField and ((CurrentType = ttWord) or (CurrentType = ttQuotedIdentifier) or
00513 (CurrentValue = '*')) then
00514 begin
00515 Catalog := Schema;
00516 Schema := Table;
00517 Table := Field;
00518 Field := CurrentValue;
00519 end
00520 { Skips a '.' in field part. }
00521 else if ReadField and (CurrentValue = '.') then
00522 begin
00523 end
00524 { Reads alias. }
00525 else if not ReadField and (CurrentType = ttWord) then
00526 begin
00527 Alias := CurrentValue;
00528 end
00529 { Ends field reading. }
00530 else if CurrentValue = ',' then
00531 begin
00532 if Field <> '' then
00533 begin
00534 SelectSchema.AddField(TZFieldRef.Create(True, Catalog, Schema, Table,
00535 Field, Alias, nil));
00536 end;
00537 ClearElements;
00538 end
00539 { Skips till the next field. }
00540 else
00541 begin
00542 ClearElements;
00543 HadWhitespace := False;
00544 while (TokenIndex < SelectTokens.Count) and (CurrentValue <> ',') do
00545 begin
00546 CurrentValue := SelectTokens[TokenIndex];
00547 if CurrentValue = '(' then
00548 SkipBracketTokens(SelectTokens, TokenIndex)
00549 else begin
00550 CurrentType := TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
00551 SelectTokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF});
00552 if HadWhitespace and (CurrentType in [ttWord, ttQuotedIdentifier]) then
00553 Alias := CurrentValue
00554 else if not (CurrentType in [ttWhitespace, ttComment])
00555 and (CurrentValue <> ',') then
00556 Alias := ''
00557 else if CurrentType = ttWhitespace then
00558 HadWhitespace := true;
00559 Inc(TokenIndex);
00560 end;
00561 end;
00562 if Alias <> '' then
00563 begin
00564 SelectSchema.AddField(TZFieldRef.Create(False, '', '', '', '',
00565 Alias, nil));
00566 ClearElements;
00567 end;
00568 Dec(TokenIndex); // go back 1 token(Because of Inc in next lines)
00569 end;
00570 Inc(TokenIndex);
00571 end;
00572
00573 { Creates a reference to the last processed field. }
00574 if Field <> '' then
00575 begin
00576 SelectSchema.AddField(TZFieldRef.Create(True, Catalog, Schema, Table,
00577 Field, Alias, nil));
00578 end;
00579 end;
00580
00581 {**
00582 Fills select schema with table references.
00583 @param SelectSchema a select schema object.
00584 @param FromTokens a list of tokens in from section.
00585 }
00586 procedure TZGenericStatementAnalyser.FillTableRefs(
00587 SelectSchema: IZSelectSchema; FromTokens: TStrings);
00588 var
00589 TokenIndex: Integer;
00590 Catalog: string;
00591 Schema: string;
00592 Table: string;
00593 Alias: string;
00594 CurrentValue: string;
00595 CurrentType: TZTokenType;
00596 CurrentUpper: string;
00597 ReadTable: Boolean;
00598
00599 procedure ClearElements;
00600 begin
00601 Catalog := '';
00602 Schema := '';
00603 Table := '';
00604 Alias := '';
00605 ReadTable := True;
00606 end;
00607
00608 begin
00609 TokenIndex := 1;
00610
00611 ClearElements;
00612 while TokenIndex < FromTokens.Count do
00613 begin
00614 CurrentValue := FromTokens[TokenIndex];
00615 CurrentUpper := AnsiUpperCase(CurrentValue);
00616 CurrentType := TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
00617 FromTokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF});
00618
00619 { Processes from join keywords. }
00620 if FromJoins.IndexOf(CurrentUpper) >= 0 then
00621 begin
00622 if Table <> '' then
00623 SelectSchema.AddTable(TZTableRef.Create(Catalog, Schema, Table, Alias));
00624 ClearElements;
00625 SkipOptionTokens(FromTokens, TokenIndex, FromJoins);
00626 Continue;
00627 end
00628 { Skips from clause keywords. }
00629 else if FromClauses.IndexOf(CurrentUpper) >= 0 then
00630 begin
00631 Inc(TokenIndex);
00632 CurrentValue := FromTokens[TokenIndex];
00633 CurrentUpper := AnsiUpperCase(CurrentValue);
00634 while (TokenIndex < FromTokens.Count)
00635 and (FromJoins.IndexOf(CurrentUpper) < 0) and (CurrentUpper <> ',') do
00636 begin
00637 if CurrentUpper = '(' then
00638 SkipBracketTokens(FromTokens, TokenIndex)
00639 else Inc(TokenIndex);
00640 if TokenIndex < FromTokens.Count then
00641 begin
00642 CurrentValue := FromTokens[TokenIndex];
00643 CurrentUpper := AnsiUpperCase(CurrentValue);
00644 CurrentType := TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
00645 FromTokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF});
00646 end;
00647 end;
00648 // We must jump 1 tokens back now when we stopped on a Join clause.
00649 // Otherwise the next table is skipped
00650 if FromJoins.IndexOf(CurrentUpper) >= 0 then
00651 begin
00652 Dec(TokenIndex);
00653 CurrentValue := FromTokens[TokenIndex];
00654 CurrentUpper := AnsiUpperCase(CurrentValue);
00655 end;
00656 end
00657 { Switches to alias part. }
00658 else if (CurrentType = ttWhitespace) or (CurrentUpper = 'AS') then
00659 begin
00660 ReadTable := ReadTable and (Table = '') and (CurrentUpper <> 'AS');
00661 end
00662 { Reads table. }
00663 else if ReadTable and ((CurrentType = ttWord) or (CurrentType = ttQuotedIdentifier)) then
00664 begin
00665 Catalog := Schema;
00666 Schema := Table;
00667 Table := CurrentValue;
00668 end
00669 { Skips a '.' in table part. }
00670 else if ReadTable and (CurrentValue = '.') then
00671 begin
00672 end
00673 { Reads alias. }
00674 else if not ReadTable and (CurrentType = ttWord) then
00675 begin
00676 Alias := CurrentValue;
00677 end;
00678 { Ends field reading. }
00679 if CurrentValue = ',' then
00680 begin
00681 if Table <> '' then
00682 SelectSchema.AddTable(TZTableRef.Create(Catalog, Schema, Table, Alias));
00683 ClearElements;
00684 end;
00685 { Skips till the next field. }
00686 if CurrentValue = '(' then
00687 SkipBracketTokens(FromTokens, TokenIndex)
00688 else Inc(TokenIndex);
00689 end;
00690
00691 { Creates a reference to the last processed field. }
00692 if Table <> '' then
00693 SelectSchema.AddTable(TZTableRef.Create(Catalog, Schema, Table, Alias));
00694 end;
00695
00696 {**
00697 Extracts a select schema from the specified parsed select statement.
00698 @param Sections a list of sections.
00699 @return a select statement schema.
00700 }
00701 function TZGenericStatementAnalyser.DefineSelectSchemaFromSections(
00702 Sections: TObjectList): IZSelectSchema;
00703 var
00704 SelectTokens: TStrings;
00705 FromTokens: TStrings;
00706 begin
00707 Result := nil;
00708 { Checks for the correct select statement. }
00709 if (Sections.Count < 2)
00710 or not ((TZStatementSection(Sections[0]).Name = 'SELECT')
00711 or ((TZStatementSection(Sections[0]).Name = '')
00712 and (TZStatementSection(Sections[1]).Name = 'SELECT'))) then
00713 Exit;
00714
00715 { Defins sections. }
00716 SelectTokens := FindSectionTokens(Sections, 'SELECT');
00717 FromTokens := FindSectionTokens(Sections, 'FROM');
00718 if (SelectTokens = nil) or (FromTokens = nil) then
00719 Exit;
00720
00721 { Creates and fills the result object. }
00722 Result := TZSelectSchema.Create;
00723 FillFieldRefs(Result, SelectTokens);
00724 FillTableRefs(Result, FromTokens);
00725 end;
00726
00727 {**
00728 Defines a select schema from the specified SQL query.
00729 @param Tokenizer a tokenizer object.
00730 @param SQL a SQL query.
00731 @return a select statement schema.
00732 }
00733 function TZGenericStatementAnalyser.DefineSelectSchemaFromQuery(
00734 Tokenizer: IZTokenizer; const SQL: string): IZSelectSchema;
00735 var
00736 Tokens: TStrings;
00737 Sections: TObjectList;
00738 begin
00739 Tokens := TokenizeQuery(Tokenizer, SQL, True);
00740 Sections := SplitSections(Tokens);
00741 try
00742 Result := DefineSelectSchemaFromSections(Sections);
00743 finally
00744 Tokens.Free;
00745 Sections.Free;
00746 end;
00747 end;
00748
00749 end.
00750