├── Source ├── SQLParserDB.mdb ├── sql.txt ├── Data.DB.Lexer.pas └── Data.DB.Parser.pas ├── Demos ├── TestSQLParser.prjmgc ├── TestSQLParser.dpr ├── TestSQLParser.dproj ├── Unit4.dfm └── Unit4.pas ├── LICENSE ├── README.md └── .gitignore /Source/SQLParserDB.mdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffsmith82/DelphiSqlParser/HEAD/Source/SQLParserDB.mdb -------------------------------------------------------------------------------- /Demos/TestSQLParser.prjmgc: -------------------------------------------------------------------------------- 1 | [Settings] 2 | AutoLibSuffix=0 3 | ClearChildAppSettings=0 4 | ClearChildPackageSettings=0 5 | ClearChildVersionInfo=1 6 | NormalizeDproj=1 7 | SplitDproj=0 8 | EnableMissingPlatforms=0 9 | RemoveUnusedPlatforms=1 10 | RefreshFormType=0 11 | RemoveExcludedPackages=1 12 | 13 | -------------------------------------------------------------------------------- /Demos/TestSQLParser.dpr: -------------------------------------------------------------------------------- 1 | program TestSQLParser; 2 | 3 | uses 4 | Vcl.Forms, 5 | Unit4 in 'Unit4.pas' {Form4}, 6 | Data.DB.Lexer in '..\Source\Data.DB.Lexer.pas', 7 | Data.DB.Parser in '..\Source\Data.DB.Parser.pas'; 8 | 9 | {$R *.res} 10 | 11 | begin 12 | Application.Initialize; 13 | Application.MainFormOnTaskbar := True; 14 | Application.CreateForm(TForm4, Form4); 15 | Application.Run; 16 | end. 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Geoffrey Smith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DelphiSqlParser 2 | ## Project Goals 3 | I have listed some goals of this project below. 4 | ### Create a SQL Parser in Delphi 5 | - To create a SQL parser in Delphi that can be used to create an AST of the SQL 6 | - Ability to create a Syntax highlighter for SQL 7 | ### Ability to prevent SQL injection attacks 8 | - by forcing the use of parameters by preventing the use of constant values in SQL 9 | - detecting multiple statements that are going to be executed together 10 | - detecting always true / always false statements 11 | - detecting statements like `select 1` in a where restriction 12 | ### SQL Manipulation 13 | - Do equivilant of SQL refactoring (renaming various things like table names,field names etc) 14 | - Convert between different dialects of SQL 15 | 16 | ## Project Status 17 | 18 | Currently the project is in a very early stage although, it can decode fair number of the simple SQL commands although for anything complicated it probably can't yet handle. 19 | - The project can currently decode 69 out of the 70 test SQL statements. 20 | - The project can compare what the expected AST is to what the decoded AST is to ensure the parser is working properly 21 | - At the moment no attempt to limit SQL dialect type has been implemented. 22 | - Initial testing of `1 = 1` like conditions for detecting SQL Injection attacks 23 | - Initial testing of `select 1` like conditions in where clauses 24 | - Detection of constant value anywhere in the SQL statement 25 | - Add check for multiple statements in SQL text -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss 69 | modules/ 70 | -------------------------------------------------------------------------------- /Demos/TestSQLParser.dproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | True 4 | Application 5 | Debug 6 | VCL 7 | TestSQLParser.dpr 8 | Win32 9 | {18AAAB51-648D-4DB4-8543-5ABFC6D3EA61} 10 | 18.8 11 | 0 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | TestSQLParser 28 | .\$(Platform)\$(Config) 29 | .\$(Platform)\$(Config) 30 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) 31 | $(BDS)\bin\delphi_PROJECTICON.ico 32 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 33 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 34 | true 35 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 36 | 3081 37 | 38 | 39 | true 40 | true 41 | DEBUG;$(DCC_Define) 42 | true 43 | false 44 | true 45 | 46 | 47 | 0 48 | RELEASE;$(DCC_Define) 49 | false 50 | 0 51 | 52 | 53 | 54 | MainSource 55 | 56 | 57 |
Form4
58 | dfm 59 |
60 | 61 | 62 | 63 | Base 64 | 65 | 66 | Cfg_1 67 | Base 68 | 69 | 70 | Cfg_2 71 | Base 72 | 73 |
74 | 75 | Delphi.Personality.12 76 | Application 77 | 78 | 79 | 80 | TestSQLParser.dpr 81 | 82 | 83 | 84 | 85 | 86 | 87 | 12 88 | 89 | 90 | 91 | 92 |
93 | -------------------------------------------------------------------------------- /Source/sql.txt: -------------------------------------------------------------------------------- 1 | SELECT * FROM My_Schema.Tables; 2 | SELECT Student_ID FROM STUDENT; 3 | SELECT * FROM STUDENT; 4 | SELECT EMP_ID, NAME FROM EMPLOYEE_TBL WHERE EMP_ID = '0000'; 5 | SELECT EMP_ID, LAST_NAME FROM EMPLOYEE WHERE CITY = 'Seattle' ORDER BY EMP_ID; 6 | SELECT EMP_ID, LAST_NAME FROM EMPLOYEE_TBL WHERE CITY = 'INDIANAPOLIS' ORDER BY EMP_ID ASC; 7 | SELECT Name, Age FROM Patients WHERE Age > 40 GROUP BY Name, Age ORDER BY Name; 8 | SELECT COUNT(price), price FROM orders WHERE price < 70 GROUP BY price ORDER BY price 9 | SELECT COUNT(CustomerID), Country FROM Customers GROUP BY Country; 10 | SELECT SUM(Salary) FROM Employee WHERE Emp_Age < 30; 11 | SELECT AVG(Price) FROM Products; 12 | SELECT * FROM My_Schema.views; 13 | SELECT * INTO newtable FROM oldtable WHERE 1 = 0; 14 | CREATE VIEW Failing_Students AS SELECT S_NAME, Student_ID FROM STUDENT WHERE GPA > 40; 15 | UPDATE Customers SET Zip=Phone, Phone=Zip; 16 | SELECT DISTINCT ID FROM Customers; 17 | SELECT TOP 25 FROM Customers WHERE Customer_ID<>NULL; 18 | SELECT * From Customers WHERE Name LIKE 'Herb%' 19 | SELECT ID FROM Orders WHERE Date BETWEEN '01/12/2018' AND '01/13/2018' 20 | SELECT ID FROM Customers INNER JOIN Orders ON Customers.ID = Orders.ID 21 | SELECT phone FROM Customers UNION SELECT item FROM Orders 22 | SELECT Item AS item_description FROM Orders 23 | CREATE DATABASE AllSales 24 | ALTER TABLE Customers ADD Birthday varchar(80) 25 | SELECT Name, Birthday, Phone, Address, Zip FROM Customers 26 | SELECT Name FROM Customers WHERE EXISTS (SELECT Item FROM Orders WHERE Customers.ID = Orders.ID AND Price < 50) 27 | INSERT INTO Yearly_Orders SELECT * FROM Orders WHERE Date<=1/1/2018; 28 | SELECT COUNT(ID), Region FROM Customers GROUP BY Region HAVING COUNT(ID) > 0; 29 | SELECT TOP 25 FROM Customers WHERE Customer_ID<>NULL; 30 | SELECT LAT_N, CITY, TEMP_F FROM STATS, STATION WHERE MONTH = 7 AND STATS.ID = STATION.ID ORDER BY TEMP_F; 31 | SELECT * From Customers WHERE Name LIKE 'Herb%' 32 | SELECT ID FROM Customers INNER JOIN Orders ON Customers.ID = Orders.ID 33 | SELECT phone FROM Customers UNION SELECT item FROM Orders 34 | SELECT MAX(TEMP_F), MIN(TEMP_F), AVG(RAIN_I), ID FROM STATS GROUP BY ID; 35 | DROP TABLE table_name 36 | DROP DATABASE Employee; 37 | DROP INDEX Employee_Info.idex_EmployeeName; 38 | DROP CONSTRAINT UC_Employee_Info; 39 | DROP VIEW V1; 40 | DROP USER someuser; 41 | CREATE TABLE Employee_Info ( EmployeeID int, EmployeeName varchar(255), EmergencyContactName varchar(255), PhoneNumber int, Address varchar(255), City varchar(255), Country varchar(255) ); 42 | TRUNCATE TABLE Employee_Info; 43 | ALTER TABLE Employee_Info ADD BloodGroup varchar(255); 44 | ALTER COLUMN ColumnName varchar(255); 45 | BACKUP DATABASE DatabaseName TO DISK = 'filepath'; 46 | ALTER TABLE Employee_Info MODIFY PhoneNumber int NOT NULL; 47 | CREATE INDEX idex_EmployeeName ON Persons (EmployeeName); 48 | USE Employee; 49 | INSERT INTO Employee_Info(EmployeeID, EmployeeName, Emergency, ContactName, PhoneNumber, Address, City, Country) VALUES ('06', 'Sanjana','Jagannath', '9921321141', 'Camel Street House No 12', 'Chennai', 'India'); 50 | UPDATE Employee_Info SET EmployeeName = 'Aahana', City= 'Ahmedabad' WHERE EmployeeID = 1; 51 | DELETE FROM Employee_Info WHERE EmployeeName='Preeti'; 52 | SELECT EmployeeName, PhoneNumber INTO EmployeeContactDetails FROM Employee; 53 | SELECT * FROM Employee_Info WHERE City='Mumbai' AND City='Hyderabad'; 54 | SELECT * FROM Employee_Info WHERE NOT City='Mumbai'; 55 | SELECT * FROM Employee_Salary WHERE Salary BETWEEN 40000 AND 50000; 56 | SELECT EmergencyContactName FROM Employee_Info WHERE EXISTS (SELECT EmergencyContactName FROM Employee_Info WHERE EmployeeId = 05 AND City = 'Kolkata'); 57 | SELECT EmployeeName, City FROM Employee_Info ORDER BY (CASE WHEN City IS NULL THEN 'Country is India by default' ELSE City END); 58 | SELECT Employee_Info.EmployeeName, Technologies.TechID FROM Employee_Info LEFT JOIN Technologies ON Employee_Info.EmployeeID = Technologies.EmpIDID ORDER BY Employee_Info.EmployeeName; 59 | SELECT Technologies.TechID FROM Technologies RIGHT JOIN Employee_Info ON Technologies.EmpID = Employee_Info.EmployeeID ORDER BY Technologies.TechID; 60 | GRANT SELECT ON Employee_Info TO user1; 61 | GRANT SELECT,INSERT,UPDATE,DELETE ON Employee_Info2 TO user2; 62 | INSERT INTO shop VALUES (1,'A',3.45),(1,'B',3.99),(2,'A',10.99),(3,'B',1.45),(3,'C',1.69), (3,'D',1.25),(4,'D',19.95); 63 | SELECT article, dealer, price FROM shop WHERE price=(SELECT MAX(price) FROM shop) 64 | SELECT article, dealer, price FROM shop ORDER BY price DESC LIMIT 1 65 | CREATE TEMPORARY TABLE tmp ( article INT(4) UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL, price DOUBLE(16,2) DEFAULT '0.00' NOT NULL); 66 | LOCK TABLES shop READ; 67 | UNLOCK TABLES; 68 | SELECT Year(t1.date), * FROM t1 AS table1 INNER JOIN table2 ON table1.f1 = table2.f2 WHERE (table1.id = 12); 69 | SELECT Customers.ID, Customers.Company FROM Customers WHERE NOT EXISTS (SELECT Orders.OrderID FROM Orders WHERE (Orders.CustomerID = Customers.CustomerID) AND (Orders.OrderDate > Date() - 90)); 70 | SELECT Year([Orders].[OrderDate]) AS TheYear, Month([Orders].[OrderDate]) AS TheMonth, Sum([Order Details].[Quantity]*[Order Details].[UnitPrice]) AS MonthAmount, (SELECT Sum(OD.Quantity * OD.UnitPrice) AS YTD FROM Orders AS A INNER JOIN [Order Details] AS OD ON A.OrderID = OD.OrderID WHERE A.OrderDate >= DateSerial(Year([Orders].[OrderDate]),1,1) AND A.OrderDate < DateSerial(Year([Orders].[OrderDate]), Month([Orders].[OrderDate]) + 1, 1)) AS YTDAmount FROM Orders INNER JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID GROUP BY Year([Orders].[OrderDate]), Month([Orders].[OrderDate]); -------------------------------------------------------------------------------- /Source/Data.DB.Lexer.pas: -------------------------------------------------------------------------------- 1 | unit Data.DB.Lexer; 2 | 3 | interface 4 | uses 5 | System.SysUtils, System.Classes; 6 | type 7 | TTokenType = (ttUnknown, ttKeyword, ttIdentifier, ttOperator, ttNumber, ttString, ttComment, ttWhitespace, ttEOF); 8 | TCharType = (ctOther, ctLetterStart, ctLetterNumber, ctNumber, ctHash, ctQuote, ctDollar, ctDash, ctComment); 9 | TToken = record 10 | TokenType: TTokenType; 11 | Value: string; 12 | Position: Integer; 13 | Line: Integer; 14 | end; 15 | TSQLLexer = class 16 | private 17 | FInput: string; 18 | FPosition: Integer; 19 | FLine: Integer; 20 | FCurrentToken: TToken; 21 | function GetNextChar: Char; 22 | function PeekNextChar: Char; 23 | function IsEOF: Boolean; 24 | function ReadWhile(const Predicate: TFunc): string; 25 | function GetSourcePos: Integer; 26 | function GetTokenString: string; 27 | function GetLinePos: Integer; 28 | function GetToken: TCharType; 29 | function ReadRest: string; 30 | public 31 | constructor Create(const AInput: string); overload; 32 | constructor Create(AStream: TStream); overload; 33 | function GetNextToken: TToken; 34 | property SourcePos: Integer read GetSourcePos; 35 | property TokenString: string read GetTokenString; 36 | property LinePos: Integer read GetLinePos; 37 | property Token: TCharType read GetToken; 38 | end; 39 | implementation 40 | constructor TSQLLexer.Create(const AInput: string); 41 | begin 42 | FInput := AInput; 43 | FPosition := 1; 44 | FLine := 1; 45 | end; 46 | constructor TSQLLexer.Create(AStream: TStream); 47 | var 48 | StringStream: TStringStream; 49 | begin 50 | StringStream := TStringStream.Create; 51 | try 52 | StringStream.LoadFromStream(AStream); 53 | FInput := StringStream.DataString; 54 | FPosition := 1; 55 | FLine := 1; 56 | finally 57 | StringStream.Free; 58 | end; 59 | end; 60 | function TSQLLexer.GetNextChar: Char; 61 | begin 62 | if FPosition > Length(FInput) then 63 | Result := #0 64 | else 65 | Result := FInput[FPosition]; 66 | Inc(FPosition); 67 | if Result = #10 then 68 | Inc(FLine); 69 | end; 70 | function TSQLLexer.PeekNextChar: Char; 71 | begin 72 | if (FPosition) > Length(FInput) then 73 | Result := #0 74 | else 75 | Result := FInput[FPosition]; 76 | end; 77 | function TSQLLexer.IsEOF: Boolean; 78 | begin 79 | Result := FPosition > Length(FInput); 80 | end; 81 | function TSQLLexer.ReadWhile(const Predicate: TFunc): string; 82 | var 83 | Ch: Char; 84 | begin 85 | Result := ''; 86 | while not IsEOF do 87 | begin 88 | Ch := GetNextChar; 89 | if not Predicate(Ch) then 90 | begin 91 | Dec(FPosition); 92 | if Ch = #10 then 93 | Dec(FLine); 94 | Break; 95 | end; 96 | Result := Result + Ch; 97 | end; 98 | end; 99 | function TSQLLexer.ReadRest: string; 100 | var 101 | Ch: Char; 102 | begin 103 | Result := ''; 104 | while not IsEOF do 105 | begin 106 | Ch := GetNextChar; 107 | Result := Result + Ch; 108 | end; 109 | end; 110 | function TSQLLexer.GetNextToken: TToken; 111 | var 112 | Ch: Char; 113 | begin 114 | while not IsEOF do 115 | begin 116 | Ch := GetNextChar; 117 | case Ch of 118 | ' ', #9, #10, #13: 119 | begin 120 | // Skip whitespace characters 121 | Continue; 122 | end; 123 | ':': 124 | begin 125 | FCurrentToken.TokenType := ttIdentifier; 126 | FCurrentToken.Value := Ch + ReadWhile(function(C: Char): Boolean 127 | begin 128 | Result := CharInSet(C, ['A'..'Z', 'a'..'z', '0'..'9', '_']); 129 | end); 130 | end; 131 | 'A'..'Z', 'a'..'z', '_': 132 | begin 133 | FCurrentToken.TokenType := ttIdentifier; 134 | FCurrentToken.Value := Ch + ReadWhile(function(C: Char): Boolean 135 | begin 136 | Result := CharInSet(C, ['A'..'Z', 'a'..'z', '0'..'9', '_']); 137 | end); 138 | end; 139 | '0'..'9': 140 | begin 141 | FCurrentToken.TokenType := ttNumber; 142 | FCurrentToken.Value := Ch + ReadWhile(function(C: Char): Boolean 143 | begin 144 | Result := CharInSet(C, ['0'..'9', '.', 'e', 'E', '+', '-']); 145 | end); 146 | if FCurrentToken.Value.EndsWith('--') then 147 | begin 148 | Dec(FPosition, 2); 149 | FCurrentToken.Value := FCurrentToken.Value.Substring(0, FCurrentToken.Value.Length - 2); 150 | end; 151 | end; 152 | '.', ',', ';': 153 | begin 154 | FCurrentToken.TokenType := ttOperator; 155 | FCurrentToken.Value := Ch; 156 | end; 157 | '`': 158 | begin 159 | FCurrentToken.TokenType := ttString; 160 | FCurrentToken.Value := Ch + ReadWhile(function(C: Char): Boolean 161 | begin 162 | Result := C <> Ch; 163 | end) + GetNextChar; 164 | end; 165 | '''', '"': 166 | begin 167 | FCurrentToken.TokenType := ttString; 168 | FCurrentToken.Value := Ch + ReadWhile(function(C: Char): Boolean 169 | begin 170 | Result := C <> Ch; 171 | end) + GetNextChar; 172 | end; 173 | '-': 174 | begin 175 | if PeekNextChar = '-' then 176 | begin 177 | GetNextChar; // Consume the second '-' 178 | FCurrentToken.TokenType := ttComment; 179 | FCurrentToken.Value := Ch + '-' + ReadWhile(function(C: Char): Boolean 180 | begin 181 | Result := C <> #10; 182 | end); 183 | end 184 | else 185 | begin 186 | FCurrentToken.TokenType := ttOperator; 187 | FCurrentToken.Value := Ch; 188 | end; 189 | end; 190 | '/': 191 | if PeekNextChar = '*' then 192 | begin 193 | GetNextChar; // Consume the '*' 194 | FCurrentToken.TokenType := ttComment; 195 | FCurrentToken.Value := Ch + '*' + ReadWhile(function(C: Char): Boolean 196 | begin 197 | Result := not ((C = '*') and (PeekNextChar = '/')); 198 | end) + '*/'; 199 | GetNextChar; // Consume the '/' 200 | end 201 | else 202 | begin 203 | FCurrentToken.TokenType := ttOperator; 204 | FCurrentToken.Value := Ch; 205 | end; 206 | '#': 207 | begin 208 | FCurrentToken.TokenType := ttOperator; 209 | FCurrentToken.Value := Ch; 210 | end; 211 | '$': 212 | begin 213 | FCurrentToken.TokenType := ttOperator; 214 | FCurrentToken.Value := Ch; 215 | end; 216 | else 217 | begin 218 | FCurrentToken.TokenType := ttOperator; 219 | FCurrentToken.Value := Ch; 220 | end; 221 | end; 222 | FCurrentToken.Position := FPosition - Length(FCurrentToken.Value); 223 | FCurrentToken.Line := FLine; 224 | Result := FCurrentToken; 225 | Exit; 226 | end; 227 | FCurrentToken.TokenType := ttEOF; 228 | FCurrentToken.Value := ''; 229 | FCurrentToken.Position := FPosition; 230 | FCurrentToken.Line := FLine; 231 | Result := FCurrentToken; 232 | end; 233 | function TSQLLexer.GetSourcePos: Integer; 234 | begin 235 | Result := FCurrentToken.Position; 236 | end; 237 | function TSQLLexer.GetTokenString: string; 238 | begin 239 | Result := FCurrentToken.Value; 240 | end; 241 | function TSQLLexer.GetLinePos: Integer; 242 | begin 243 | Result := FCurrentToken.Line; 244 | end; 245 | function TSQLLexer.GetToken: TCharType; 246 | var 247 | Ch: Char; 248 | begin 249 | if IsEOF then 250 | Exit(ctOther); 251 | Ch := FCurrentToken.Value[1]; 252 | case Ch of 253 | 'A'..'Z', 'a'..'z', '_': 254 | Result := ctLetterStart; 255 | '0'..'9': 256 | Result := ctNumber; 257 | '#': 258 | Result := ctHash; 259 | '''', '"': 260 | Result := ctQuote; 261 | '$': 262 | Result := ctDollar; 263 | '-': 264 | if (Length(FCurrentToken.Value) > 1) and (FCurrentToken.Value[2] = '-') then 265 | Result := ctComment 266 | else 267 | Result := ctDash; 268 | '/': 269 | if (Length(FCurrentToken.Value) > 1) and (FCurrentToken.Value[2] = '*') then 270 | Result := ctComment 271 | else 272 | Result := ctOther; 273 | '.', ',', ';': 274 | Result := ctOther; 275 | else 276 | Result := ctOther; 277 | end; 278 | end; 279 | end. 280 | 281 | -------------------------------------------------------------------------------- /Demos/Unit4.dfm: -------------------------------------------------------------------------------- 1 | object Form4: TForm4 2 | Left = 0 3 | Top = 991 4 | Margins.Left = 1 5 | Margins.Top = 1 6 | Margins.Right = 1 7 | Margins.Bottom = 1 8 | Caption = 'Test SQL Parser' 9 | ClientHeight = 589 10 | ClientWidth = 1001 11 | Color = clBtnFace 12 | Font.Charset = DEFAULT_CHARSET 13 | Font.Color = clWindowText 14 | Font.Height = -11 15 | Font.Name = 'Tahoma' 16 | Font.Style = [] 17 | Position = poDesigned 18 | OnCreate = FormCreate 19 | DesignSize = ( 20 | 1001 21 | 589) 22 | TextHeight = 13 23 | object Memo1: TMemo 24 | Left = 6 25 | Top = 176 26 | Width = 571 27 | Height = 122 28 | HideSelection = False 29 | Lines.Strings = ( 30 | 31 | 'SELECT Year(t1.date), * FROM t1 AS table1 INNER JOIN table2 ON t' + 32 | 'able1.f1 = table2.f2 WHERE (table1.id = ' 33 | '12);') 34 | TabOrder = 0 35 | end 36 | object Memo2: TMemo 37 | Left = 6 38 | Top = 304 39 | Width = 571 40 | Height = 226 41 | ScrollBars = ssBoth 42 | TabOrder = 1 43 | end 44 | object Button1: TButton 45 | Left = 544 46 | Top = 536 47 | Width = 75 48 | Height = 26 49 | Caption = 'Decode SQL' 50 | TabOrder = 2 51 | OnClick = Button1Click 52 | end 53 | object Button2: TButton 54 | Left = 376 55 | Top = 536 56 | Width = 131 57 | Height = 26 58 | Caption = 'Decode SQL From File' 59 | TabOrder = 3 60 | OnClick = Button2Click 61 | end 62 | object DBGrid1: TDBGrid 63 | Left = 6 64 | Top = 16 65 | Width = 571 66 | Height = 145 67 | DataSource = dsTestSQLStatements 68 | TabOrder = 4 69 | TitleFont.Charset = DEFAULT_CHARSET 70 | TitleFont.Color = clWindowText 71 | TitleFont.Height = -11 72 | TitleFont.Name = 'Tahoma' 73 | TitleFont.Style = [] 74 | Columns = < 75 | item 76 | Expanded = False 77 | FieldName = 'ID' 78 | Width = 67 79 | Visible = True 80 | end 81 | item 82 | Expanded = False 83 | FieldName = 'Dialect' 84 | Width = 60 85 | Visible = True 86 | end 87 | item 88 | Expanded = False 89 | FieldName = 'Statements' 90 | Width = 67 91 | Visible = True 92 | end> 93 | end 94 | object DBGrid2: TDBGrid 95 | Left = 583 96 | Top = 16 97 | Width = 413 98 | Height = 514 99 | Anchors = [akLeft, akTop, akRight] 100 | DataSource = dsSQLStatementTokens 101 | TabOrder = 5 102 | TitleFont.Charset = DEFAULT_CHARSET 103 | TitleFont.Color = clWindowText 104 | TitleFont.Height = -11 105 | TitleFont.Name = 'Tahoma' 106 | TitleFont.Style = [] 107 | Columns = < 108 | item 109 | Expanded = False 110 | FieldName = 'PositionNo' 111 | Visible = True 112 | end 113 | item 114 | Expanded = False 115 | FieldName = 'TokenID' 116 | Visible = True 117 | end 118 | item 119 | Expanded = False 120 | FieldName = 'TokenTypeName' 121 | Width = 122 122 | Visible = True 123 | end 124 | item 125 | Expanded = False 126 | FieldName = 'TokenText' 127 | Width = 80 128 | Visible = True 129 | end 130 | item 131 | Expanded = False 132 | FieldName = 'TokenCurrentlyDecodedAs' 133 | Visible = True 134 | end 135 | item 136 | Expanded = False 137 | FieldName = 'TokenMatch' 138 | Width = 131 139 | Visible = True 140 | end 141 | item 142 | Expanded = False 143 | FieldName = 'TokenCurrentString' 144 | Visible = True 145 | end> 146 | end 147 | object Button3: TButton 148 | Left = 160 149 | Top = 536 150 | Width = 203 151 | Height = 26 152 | Caption = 'Test SQL and Compare from DB' 153 | TabOrder = 6 154 | OnClick = Button3Click 155 | end 156 | object StatusBar1: TStatusBar 157 | Left = 0 158 | Top = 570 159 | Width = 1001 160 | Height = 19 161 | Panels = < 162 | item 163 | Width = 20 164 | end> 165 | end 166 | object Button4: TButton 167 | Left = 656 168 | Top = 536 169 | Width = 75 170 | Height = 25 171 | Caption = 'Button4' 172 | TabOrder = 8 173 | OnClick = Button4Click 174 | end 175 | object Button5: TButton 176 | Left = 760 177 | Top = 536 178 | Width = 75 179 | Height = 25 180 | Caption = 'ResetDB' 181 | TabOrder = 9 182 | OnClick = Button5Click 183 | end 184 | object AccessConnection: TFDConnection 185 | Params.Strings = ( 186 | 'Database=D:\Programming\DelphiSQLParser\Source\SQLParserDB.mdb' 187 | 'DriverID=MSAcc') 188 | LoginPrompt = False 189 | Left = 352 190 | Top = 120 191 | end 192 | object tblTestSQLStatements: TFDTable 193 | IndexFieldNames = 'ID' 194 | Connection = AccessConnection 195 | TableName = 'TestSQLStatements' 196 | Left = 120 197 | Top = 200 198 | object tblTestSQLStatementsID: TFDAutoIncField 199 | FieldName = 'ID' 200 | Origin = 'ID' 201 | ProviderFlags = [pfInWhere, pfInKey] 202 | end 203 | object tblTestSQLStatementsDialect: TWideStringField 204 | FieldName = 'Dialect' 205 | Origin = 'Dialect' 206 | Size = 255 207 | end 208 | object tblTestSQLStatementsStatements: TWideMemoField 209 | FieldName = 'Statements' 210 | Origin = 'Statements' 211 | BlobType = ftWideMemo 212 | end 213 | end 214 | object dsTestSQLStatements: TDataSource 215 | DataSet = tblTestSQLStatements 216 | Left = 124 217 | Top = 420 218 | end 219 | object tblTestSQLStatementTokens: TFDTable 220 | OnCalcFields = tblTestSQLStatementTokensCalcFields 221 | IndexName = 'StatementID' 222 | MasterSource = dsTestSQLStatements 223 | MasterFields = 'ID' 224 | Connection = AccessConnection 225 | TableName = 'TestSQLStatementsTokens' 226 | Left = 600 227 | Top = 220 228 | object tblTestSQLStatementTokensTestSqlId: TFDAutoIncField 229 | FieldName = 'TestSqlId' 230 | Origin = 'TestSqlId' 231 | ProviderFlags = [pfInWhere, pfInKey] 232 | end 233 | object tblTestSQLStatementTokensStatementID: TIntegerField 234 | FieldName = 'StatementID' 235 | Origin = 'StatementID' 236 | end 237 | object tblTestSQLStatementTokensPositionNo: TIntegerField 238 | FieldName = 'PositionNo' 239 | Origin = 'PositionNo' 240 | end 241 | object tblTestSQLStatementTokensTokenText: TWideStringField 242 | FieldName = 'TokenText' 243 | Origin = 'TokenText' 244 | Size = 255 245 | end 246 | object tblTestSQLStatementTokensTokenID: TIntegerField 247 | FieldName = 'TokenID' 248 | Origin = 'TokenID' 249 | end 250 | object tblTestSQLStatementTokensTokenTypeName: TStringField 251 | FieldKind = fkCalculated 252 | FieldName = 'TokenTypeName' 253 | Calculated = True 254 | end 255 | object tblTestSQLStatementTokensTokenMatch: TStringField 256 | FieldKind = fkCalculated 257 | FieldName = 'TokenMatch' 258 | Calculated = True 259 | end 260 | end 261 | object dsSQLStatementTokens: TDataSource 262 | DataSet = tblResultsTokens 263 | Left = 736 264 | Top = 80 265 | end 266 | object FirebirdConnection: TFDConnection 267 | Params.Strings = ( 268 | 'DriverID=FB' 269 | 'User_Name=sysdba' 270 | 'Password=masterkey') 271 | LoginPrompt = False 272 | Left = 1552 273 | Top = 600 274 | end 275 | object MySqlConnection: TFDConnection 276 | Params.Strings = ( 277 | 'DriverID=MySQL') 278 | LoginPrompt = False 279 | Left = 1552 280 | Top = 760 281 | end 282 | object MSSQLConnection: TFDConnection 283 | Params.Strings = ( 284 | 'DriverID=MSSQL') 285 | LoginPrompt = False 286 | Left = 1572 287 | Top = 920 288 | end 289 | object tblResultsTokens: TFDMemTable 290 | FetchOptions.AssignedValues = [evMode] 291 | FetchOptions.Mode = fmAll 292 | ResourceOptions.AssignedValues = [rvSilentMode] 293 | ResourceOptions.SilentMode = True 294 | UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates] 295 | UpdateOptions.CheckRequired = False 296 | UpdateOptions.AutoCommitUpdates = True 297 | Left = 440 298 | Top = 56 299 | object tblResultsTokensPositionNo: TIntegerField 300 | FieldName = 'PositionNo' 301 | end 302 | object tblResultsTokensTokenID: TStringField 303 | FieldName = 'TokenID' 304 | end 305 | object tblResultsTokensTokenTypeName: TStringField 306 | FieldName = 'TokenTypeName' 307 | Size = 30 308 | end 309 | object tblResultsTokensTokenText: TStringField 310 | FieldName = 'TokenText' 311 | end 312 | object tblResultsTokensTokenMatch: TStringField 313 | FieldName = 'TokenMatch' 314 | end 315 | object tblResultsTokensTokenCurrentlyDecodedAs: TStringField 316 | FieldName = 'TokenCurrentlyDecodedAs' 317 | Size = 30 318 | end 319 | object tblResultsTokensTokenCurrentString: TStringField 320 | FieldName = 'TokenCurrentString' 321 | Size = 40 322 | end 323 | end 324 | end 325 | -------------------------------------------------------------------------------- /Demos/Unit4.pas: -------------------------------------------------------------------------------- 1 | unit Unit4; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows 7 | , Winapi.Messages 8 | , System.SysUtils 9 | , System.Variants 10 | , System.Classes 11 | , Vcl.Graphics 12 | , Vcl.Controls 13 | , Vcl.Forms 14 | , Vcl.Dialogs 15 | , Vcl.StdCtrls 16 | , Vcl.Grids 17 | , Vcl.DBGrids 18 | , Data.DB 19 | , FireDAC.Stan.Intf 20 | , FireDAC.Stan.Option 21 | , FireDAC.Stan.Error 22 | , FireDAC.UI.Intf 23 | , FireDAC.Phys.Intf 24 | , FireDAC.Stan.Def 25 | , FireDAC.Stan.Pool 26 | , FireDAC.Stan.Async 27 | , FireDAC.Phys 28 | , FireDAC.Phys.MSAcc 29 | , FireDAC.Phys.MSAccDef 30 | , FireDAC.VCLUI.Wait 31 | , FireDAC.Comp.Client 32 | , FireDAC.Stan.Param 33 | , FireDAC.DatS 34 | , FireDAC.DApt.Intf 35 | , FireDAC.DApt 36 | , FireDAC.Comp.DataSet 37 | , FireDAC.Phys.FB 38 | , FireDAC.Phys.FBDef 39 | , FireDAC.Phys.MySQL 40 | , FireDAC.Phys.MySQLDef 41 | , FireDAC.Phys.MSSQL 42 | , FireDAC.Phys.MSSQLDef 43 | , Data.DB.Parser 44 | , Vcl.ComCtrls 45 | , RTTI 46 | , System.TypInfo 47 | , RegularExpressions 48 | ; 49 | 50 | type 51 | TForm4 = class(TForm) 52 | Memo1: TMemo; 53 | Memo2: TMemo; 54 | Button1: TButton; 55 | Button2: TButton; 56 | AccessConnection: TFDConnection; 57 | FirebirdConnection: TFDConnection; 58 | MySqlConnection: TFDConnection; 59 | MSSQLConnection: TFDConnection; 60 | tblTestSQLStatements: TFDTable; 61 | dsTestSQLStatements: TDataSource; 62 | DBGrid1: TDBGrid; 63 | tblTestSQLStatementTokens: TFDTable; 64 | dsSQLStatementTokens: TDataSource; 65 | DBGrid2: TDBGrid; 66 | tblTestSQLStatementsID: TFDAutoIncField; 67 | tblTestSQLStatementsDialect: TWideStringField; 68 | tblTestSQLStatementsStatements: TWideMemoField; 69 | tblTestSQLStatementTokensTestSqlId: TFDAutoIncField; 70 | tblTestSQLStatementTokensStatementID: TIntegerField; 71 | tblTestSQLStatementTokensPositionNo: TIntegerField; 72 | tblTestSQLStatementTokensTokenText: TWideStringField; 73 | tblTestSQLStatementTokensTokenID: TIntegerField; 74 | tblTestSQLStatementTokensTokenTypeName: TStringField; 75 | Button3: TButton; 76 | tblTestSQLStatementTokensTokenMatch: TStringField; 77 | StatusBar1: TStatusBar; 78 | tblResultsTokens: TFDMemTable; 79 | tblResultsTokensPositionNo: TIntegerField; 80 | tblResultsTokensTokenID: TStringField; 81 | tblResultsTokensTokenTypeName: TStringField; 82 | tblResultsTokensTokenText: TStringField; 83 | tblResultsTokensTokenMatch: TStringField; 84 | Button4: TButton; 85 | tblResultsTokensTokenCurrentlyDecodedAs: TStringField; 86 | Button5: TButton; 87 | tblResultsTokensTokenCurrentString: TStringField; 88 | procedure FormCreate(Sender: TObject); 89 | procedure Button1Click(Sender: TObject); 90 | procedure Button2Click(Sender: TObject); 91 | procedure Button3Click(Sender: TObject); 92 | procedure Button4Click(Sender: TObject); 93 | procedure Button5Click(Sender: TObject); 94 | procedure tblTestSQLStatementsAfterScroll(DataSet: TDataSet); 95 | procedure tblTestSQLStatementTokensCalcFields(DataSet: TDataSet); 96 | private 97 | { Private declarations } 98 | SqlTokens : TSQLParser; 99 | public 100 | { Public declarations } 101 | function ProcessSQL(const SQL: string): Integer; 102 | end; 103 | 104 | TIntFieldHelper = class helper for TField 105 | private 106 | procedure SetTokenType(const Value: TTokenTypes); 107 | function AsTTokenType: TTokenTypes; 108 | published 109 | property AsTokenType: TTokenTypes read AsTTokenType write SetTokenType; 110 | end; 111 | 112 | TTokenHelper = record helper for TTokenTypes 113 | function ToString: string; 114 | function AsInteger: Integer; 115 | end; 116 | 117 | var 118 | Form4: TForm4; 119 | 120 | implementation 121 | 122 | {$R *.dfm} 123 | 124 | uses 125 | System.IOUtils 126 | ; 127 | 128 | procedure TForm4.FormCreate(Sender: TObject); 129 | var 130 | filename : string; 131 | begin 132 | SqlTokens := TSQLParser.Create; 133 | filename := ExtractFilePath(ParamStr(0)); 134 | filename := TPath.Combine(filename, '..\..\..\Source\SQLParserDB.mdb'); 135 | AccessConnection.Params.Database := filename; 136 | AccessConnection.Connected := True; 137 | tblTestSQLStatements.Active := True; 138 | tblTestSQLStatementTokens.Active := True; 139 | tblTestSQLStatements.AfterScroll := tblTestSQLStatementsAfterScroll; 140 | 141 | end; 142 | 143 | procedure TForm4.Button1Click(Sender: TObject); 144 | begin 145 | ProcessSQL(Memo1.Text); 146 | end; 147 | 148 | procedure TForm4.Button2Click(Sender: TObject); 149 | var 150 | statements : TStringList; 151 | filename : string; 152 | I: Integer; 153 | GoodCount : Integer; 154 | j: Integer; 155 | undecodedCount : Integer; 156 | begin 157 | GoodCount := 0; 158 | statements := TStringList.Create; 159 | try 160 | filename := ExtractFilePath(ParamStr(0)); 161 | filename := TPath.Combine(filename, '..\..\..\Source\sql.txt'); 162 | statements.LoadFromFile(filename); 163 | for I := 0 to statements.Count - 1 do 164 | begin 165 | undecodedCount := 0; 166 | ProcessSQL(statements[i]); 167 | for j := 0 to SqlTokens.Tokens.Count - 1 do 168 | begin 169 | if SqlTokens.Tokens[j].TokenSQL = tkUnknownToken then 170 | Inc(undecodedCount); 171 | end; 172 | Memo2.Lines.Add('Missed Decoding :' + undecodedCount.ToString); 173 | if undecodedCount = 0 then 174 | Inc(GoodCount); 175 | end; 176 | Memo2.Lines.Add('Total Good Statements :' + GoodCount.ToString); 177 | Memo2.Lines.Add('Total Statements :' + statements.Count.ToString); 178 | finally 179 | FreeAndNil(statements); 180 | end; 181 | end; 182 | 183 | procedure TForm4.Button3Click(Sender: TObject); 184 | var 185 | i: Integer; 186 | begin 187 | tblTestSQLStatementsAfterScroll(tblTestSQLStatements); 188 | repeat 189 | Assert(SqlTokens.Tokens.Count = tblTestSQLStatementTokens.RecordCount); 190 | for i := 0 to SqlTokens.Tokens.Count - 1 do 191 | begin 192 | OutputDebugString(PChar('i = ' + i.ToString)); 193 | if tblTestSQLStatementTokens.Locate('PositionNo', i, []) then 194 | begin 195 | Assert(SqlTokens.Tokens[i].Token = tblTestSQLStatementTokensTokenText.AsString); 196 | Assert(SqlTokens.Tokens[i].TokenSQL = tblTestSQLStatementTokensTokenID.AsTokenType); 197 | end; 198 | end; 199 | 200 | tblTestSQLStatements.Next; 201 | until tblTestSQLStatements.Eof; 202 | end; 203 | 204 | procedure TForm4.Button4Click(Sender: TObject); 205 | var 206 | i: Integer; 207 | begin 208 | for i := 0 to 170 do 209 | begin 210 | try 211 | Memo1.Lines.Add(SqlTokens.TokenIdToTokenString(TTokenTypes(i)) + ' = ' + i.ToString); 212 | except 213 | 214 | end; 215 | end; 216 | end; 217 | 218 | procedure TForm4.Button5Click(Sender: TObject); 219 | begin 220 | tblTestSQLStatementTokens.First; 221 | repeat 222 | tblTestSQLStatementTokens.Delete; 223 | until tblTestSQLStatementTokens.Eof; 224 | end; 225 | 226 | function TForm4.ProcessSQL(const SQL: string): Integer; 227 | var 228 | i : Integer; 229 | v : TArray; 230 | begin 231 | try 232 | Result := SqlTokens.ProcessSQL(SQL); 233 | except 234 | 235 | end; 236 | Memo2.LockDrawing; 237 | try 238 | for i := 0 to SqlTokens.Tokens.Count - 1 do 239 | begin 240 | if SqlTokens.Tokens[i].TokenSQL = tkUnknownToken then 241 | Memo2.Lines.Add(i.ToString + ' ============= ' + SqlTokens.Tokens[i].token + ' ' + SqlTokens.Tokens[i].TokenSQL.ToString) 242 | else 243 | Memo2.Lines.Add(i.ToString + ' ' + SqlTokens.Tokens[i].token + ' ' + SqlTokens.Tokens[i].TokenSQL.AsInteger.ToString); 244 | end; 245 | Memo2.Lines.Add('///////////////////////////'); 246 | finally 247 | Memo2.UnlockDrawing; 248 | end; 249 | 250 | end; 251 | 252 | procedure TForm4.tblTestSQLStatementsAfterScroll(DataSet: TDataSet); 253 | var 254 | undecodedCount : Integer; 255 | j : Integer; 256 | errPos : Integer; 257 | begin 258 | if not Assigned(SqlTokens) then 259 | SqlTokens := TSQLParser.Create; 260 | Memo1.Clear; 261 | Memo2.Clear; 262 | Memo1.Lines.Text := DataSet.FieldByName('Statements').AsString; 263 | undecodedCount := 0; 264 | errPos := ProcessSQL(Memo1.Lines.Text); 265 | if errPos > 0 then 266 | begin 267 | Memo1.SelStart := errPos; 268 | Memo1.SelLength := 100; 269 | end; 270 | 271 | for j := 0 to SqlTokens.Tokens.Count - 1 do 272 | begin 273 | if SqlTokens.Tokens[j].TokenSQL = tkUnknownToken then 274 | Inc(undecodedCount); 275 | end; 276 | if tblTestSQLStatementTokens.RecordCount = 0 then 277 | begin 278 | for j := 0 to SqlTokens.Tokens.Count - 1 do 279 | begin 280 | tblTestSQLStatementTokens.Append; 281 | tblTestSQLStatementTokens.FieldByName('StatementID').AsInteger := tblTestSQLStatementsID.AsInteger; 282 | tblTestSQLStatementTokens.FieldByName('PositionNo').AsInteger := j; 283 | if j < SqlTokens.Tokens.Count then 284 | begin 285 | tblTestSQLStatementTokens.FieldByName('TokenText').AsString := SqlTokens.Tokens[j].Token; 286 | tblTestSQLStatementTokens.FieldByName('TokenID').AsTokenType := SqlTokens.Tokens[j].TokenSQL; 287 | end; 288 | tblTestSQLStatementTokens.Post; 289 | end; 290 | end; 291 | 292 | tblResultsTokens.Active := False; 293 | tblResultsTokens.Active := True; 294 | 295 | tblTestSQLStatementTokens.First; 296 | repeat 297 | tblResultsTokens.Append; 298 | tblResultsTokens.FieldByName('PositionNo').AsInteger := tblTestSQLStatementTokens.FieldByName('PositionNo').AsInteger; 299 | tblResultsTokens.FieldByName('TokenText').AsString := tblTestSQLStatementTokens.FieldByName('TokenText').AsString; 300 | tblResultsTokens.FieldByName('TokenID').AsInteger := tblTestSQLStatementTokens.FieldByName('TokenID').AsInteger; 301 | tblResultsTokens.FieldByName('TokenTypeName').AsString := SqlTokens.TokenIdToTokenString(tblResultsTokens.FieldByName('TokenID').AsTTokenType); 302 | tblResultsTokens.FieldByName('TokenCurrentlyDecodedAs').AsString := SqlTokens.TokenIdToTokenString(SqlTokens.Tokens.LookupTokenByPosition(tblTestSQLStatementTokens.FieldByName('PositionNo').AsInteger).TokenSQL); 303 | tblResultsTokens.FieldByName('TokenCurrentString').AsString := SqlTokens.Tokens.LookupTokenByPosition(tblTestSQLStatementTokens.FieldByName('PositionNo').AsInteger).Token; 304 | 305 | if tblResultsTokens.FieldByName('TokenID').AsInteger = Ord(SqlTokens.Tokens.LookupTokenByPosition(tblTestSQLStatementTokens.FieldByName('PositionNo').AsInteger).TokenSQL) then 306 | begin 307 | tblResultsTokens.FieldByName('TokenMatch').AsString := 'Match'; 308 | end 309 | else 310 | begin 311 | tblResultsTokens.FieldByName('TokenMatch').AsString := '.'; 312 | end; 313 | 314 | tblResultsTokens.Post; 315 | tblTestSQLStatementTokens.Next; 316 | until tblTestSQLStatementTokens.Eof; 317 | 318 | 319 | 320 | if SqlTokens.DoesStatementModifyDB then 321 | begin 322 | Memo2.Lines.Add('Statement Modifies Database'); 323 | end; 324 | if SqlTokens.DoesCommentExist then 325 | begin 326 | Memo2.Lines.Add('Statement Contains COMMENT!'); 327 | end; 328 | if SqlTokens.DoesDoubleConstantExpressionExist then 329 | begin 330 | Memo2.Lines.Add('Double Constant Expression EXISTS!!!!!!!!!!!!!!'); 331 | end; 332 | if SqlTokens.DoesSelectConstantExist then 333 | begin 334 | Memo2.Lines.Add('Select Constant Expression EXISTS!!!!!!!!!!!!!!'); 335 | end; 336 | if SqlTokens.DoesConstantValueExist then 337 | begin 338 | Memo2.Lines.Add('Constant Value EXISTS. Should this come in via a sql parameter?'); 339 | end; 340 | 341 | if SqlTokens.StatementCount > 1 then 342 | begin 343 | Memo2.Lines.Add('MULTIPLE STATEMENTS EXISTS!!!!!!!!!!!!!!'); 344 | end; 345 | 346 | if SqlTokens.IsDDL then 347 | begin 348 | Memo2.Lines.Add('Statement is DDL'); 349 | end; 350 | Memo2.Lines.Add('Missed Decoding :' + undecodedCount.ToString); 351 | end; 352 | 353 | procedure TForm4.tblTestSQLStatementTokensCalcFields(DataSet: TDataSet); 354 | var 355 | TokenID : TTokenTypes; 356 | TokenCount : Integer; 357 | PositionNo : Integer; 358 | begin 359 | DataSet.FieldByName('TokenTypeName').AsString := SqlTokens.TokenIdToTokenString(DataSet.FieldByName('TokenID').AsTTokenType); 360 | PositionNo := 0; 361 | 362 | if DataSet.Active and (SqlTokens.TokenCount > 0) and 363 | (SqlTokens.TokenCount > tblTestSQLStatementTokens.FieldByName('PositionNo').AsInteger) then 364 | begin 365 | TokenCount := SqlTokens.TokenCount; 366 | PositionNo := DataSet.FieldByName('PositionNo').AsInteger; 367 | if PositionNo > 300 then 368 | Exit; 369 | TokenID := SqlTokens.Tokens.Items[PositionNo].TokenSQL; 370 | // value.FTokens.T 371 | if TokenID = DataSet.FieldByName('TokenID').AsTokenType then 372 | DataSet.FieldByName('TokenMatch').AsString := 'Matches' 373 | else 374 | DataSet.FieldByName('TokenMatch').AsString := '.'; 375 | end; 376 | end; 377 | 378 | { TIntFieldHelper } 379 | 380 | function TIntFieldHelper.AsTTokenType: TTokenTypes; 381 | begin 382 | Result := TTokenTypes(AsInteger); 383 | end; 384 | 385 | procedure TIntFieldHelper.SetTokenType(const Value: TTokenTypes); 386 | begin 387 | AsInteger := Ord(Value); 388 | end; 389 | 390 | { TTokenHelper } 391 | 392 | function TTokenHelper.AsInteger: Integer; 393 | begin 394 | Result := Ord(Self); 395 | end; 396 | 397 | function TTokenHelper.ToString: string; 398 | begin 399 | if self = tkUnknownToken then 400 | Result := 'tkUnknownToken' 401 | else 402 | Result := GetEnumName(TypeInfo(TTokenHelper), Ord(Self)); 403 | end; 404 | 405 | end. 406 | 407 | -------------------------------------------------------------------------------- /Source/Data.DB.Parser.pas: -------------------------------------------------------------------------------- 1 | unit Data.DB.Parser; 2 | 3 | interface 4 | 5 | uses 6 | System.Generics.Collections, 7 | System.Classes, 8 | Data.DB.Lexer; 9 | 10 | type 11 | TTokenTypes = ( 12 | tkSELECT = 0, tkFROM, tkWHERE, tkLeft, tkRight, tkInner, tkFull, tkOuter, 13 | tkJoin, tkInto, tkOrder, tkGroup, tkBy, tkIn, tkAsterisk, tkEquals, 14 | tkEndStatement, tkLeftBracket, tkRightBracket, tkDotSeperator, tkAS, tkOn, 15 | tkTableName, tkTableRef, tkFieldName, tkFieldRefName, tkInsert, tkUpdate, 16 | tkSet, tkAlter, tkCreate, tkTable, tkBetween, tkCase, tkDelete, tkHaving, 17 | tkLIKE, tkLimit, tkDISTINCT, tkWith, tkTop, tkDrop, tkTRUNCATE, tkCOLUMN, 18 | tkAdd, tkUnique, tkConstraint, tkIndex, tkValues, tkAsc, tkDesc, tkElse, 19 | tkEnd, tkGrant, tkTo, tkRevoke, tkView, tkReplace, tkTrigger, tkCommit, 20 | tkRollback, tkDatabase, tkDatabaseName, tkViewName, 21 | tkDBOperation, tkUsername, tkConstantNumber, tkConstantString, tkUse, tkUser, 22 | tkGroupBy, tkSchemaName, tkRightOuterJoin, tkInnerJoin, tkLeftJoin, tkRightJoin, 23 | tkOuterJoin, tkOrderBy, tkCreateDatabase, tkSum, tkCount, tkAvg, tkExists, tkOr, 24 | tkAnd, tkNot, tkComma, tkLessThan, tkGreaterThan, tkDivide, tkUNION, tkNULL, 25 | tkWhen, tkIs, tkThen, tkBackup, tkDisk, tkVarchar, tkInt, tkModify, tkCreateView, 26 | tkCreateUser, tkCreateTable, tkDropDatabase, tkDropView, tkDropTable, tkDropUser, 27 | tkTruncateTable, tkIf, tkIfExists, tkAlterTable, tkCreateIndex, tkDropIndex, 28 | tkDeleteFrom, tkNotEqual, tkBackupDatabase, tkToDisk, tkInsertInto, tkDropConstraint, 29 | tkIndexName, tkConstraintName, tkLockTables, tkUnlockTables, tkLock, tkUnLock, tkTables, 30 | tkDouble, tkDefault, tkTemporary, tkMax, tkNotNull, tkZeroFill, tkUnsigned, tkMin, 31 | tkRead, tkFunctionName, tkMinus, tkPlus, tkLessThanOrEqual, tkGreaterThanOrEqual, 32 | tkAlterColumn, tkCast, tkFloat, tkCurrentDate, tkCurrentTime, tkIIf, tkComment, 33 | tkConcat, tkSubstr, tkCreateTemporaryTable, tkParam, tkAll, tkUnionAll, tkPrimary, 34 | tkKey, tkPrimaryKey, tkDropColumn, tkCreateOrReplaceView, tkCheck, tkCreateUniqueIndex, 35 | tkAutoIncrement, tkForeign, tkReferences, tkForeignKey, tkOpenSquareBracket, tkCloseSquareBracket, 36 | tkMultiply, tkHash, tkUnknownToken, tk68, tk150, tkYear, tkDateFunction, tkCross, tkCrossJoin, 37 | tkDate, tkCreateTrigger, tkCreateOrAlterTrigger, tkDropTrigger, tkDropTriggerIfExists, 38 | tkEnableTrigger, tkDisableTrigger, tkCreateDefiner, tkCreateFunction, tkDropProcedure, 39 | tkDropFunction, tkDropFunctionIfExists, tkDropProcedureIfExists, tkRenameTable, 40 | tkShowBinaryLogStatus, tkShowBinaryLogs, tkShowBinLogEvents, tkShowCollation, 41 | tkShowCollumns, tkShowCreateDatabase, tkShowCreateEvent, tkShowCreateFunction, 42 | tkShowCreateProcedure, tkShowCreateTable, tkShowCreateTrigger, tkShowCreateUser, 43 | tkShowCreateView, tkShowDatabases, tkShowEngine, tkShowEngines, tkShowErrors, 44 | tkShowEvents, tkShowFunctionCode, tkShowFunctionStatus, tkShowGrants, tkShowIndex, 45 | tkOpenTables, tkShowTriggers, tkShowStatus, tkShowCharSet, tkShowEngineInnoDBMutex, 46 | tkShowFullProcessList, tkShowFullTables, tkShowPlugins, tkShowProcedureStatus, 47 | tkShowProcessList, tkShowProfile, tkShowProfiles, tkShowSchemas, 48 | tkShowStorageEngines, tkShowTableStatus, tkShowTables, tkShowEngineInnoDBStatus, 49 | tkShowWarnings, tkConstantDate, tkColon, tkExplain, tkAnalyzeTable, 50 | tkChecksumTable, tkRepairTable, tkSetCharacterSet, tkFlushPriviledges, 51 | tkRevokeAllPrivileges, tkStartTransaction, tkCreateRole, tkSetPasswordFor, 52 | tkAlterIndex, tkAttachPartition, tkAlterSystem, tkAlterView, tkCluster, 53 | tkCommentOn, tkCopy, tkCreateAggregate, tkCreateCast, tkCreateDomain, 54 | tkCreateExtension, tkCreateLanguage, tkCreateOperator, tkCreatePolicy, 55 | tkCreateRule, tkCreateSchema, tkCreateSequence, tkCreateSubscription, 56 | tkCreateTablespace, tkCreateType, tkCreateUserMapping, tkCreatePublication, 57 | tkDiscard, tkDo, tkExecute, tkListen, tkLoad, tkMove, 58 | tkNotify, tkReassignOwned, tkReindex, tkReset, 59 | tkSavepoint, tkSecurityLabel, tkUnlisten, tkVacuum 60 | ); 61 | 62 | TTokenInfo = class 63 | strict private 64 | FTokenSQL: TTokenTypes; 65 | private 66 | function GetTokenSQL: TTokenTypes; 67 | procedure SetTokenSQL(const Value: TTokenTypes; overrides: Boolean); overload; 68 | procedure SetTokenSQL(const Value: TTokenTypes); overload; 69 | public 70 | TokenType: TTokenType; 71 | Token: string; 72 | SourcePos: Int64; 73 | property TokenSQL: TTokenTypes read GetTokenSQL write SetTokenSQL; 74 | end; 75 | 76 | TTokenBucket = class(TObjectList) 77 | public 78 | procedure Join(istart: Integer); 79 | function LookupTokenByPosition(iPosition: Integer): TTokenInfo; 80 | end; 81 | 82 | TLexerParser = class(TParser) 83 | end; 84 | 85 | TSQLParser = class 86 | private 87 | FTokenDict: TDictionary; 88 | FTokens: TTokenBucket; 89 | procedure InitializeTokenDict; 90 | procedure CombineTokens; 91 | procedure DropIndex(i: Integer); 92 | procedure ScanForDateCombination; 93 | public 94 | function TokenStringToTokenSQL(info: TTokenInfo): TTokenTypes; 95 | function TokenIdToTokenString(inTokenId: TTokenTypes): string; 96 | function ProcessSQL(SQL: string): Integer; 97 | function DoesStatementModifyDB: Boolean; 98 | function IsDDL: Boolean; 99 | function DoesDoubleConstantExpressionExist: Boolean; 100 | function DoesConstantValueExist: Boolean; 101 | function DoesSelectConstantExist: Boolean; 102 | function DoesCommentExist: Boolean; 103 | function StatementCount: Integer; 104 | function TokenCount: Integer; 105 | constructor Create; 106 | destructor Destroy; override; 107 | property Tokens: TTokenBucket read FTokens; 108 | end; 109 | 110 | implementation 111 | 112 | uses 113 | System.SysUtils, System.StrUtils, Winapi.Windows, System.Rtti, 114 | System.TypInfo; 115 | 116 | type 117 | TTokenMatch = record 118 | Tokens: TArray; 119 | ResultTokenSQL: TTokenTypes; 120 | end; 121 | 122 | 123 | 124 | 125 | function TSQLParser.TokenIdToTokenString(inTokenId: TTokenTypes): string; 126 | begin 127 | Result := GetEnumName(TypeInfo(TTokenTypes), Integer(inTokenId)); 128 | end; 129 | 130 | { TTokenBucket } 131 | 132 | procedure TTokenBucket.Join(istart: Integer); 133 | begin 134 | Self[istart].Token := Self[istart].Token + ' ' + Self[istart + 1].Token; 135 | Self.Delete(istart + 1); 136 | end; 137 | 138 | function TTokenBucket.LookupTokenByPosition(iPosition: Integer): TTokenInfo; 139 | var 140 | i: Integer; 141 | begin 142 | Result := Self[iPosition]; 143 | end; 144 | 145 | { TTokenInfo } 146 | 147 | function TTokenInfo.GetTokenSQL: TTokenTypes; 148 | begin 149 | Result := FTokenSQL; 150 | end; 151 | 152 | procedure TTokenInfo.SetTokenSQL(const Value: TTokenTypes; overrides: Boolean); 153 | begin 154 | if ((TokenSQL = tkConstantNumber) or 155 | (TokenSQL = tkConstantString)) and not overrides then 156 | begin 157 | Exit; 158 | end; 159 | if (Token = '=') and (Value <> tkEquals) then 160 | begin 161 | // Do nothing 162 | end 163 | else 164 | begin 165 | FTokenSQL := Value; 166 | end; 167 | end; 168 | 169 | procedure TTokenInfo.SetTokenSQL(const Value: TTokenTypes); 170 | begin 171 | SetTokenSQL(value, False); 172 | end; 173 | 174 | { TSQLParser } 175 | 176 | constructor TSQLParser.Create; 177 | begin 178 | FTokens := TTokenBucket.Create; 179 | FTokenDict := TDictionary.Create; 180 | InitializeTokenDict; 181 | end; 182 | 183 | destructor TSQLParser.Destroy; 184 | begin 185 | FTokens.Free; 186 | FTokenDict.Free; 187 | inherited; 188 | end; 189 | 190 | procedure TSQLParser.InitializeTokenDict; 191 | begin 192 | FTokenDict.Add('SELECT', tkSELECT); 193 | FTokenDict.Add('FROM', tkFROM); 194 | FTokenDict.Add('WHERE', tkWHERE); 195 | FTokenDict.Add('LEFT', tkLeft); 196 | FTokenDict.Add('RIGHT', tkRight); 197 | FTokenDict.Add('INNER', tkInner); 198 | FTokenDict.Add('FULL', tkFull); 199 | FTokenDict.Add('OUTER', tkOuter); 200 | FTokenDict.Add('JOIN', tkJoin); 201 | FTokenDict.Add('INTO', tkInto); 202 | FTokenDict.Add('ORDER', tkOrder); 203 | FTokenDict.Add('GROUP', tkGroup); 204 | FTokenDict.Add('BY', tkBy); 205 | FTokenDict.Add('IN', tkIn); 206 | FTokenDict.Add('*', tkAsterisk); 207 | FTokenDict.Add('=', tkEquals); 208 | FTokenDict.Add(';', tkEndStatement); 209 | FTokenDict.Add('(', tkLeftBracket); 210 | FTokenDict.Add(')', tkRightBracket); 211 | FTokenDict.Add('.', tkDotSeperator); 212 | FTokenDict.Add('AS', tkAS); 213 | FTokenDict.Add('ON', tkOn); 214 | FTokenDict.Add('INSERT', tkInsert); 215 | FTokenDict.Add('UPDATE', tkUpdate); 216 | FTokenDict.Add('SET', tkSet); 217 | FTokenDict.Add('ALTER', tkAlter); 218 | FTokenDict.Add('CREATE', tkCreate); 219 | FTokenDict.Add('TABLE', tkTable); 220 | FTokenDict.Add('BETWEEN', tkBetween); 221 | FTokenDict.Add('CASE', tkCase); 222 | FTokenDict.Add('DELETE', tkDelete); 223 | FTokenDict.Add('HAVING', tkHaving); 224 | FTokenDict.Add('LIKE', tkLIKE); 225 | FTokenDict.Add('LIMIT', tkLimit); 226 | FTokenDict.Add('DISTINCT', tkDISTINCT); 227 | FTokenDict.Add('WITH', tkWith); 228 | FTokenDict.Add('TOP', tkTop); 229 | FTokenDict.Add('DROP', tkDrop); 230 | FTokenDict.Add('TRUNCATE', tkTRUNCATE); 231 | FTokenDict.Add('COLUMN', tkCOLUMN); 232 | FTokenDict.Add('ADD', tkAdd); 233 | FTokenDict.Add('UNIQUE', tkUnique); 234 | FTokenDict.Add('CONSTRAINT', tkConstraint); 235 | FTokenDict.Add('INDEX', tkIndex); 236 | FTokenDict.Add('VALUES', tkValues); 237 | FTokenDict.Add('ASC', tkAsc); 238 | FTokenDict.Add('DESC', tkDesc); 239 | FTokenDict.Add('ELSE', tkElse); 240 | FTokenDict.Add('END', tkEnd); 241 | FTokenDict.Add('GRANT', tkGrant); 242 | FTokenDict.Add('TO', tkTo); 243 | FTokenDict.Add('REVOKE', tkRevoke); 244 | FTokenDict.Add('VIEW', tkView); 245 | FTokenDict.Add('REPLACE', tkReplace); 246 | FTokenDict.Add('TRIGGER', tkTrigger); 247 | FTokenDict.Add('COMMIT', tkCommit); 248 | FTokenDict.Add('ROLLBACK', tkRollback); 249 | FTokenDict.Add('DATABASE', tkDatabase); 250 | FTokenDict.Add('USE', tkUse); 251 | FTokenDict.Add('USER', tkUser); 252 | FTokenDict.Add('GROUP BY', tkGroupBy); 253 | FTokenDict.Add('RIGHT OUTER JOIN', tkRightOuterJoin); 254 | FTokenDict.Add('INNER JOIN', tkInnerJoin); 255 | FTokenDict.Add('LEFT JOIN', tkLeftJoin); 256 | FTokenDict.Add('RIGHT JOIN', tkRightJoin); 257 | FTokenDict.Add('OUTER JOIN', tkOuterJoin); 258 | FTokenDict.Add('ORDER BY', tkOrderBy); 259 | FTokenDict.Add('CREATE DATABASE', tkCreateDatabase); 260 | FTokenDict.Add('SUM', tkSum); 261 | FTokenDict.Add('COUNT', tkCount); 262 | FTokenDict.Add('AVG', tkAvg); 263 | FTokenDict.Add('EXISTS', tkExists); 264 | FTokenDict.Add('OR', tkOr); 265 | FTokenDict.Add('AND', tkAnd); 266 | FTokenDict.Add('NOT', tkNot); 267 | FTokenDict.Add(',', tkComma); 268 | FTokenDict.Add('<', tkLessThan); 269 | FTokenDict.Add('>', tkGreaterThan); 270 | FTokenDict.Add('/', tkDivide); 271 | FTokenDict.Add('UNION', tkUNION); 272 | FTokenDict.Add('NULL', tkNULL); 273 | FTokenDict.Add('WHEN', tkWhen); 274 | FTokenDict.Add('IS', tkIs); 275 | FTokenDict.Add('THEN', tkThen); 276 | FTokenDict.Add('BACKUP', tkBackup); 277 | FTokenDict.Add('DISK', tkDisk); 278 | FTokenDict.Add('VARCHAR', tkVarchar); 279 | FTokenDict.Add('INT', tkInt); 280 | FTokenDict.Add('MODIFY', tkModify); 281 | FTokenDict.Add('IF', tkIf); 282 | FTokenDict.Add('IF EXISTS', tkIfExists); 283 | FTokenDict.Add('ALTER TABLE', tkAlterTable); 284 | FTokenDict.Add('CREATE INDEX', tkCreateIndex); 285 | FTokenDict.Add('DROP INDEX', tkDropIndex); 286 | FTokenDict.Add('DELETE FROM', tkDeleteFrom); 287 | FTokenDict.Add('<>', tkNotEqual); 288 | FTokenDict.Add('BACKUP DATABASE', tkBackupDatabase); 289 | FTokenDict.Add('TO DISK', tkToDisk); 290 | FTokenDict.Add('INSERT INTO', tkInsertInto); 291 | FTokenDict.Add('DROP CONSTRAINT', tkDropConstraint); 292 | FTokenDict.Add('LOCK TABLES', tkLockTables); 293 | FTokenDict.Add('UNLOCK TABLES', tkUnlockTables); 294 | FTokenDict.Add('LOCK', tkLock); 295 | FTokenDict.Add('UNLOCK', tkUnLock); 296 | FTokenDict.Add('TABLES', tkTables); 297 | FTokenDict.Add('DOUBLE', tkDouble); 298 | FTokenDict.Add('DEFAULT', tkDefault); 299 | FTokenDict.Add('TEMPORARY', tkTemporary); 300 | FTokenDict.Add('MAX', tkMax); 301 | FTokenDict.Add('NOT NULL', tkNotNull); 302 | FTokenDict.Add('ZEROFILL', tkZeroFill); 303 | FTokenDict.Add('UNSIGNED', tkUnsigned); 304 | FTokenDict.Add('MIN', tkMin); 305 | FTokenDict.Add('READ', tkRead); 306 | FTokenDict.Add('FUNCTION NAME', tkFunctionName); 307 | FTokenDict.Add('-', tkMinus); 308 | FTokenDict.Add('+', tkPlus); 309 | FTokenDict.Add('<=', tkLessThanOrEqual); 310 | FTokenDict.Add('>=', tkGreaterThanOrEqual); 311 | FTokenDict.Add('ALTER COLUMN', tkAlterColumn); 312 | FTokenDict.Add('CAST', tkCast); 313 | FTokenDict.Add('FLOAT', tkFloat); 314 | FTokenDict.Add('CURRENT_DATE', tkCurrentDate); 315 | FTokenDict.Add('CURRENT_TIME', tkCurrentTime); 316 | FTokenDict.Add('IIF', tkIIf); 317 | FTokenDict.Add('--', tkComment); 318 | FTokenDict.Add('CONCAT', tkConcat); 319 | FTokenDict.Add('SUBSTR', tkSubstr); 320 | FTokenDict.Add('CREATE TEMPORARY TABLE', tkCreateTemporaryTable); 321 | FTokenDict.Add('PARAM', tkParam); 322 | FTokenDict.Add('ALL', tkAll); 323 | FTokenDict.Add('UNION ALL', tkUnionAll); 324 | FTokenDict.Add('PRIMARY', tkPrimary); 325 | FTokenDict.Add('KEY', tkKey); 326 | FTokenDict.Add('PRIMARY KEY', tkPrimaryKey); 327 | FTokenDict.Add('DROP COLUMN', tkDropColumn); 328 | FTokenDict.Add('CREATE OR REPLACE VIEW', tkCreateOrReplaceView); 329 | FTokenDict.Add('CHECK', tkCheck); 330 | FTokenDict.Add('CREATE UNIQUE INDEX', tkCreateUniqueIndex); 331 | FTokenDict.Add('AUTO_INCREMENT', tkAutoIncrement); 332 | FTokenDict.Add('FOREIGN', tkForeign); 333 | FTokenDict.Add('REFERENCES', tkReferences); 334 | FTokenDict.Add('FOREIGN KEY', tkForeignKey); 335 | FTokenDict.Add('[', tkOpenSquareBracket); 336 | FTokenDict.Add(']', tkCloseSquareBracket); 337 | FTokenDict.Add('#', tkHash); 338 | FTokenDict.Add('YEAR', tkYear); 339 | FTokenDict.Add('CROSS', tkCross); 340 | end; 341 | 342 | function TSQLParser.TokenStringToTokenSQL(info: TTokenInfo): TTokenTypes; 343 | var 344 | token: string; 345 | intValue : Int64; 346 | floatValue: Double; 347 | begin 348 | token := info.Token.ToUpper; 349 | 350 | if FTokenDict.TryGetValue(token, Result) then 351 | Exit; 352 | 353 | if (TryStrToInt64(token, intValue)) or (TryStrToFloat(token, floatValue)) then 354 | Result := TTokenTypes.tkConstantNumber 355 | else if info.TokenType = ttString then 356 | Result := TTokenTypes.tkConstantString 357 | else 358 | Result := TTokenTypes.tkUnknownToken; 359 | end; 360 | 361 | function TSQLParser.StatementCount: Integer; 362 | var 363 | count: Integer; 364 | i: Integer; 365 | begin 366 | count := 0; 367 | for i := 0 to FTokens.Count - 1 do 368 | begin 369 | if FTokens[i].TokenSQL = TTokenTypes.tkEndStatement then 370 | Inc(count); 371 | end; 372 | Result := count; 373 | end; 374 | 375 | function TSQLParser.TokenCount: Integer; 376 | begin 377 | Result := FTokens.Count; 378 | end; 379 | 380 | function TSQLParser.DoesConstantValueExist: Boolean; 381 | var 382 | i: Integer; 383 | begin 384 | Result := False; 385 | for i := 0 to FTokens.Count - 1 do 386 | begin 387 | if (FTokens[i].TokenSQL in [tkConstantNumber, 388 | tkConstantString, 389 | tkConstantDate]) then 390 | begin 391 | Result := True; 392 | end; 393 | end; 394 | end; 395 | 396 | function TSQLParser.DoesCommentExist: Boolean; 397 | var 398 | i: Integer; 399 | begin 400 | Result := False; 401 | for i := 0 to FTokens.Count - 1 do 402 | begin 403 | if (FTokens[i].TokenSQL in [TTokenTypes.tkComment]) then 404 | begin 405 | Result := True; 406 | end; 407 | end; 408 | end; 409 | 410 | function TSQLParser.DoesDoubleConstantExpressionExist: Boolean; 411 | var 412 | i: Integer; 413 | begin 414 | Result := False; 415 | for i := 0 to FTokens.Count - 1 do 416 | begin 417 | if i - 2 >= 0 then 418 | begin 419 | if (FTokens[i - 2].TokenSQL in [TTokenTypes.tkConstantNumber, TTokenTypes.tkConstantString]) and 420 | (FTokens[i - 1].TokenSQL in [TTokenTypes.tkEquals, TTokenTypes.tkLessThan, TTokenTypes.tkLessThanOrEqual, TTokenTypes.tkNotEqual, TTokenTypes.tkGreaterThan, TTokenTypes.tkGreaterThanOrEqual]) and 421 | (FTokens[i].TokenSQL in [TTokenTypes.tkConstantNumber, TTokenTypes.tkConstantString]) then 422 | begin 423 | Result := True; 424 | end; 425 | end; 426 | end; 427 | end; 428 | 429 | function TSQLParser.DoesSelectConstantExist: Boolean; 430 | var 431 | i: Integer; 432 | begin 433 | Result := False; 434 | for i := 0 to FTokens.Count - 1 do 435 | begin 436 | if i - 2 >= 0 then 437 | begin 438 | if ((FTokens[i - 2].TokenSQL = TTokenTypes.tkSelect) and 439 | (FTokens[i - 1].TokenSQL in [tkConstantNumber, tkConstantString]) and 440 | (FTokens[i].TokenSQL in [tkRightBracket, tkEndStatement])) then 441 | begin 442 | Result := True; 443 | end; 444 | end; 445 | end; 446 | end; 447 | 448 | function TSQLParser.DoesStatementModifyDB: Boolean; 449 | var 450 | i: Integer; 451 | begin 452 | Result := False; 453 | for i := 0 to FTokens.Count - 1 do 454 | begin 455 | if FTokens[i].TokenSQL in [ 456 | tkJoin, 457 | tkInto, 458 | tkInsert, 459 | tkUpdate, 460 | tkSet, 461 | tkAlter, 462 | tkCreate, 463 | tkDelete, 464 | tkDrop, 465 | tkAdd, 466 | tkGrant, 467 | tkView, 468 | tkCreateDatabase, 469 | tkCreateView, 470 | tkCreateUser, 471 | tkCreateTable, 472 | tkCreateIndex, 473 | tkCreateUniqueIndex, 474 | tkCreateOrReplaceView, 475 | tkDropDatabase, 476 | tkDropView, 477 | tkDropTable, 478 | tkDropUser, 479 | tkTruncateTable, 480 | tkAlterTable, 481 | tkDropIndex, 482 | tkDeleteFrom, 483 | tkInsertInto, 484 | tkDropConstraint, 485 | tkAlterColumn 486 | ] then 487 | begin 488 | Result := True; 489 | Exit; 490 | end; 491 | end; 492 | end; 493 | 494 | function TSQLParser.IsDDL: Boolean; 495 | var 496 | i: Integer; 497 | begin 498 | Result := False; 499 | for i := 0 to FTokens.Count - 1 do 500 | begin 501 | if FTokens[i].TokenSQL in [ 502 | tkInto, 503 | tkCreate, 504 | tkGrant, 505 | tkCreateDatabase, 506 | tkCreateView, 507 | tkCreateOrReplaceView, 508 | tkCreateTable, 509 | tkCreateUniqueIndex, 510 | tkDropDatabase, 511 | tkDropView, 512 | tkDropTable, 513 | tkAlterTable, 514 | tkCreateIndex, 515 | tkDropIndex, 516 | tkDropConstraint, 517 | tkAlterColumn 518 | ] then 519 | begin 520 | Result := True; 521 | Exit; 522 | end; 523 | end; 524 | end; 525 | 526 | procedure TSQLParser.CombineTokens; 527 | var 528 | i: Integer; 529 | TokenCombinations: array of TTokenMatch; 530 | 531 | procedure CombineIfMatch(const ATokenMatch: TTokenMatch); 532 | var 533 | j: Integer; 534 | match: Boolean; 535 | begin 536 | match := True; 537 | for j := 0 to High(ATokenMatch.Tokens) do 538 | begin 539 | if FTokens[i + j].Token.ToUpper <> ATokenMatch.Tokens[j].ToUpper then 540 | begin 541 | match := False; 542 | Break; 543 | end; 544 | end; 545 | 546 | if match then 547 | begin 548 | for j := 1 to High(ATokenMatch.Tokens) do 549 | FTokens.Join(i); 550 | FTokens[i].TokenSQL := ATokenMatch.ResultTokenSQL; 551 | end; 552 | end; 553 | 554 | procedure AddTokenCombination(const Tokens: TArray; ResultTokenSQL: TTokenTypes); 555 | var 556 | CurrentIndex: Integer; 557 | begin 558 | CurrentIndex := Length(TokenCombinations); 559 | SetLength(TokenCombinations, CurrentIndex + 1); 560 | TokenCombinations[CurrentIndex].Tokens := Copy(Tokens); 561 | TokenCombinations[CurrentIndex].ResultTokenSQL := ResultTokenSQL; 562 | end; 563 | 564 | begin 565 | i := 0; 566 | 567 | // Initialize token combinations 568 | AddTokenCombination(['`', '`'], tkUnknownToken); 569 | AddTokenCombination(['[', ']'], tkUnknownToken); 570 | AddTokenCombination(['<', '='], tkLessThanOrEqual); 571 | AddTokenCombination(['<', '>'], tkNotEqual); 572 | AddTokenCombination(['>', '='], tkGreaterThanOrEqual); 573 | AddTokenCombination(['ANALYZE', 'TABLE'], tkAnalyzeTable); 574 | AddTokenCombination(['ALTER', 'COLUMN'], tkAlterColumn); 575 | AddTokenCombination(['ALTER', 'INDEX'], tkAlterIndex); 576 | AddTokenCombination(['ALTER', 'TABLE'], tkAlterTable); 577 | AddTokenCombination(['ATTACH', 'PARTITION'], tkAttachPartition); 578 | AddTokenCombination(['BACKUP', 'DATABASE'], tkBackupDatabase); 579 | AddTokenCombination(['CREATE', 'DATABASE'], tkCreateDatabase); 580 | AddTokenCombination(['CREATE', 'DEFINER'], tkCreateDefiner); 581 | AddTokenCombination(['CREATE', 'FUNCTION'], tkCreateFunction); 582 | AddTokenCombination(['CHECKSUM', 'TABLE'], tkChecksumTable); 583 | AddTokenCombination(['CREATE', 'INDEX'], tkCreateIndex); 584 | AddTokenCombination(['CREATE', 'OR', 'REPLACE', 'TRIGGER'], tkCreateOrAlterTrigger); 585 | AddTokenCombination(['CREATE', 'OR', 'REPLACE', 'VIEW'], tkCreateOrReplaceView); 586 | AddTokenCombination(['CREATE', 'TABLE'], tkCreateTable); 587 | AddTokenCombination(['CREATE', 'TEMPORARY', 'TABLE'], tkCreateTemporaryTable); 588 | AddTokenCombination(['CREATE', 'TRIGGER'], tkCreateTrigger); 589 | AddTokenCombination(['CREATE', 'UNIQUE', 'INDEX'], tkCreateUniqueIndex); 590 | AddTokenCombination(['CREATE', 'USER'], tkCreateUser); 591 | AddTokenCombination(['CREATE', 'ROLE'], tkCreateRole); 592 | AddTokenCombination(['CREATE', 'VIEW'], tkCreateView); 593 | AddTokenCombination(['DELETE', 'FROM'], tkDeleteFrom); 594 | AddTokenCombination(['DISABLE', 'TRIGGER'], tkDisableTrigger); 595 | AddTokenCombination(['DROP', 'COLUMN'], tkDropColumn); 596 | AddTokenCombination(['DROP', 'CONSTRAINT'], tkDropConstraint); 597 | AddTokenCombination(['DROP', 'DATABASE'], tkDropDatabase); 598 | AddTokenCombination(['DROP', 'FUNCTION', 'IF', 'EXISTS'], tkDropFunctionIfExists); 599 | AddTokenCombination(['DROP', 'FUNCTION'], tkDropFunction); 600 | AddTokenCombination(['DROP', 'INDEX'], tkDropIndex); 601 | AddTokenCombination(['DROP', 'PROCEDURE', 'IF', 'EXISTS'], tkDropProcedureIfExists); 602 | AddTokenCombination(['DROP', 'PROCEDURE'], tkDropProcedure); 603 | AddTokenCombination(['DROP', 'TABLE'], tkDropTable); 604 | AddTokenCombination(['DROP', 'TRIGGER', 'IF', 'EXISTS'], tkDropTriggerIfExists); 605 | AddTokenCombination(['DROP', 'TRIGGER'], tkDropTrigger); 606 | AddTokenCombination(['DROP', 'USER'], tkDropUser); 607 | AddTokenCombination(['DROP', 'VIEW'], tkDropView); 608 | AddTokenCombination(['ENABLE', 'TRIGGER'], tkEnableTrigger); 609 | AddTokenCombination(['FOREIGN', 'KEY'], tkForeignKey); 610 | AddTokenCombination(['FLUSH', 'PRIVILEGES'], tkFlushPriviledges); 611 | AddTokenCombination(['GROUP', 'BY'], tkGroupBy); 612 | AddTokenCombination(['IF', 'EXISTS'], tkIfExists); 613 | AddTokenCombination(['INNER', 'JOIN'], tkInnerJoin); 614 | AddTokenCombination(['INSERT', 'INTO'], tkInsertInto); 615 | AddTokenCombination(['LEFT', 'JOIN'], tkLeftJoin); 616 | AddTokenCombination(['LOCK', 'TABLES'], tkLockTables); 617 | AddTokenCombination(['NOT', 'NULL'], tkNotNull); 618 | AddTokenCombination(['OPTIMIZE', 'TABLE'], tkOuterJoin); 619 | AddTokenCombination(['ORDER', 'BY'], tkOrderBy); 620 | AddTokenCombination(['OUTER', 'JOIN'], tkOuterJoin); 621 | AddTokenCombination(['PRIMARY', 'KEY'], tkPrimaryKey); 622 | AddTokenCombination(['RENAME', 'TABLE'], tkRenameTable); 623 | AddTokenCombination(['REVOKE', 'ALL', 'PRIVILEGES'], tkRevokeAllPrivileges); 624 | AddTokenCombination(['REPAIR', 'TABLE'], tkRepairTable); 625 | AddTokenCombination(['RIGHT', 'JOIN'], tkRightJoin); 626 | AddTokenCombination(['RIGHT', 'OUTER', 'JOIN'], tkRightOuterJoin); 627 | AddTokenCombination(['SET', 'CHARACTER', 'SET'], tkSetCharacterSet); 628 | AddTokenCombination(['SET', 'PASSWORD', 'FOR'], tkSetPasswordFor); 629 | AddTokenCombination(['SHOW', 'BINARY', 'LOG', 'STATUS'], tkShowBinaryLogStatus); 630 | AddTokenCombination(['SHOW', 'BINARY', 'LOGS'], tkShowBinaryLogs); 631 | AddTokenCombination(['SHOW', 'BINLOG', 'EVENTS'], tkShowBinLogEvents); 632 | AddTokenCombination(['SHOW', 'CHARACTER', 'SET'], tkShowCharset); 633 | AddTokenCombination(['SHOW', 'CHARSET'], tkShowCharset); 634 | AddTokenCombination(['SHOW', 'COLLATION'], tkShowCollation); 635 | AddTokenCombination(['SHOW', 'COLUMNS'], tkShowCollumns); 636 | AddTokenCombination(['SHOW', 'CREATE', 'DATABASE'], tkShowCreateDatabase); 637 | AddTokenCombination(['SHOW', 'CREATE', 'EVENT'], tkShowCreateEvent); 638 | AddTokenCombination(['SHOW', 'CREATE', 'FUNCTION'], tkShowCreateFunction); 639 | AddTokenCombination(['SHOW', 'CREATE', 'PROCEDURE'], tkShowCreateProcedure); 640 | AddTokenCombination(['SHOW', 'CREATE', 'TABLE'], tkShowCreateTable); 641 | AddTokenCombination(['SHOW', 'CREATE', 'TRIGGER'], tkShowCreateTrigger); 642 | AddTokenCombination(['SHOW', 'CREATE', 'USER'], tkShowCreateUser); 643 | AddTokenCombination(['SHOW', 'CREATE', 'VIEW'], tkShowCreateView); 644 | AddTokenCombination(['SHOW', 'DATABASES'], tkShowDatabases); 645 | AddTokenCombination(['SHOW', 'ENGINES'], tkShowEngines); 646 | AddTokenCombination(['SHOW', 'ENGINE', 'INNODB', 'MUTEX'], tkShowEngineInnoDBMutex); 647 | AddTokenCombination(['SHOW', 'ENGINE', 'INNODB', 'STATUS'], tkShowEngineInnoDBStatus); 648 | AddTokenCombination(['SHOW', 'ENGINE'], tkShowEngine); 649 | AddTokenCombination(['SHOW', 'ERRORS'], tkShowErrors); 650 | AddTokenCombination(['SHOW', 'EVENTS'], tkShowEvents); 651 | AddTokenCombination(['SHOW', 'FULL', 'PROCESSLIST'], tkShowFullProcessList); 652 | AddTokenCombination(['SHOW', 'FULL', 'TABLES'], tkShowFullTables); 653 | AddTokenCombination(['SHOW', 'FUNCTION', 'STATUS'], tkShowFunctionStatus); 654 | AddTokenCombination(['SHOW', 'FUNCTION', 'CODE'], tkShowFunctionCode); 655 | AddTokenCombination(['SHOW', 'GRANTS'], tkShowGrants); 656 | AddTokenCombination(['SHOW', 'INDEX'], tkShowIndex); 657 | AddTokenCombination(['SHOW', 'OPEN', 'TABLES'], tkOpenTables); 658 | AddTokenCombination(['SHOW', 'PLUGINS'], tkShowPlugins); 659 | AddTokenCombination(['SHOW', 'PROCEDURE', 'STATUS'], tkShowProcedureStatus); 660 | AddTokenCombination(['SHOW', 'PROCESSLIST'], tkShowProcessList); 661 | AddTokenCombination(['SHOW', 'PROFILE'], tkShowProfile); 662 | AddTokenCombination(['SHOW', 'PROFILES'], tkShowProfiles); 663 | AddTokenCombination(['SHOW', 'SCHEMAS'], tkShowSchemas); 664 | AddTokenCombination(['SHOW', 'STATUS'], tkShowStatus); 665 | AddTokenCombination(['SHOW', 'STORAGE', 'ENGINES'], tkShowStorageEngines); 666 | AddTokenCombination(['SHOW', 'TABLE', 'STATUS'], tkShowTableStatus); 667 | AddTokenCombination(['SHOW', 'TABLES'], tkShowTables); 668 | AddTokenCombination(['SHOW', 'TRIGGERS'], tkShowTriggers); 669 | AddTokenCombination(['SHOW', 'WARNINGS'], tkShowWarnings); 670 | AddTokenCombination(['START', 'TRANSACTION'], tkStartTransaction); 671 | AddTokenCombination(['TO', 'DISK'], tkToDisk); 672 | AddTokenCombination(['TRUNCATE', 'TABLE'], tkTruncateTable); 673 | AddTokenCombination(['UNION', 'ALL'], tkUnionAll); 674 | AddTokenCombination(['UNLOCK', 'TABLES'], tkUnlockTables); 675 | 676 | // PostgreSQL specific commands 677 | AddTokenCombination(['ALTER', 'SYSTEM'], tkAlterSystem); 678 | AddTokenCombination(['ALTER', 'VIEW'], tkAlterView); 679 | AddTokenCombination(['CLUSTER'], tkCluster); 680 | AddTokenCombination(['COMMENT', 'ON'], tkCommentOn); 681 | AddTokenCombination(['COPY'], tkCopy); 682 | AddTokenCombination(['CREATE', 'AGGREGATE'], tkCreateAggregate); 683 | AddTokenCombination(['CREATE', 'CAST'], tkCreateCast); 684 | AddTokenCombination(['CREATE', 'DOMAIN'], tkCreateDomain); 685 | AddTokenCombination(['CREATE', 'EXTENSION'], tkCreateExtension); 686 | AddTokenCombination(['CREATE', 'LANGUAGE'], tkCreateLanguage); 687 | AddTokenCombination(['CREATE', 'OPERATOR'], tkCreateOperator); 688 | AddTokenCombination(['CREATE', 'POLICY'], tkCreatePolicy); 689 | AddTokenCombination(['CREATE', 'RULE'], tkCreateRule); 690 | AddTokenCombination(['CREATE', 'SCHEMA'], tkCreateSchema); 691 | AddTokenCombination(['CREATE', 'SEQUENCE'], tkCreateSequence); 692 | AddTokenCombination(['CREATE', 'SUBSCRIPTION'], tkCreateSubscription); 693 | AddTokenCombination(['CREATE', 'TABLESPACE'], tkCreateTablespace); 694 | AddTokenCombination(['CREATE', 'TYPE'], tkCreateType); 695 | AddTokenCombination(['CREATE', 'USER', 'MAPPING'], tkCreateUserMapping); 696 | AddTokenCombination(['CREATE', 'PUBLICATION'], tkCreatePublication); 697 | AddTokenCombination(['DISCARD'], tkDiscard); 698 | AddTokenCombination(['DO'], tkDo); 699 | AddTokenCombination(['EXECUTE'], tkExecute); 700 | AddTokenCombination(['EXPLAIN'], tkExplain); 701 | AddTokenCombination(['LISTEN'], tkListen); 702 | AddTokenCombination(['LOAD'], tkLoad); 703 | AddTokenCombination(['MOVE'], tkMove); 704 | AddTokenCombination(['NOTIFY'], tkNotify); 705 | AddTokenCombination(['REASSIGN', 'OWNED'], tkReassignOwned); 706 | AddTokenCombination(['REINDEX'], tkReindex); 707 | AddTokenCombination(['RESET'], tkReset); 708 | AddTokenCombination(['REVOKE'], tkRevoke); 709 | AddTokenCombination(['ROLLBACK'], tkRollback); 710 | AddTokenCombination(['SAVEPOINT'], tkSavepoint); 711 | AddTokenCombination(['SECURITY', 'LABEL'], tkSecurityLabel); 712 | AddTokenCombination(['SET'], tkSet); 713 | AddTokenCombination(['UNLISTEN'], tkUnlisten); 714 | AddTokenCombination(['VACUUM'], tkVacuum); 715 | AddTokenCombination(['VALUES'], tkValues); 716 | AddTokenCombination(['WITH'], tkWith); 717 | 718 | while i < FTokens.Count - 1 do 719 | begin 720 | for var TokenMatch in TokenCombinations do 721 | CombineIfMatch(TokenMatch); 722 | Inc(i); 723 | end; 724 | 725 | ScanForDateCombination; 726 | end; 727 | 728 | 729 | 730 | procedure TSQLParser.ScanForDateCombination; 731 | var 732 | i: Integer; 733 | begin 734 | for i := 0 to FTokens.Count - 1 do 735 | begin 736 | if (FTokens[i].Token.StartsWith(':') ) then 737 | begin 738 | FTokens[i].TokenSQL := tkParam; 739 | end 740 | end; 741 | i := 0; 742 | 743 | while i <= FTokens.Count - 7 do 744 | begin 745 | if (FTokens[i].TokenSQL = tkHash) and 746 | (FTokens[i + 1].TokenSQL = tkConstantNumber) and 747 | (FTokens[i + 2].TokenSQL = tkDivide) and 748 | (FTokens[i + 3].TokenSQL = tkConstantNumber) and 749 | (FTokens[i + 4].TokenSQL = tkDivide) and 750 | (FTokens[i + 5].TokenSQL = tkConstantNumber) and 751 | (FTokens[i + 6].TokenSQL = tkHash) then 752 | begin 753 | // Join the tokens 754 | for var j := 1 to 6 do 755 | FTokens.Join(i); 756 | FTokens[i].TokenSQL := tkConstantDate; // Example: You can set to any relevant type if needed 757 | FTokens[i].Token := FTokens[i].Token.Replace(' ', ''); 758 | FTokens[i].Token := FTokens[i].Token.Replace('#', ''); 759 | end 760 | else 761 | Inc(i); 762 | end; 763 | end; 764 | 765 | 766 | procedure TSQLParser.DropIndex(i: Integer); 767 | begin 768 | FTokens.Delete(i); 769 | end; 770 | 771 | function ExtractNumberAsString(const S: string): string; 772 | var 773 | I: Integer; 774 | begin 775 | Result := ''; 776 | for I := 1 to Length(S) do 777 | begin 778 | if CharInSet(S[I], ['0'..'9']) then 779 | Result := Result + S[I] 780 | else //if Result <> '' then 781 | Break; 782 | end; 783 | end; 784 | 785 | function TSQLParser.ProcessSQL(SQL: string): Integer; 786 | var 787 | parser: TSQLLexer; 788 | strStrm: TStringStream; 789 | tokeninf : TToken; 790 | token: string; 791 | info: TTokenInfo; 792 | i: Integer; 793 | tmp: string; 794 | begin 795 | Result := -1; 796 | strStrm := TStringStream.Create; 797 | try 798 | FTokens.Clear; 799 | strStrm.WriteString(SQL); 800 | strStrm.Position := 0; 801 | try 802 | parser := TSQLLexer .Create(strStrm); 803 | except 804 | on e: Exception do 805 | begin 806 | OutputDebugString(PChar('LinePos:' + parser.LinePos.ToString)); 807 | end; 808 | end; 809 | try 810 | tokeninf := parser.GetNextToken; 811 | repeat 812 | info := TTokenInfo.Create; 813 | info.SetTokenSQL(tkUnknownToken); 814 | info.TokenType := ttUnknown; 815 | info.TokenType := tokeninf.TokenType; 816 | info.Token := tokeninf.Value; 817 | info.SourcePos := tokeninf.Position; 818 | OutputDebugString(PChar(info.Token)); 819 | info.TokenSQL := TokenStringToTokenSQL(info); 820 | if info.Token.StartsWith('--') then 821 | begin 822 | info.TokenSQL := tkComment; 823 | end; 824 | FTokens.Add(info); 825 | tokeninf := parser.GetNextToken; 826 | until (tokeninf.TokenType = ttEOF); 827 | OutputDebugString(PChar('Tokens: ' + FTokens.Count.ToString)); 828 | except 829 | on e: Exception do 830 | begin 831 | OutputDebugString(PChar('LinePos:' + parser.LinePos.ToString)); 832 | Result := parser.LinePos; 833 | end; 834 | end; 835 | i := 0; 836 | 837 | CombineTokens; 838 | 839 | for i := 1 to FTokens.Count - 1 do 840 | begin 841 | if FTokens[i - 1].TokenSQL = TTokenTypes.tkFROM then 842 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 843 | 844 | if FTokens[i - 1].TokenSQL = TTokenTypes.tkDeleteFrom then 845 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 846 | 847 | if (i - 3 >= 0) and (FTokens[i - 3].TokenSQL = TTokenTypes.tkFROM) and 848 | (FTokens[i - 1].TokenSQL = TTokenTypes.tkDotSeperator) then 849 | begin 850 | FTokens[i - 2].TokenSQL := TTokenTypes.tkSchemaName; 851 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 852 | end; 853 | 854 | if (i - 3 >= 0) and (FTokens[i - 3].TokenSQL = TTokenTypes.tkDropIndex) and 855 | (FTokens[i - 1].TokenSQL = TTokenTypes.tkDotSeperator) then 856 | begin 857 | FTokens[i - 2].TokenSQL := TTokenTypes.tkSchemaName; 858 | FTokens[i].TokenSQL := TTokenTypes.tkIndexName; 859 | end; 860 | 861 | if (i - 2 >= 0) and (FTokens[i - 2].TokenSQL = TTokenTypes.tkInto) and 862 | (FTokens[i].TokenSQL = tkFrom) then 863 | begin 864 | FTokens[i - 1].TokenSQL := TTokenTypes.tkTableName; 865 | end; 866 | 867 | if i - 2 >= 0 then 868 | begin 869 | if (FTokens[i - 1].TokenSQL = TTokenTypes.tkAS) and 870 | (FTokens[i - 2].TokenSQL = TTokenTypes.tkTableName) then 871 | FTokens[i].TokenSQL := TTokenTypes.tkTableRef; 872 | end; 873 | 874 | if i - 2 >= 0 then 875 | begin 876 | if (FTokens[i - 1].TokenSQL = tkSum) and 877 | (FTokens[i - 2].TokenSQL = TTokenTypes.tkLeftBracket) then 878 | FTokens[i].TokenSQL := tkSum; 879 | end; 880 | 881 | if i - 2 >= 0 then 882 | begin 883 | if (FTokens[i - 2].TokenSQL = tkComma) and 884 | (FTokens[i].TokenSQL = tkFrom) and 885 | (FTokens[i - 1].TokenSQL = tkUnknownToken) then 886 | FTokens[i - 1].TokenSQL := tkFieldName 887 | else if (FTokens[i - 2].TokenSQL = tkAs) and 888 | (FTokens[i].TokenSQL = tkFrom) and 889 | (FTokens[i - 1].TokenSQL = tkUnknownToken) then 890 | FTokens[i - 1].TokenSQL := tkFieldName 891 | end; 892 | 893 | if i - 2 >= 0 then 894 | begin 895 | if (FTokens[i - 2].TokenSQL = tkSelect) and 896 | (FTokens[i].TokenSQL = tkFrom) and 897 | (FTokens[i - 1].TokenSQL = tkUnknownToken) then 898 | FTokens[i - 1].TokenSQL := tkFieldName 899 | else if (FTokens[i - 2].TokenSQL = tkSelect) and 900 | (FTokens[i].TokenSQL = tkAs) and 901 | (FTokens[i - 1].TokenSQL = tkUnknownToken) then 902 | FTokens[i - 1].TokenSQL := tkFieldName 903 | else if (FTokens[i - 2].TokenSQL = tkSelect) and 904 | (FTokens[i].TokenSQL = tkComma) and 905 | (FTokens[i - 1].TokenSQL = tkUnknownToken) then 906 | FTokens[i - 1].TokenSQL := tkFieldName; 907 | end; 908 | 909 | if i - 2 >= 0 then 910 | begin 911 | if (FTokens[i - 1].TokenSQL = tkCount) and 912 | (FTokens[i - 2].TokenSQL = TTokenTypes.tkLeftBracket) then 913 | FTokens[i].TokenSQL := tkCount; 914 | end; 915 | 916 | if i - 2 >= 0 then 917 | begin 918 | if (FTokens[i - 1].TokenSQL = tkAvg) and 919 | (FTokens[i - 2].TokenSQL = TTokenTypes.tkLeftBracket) then 920 | FTokens[i].TokenSQL := tkAvg; 921 | end; 922 | 923 | if i - 2 >= 0 then 924 | begin 925 | if (FTokens[i - 1].TokenSQL = tkExists) and 926 | (FTokens[i - 2].TokenSQL = TTokenTypes.tkLeftBracket) then 927 | FTokens[i].TokenSQL := tkExists; 928 | end; 929 | 930 | if (i - 2 >= 0) and (i < FTokens.Count - 2) then 931 | begin 932 | if (FTokens[i - 1].token = 'SELECT') and 933 | (FTokens[i + 1].token = '.') then 934 | begin 935 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 936 | FTokens[i + 2].TokenSQL := TTokenTypes.tkFieldName; 937 | end; 938 | end; 939 | 940 | if i - 2 >= 0 then 941 | begin 942 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkUpdate) and 943 | (FTokens[i].tokenSQL = TTokenTypes.tkSet) then 944 | FTokens[i - 1].TokenSQL := TTokenTypes.tkTableName; 945 | end; 946 | 947 | if i - 2 >= 0 then 948 | begin 949 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkBackupDatabase) and 950 | (FTokens[i].tokenSQL = TTokenTypes.tkToDisk) then 951 | FTokens[i - 1].TokenSQL := TTokenTypes.tkTableName; 952 | end; 953 | 954 | if i - 2 >= 0 then 955 | begin 956 | if (FTokens[i - 1].token = 'JOIN') or 957 | (FTokens[i - 1].token = 'INNER JOIN') or 958 | (FTokens[i - 1].token = 'LEFT JOIN') or 959 | (FTokens[i - 1].token = 'RIGHT JOIN') or 960 | (FTokens[i - 1].token = 'OUTER JOIN') or 961 | (FTokens[i - 1].token = 'FULL OUTER JOIN') then 962 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 963 | if FTokens[i].Token = '[' then 964 | FTokens[i].TokenSQL := tk150; 965 | end; 966 | 967 | if i - 2 >= 0 then 968 | begin 969 | if (FTokens[i - 1].token = 'ON') and 970 | (FTokens[i + 1].token = '.') and 971 | (FTokens[i + 3].token = '=') and 972 | (FTokens[i + 5].token = '.') then 973 | begin 974 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 975 | FTokens[i + 2].TokenSQL := TTokenTypes.tkFieldName; 976 | FTokens[i + 4].TokenSQL := TTokenTypes.tkTableName; 977 | FTokens[i + 6].TokenSQL := TTokenTypes.tkFieldName; 978 | end; 979 | end; 980 | 981 | if i - 2 >= 0 then 982 | begin 983 | if (FTokens[i - 1].token = '(') and 984 | (FTokens[i + 1].token = '.') and 985 | ((FTokens[i + 3].token = '=') or 986 | (FTokens[i + 3].token = '<') or 987 | (FTokens[i + 3].token = '>')) then 988 | begin 989 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 990 | FTokens[i + 2].TokenSQL := TTokenTypes.tkFieldName; 991 | end; 992 | end; 993 | 994 | if i - 2 >= 0 then 995 | begin 996 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkTableName) and 997 | (FTokens[i - 1].tokenSQL = TTokenTypes.tkModify) then 998 | FTokens[i].TokenSQL := TTokenTypes.tkFieldName; 999 | end; 1000 | 1001 | if i - 4 >= 0 then 1002 | begin 1003 | if (FTokens[i - 3].token = '=') and 1004 | (FTokens[i - 1].token = '.') then 1005 | begin 1006 | FTokens[i - 2].TokenSQL := TTokenTypes.tkTableName; 1007 | FTokens[i].TokenSQL := TTokenTypes.tkFieldName; 1008 | end; 1009 | end; 1010 | 1011 | if i - 1 >= 0 then 1012 | begin 1013 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkUse) then 1014 | FTokens[i].TokenSQL := TTokenTypes.tkDatabaseName; 1015 | end; 1016 | 1017 | if i - 1 >= 0 then 1018 | begin 1019 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkDropTable) then 1020 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1021 | end; 1022 | 1023 | if i - 1 >= 0 then 1024 | begin 1025 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkTruncateTable) then 1026 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1027 | end; 1028 | 1029 | if i - 1 >= 0 then 1030 | begin 1031 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkAlterTable) then 1032 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1033 | end; 1034 | 1035 | if i - 1 >= 0 then 1036 | begin 1037 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkAlterTable) then 1038 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1039 | end; 1040 | 1041 | if i - 1 >= 0 then 1042 | begin 1043 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkCreateTemporaryTable) then 1044 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1045 | end; 1046 | 1047 | if i - 1 >= 0 then 1048 | begin 1049 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkLockTables) then 1050 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1051 | end; 1052 | 1053 | if i - 1 >= 0 then 1054 | begin 1055 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkDropView) then 1056 | FTokens[i].TokenSQL := TTokenTypes.tkViewName; 1057 | end; 1058 | 1059 | if i - 1 >= 0 then 1060 | begin 1061 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkDropUser) then 1062 | FTokens[i].TokenSQL := TTokenTypes.tkUsername; 1063 | end; 1064 | 1065 | if i - 1 >= 0 then 1066 | begin 1067 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkDropIndex) then 1068 | FTokens[i].TokenSQL := TTokenTypes.tkIndexName; 1069 | end; 1070 | 1071 | if i - 1 >= 0 then 1072 | begin 1073 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkCreateTable) then 1074 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1075 | end; 1076 | 1077 | if i - 1 >= 0 then 1078 | begin 1079 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkCreateView) then 1080 | FTokens[i].TokenSQL := TTokenTypes.tkViewName; 1081 | end; 1082 | 1083 | if i - 1 >= 0 then 1084 | begin 1085 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkDropColumn) then 1086 | FTokens[i].TokenSQL := TTokenTypes.tkFieldName; 1087 | end; 1088 | 1089 | 1090 | if i - 5 >= 0 then 1091 | begin 1092 | if ((FTokens[i - 3].token = '<') or 1093 | (FTokens[i - 3].token = '=') or 1094 | (FTokens[i - 3].token = '>')) and 1095 | (FTokens[i - 1].token = '.') then 1096 | begin 1097 | FTokens[i - 2].TokenSQL := TTokenTypes.tkTableName; 1098 | FTokens[i].TokenSQL := TTokenTypes.tkFieldName; 1099 | end; 1100 | end; 1101 | 1102 | if i - 5 >= 0 then 1103 | begin 1104 | if (FTokens[i - 5].tokenSQL = TTokenTypes.tkSELECT) and 1105 | (FTokens[i - 3].tokenSQL = TTokenTypes.tkDotSeperator) and 1106 | (FTokens[i - 1].tokenSQL = TTokenTypes.tkAS) then 1107 | begin 1108 | FTokens[i - 4].tokenSQL := TTokenTypes.tkTableName; 1109 | FTokens[i - 2].tokenSQL := TTokenTypes.tkFieldName; 1110 | FTokens[i].tokenSQL := TTokenTypes.tkFieldRefName; 1111 | end; 1112 | end; 1113 | 1114 | if i - 2 >= 0 then 1115 | begin 1116 | if (FTokens[i - 1].token = '(') and 1117 | (FTokens[i + 1].token = '.') and 1118 | (FTokens[i + 3].token = ')') then 1119 | begin 1120 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1121 | FTokens[i + 2].TokenSQL := TTokenTypes.tkFieldName; 1122 | end; 1123 | end; 1124 | 1125 | if i - 2 >= 0 then 1126 | begin 1127 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkSELECT) and 1128 | (FTokens[i + 1].tokenSQL = TTokenTypes.tkDotSeperator) then 1129 | begin 1130 | FTokens[i].tokenSQL := TTokenTypes.tkTableName; 1131 | FTokens[i + 2].tokenSQL := TTokenTypes.tkFieldName; 1132 | end 1133 | else if (FTokens[i - 1].tokenSQL = TTokenTypes.tkSELECT) and 1134 | (FTokens[i + 1].tokenSQL = TTokenTypes.tkComma) then 1135 | begin 1136 | FTokens[i].tokenSQL := TTokenTypes.tkFieldName; 1137 | end 1138 | else if (FTokens[i - 1].tokenSQL = TTokenTypes.tkSELECT) and 1139 | (FTokens[i + 1].tokenSQL = tkFROM) then 1140 | begin 1141 | if FTokens[i].Token = '*' then 1142 | FTokens[i].tokenSQL := TTokenTypes.tkAsterisk 1143 | else 1144 | FTokens[i].tokenSQL := TTokenTypes.tkFieldName; 1145 | end; 1146 | end; 1147 | 1148 | if i - 2 >= 0 then 1149 | begin 1150 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkFieldName) and 1151 | (FTokens[i - 1].tokenSQL = TTokenTypes.tkComma) and 1152 | (FTokens[i].tokenSQL = TTokenTypes.tkUnknownToken) then 1153 | begin 1154 | FTokens[i].tokenSQL := TTokenTypes.tkFieldName; 1155 | end; 1156 | end; 1157 | 1158 | if i - 3 >= 0 then 1159 | begin 1160 | if (FTokens[i - 3].tokenSQL = TTokenTypes.tkFieldName) and 1161 | (FTokens[i - 2].tokenSQL = TTokenTypes.tkComma) and 1162 | (FTokens[i].tokenSQL = TTokenTypes.tkFROM) then 1163 | begin 1164 | FTokens[i - 1].tokenSQL := TTokenTypes.tkFieldName; 1165 | end; 1166 | end; 1167 | 1168 | if i - 2 >= 0 then 1169 | begin 1170 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkWHERE) and 1171 | (FTokens[i].tokenSQL = TTokenTypes.tkDotSeperator) then 1172 | begin 1173 | FTokens[i - 1].tokenSQL := TTokenTypes.tkTableName; 1174 | FTokens[i + 1].tokenSQL := TTokenTypes.tkFieldName; 1175 | end 1176 | else if (FTokens[i - 2].tokenSQL = TTokenTypes.tkWHERE) then 1177 | begin 1178 | FTokens[i - 1].tokenSQL := TTokenTypes.tkFieldName; 1179 | end; 1180 | end; 1181 | 1182 | if i - 2 >= 0 then 1183 | begin 1184 | if ((FTokens[i - 2].tokenSQL = TTokenTypes.tkDISTINCT) or 1185 | (FTokens[i - 2].tokenSQL = tkAnd) or 1186 | (FTokens[i - 2].tokenSQL = TTokenTypes.tkOrderBy)) and 1187 | (FTokens[i].tokenSQL = TTokenTypes.tkDotSeperator) then 1188 | begin 1189 | FTokens[i - 1].tokenSQL := TTokenTypes.tkTableName; 1190 | FTokens[i + 1].tokenSQL := TTokenTypes.tkFieldName; 1191 | end 1192 | else if ((FTokens[i - 2].tokenSQL = TTokenTypes.tkDISTINCT) or 1193 | (FTokens[i - 2].tokenSQL = tkAnd) or 1194 | (FTokens[i - 2].tokenSQL = TTokenTypes.tkOrderBy)) then 1195 | begin 1196 | FTokens[i - 1].tokenSQL := TTokenTypes.tkFieldName; 1197 | end; 1198 | end; 1199 | 1200 | if i - 2 >= 0 then 1201 | begin 1202 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkGroupBy) and 1203 | (FTokens[i].tokenSQL = TTokenTypes.tkDotSeperator) then 1204 | begin 1205 | FTokens[i - 1].tokenSQL := TTokenTypes.tkTableName; 1206 | FTokens[i + 1].tokenSQL := TTokenTypes.tkFieldName; 1207 | end 1208 | else if (FTokens[i - 2].tokenSQL = TTokenTypes.tkGroupBy) then 1209 | begin 1210 | FTokens[i - 1].tokenSQL := TTokenTypes.tkFieldName; 1211 | end; 1212 | end; 1213 | 1214 | if i - 3 >= 0 then 1215 | begin 1216 | if (FTokens[i - 3].tokenSQL = TTokenTypes.tkOrderBy) and 1217 | (FTokens[i - 1].tokenSQL = TTokenTypes.tkDotSeperator) then 1218 | begin 1219 | FTokens[i - 2].tokenSQL := TTokenTypes.tkTableName; 1220 | FTokens[i].tokenSQL := TTokenTypes.tkFieldName; 1221 | end; 1222 | end; 1223 | 1224 | if i - 2 >= 0 then 1225 | begin 1226 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkOrderBy) then 1227 | FTokens[i].tokenSQL := TTokenTypes.tkFieldName; 1228 | end; 1229 | 1230 | if i - 2 >= 0 then 1231 | begin 1232 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkSet) and 1233 | (FTokens[i].tokenSQL = TTokenTypes.tkDotSeperator) then 1234 | begin 1235 | FTokens[i - 1].tokenSQL := TTokenTypes.tkTableName; 1236 | FTokens[i + 1].tokenSQL := TTokenTypes.tkFieldName; 1237 | end 1238 | else if (FTokens[i - 2].tokenSQL = TTokenTypes.tkSet) then 1239 | begin 1240 | FTokens[i - 1].tokenSQL := TTokenTypes.tkFieldName; 1241 | end; 1242 | end; 1243 | 1244 | if i - 2 >= 0 then 1245 | begin 1246 | if (FTokens[i - 1].token = ',') and 1247 | (FTokens[i + 1].token = '.') then 1248 | begin 1249 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1250 | FTokens[i + 2].TokenSQL := TTokenTypes.tkFieldName; 1251 | end; 1252 | end; 1253 | 1254 | if i - 2 >= 0 then 1255 | begin 1256 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkCreateIndex) and 1257 | (FTokens[i].tokenSQL = TTokenTypes.tkOn) then 1258 | begin 1259 | FTokens[i - 1].TokenSQL := TTokenTypes.tkIndexName; 1260 | end; 1261 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkCreateOrReplaceView) and 1262 | (FTokens[i].tokenSQL = TTokenTypes.tkAs) then 1263 | begin 1264 | FTokens[i - 1].TokenSQL := TTokenTypes.tkViewName; 1265 | end; 1266 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkCreateUniqueIndex) and 1267 | (FTokens[i].tokenSQL = TTokenTypes.tkOn) then 1268 | begin 1269 | FTokens[i - 1].TokenSQL := TTokenTypes.tkIndexName; 1270 | end; 1271 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkOn) and 1272 | (FTokens[i].tokenSQL = TTokenTypes.tkLeftBracket) then 1273 | begin 1274 | FTokens[i - 1].TokenSQL := TTokenTypes.tkTableName; 1275 | end; 1276 | if (FTokens[i - 2].tokenSQL = TTokenTypes.tkInsertInto) and 1277 | (FTokens[i].tokenSQL = TTokenTypes.tkSELECT) then 1278 | begin 1279 | FTokens[i - 1].TokenSQL := TTokenTypes.tkTableName; 1280 | end; 1281 | end; 1282 | 1283 | if i - 1 >= 0 then 1284 | begin 1285 | if (FTokens[i - 1].token = '(') and 1286 | (FTokens[i + 1].token = ')') then 1287 | begin 1288 | FTokens[i].TokenSQL := TTokenTypes.tkFieldName; 1289 | end; 1290 | end; 1291 | 1292 | if i - 1 >= 0 then 1293 | begin 1294 | if (FTokens[i - 1].TokenSQL = TTokenTypes.tkDropConstraint) then 1295 | begin 1296 | FTokens[i].TokenSQL := TTokenTypes.tkConstraintName; 1297 | end; 1298 | end; 1299 | 1300 | if i - 1 >= 0 then 1301 | begin 1302 | if (FTokens[i - 1].TokenSQL = TTokenTypes.tkInsertInto) then 1303 | begin 1304 | FTokens[i].TokenSQL := TTokenTypes.tkTableName; 1305 | end; 1306 | end; 1307 | 1308 | if i - 1 >= 0 then 1309 | begin 1310 | if (FTokens[i - 1].token = ':') then 1311 | begin 1312 | FTokens[i].TokenSQL := TTokenTypes.tkParam; 1313 | end; 1314 | end; 1315 | 1316 | if i - 5 >= 0 then 1317 | begin 1318 | if (FTokens[i - 5].tokenSQL = TTokenTypes.tkGrant) and 1319 | (FTokens[i - 3].tokenSQL = TTokenTypes.tkOn) and 1320 | (FTokens[i - 1].tokenSQL = TTokenTypes.tkTo) and 1321 | (MatchStr(FTokens[i - 4].token, ['SELECT', 'INSERT', 'DELETE']) = TRUE) then 1322 | begin 1323 | FTokens[i - 4].TokenSQL := TTokenTypes.tkDBOperation; 1324 | FTokens[i - 2].TokenSQL := tk68; 1325 | FTokens[i].TokenSQL := TTokenTypes.tkUsername; 1326 | end; 1327 | end; 1328 | 1329 | if i - 3 >= 0 then 1330 | begin 1331 | if (FTokens[i - 3].tokenSQL = TTokenTypes.tkOn) and 1332 | (FTokens[i - 1].tokenSQL = TTokenTypes.tkTo) then 1333 | begin 1334 | FTokens[i - 2].TokenSQL := tk68; 1335 | FTokens[i].TokenSQL := TTokenTypes.tkUsername; 1336 | end; 1337 | end; 1338 | 1339 | if i - 1 >= 0 then 1340 | begin 1341 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkDatabase) then 1342 | begin 1343 | FTokens[i].TokenSQL := TTokenTypes.tkDatabaseName; 1344 | end; 1345 | end; 1346 | 1347 | if i - 1 >= 0 then 1348 | begin 1349 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkDropDatabase) then 1350 | begin 1351 | FTokens[i].TokenSQL := TTokenTypes.tkDatabaseName; 1352 | end; 1353 | end; 1354 | 1355 | if i - 1 >= 0 then 1356 | begin 1357 | if (FTokens[i - 1].tokenSQL = TTokenTypes.tkCreateDatabase) then 1358 | begin 1359 | FTokens[i].TokenSQL := TTokenTypes.tkDatabaseName; 1360 | end; 1361 | end; 1362 | end; 1363 | finally 1364 | parser.Free; 1365 | strStrm.Free; 1366 | end; 1367 | end; 1368 | end. 1369 | 1370 | --------------------------------------------------------------------------------