fpsqlscanner.pp 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  1. {
  2. This file is part of the Free Component Library
  3. Copyright (c) 2010-2014 by the Free Pascal development team
  4. SQL source lexical scanner
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$h+}
  13. unit fpsqlscanner;
  14. interface
  15. uses SysUtils, Classes, Contnrs;
  16. resourcestring
  17. SErrInvalidCharacter = 'Invalid character ''%s''';
  18. SErrOpenString = 'string exceeds end of line';
  19. SErrIncludeFileNotFound = 'Could not find include file ''%s''';
  20. SErrIfXXXNestingLimitReached = 'Nesting of $IFxxx too deep';
  21. SErrInvalidPPElse = '$ELSE without matching $IFxxx';
  22. SErrInvalidPPEndif = '$ENDIF without matching $IFxxx';
  23. SInvalidHexadecimalNumber = 'Invalid hexadecimal number';
  24. SErrInvalidNonEqual = 'SyntaxError: != or !== expected';
  25. SBarExpected = '| character expected';
  26. type
  27. TSQLToken = (tsqlUnknown,
  28. // Specials
  29. tsqlEOF,tsqlWhiteSpace,
  30. tsqlString {string literal},
  31. tsqlIdentifier {a table etc name},
  32. tsqlSymbolString {a string containing symbols/punctuation marks; only rarely used - e.g. in SET TERM ^ ;},
  33. tsqlIntegerNumber,tsqlFloatNumber,tsqlComment,
  34. tsqlBraceOpen,tsqlBraceClose,tsqlSquareBraceOpen,tsqlSquareBraceClose,
  35. tsqlPlaceHolder {question mark},
  36. tsqlCOMMA,tsqlCOLON,tsqlDOT,tsqlSEMICOLON,tsqlTerminator,
  37. tsqlGT,tsqlLT,tsqlPLUS,tsqlMINUS,tsqlMUL,tsqlDIV,tsqlConcatenate,
  38. tsqlEQ,tsqlGE,tsqlLE,tsqlNE,
  39. { Reserved words/keywords start here. They must be last }
  40. { Note: if adding before tsqlALL or after tsqlWHEN please update FirstKeyword/LastKeyword }
  41. tsqlALL, tsqlAND, tsqlANY, tsqlASC, tsqlASCENDING, tsqlAVG, tsqlALTER, tsqlAdd, tsqlActive, tsqlAction, tsqlAs,tsqlAt, tsqlAuto, tsqlAfter,tsqlAdmin,
  42. tsqlBETWEEN, tsqlBinary, tsqlBY, tsqlBLOB, tsqlBegin, tsqlBefore,
  43. tsqlCASE, tsqlCOLLATE, tsqlCONTAINING, tsqlCOUNT, tsqlCREATE, tsqlCOLUMN, tsqlCONSTRAINT, tsqlChar,tsqlCHARACTER, tsqlCHECK, tsqlComputed,tsqlCASCADE, tsqlCast, tsqlCommit,tsqlConnect,tsqlCache,tsqlConditional,tsqlCString,
  44. tsqlDESC, tsqlDESCENDING, tsqlDISTINCT, tsqlDEFAULT, tsqlDELETE, tsqlDO, tsqlDouble, tsqlDECLARE, tsqlDROP, tsqlDomain, tsqlDecimal, tsqlDate,tsqlDatabase,
  45. tsqlESCAPE, tsqlEXISTS, tsqlELSE, tsqlException, tsqlExternal, tsqlExecute, tsqlEnd,tsqlExit,tsqlEntrypoint,tsqlExtract,
  46. tsqlFIRST, tsqlFROM, tsqlFULL, tsqlFOREIGN, tsqlFOR, tsqlFUNCTION, tsqlFLOAT, tsqlFile,tsqlFreeIt,
  47. tsqlGenerator, tsqlGROUP, tsqlGenID,tsqlGDSCODE,tsqlGrant,
  48. tsqlHAVING,
  49. tsqlIF, tsqlIN, tsqlINNER, tsqlINSERT, tsqlINT, tsqlINTEGER, tsqlINTO, tsqlIS, tsqlINDEX, tsqlInactive,
  50. tsqlJOIN,
  51. tsqlKEY,
  52. tsqlLEFT, tsqlLIKE, tsqlLIMIT, tsqlLength,
  53. tsqlMAX, tsqlMIN, tsqlMERGE, tsqlManual, tsqlModuleName,
  54. tsqlNOT, tsqlNULL, tsqlNUMERIC , tsqlNChar, tsqlNATIONAL,tsqlNO, tsqlNatural,
  55. tsqlOFF {not an FB reserved word; used in isql scripts}, tsqlOFFSET, tsqlON, tsqlOR, tsqlORDER, tsqlOUTER, tsqlOption,
  56. tsqlPrecision, tsqlPRIMARY, tsqlProcedure, tsqlPosition, tsqlPlan, tsqlPassword, tsqlPage,tsqlPages,tsqlPageSize,tsqlPostEvent,tsqlPrivileges,tsqlPublic,
  57. tsqlRIGHT, tsqlROLE, tsqlReferences, tsqlRollBack, tsqlRelease, tsqlretain, tsqlReturningValues,tsqlReturns, tsqlrevoke,
  58. tsqlSELECT, tsqlSET, tsqlSINGULAR, tsqlSOME, tsqlSTARTING, tsqlSUM, tsqlSKIP,tsqlSUBTYPE,tsqlSize,tsqlSegment, tsqlSORT, tsqlSnapShot,tsqlSchema,tsqlShadow,tsqlSuspend,tsqlSQLCode,tsqlSmallint,
  59. tSQLTABLE, tsqlText, tsqlTrigger, tsqlTime, tsqlTimeStamp, tsqlType, tsqlTo, tsqlTop, tsqlTransaction, tsqlThen,
  60. tsqlUNION, tsqlUPDATE, tsqlUPPER, tsqlUNIQUE, tsqlUSER,
  61. tsqlValue, tsqlVALUES, tsqlVARIABLE, tsqlVIEW, tsqlVARCHAR,TSQLVARYING,
  62. tsqlWHERE, tsqlWITH, tsqlWHILE, tsqlWork, tsqlWhen,tsqlSequence,tsqlRestart,tsqlrecreate,tsqlterm
  63. );
  64. TSQLTokens = set of TSQLToken;
  65. const
  66. FirstKeyword = tsqlAll;
  67. LastKeyWord = tsqlTerm;
  68. sqlComparisons = [tsqleq,tsqlGE,tsqlLE,tsqlNE,tsqlGT,tsqlLT,tsqlIn,tsqlIS,
  69. tsqlbetween,tsqlLike,tsqlContaining,tsqlStarting,tsqlNOT];
  70. sqlInvertableComparisons = [tsqlLike,tsqlContaining,tsqlStarting,tsqlIN,tsqlIS, tsqlBetween];
  71. // Strings that represent tokens in TSQLToken
  72. TokenInfos: array[TSQLToken] of string = ('unknown',
  73. // Specials
  74. 'EOF','whitespace',
  75. 'String',
  76. 'identifier',
  77. 'symbol string',
  78. 'integer number','float number', 'comment',
  79. '(',')', '[',']',
  80. '?',',',':','.',';','',
  81. '>','<',
  82. '+','-','*','/','||',
  83. '=','>=','<=','<>',
  84. // Identifiers last:
  85. 'ALL', 'AND', 'ANY', 'ASC', 'ASCENDING', 'AVG', 'ALTER', 'ADD','ACTIVE','ACTION', 'AS', 'AT', 'AUTO', 'AFTER', 'ADMIN',
  86. 'BETWEEN', 'BINARY', 'BY', 'BLOB','BEGIN', 'BEFORE',
  87. 'CASE', 'COLLATE', 'CONTAINING', 'COUNT', 'CREATE', 'COLUMN', 'CONSTRAINT', 'CHAR','CHARACTER','CHECK', 'COMPUTED','CASCADE','CAST', 'COMMIT', 'CONNECT', 'CACHE','CONDITIONAL', 'CSTRING',
  88. 'DESC', 'DESCENDING', 'DISTINCT', 'DEFAULT', 'DELETE', 'DO', 'DOUBLE', 'DECLARE', 'DROP', 'DOMAIN', 'DECIMAL', 'DATE','DATABASE',
  89. 'ESCAPE', 'EXISTS', 'ELSE', 'EXCEPTION', 'EXTERNAL','EXECUTE', 'END','EXIT','ENTRY_POINT','EXTRACT',
  90. 'FIRST', 'FROM', 'FULL','FOREIGN', 'FOR', 'FUNCTION', 'FLOAT','FILE', 'FREE_IT',
  91. 'GENERATOR', 'GROUP', 'GEN_ID','GDSCODE','GRANT',
  92. 'HAVING',
  93. 'IF', 'IN', 'INNER', 'INSERT', 'INT', 'INTEGER', 'INTO', 'IS', 'INDEX', 'INACTIVE',
  94. 'JOIN',
  95. 'KEY',
  96. 'LEFT', 'LIKE', 'LIMIT', 'LENGTH',
  97. 'MAX', 'MIN', 'MERGE', 'MANUAL', 'MODULE_NAME',
  98. 'NOT', 'NULL', 'NUMERIC','NCHAR','NATIONAL', 'NO', 'NATURAL',
  99. 'OFF', 'OFFSET', 'ON', 'OR', 'ORDER', 'OUTER', 'OPTION',
  100. 'PRECISION', 'PRIMARY', 'PROCEDURE','POSITION','PLAN', 'PASSWORD','PAGE','PAGES','PAGE_SIZE','POST_EVENT','PRIVILEGES','PUBLIC',
  101. 'RIGHT', 'ROLE', 'REFERENCES', 'ROLLBACK','RELEASE', 'RETAIN', 'RETURNING_VALUES', 'RETURNS','REVOKE',
  102. 'SELECT', 'SET', 'SINGULAR', 'SOME', 'STARTING', 'SUM', 'SKIP','SUB_TYPE', 'SIZE', 'SEGMENT', 'SORT', 'SNAPSHOT','SCHEMA','SHADOW','SUSPEND','SQLCODE','SMALLINT',
  103. 'TABLE', 'TEXT', 'TRIGGER', 'TIME', 'TIMESTAMP', 'TYPE', 'TO', 'TOP', 'TRANSACTION', 'THEN',
  104. 'UNION', 'UPDATE', 'UPPER', 'UNIQUE', 'USER',
  105. 'VALUE','VALUES','VARIABLE', 'VIEW','VARCHAR','VARYING',
  106. 'WHERE', 'WITH', 'WHILE','WORK','WHEN','SEQUENCE','RESTART','RECREATE','TERM'
  107. );
  108. Type
  109. TLineReader = class
  110. public
  111. function IsEOF: Boolean; virtual; abstract;
  112. function ReadLine: string; virtual; abstract;
  113. end;
  114. { TStreamLineReader }
  115. TStreamLineReader = class(TLineReader)
  116. private
  117. FStream : TStream;
  118. Buffer : Array[0..1024] of Byte;
  119. FBufPos,
  120. FBufLen : Integer;
  121. procedure FillBuffer;
  122. public
  123. Constructor Create(AStream : TStream);
  124. function IsEOF: Boolean; override;
  125. function ReadLine: string; override;
  126. end;
  127. TFileLineReader = class(TLineReader)
  128. private
  129. FTextFile: Text;
  130. FileOpened: Boolean;
  131. public
  132. constructor Create(const AFilename: string);
  133. destructor Destroy; override;
  134. function IsEOF: Boolean; override;
  135. function ReadLine: string; override;
  136. end;
  137. ESQLScannerError = class(Exception);
  138. { TSQLScanner }
  139. TSQLScannerOption = (soReturnComments,
  140. soReturnWhiteSpace,
  141. soBackslashEscapes,
  142. soNoDoubleDelimIsChar,
  143. soDoubleQuoteStringLiteral, // Default: single quote is string literal
  144. soSingleQuoteIdentifier, // Default: double quote is identifier. Ignored if soDoubleQuoteStringLiteral is not specified
  145. soBackQuoteIdentifier, // Default: double quote is identifier
  146. soSquareBracketsIdentifier // Default: square brackets are not supported. (Enable for MSSQL support.)
  147. );
  148. TSQLScannerOptions = Set of TSQLScannerOption;
  149. TSQLScanner = class
  150. private
  151. FAlternateTerminator: String;
  152. FOptions: TSQLScannerOptions;
  153. FSourceFile: TLineReader;
  154. FSourceFilename: string;
  155. FCurRow: Integer;
  156. FCurToken: TSQLToken;
  157. FCurTokenString: string;
  158. FCurTokenRow: Integer;
  159. FCurTokenColumn: Integer;
  160. FCurLine: string;
  161. TokenStr: PChar;
  162. FSourceStream : TStream;
  163. FOwnSourceFile : Boolean;
  164. FKeyWords : TFPHashList;
  165. FExclude : TStringList;
  166. function CommentDiv: TSQLToken;
  167. // Used to parse out an identifier/name and store it in the list of identifiers
  168. function DoIdentifier : TSQLToken;
  169. // Used to parse out a string containing symbols
  170. function DoSymbolString : TSQLToken;
  171. function DoMultiLineComment: TSQLToken;
  172. function DoNumericLiteral: TSQLToken;
  173. function DoSingleLineComment: TSQLToken;
  174. function DoStringLiteral: TSQLToken;
  175. function DoWhiteSpace: TSQLToken;
  176. // Reads a new line into TokenStr and returns true
  177. // If no new lines, returns false
  178. function FetchLine: Boolean;
  179. function GetCurColumn: Integer;
  180. function GetExcludeKeywords: TStrings;
  181. function ReadUnicodeEscape: WideChar;
  182. procedure SetExcludeKeywords(const AValue: TStrings);
  183. procedure Setoptions(const AValue: TSQLScannerOptions);
  184. procedure ClearKeywords(Sender: TObject);
  185. protected
  186. Procedure BuildKeyWords; virtual;
  187. procedure Error(const Msg: string);overload;
  188. procedure Error(const Msg: string; Args: array of Const);overload;
  189. public
  190. constructor Create(ALineReader: TLineReader);
  191. constructor Create(AStream : TStream);
  192. destructor Destroy; override;
  193. procedure OpenFile(const AFilename: string);
  194. Function FetchToken: TSQLToken;
  195. Function IsEndOfLine : Boolean;
  196. Property Options : TSQLScannerOptions Read FOptions Write Setoptions;
  197. property SourceFile: TLineReader read FSourceFile;
  198. property CurFilename: string read FSourceFilename;
  199. property CurLine: string read FCurLine;
  200. property CurRow: Integer read FCurRow;
  201. property CurColumn: Integer read GetCurColumn;
  202. property CurToken: TSQLToken read FCurToken;
  203. property CurTokenString: string read FCurTokenString;
  204. Property CurTokenRow : Integer Read FCurTokenRow;
  205. Property CurTokenColumn : Integer Read FCurTokenColumn;
  206. Property ExcludeKeywords : TStrings Read GetExcludeKeywords Write SetExcludeKeywords;
  207. Property AlternateTerminator : String Read FAlternateTerminator Write FAlternateTerminator;
  208. end;
  209. implementation
  210. Var
  211. // Keeps track of identifiers used
  212. IdentifierTokens : array[FirstKeyword..LastKeyWord] of TSQLToken;
  213. IdentifierTokensOK : Boolean;
  214. Resourcestring
  215. SErrUnknownToken = 'Unknown token: %s';
  216. Procedure BuildIdentifierTokens;
  217. Var
  218. T : TSQLToken;
  219. begin
  220. For T:=FirstKeyword to LastKeyWord do
  221. IdentifierTokens[T]:=T;
  222. IdentifierTokensOK:=True;
  223. end;
  224. constructor TFileLineReader.Create(const AFilename: string);
  225. begin
  226. inherited Create;
  227. Assign(FTextFile, AFilename);
  228. Reset(FTextFile);
  229. FileOpened := true;
  230. end;
  231. destructor TFileLineReader.Destroy;
  232. begin
  233. if FileOpened then
  234. Close(FTextFile);
  235. inherited Destroy;
  236. end;
  237. function TFileLineReader.IsEOF: Boolean;
  238. begin
  239. Result := EOF(FTextFile);
  240. end;
  241. function TFileLineReader.ReadLine: string;
  242. begin
  243. ReadLn(FTextFile, Result);
  244. end;
  245. constructor TSQLScanner.Create(ALineReader: TLineReader);
  246. begin
  247. inherited Create;
  248. FSourceFile := ALineReader;
  249. FKeywords:=TFPHashList.Create;
  250. end;
  251. constructor TSQLScanner.Create(AStream: TStream);
  252. begin
  253. FSourceStream:=ASTream;
  254. FOwnSourceFile:=True;
  255. Create(TStreamLineReader.Create(AStream));
  256. end;
  257. destructor TSQLScanner.Destroy;
  258. begin
  259. If FOwnSourceFile then
  260. FSourceFile.Free;
  261. FreeAndNil(FKeywords);
  262. inherited Destroy;
  263. end;
  264. procedure TSQLScanner.OpenFile(const AFilename: string);
  265. begin
  266. FSourceFile := TFileLineReader.Create(AFilename);
  267. FOwnSourceFile:=True;
  268. FSourceFilename := AFilename;
  269. end;
  270. procedure TSQLScanner.Error(const Msg: string);
  271. begin
  272. raise ESQLScannerError.Create(Msg);
  273. end;
  274. procedure TSQLScanner.Error(const Msg: string; Args: array of Const);
  275. begin
  276. raise ESQLScannerError.CreateFmt(Msg, Args);
  277. end;
  278. function TSQLScanner.FetchLine: Boolean;
  279. begin
  280. if FSourceFile.IsEOF then
  281. begin
  282. FCurLine := '';
  283. TokenStr := nil;
  284. Result := false;
  285. end else
  286. begin
  287. FCurLine := FSourceFile.ReadLine;
  288. TokenStr := PChar(CurLine);
  289. Result := true;
  290. Inc(FCurRow);
  291. end;
  292. end;
  293. function TSQLScanner.DoWhiteSpace : TSQLToken;
  294. begin
  295. Result:=tsqlWhitespace;
  296. repeat
  297. Inc(TokenStr);
  298. if TokenStr[0] = #0 then
  299. if not FetchLine then
  300. begin
  301. FCurToken := Result;
  302. exit;
  303. end;
  304. until not (TokenStr[0] in [#9, ' ']);
  305. end;
  306. function TSQLScanner.DoSingleLineComment : TSQLToken;
  307. Var
  308. TokenStart : PChar;
  309. Len : Integer;
  310. begin
  311. Inc(TokenStr);
  312. TokenStart := TokenStr;
  313. while TokenStr[0] <> #0 do
  314. Inc(TokenStr);
  315. Len:=TokenStr-TokenStart;
  316. SetLength(FCurTokenString, Len);
  317. if (Len>0) then
  318. Move(TokenStart^,FCurTokenString[1],Len);
  319. Result := tsqlComment;
  320. end;
  321. function TSQLScanner.DoMultiLineComment : TSQLToken;
  322. Var
  323. TokenStart : PChar;
  324. Len,OLen : Integer;
  325. PrevToken : Char;
  326. begin
  327. Inc(TokenStr);
  328. TokenStart := TokenStr;
  329. FCurTokenString := '';
  330. OLen:= 0;
  331. PrevToken:=#0;
  332. while Not ((TokenStr[0]='/') and (PrevToken='*')) do
  333. begin
  334. if (TokenStr[0]=#0) then
  335. begin
  336. Len:=TokenStr-TokenStart+1;
  337. SetLength(FCurTokenString,OLen+Len);
  338. if Len>1 then
  339. Move(TokenStart^,FCurTokenString[OLen+1],Len-1);
  340. Inc(OLen,Len);
  341. FCurTokenString[OLen]:=#10;
  342. if not FetchLine then
  343. begin
  344. Result := tsqlEOF;
  345. FCurToken := Result;
  346. exit;
  347. end;
  348. TokenStart := TokenStr;
  349. PrevToken:=#0;
  350. end
  351. else
  352. begin
  353. PrevToken:=TokenStr[0];
  354. Inc(TokenStr);
  355. end;
  356. end;
  357. Len:=TokenStr-TokenStart-1; // -1 for *
  358. SetLength(FCurTokenString, Olen+Len);
  359. if (Len>0) then
  360. begin
  361. Move(TokenStart^, FCurTokenString[Olen + 1], Len);
  362. end;
  363. If TokenStr[0]<>#0 then
  364. Inc(TokenStr);
  365. Result := tsqlComment;
  366. end;
  367. function TSQLScanner.CommentDiv : TSQLToken;
  368. begin
  369. FCurTokenString := '';
  370. Inc(TokenStr);
  371. if (TokenStr[0]='*') then
  372. Result:=DoMultiLineComment
  373. else
  374. Result:=tsqlDiv;
  375. end;
  376. Function TSQLScanner.ReadUnicodeEscape : WideChar;
  377. Var
  378. S : String;
  379. I : Integer;
  380. begin
  381. S:='0000';
  382. For I:=1 to 4 do
  383. begin
  384. Inc(TokenStr);
  385. Case TokenStr[0] of
  386. '0'..'9','A'..'F','a'..'f' :
  387. S[i]:=Upcase(TokenStr[0]);
  388. else
  389. Error(SErrInvalidCharacter, [TokenStr[0]]);
  390. end;
  391. end;
  392. // Takes care of conversion... This needs improvement !!
  393. Result:=WideChar(StrToInt('$'+S));
  394. end;
  395. procedure TSQLScanner.SetExcludeKeywords(const AValue: TStrings);
  396. begin
  397. With ExcludeKeywords do
  398. begin
  399. Clear;
  400. AddStrings(AValue);
  401. end;
  402. end;
  403. procedure TSQLScanner.Setoptions(const AValue: TSQLScannerOptions);
  404. Const
  405. F = [soDoubleQuoteStringLiteral,soSingleQuoteIdentifier];
  406. begin
  407. FOptions:=AValue;
  408. if ((Foptions * F) = [soSingleQuoteIdentifier]) then
  409. Exclude(FOptions,soSingleQuoteIdentifier);
  410. end;
  411. procedure TSQLScanner.BuildKeyWords;
  412. Var
  413. I : TSQLToken;
  414. begin
  415. If Not IdentifierTokensOK then
  416. BuildIdentifierTokens;
  417. If FKeywords.Count>0 then
  418. FKeywords.Clear;
  419. for I:=FirstKeyword to LastKeyword do
  420. if (not Assigned(FExclude)) or (FExclude.IndexOf(TokenInfos[I])=-1) then
  421. FKeywords.Add(TokenInfos[I],@IdentifierTokens[i]);
  422. end;
  423. function TSQLScanner.DoStringLiteral: TSQLToken;
  424. Var
  425. Delim : Char;
  426. TokenStart : PChar;
  427. Len,OLen : Integer;
  428. S : UnicodeString;
  429. Procedure AppendBufToTokenString(DoNextToken : Boolean);
  430. begin
  431. SetLength(FCurTokenString, OLen + Len+Length(S));
  432. if Len > 0 then
  433. Move(TokenStart^, FCurTokenString[OLen + 1], Len);
  434. If Length(S)>0 then
  435. Move(S[1],FCurTokenString[OLen + Len+1],Length(S));
  436. Inc(OLen, Len+Length(S));
  437. If DoNextToken then
  438. Inc(TokenStr);
  439. TokenStart := TokenStr+1;
  440. end;
  441. Function CheckTokenBuf : Boolean;
  442. begin
  443. Result:=(TokenStr[0]<>#0);
  444. If Not Result then
  445. begin
  446. S:='';
  447. Len:=TokenStr-TokenStart;
  448. AppendBufToTokenString(False);
  449. Result:=FetchLine;
  450. TokenStart:=TokenStr;
  451. end;
  452. end;
  453. begin
  454. Delim:=TokenStr[0];
  455. if Delim='[' then
  456. Delim:=']';
  457. Inc(TokenStr);
  458. TokenStart := TokenStr;
  459. OLen := 0;
  460. FCurTokenString := '';
  461. while not (TokenStr[0]=#0) do
  462. begin
  463. If (TokenStr[0]=Delim) then
  464. begin
  465. if (not (soNoDoubleDelimIsChar in options)) and (TokenStr[1]=Delim) then
  466. begin
  467. S:=Delim;
  468. Len := TokenStr - TokenStart;
  469. AppendBufToTokenString(True);
  470. end
  471. else
  472. Break;
  473. end
  474. else if (TokenStr[0]='\') and (soBackSlashEscapes in Options) then
  475. begin
  476. // Save length
  477. Len := TokenStr - TokenStart;
  478. Inc(TokenStr);
  479. if not CheckTokenBuf then
  480. Error(SErrOpenString);
  481. // Read escaped token
  482. Case TokenStr[0] of
  483. '"' : S:='"';
  484. '''' : S:='''';
  485. 't' : S:=#9;
  486. 'b' : S:=#8;
  487. 'n' : S:=#10;
  488. 'r' : S:=#13;
  489. 'f' : S:=#12;
  490. '\' : S:='\';
  491. '/' : S:='/';
  492. 'u' : begin
  493. S:=ReadUniCodeEscape;
  494. end;
  495. else
  496. Error(SErrInvalidCharacter, [TokenStr[0]]);
  497. end;
  498. AppendBufToTokenString(False);
  499. end;
  500. Inc(TokenStr);
  501. if not CheckTokenBuf then
  502. Error(SErrOpenString);
  503. end;
  504. if Not CheckTokenBuf then
  505. Error(SErrOpenString);
  506. S:='';
  507. Len := TokenStr - TokenStart;
  508. AppendBufToTokenString(True);
  509. Result := tsqlString;
  510. end;
  511. function TSQLScanner.DoNumericLiteral :TSQLToken;
  512. Var
  513. TokenStart : PChar;
  514. Len : Integer;
  515. isFloat : boolean;
  516. begin
  517. TokenStart := TokenStr;
  518. IsFloat:=False;
  519. while true do
  520. begin
  521. Inc(TokenStr);
  522. case TokenStr[0] of
  523. 'x':
  524. If (TokenStart[0]='0') and ((TokenStr-TokenStart)=1) then
  525. begin
  526. Inc(TokenStr);
  527. while Upcase(TokenStr[0]) in ['0'..'9','A'..'F'] do
  528. Inc(TokenStr);
  529. end
  530. else
  531. Error(SInvalidHexadecimalNumber);
  532. '.':
  533. begin
  534. isfloat:=true;
  535. if TokenStr[1] in ['0'..'9', 'e', 'E'] then
  536. begin
  537. Inc(TokenStr);
  538. repeat
  539. Inc(TokenStr);
  540. until not (TokenStr[0] in ['0'..'9', 'e', 'E','-','+']);
  541. end;
  542. break;
  543. end;
  544. '0'..'9': ;
  545. 'e', 'E':
  546. begin
  547. isFloat:=true;
  548. Inc(TokenStr);
  549. if TokenStr[0] in ['-','+'] then
  550. Inc(TokenStr);
  551. while TokenStr[0] in ['0'..'9'] do
  552. Inc(TokenStr);
  553. break;
  554. end;
  555. else
  556. break;
  557. end;
  558. end;
  559. Len:=TokenStr-TokenStart;
  560. Setlength(FCurTokenString, Len);
  561. if (Len>0) then
  562. Move(TokenStart^,FCurTokenString[1],Len);
  563. If IsFloat then
  564. Result := tsqlFloatNumber
  565. else
  566. Result:=tsqlIntegerNumber;
  567. end;
  568. function TSQLScanner.DoIdentifier : TSQLToken;
  569. Var
  570. TokenStart:PChar;
  571. Len : Integer;
  572. {I : TSQLToken;}
  573. S : ShortString;
  574. P : ^TSQLToken;
  575. begin
  576. Result:=tsqlIdentifier;
  577. TokenStart := TokenStr;
  578. repeat
  579. Inc(TokenStr);
  580. If (TokenStr[0]='\') and (TokenStr[1]='u') then
  581. until not (TokenStr[0] in ['A'..'Z', 'a'..'z', '0'..'9', '_','$']);
  582. Len:=(TokenStr-TokenStart);
  583. SetLength(FCurTokenString,Len);
  584. if Len > 0 then
  585. Move(TokenStart^,FCurTokenString[1],Len);
  586. S:=UpperCase(FCurTokenString);
  587. // Check if this is a keyword or identifier
  588. // to do: Optimize this!
  589. If FKeyWords.Count=0 then
  590. BuildKeyWords;
  591. P:=FKeyWords.Find(S);
  592. If (P<>Nil) then
  593. Result:=P^ //keyword found
  594. else if (AlternateTerminator<>'') and (S=AlternateTerminator) then
  595. Result:=tsqlTerminator;
  596. { I:=FirstKeyword;
  597. While (Result=tsqlIdentifier) and (I<=Lastkeyword) do
  598. begin
  599. if (S=TokenInfos[i]) then
  600. begin
  601. Result := i;
  602. FCurToken := Result;
  603. exit;
  604. end;
  605. I:=Succ(I);
  606. end;}
  607. end;
  608. function TSQLScanner.DoSymbolString : TSQLToken;
  609. Var
  610. Len : Integer;
  611. P : ^TSQLToken;
  612. TokenStart : PChar;
  613. begin
  614. Result:=tsqlUnknown;
  615. // Get "word" finalized by end of string, space/tab/line ending.
  616. TokenStart:=TokenStr;
  617. repeat
  618. Inc(TokenStr);
  619. until (TokenStr[0] in [#0, #9, #10, #13, ' ']);
  620. Len:=(TokenStr-TokenStart);
  621. if Len > 0 then
  622. begin
  623. result:=tsqlSymbolString;
  624. SetLength(FCurTokenString,Len);
  625. Move(TokenStart^,FCurTokenString[1],Len);
  626. if (AlternateTerminator<>'') and (CurtokenString=AlternateTerminator) then
  627. Exit(tsqlTerminator);
  628. // Check if this is a keyword or identifier/literal
  629. // Probably not (due to naming rules) but it doesn't hurt
  630. If FKeyWords.Count=0 then
  631. BuildKeyWords;
  632. P:=FKeyWords.Find(FCurTokenString); //case-sensitive search
  633. If (P<>Nil) then
  634. Result:=P^; //keyword found, just in case
  635. end;
  636. end;
  637. function TSQLScanner.FetchToken: TSQLToken;
  638. begin
  639. Repeat
  640. if TokenStr = nil then
  641. if not FetchLine then
  642. begin
  643. Result := tsqlEOF;
  644. FCurToken := Result;
  645. exit;
  646. end;
  647. FCurTokenRow:=CurRow;
  648. FCurTokenColumn:=CurColumn;
  649. FCurTokenString := '';
  650. case TokenStr[0] of
  651. #0: // Empty line
  652. begin
  653. FetchLine;
  654. Result := tsqlWhitespace;
  655. end;
  656. '/' :
  657. Result:=CommentDiv;
  658. #9, ' ',#10,#13:
  659. Result := DoWhiteSpace;
  660. '''':
  661. begin
  662. Result:=DoStringLiteral;
  663. if (soSingleQuoteIdentifier in Options) then
  664. result:=tsqlIdentifier;
  665. end;
  666. '"':
  667. begin
  668. Result:=DoStringLiteral;
  669. If (soDoubleQuoteStringLiteral in options) then
  670. Result:=tsqlString
  671. else
  672. Result:=tsqlIdentifier;
  673. end;
  674. '`':
  675. begin
  676. Result:=DoStringLiteral;
  677. If (soBackQuoteIdentifier in options) then
  678. Result:=tsqlIdentifier
  679. else
  680. Error(SErrUnknownToken,['`']);
  681. end;
  682. '0'..'9':
  683. Result:=DoNumericLiteral;
  684. '?':
  685. begin
  686. Inc(TokenStr);
  687. Result:=tsqlPlaceHolder;
  688. end;
  689. '!':
  690. begin
  691. Inc(TokenStr);
  692. If TokenStr[0]='>' then
  693. Result:=tsqlLE
  694. else if (TokenStr[0]='<') then
  695. Result:=tsqlGE
  696. else if (TokenStr[0]='=') then
  697. Result:=tsqlNE
  698. else
  699. Result:=tsqlUnknown;
  700. Inc(TokenStr);
  701. end;
  702. '|':
  703. begin
  704. Inc(TokenStr);
  705. If Tokenstr[0]='|' then
  706. begin
  707. Inc(TokenStr);
  708. Result := tsqlConcatenate
  709. end
  710. else
  711. Error(SBarExpected);
  712. end;
  713. '(':
  714. begin
  715. Inc(TokenStr);
  716. Result := tsqlBraceOpen;
  717. end;
  718. ')':
  719. begin
  720. Inc(TokenStr);
  721. Result := tsqlBraceClose;
  722. end;
  723. '[':
  724. begin
  725. If (soSquareBracketsIdentifier in options) then
  726. begin
  727. Result:=DoStringLiteral;
  728. Result:=tsqlIdentifier;
  729. end
  730. Else
  731. begin
  732. Inc(TokenStr);
  733. Result := tsqlSquareBraceOpen;
  734. end;
  735. end;
  736. ']':
  737. begin
  738. Inc(TokenStr);
  739. Result := tsqlSquareBraceClose;
  740. end;
  741. '*':
  742. begin
  743. Inc(TokenStr);
  744. Result := tsqlMul;
  745. end;
  746. '+':
  747. begin
  748. Inc(TokenStr);
  749. Result := tsqlPlus;
  750. end;
  751. ',':
  752. begin
  753. Inc(TokenStr);
  754. Result := tsqlComma;
  755. end;
  756. '-':
  757. begin
  758. Inc(TokenStr);
  759. If (TokenStr[0]='-') then
  760. begin
  761. Inc(TokenStr);
  762. Result:=DoSingleLineComment
  763. end
  764. else if (TokenStr[0] in ['0'..'9']) then
  765. begin
  766. Result:=DoNumericLiteral;
  767. If (Result in [tsqlIntegerNumber,tsqlFloatNumber]) then
  768. FCurTokenString:='-'+FCurTokenString;
  769. end
  770. else
  771. Result := tsqlMinus;
  772. end;
  773. '.':
  774. begin
  775. Inc(TokenStr);
  776. Result := tsqlDot;
  777. end;
  778. ':':
  779. begin
  780. Inc(TokenStr);
  781. Result := tsqlColon;
  782. end;
  783. ';':
  784. begin
  785. Inc(TokenStr);
  786. Result := tsqlSemicolon;
  787. end;
  788. '<':
  789. begin
  790. Inc(TokenStr);
  791. if TokenStr[0] = '>' then
  792. begin
  793. Inc(TokenStr);
  794. Result := tsqlNE;
  795. end
  796. else if (TokenStr[0] = '=') then
  797. begin
  798. Inc(TokenStr);
  799. Result := tsqlLE;
  800. end
  801. else
  802. Result := tsqlLT;
  803. end;
  804. '=':
  805. begin
  806. Inc(TokenStr);
  807. Result := tsqleQ;
  808. end;
  809. '>':
  810. begin
  811. Inc(TokenStr);
  812. if TokenStr[0] = '=' then
  813. begin
  814. Inc(TokenStr);
  815. Result:=tsqlGE;
  816. end
  817. else
  818. Result := tsqlGT;
  819. end;
  820. 'a'..'z',
  821. 'A'..'Z', '_':
  822. Result:=DoIdentifier;
  823. else
  824. // Symbol of some sort
  825. Result:=DoSymbolString;
  826. //Error(SErrUnknownToken,[TokenStr[0]]);
  827. end; // Case
  828. Until (Not (Result in [tsqlComment,tsqlWhitespace])) or
  829. ((Result=tsqlComment) and (soReturnComments in options)) or
  830. ((Result=tsqlWhiteSpace) and (soReturnWhiteSpace in Options));
  831. FCurToken:=Result;
  832. end;
  833. function TSQLScanner.IsEndOfLine: Boolean;
  834. begin
  835. Result:=(TokenStr=Nil) or (TokenStr[0] in [#0,#10,#13]);
  836. end;
  837. function TSQLScanner.GetCurColumn: Integer;
  838. begin
  839. Result := TokenStr - PChar(FCurLine) + 1;
  840. end;
  841. Procedure TSQLScanner.ClearKeywords(Sender : TObject);
  842. begin
  843. If Assigned(FKeywords) then
  844. FKeywords.Clear;
  845. end;
  846. function TSQLScanner.GetExcludeKeywords: TStrings;
  847. begin
  848. If FExclude=Nil then
  849. begin
  850. FExclude:=TStringList.Create;
  851. FExclude.Duplicates:=dupIgnore;
  852. FExclude.Sorted:=true;
  853. FExclude.OnChange:=@ClearKeywords;
  854. end;
  855. Result:=FExclude;
  856. end;
  857. { TStreamLineReader }
  858. constructor TStreamLineReader.Create(AStream: TStream);
  859. begin
  860. FStream:=AStream;
  861. FBufPos:=0;
  862. FBufLen:=0;
  863. end;
  864. function TStreamLineReader.IsEOF: Boolean;
  865. begin
  866. Result:=(FBufPos>=FBufLen);
  867. If Result then
  868. begin
  869. FillBuffer;
  870. Result:=(FBufLen=0);
  871. end;
  872. end;
  873. procedure TStreamLineReader.FillBuffer;
  874. begin
  875. FBufLen:=FStream.Read(Buffer,SizeOf(Buffer)-1);
  876. Buffer[FBufLen]:=0;
  877. FBufPos:=0;
  878. end;
  879. function TStreamLineReader.ReadLine: string;
  880. Var
  881. FPos,OLen,Len: Integer;
  882. PRun : PByte;
  883. begin
  884. FPos:=FBufPos;
  885. Result:='';
  886. Repeat
  887. PRun:=@Buffer[FBufPos];
  888. While (FBufPos<FBufLen) and Not (PRun^ in [10,13]) do
  889. begin
  890. Inc(PRun);
  891. Inc(FBufPos);
  892. end;
  893. If (FBufPos=FBufLen) then
  894. begin
  895. Len:=FBufPos-FPos;
  896. If (Len>0) then
  897. begin
  898. Olen:=Length(Result);
  899. SetLength(Result,OLen+Len);
  900. Move(Buffer[FPos],Result[OLen+1],Len);
  901. end;
  902. FillBuffer;
  903. FPos:=FBufPos;
  904. end;
  905. until (FBufPos=FBufLen) or (PRun^ in [10,13]);
  906. Len:=FBufPos-FPos+1;
  907. If (Len>0) then
  908. begin
  909. Olen:=Length(Result);
  910. SetLength(Result,OLen+Len);
  911. Move(Buffer[FPos],Result[OLen+1],Len);
  912. end;
  913. If (PRun^ in [10,13]) and (FBufPos<FBufLen) then
  914. begin
  915. Inc(FBufPos);
  916. // Check #13#10
  917. If (PRun^=13) then
  918. begin
  919. If (FBufPos=FBufLen) then
  920. FillBuffer;
  921. If (FBufPos<FBufLen) and (Buffer[FBufpos]=10) then
  922. begin
  923. Inc(FBufPos);
  924. Result:=Result+#10;
  925. end;
  926. end;
  927. end;
  928. end;
  929. end.