00001 {*********************************************************}
00002 { }
00003 { Zeos Database Objects }
00004 { String tokenizing classes for MySQL }
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 ZMySqlToken;
00055
00056 interface
00057
00058 {$I ZParseSql.inc}
00059
00060 uses
00061 Classes, ZSysUtils, ZTokenizer, ZGenericSqlToken;
00062
00063 type
00064
00065 {** Implements a MySQL-specific number state object. }
00066 TZMySQLNumberState = class (TZNumberState)
00067 public
00068 function NextToken(Stream: TStream; FirstChar: Char;
00069 Tokenizer: TZTokenizer): TZToken; override;
00070 end;
00071
00072 {** Implements a MySQL-specific quote string state object. }
00073 TZMySQLQuoteState = class (TZQuoteState)
00074 public
00075 function NextToken(Stream: TStream; FirstChar: Char;
00076 Tokenizer: TZTokenizer): TZToken; override;
00077
00078 function EncodeString(const Value: string; QuoteChar: Char): string; override;
00079 function DecodeString(const Value: string; QuoteChar: Char): string; override;
00080 end;
00081
00082 {**
00083 This state will either delegate to a comment-handling
00084 state, or return a token with just a slash in it.
00085 }
00086 TZMySQLCommentState = class (TZCppCommentState)
00087 public
00088 function NextToken(Stream: TStream; FirstChar: Char;
00089 Tokenizer: TZTokenizer): TZToken; override;
00090 end;
00091
00092 {** Implements a symbol state object. }
00093 TZMySQLSymbolState = class (TZSymbolState)
00094 public
00095 constructor Create;
00096 end;
00097
00098 {** Implements a word state object. }
00099 TZMySQLWordState = class (TZGenericSQLWordState)
00100 public
00101 constructor Create;
00102 end;
00103
00104 {** Implements a default tokenizer object. }
00105 TZMySQLTokenizer = class (TZTokenizer)
00106 public
00107 constructor Create;
00108 end;
00109
00110 implementation
00111
00112 { TZMySQLNumberState }
00113
00114 {**
00115 Return a number token from a reader.
00116 @return a number token from a reader
00117 }
00118 function TZMySQLNumberState.NextToken(Stream: TStream; FirstChar: Char;
00119 Tokenizer: TZTokenizer): TZToken;
00120 var
00121 HexDecimal: Boolean;
00122 FloatPoint: Boolean;
00123 LastChar: Char;
00124
00125 function ReadHexDigits: string;
00126 begin
00127 Result := '';
00128 LastChar := #0;
00129 while Stream.Read(LastChar, 1) > 0 do
00130 begin
00131 if LastChar in ['0'..'9','a'..'f','A'..'F'] then
00132 begin
00133 Result := Result + LastChar;
00134 HexDecimal := HexDecimal or (LastChar in ['a'..'f','A'..'F']);
00135 LastChar := #0;
00136 end
00137 else
00138 begin
00139 Stream.Seek(-1, soFromCurrent);
00140 Break;
00141 end;
00142 end;
00143 end;
00144
00145 function ReadDecDigits: string;
00146 begin
00147 Result := '';
00148 LastChar := #0;
00149 while Stream.Read(LastChar, 1) > 0 do
00150 begin
00151 if LastChar in ['0'..'9'] then
00152 begin
00153 Result := Result + LastChar;
00154 LastChar := #0;
00155 end
00156 else
00157 begin
00158 Stream.Seek(-1, soFromCurrent);
00159 Break;
00160 end;
00161 end;
00162 end;
00163
00164 begin
00165 HexDecimal := False;
00166 FloatPoint := FirstChar = '.';
00167 Result.Value := FirstChar;
00168 Result.TokenType := ttUnknown;
00169 LastChar := #0;
00170
00171 { Reads the first part of the number before decimal point }
00172 if not FloatPoint then
00173 begin
00174 Result.Value := Result.Value + ReadDecDigits;
00175 FloatPoint := (LastChar = '.') and not HexDecimal;
00176 if FloatPoint then
00177 begin
00178 Stream.Read(LastChar, 1);
00179 Result.Value := Result.Value + LastChar;
00180 end;
00181 end;
00182
00183 { Reads the second part of the number after decimal point }
00184 if FloatPoint then
00185 Result.Value := Result.Value + ReadDecDigits;
00186
00187 { Reads a power part of the number }
00188 if not HexDecimal and (LastChar in ['e','E']) then
00189 begin
00190 Stream.Read(LastChar, 1);
00191 Result.Value := Result.Value + LastChar;
00192 FloatPoint := True;
00193
00194 Stream.Read(LastChar, 1);
00195 if LastChar in ['0'..'9','-','+'] then
00196 Result.Value := Result.Value + LastChar + ReadDecDigits
00197 else
00198 begin
00199 Result.Value := Copy(Result.Value, 1, Length(Result.Value) - 1);
00200 Stream.Seek(-2, soFromCurrent);
00201 end;
00202 end;
00203
00204 { Reads the nexdecimal number }
00205 if (Result.Value = '0') and (LastChar in ['x','X']) then
00206 begin
00207 Stream.Read(LastChar, 1);
00208 Result.Value := Result.Value + LastChar + ReadHexDigits;
00209 HexDecimal := True;
00210 end;
00211
00212 { Prepare the result }
00213 if Result.Value = '.' then
00214 begin
00215 if Tokenizer.SymbolState <> nil then
00216 Result := Tokenizer.SymbolState.NextToken(Stream, FirstChar, Tokenizer);
00217 end
00218 else
00219 begin
00220 if HexDecimal then
00221 Result.TokenType := ttHexDecimal
00222 else if FloatPoint then
00223 Result.TokenType := ttFloat
00224 else Result.TokenType := ttInteger;
00225 end;
00226 end;
00227
00228 { TZMySQLQuoteState }
00229
00230 {**
00231 Return a quoted string token from a reader. This method
00232 will collect characters until it sees a match to the
00233 character that the tokenizer used to switch to this state.
00234
00235 @return a quoted string token from a reader
00236 }
00237 function TZMySQLQuoteState.NextToken(Stream: TStream; FirstChar: Char;
00238 Tokenizer: TZTokenizer): TZToken;
00239 var
00240 ReadChar: Char;
00241 LastChar: Char;
00242 begin
00243 Result.Value := FirstChar;
00244 If FirstChar = '`' then
00245 Result.TokenType := ttQuotedIdentifier
00246 Else
00247 Result.TokenType := ttQuoted;
00248
00249 LastChar := #0;
00250
00251 while Stream.Read(ReadChar, 1) > 0 do
00252 begin
00253 if (LastChar = FirstChar) and (ReadChar <> FirstChar) then
00254 begin
00255 Stream.Seek(-1, soFromCurrent);
00256 Break;
00257 end;
00258 Result.Value := Result.Value + ReadChar;
00259 if LastChar = '\' then
00260 LastChar := #0
00261 else if (LastChar = FirstChar) and (ReadChar = FirstChar) then
00262 LastChar := #0
00263 else LastChar := ReadChar;
00264 end;
00265 end;
00266
00267 {**
00268 Encodes a string value.
00269 @param Value a string value to be encoded.
00270 @param QuoteChar a string quote character.
00271 @returns an encoded string.
00272 }
00273 function TZMySQLQuoteState.EncodeString(const Value: string; QuoteChar: Char): string;
00274 begin
00275 if QuoteChar in [#39, '"', '`'] then
00276 Result := QuoteChar + EncodeCString(Value) + QuoteChar
00277 else Result := Value;
00278 end;
00279
00280 {**
00281 Decodes a string value.
00282 @param Value a string value to be decoded.
00283 @param QuoteChar a string quote character.
00284 @returns an decoded string.
00285 }
00286 function TZMySQLQuoteState.DecodeString(const Value: string; QuoteChar: Char): string;
00287 var
00288 Len: Integer;
00289 begin
00290 Len := Length(Value);
00291 if (Len >= 2) and (QuoteChar in [#39, '"', '`'])
00292 and (Value[1] = QuoteChar) and (Value[Len] = QuoteChar) then
00293 begin
00294 if Len > 2 then
00295 Result := DecodeCString(Copy(Value, 2, Len - 2))
00296 else Result := '';
00297 end
00298 else Result := Value;
00299 end;
00300
00301 { TZMySQLCommentState }
00302
00303 {**
00304 Gets a MySQL specific comments like # or .
00305 @return either just a slash token, or the results of
00306 delegating to a comment-handling state
00307 }
00308 function TZMySQLCommentState.NextToken(Stream: TStream; FirstChar: Char;
00309 Tokenizer: TZTokenizer): TZToken;
00310 var
00311 ReadChar: Char;
00312 ReadNum, ReadNum2: Integer;
00313 begin
00314 Result.TokenType := ttUnknown;
00315 Result.Value := FirstChar;
00316
00317 if FirstChar = '-' then
00318 begin
00319 ReadNum := Stream.Read(ReadChar, 1);
00320 if (ReadNum > 0) and (ReadChar = '-') then
00321 begin
00322 Result.TokenType := ttComment;
00323 Result.Value := '--' + GetSingleLineComment(Stream);
00324 end
00325 else
00326 begin
00327 if ReadNum > 0 then
00328 Stream.Seek(-1, soFromCurrent);
00329 end;
00330 end
00331 else if FirstChar = '#' then
00332 begin
00333 Result.TokenType := ttComment;
00334 Result.Value := '#' + GetSingleLineComment(Stream);
00335 end
00336 else if FirstChar = '/' then
00337 begin
00338 ReadNum := Stream.Read(ReadChar, 1);
00339 if (ReadNum > 0) and (ReadChar = '*') then
00340 begin
00341 ReadNum2 := Stream.Read(ReadChar, 1);
00342
00343 if (ReadNum2 > 0) and (ReadChar <> '!') then
00344 begin
00345 Result.TokenType := ttComment;
00346 Result.Value := '
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438