├── 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 |
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 |
--------------------------------------------------------------------------------