ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQLParser.pas
Revision: 31
Committed: Tue Jul 14 15:31:25 2015 UTC (9 years, 4 months ago) by tony
Content type: text/x-pascal
File size: 25629 byte(s)
Log Message:
Committing updates for Release R1-3-0

File Contents

# Content
1 (*
2 * IBX For Lazarus (Firebird Express)
3 *
4 * The contents of this file are subject to the Initial Developer's
5 * Public License Version 1.0 (the "License"); you may not use this
6 * file except in compliance with the License. You may obtain a copy
7 * of the License here:
8 *
9 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
10 *
11 * Software distributed under the License is distributed on an "AS
12 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13 * implied. See the License for the specific language governing rights
14 * and limitations under the License.
15 *
16 * The Initial Developer of the Original Code is Tony Whyman.
17 *
18 * The Original Code is (C) 2014 Tony Whyman, MWA Software
19 * (http://www.mwasoftware.co.uk).
20 *
21 * All Rights Reserved.
22 *
23 * Contributor(s): ______________________________________.
24 *
25 *)
26 unit IBSQLParser;
27
28 {$Mode Delphi}
29
30 interface
31
32 uses Classes, DB;
33
34 {
35 The SQL Parser is a partial SQL Parser intended to parser a Firebird DML (Select)
36 statement with the intention of being able to modify the "Where", Having" and
37 "Order By" clauses. It is not an SQL validator as while invalid SQL may be detected,
38 there are many cases of non-compliant SQL that will still be parsed successfully.
39
40 In use, when a TSelectSQLParser object is created, it is passed a Select SQL Statement
41 that is then parsed into its constituent clauses. CTEs are brought out separately, as is
42 each union. The main clauses are made visible as text properties. Some, such as the
43 order by clause can be replaced fully. The Where and Having Clauses are manipulated by
44 the Add2WhereClause and the Add2HavingClause.
45
46 Add2WhereClause allows an SQL Condition statement (e.g. A = 1) to be appended to the
47 current WhereClause, ANDing or ORing in the new condition. Normally, Add2WhereClause
48 only manipulates the first Select in a UNION, and conditions must be applied separately
49 to each member of the union. However, Add2WhereClause can also apply the same SQL
50 condition to each member of the UNION.
51
52 Add2HavingClause behaves identically, except that it applies to the Having Clause.
53
54 TSelectSQLParser.Reset will return the Where, Having and OrderBy Clauses of all members
55 of the Union to their initial state. ResetWhereClause, ResetHavingClause and
56 ResetOrderByClause allow each clause to be individually reset to their initial
57 state.
58
59 The property SQLText is used to access the current Select SQL statement including
60 CTEs and UNIONs and modified clauses.
61
62 }
63
64 type
65 TSQLSymbol = (sqNone,sqSpace,sqSemiColon,sqSingleQuotes,sqDoubleQuotes, sqComma,
66 sqString,sqCommentStart,sqUnion,sqAll,sqColon,
67 sqCommentEnd,sqCommentLine,sqAsterisk,sqForwardSlash,
68 sqSelect,sqFrom,sqWhere,sqGroup,sqOrder,sqBy,sqOpenBracket,
69 sqCloseBracket,sqHaving,sqPlan,sqEOL,sqWith,sqRecursive,sqAs);
70
71 TSQLStates = (stInit, stError, stInSelect,stInFrom,stInWhere,stInGroupBy,
72 stInHaving,stInPlan, stNestedSelect,stInSingleQuotes, stInGroup,
73 stInDoubleQuotes, stInComment, stInCommentLine, stInOrder,
74 stNestedWhere,stNestedFrom,stInOrderBy,stDone,stUnion,
75 stInParam,stNestedGroupBy,stCTE,stCTE1,stCTE2,stCTE3, stCTEquoted,
76 stInCTE,stCTEClosed);
77
78 PCTEDef = ^TCTEDef;
79 TCTEDef = record
80 Recursive: boolean;
81 Name: string;
82 Text: string;
83 end;
84
85 { TSelectSQLParser }
86
87 TSelectSQLParser = class
88 private
89 FDataSet: TDataSet;
90 FHavingClause: string;
91 FOriginalHavingClause: string;
92 FOnSQLChanging: TNotifyEvent;
93 FSelectClause: string;
94 FGroupClause: string;
95 FUnionAll: boolean;
96 FWhereClause: string;
97 FOriginalWhereClause: string;
98 FOrderByClause: string;
99 FOriginalOrderByClause: string;
100 FPlanClause: string;
101 FFromClause: string;
102 FState: TSQLStates;
103 FString: string;
104 FLastSymbol: TSQLSymbol;
105 FLastChar: char;
106 FStack: array [0..16] of TSQLStates;
107 FStackindex: integer;
108 FIndex: integer;
109 FStartLine: integer;
110 FUnion: TSelectSQLParser;
111 FAllowUnionAll: boolean;
112 FLiteral: string;
113 FParamList: TStringList;
114 FCTEs: TList;
115 FCTE: TCTEDef;
116 FNested: integer;
117 FDestroying: boolean;
118 procedure AddToSQL(const Word: string);
119 procedure CTEClear;
120 function GetCTE(Index: integer): PCTEDef;
121 function GetCTECount: integer;
122 function GetSQlText: string;
123 function Check4ReservedWord(const Text: string): TSQLSymbol;
124 procedure AnalyseLine(const Line: string);
125 procedure AnalyseSQL(Lines: TStrings);
126 procedure InitCTE;
127 procedure AddCTE;
128 function GetNextSymbol(C: char): TSQLSymbol;
129 function GetSymbol(const Line: string; var index: integer): TSQLSymbol;
130 function PopState: TSQLStates;
131 procedure SetState(AState: TSQLStates);
132 procedure SetSelectClause(const Value: string);
133 procedure SetOrderByClause(const Value: string);
134 procedure SetGroupClause(const Value: string);
135 procedure SetFromClause(const Value: string);
136 protected
137 constructor Create(SQLText: TStrings; StartLine, StartIndex: integer); overload;
138 procedure Changed;
139 public
140 constructor Create(aDataSet: TDataSet; SQLText: TStrings); overload;
141 constructor Create(aDataSet: TDataSet; const SQLText: string); overload;
142 destructor Destroy; override;
143 procedure Add2WhereClause(const Condition: string; OrClause: boolean=false;
144 IncludeUnions: boolean = false);
145 procedure Add2HavingClause(const Condition: string; OrClause: boolean=false;
146 IncludeUnions: boolean = false);
147 procedure DropUnion;
148 function GetFieldPosition(AliasName: string): integer;
149 procedure ResetWhereClause;
150 procedure ResetHavingClause;
151 procedure ResetOrderByClause;
152 procedure Reset;
153 property CTEs[Index: integer]: PCTEDef read GetCTE;
154 property CTECount: integer read GetCTECount;
155 property DataSet: TDataSet read FDataSet;
156 property SelectClause: string read FSelectClause write SetSelectClause;
157 property FromClause: string read FFromClause write SetFromClause;
158 property GroupClause: string read FGroupClause write SetGroupClause;
159 property HavingClause: string read FHavingClause write FHavingClause;
160 property PlanClause: string read FPlanClause;
161 property WhereClause: string read FWhereClause write FWhereClause;
162 property OrderByClause: string read FOrderByClause write SetOrderByClause;
163 property SQLText: string read GetSQLText;
164 property Union: TSelectSQLParser read FUnion;
165 property UnionAll: boolean read FUnionAll write FUnionAll;
166 {When true this is joined by "Union All" to the parent Select}
167 property ParamList: TStringList read FParamList;
168 property OnSQLChanging: TNotifyEvent read FOnSQLChanging write FOnSQLChanging;
169 end;
170
171 TFilterCallback = procedure(Parser: TSelectSQLParser; Key: integer) of object;
172
173 implementation
174
175 uses Sysutils, IBCustomDataSet;
176
177 resourcestring
178 sNoEndToThis = 'Unterminated string';
179 sBadBy = 'Unexpected symbol "BY" in: %s';
180 sBadSymbol = 'Unknown Symbol';
181 sIncomplete = 'Incomplete Union';
182 sBadSQL = 'Error processing SQL "%s" - %s';
183 sStackUnderFlow = 'Stack Underflow';
184 sStackOverFlow = 'Stack Overflow';
185 sBadParameter = 'Bad SQL Parameter';
186
187 { TSelectSQLParser }
188
189 procedure TSelectSQLParser.AddToSQL(const Word: string);
190 begin
191 case FState of
192 stNestedSelect,
193 stInSelect:
194 FSelectClause := FSelectClause + Word;
195 stNestedFrom,
196 stInFrom:
197 FFromClause := FFromClause + Word;
198 stNestedWhere,
199 stInWhere:
200 FWhereClause := FWhereClause + Word;
201 stNestedGroupBy,
202 stInGroupBy:
203 FGroupClause := FGroupClause + Word;
204 stInHaving:
205 FHavingClause := FHavingClause + Word;
206 stInPlan:
207 FPlanClause := FPlanClause + Word;
208 stInOrderBy:
209 FOrderByClause := FOrderByClause + Word;
210 stInDoubleQuotes,
211 stInSingleQuotes:
212 FLiteral := FLiteral + Word;
213 stInCTE:
214 FCTE.Text := FCTE.Text + Word;
215 stCTE2:
216 FCTE.Name := Trim(FCTE.Name + Word);
217 end;
218 end;
219
220 procedure TSelectSQLParser.CTEClear;
221 var i: integer;
222 begin
223 for i := 0 to FCTEs.Count - 1 do
224 dispose(PCTEDef(FCTEs[i]));
225 FCTEs.Clear;
226 end;
227
228 function TSelectSQLParser.GetCTE(Index: integer): PCTEDef;
229 begin
230 if (Index < 0) or (index >= FCTEs.Count) then
231 raise Exception.Create('CTE Index out of bounds');
232
233 Result := FCTEs[Index]
234 end;
235
236 function TSelectSQLParser.GetCTECount: integer;
237 begin
238 Result := FCTEs.Count;
239 end;
240
241 procedure TSelectSQLParser.Add2WhereClause(const Condition: string;
242 OrClause: boolean; IncludeUnions: boolean);
243 begin
244 if WhereClause <> '' then
245 if OrClause then
246 FWhereClause := '(' + WhereClause + ') OR (' + Condition + ')'
247 else
248 FWhereClause := '(' + WhereClause + ') AND (' + Condition + ')'
249 else
250 FWhereClause := Condition;
251 if IncludeUnions and (Union <> nil) then
252 Union.Add2WhereClause(Condition,OrClause,IncludeUnions);
253 Changed;
254 end;
255
256 procedure TSelectSQLParser.Add2HavingClause(const Condition: string;
257 OrClause: boolean; IncludeUnions: boolean);
258 begin
259 if HavingClause <> '' then
260 if OrClause then
261 FHavingClause := '(' + HavingClause + ') OR (' + Condition + ')'
262 else
263 FHavingClause := '(' + HavingClause + ') AND (' + Condition + ')'
264 else
265 FHavingClause := Condition;
266 if IncludeUnions and (Union <> nil) then
267 Union.Add2HavingClause(Condition,OrClause,IncludeUnions);
268 Changed;
269 end;
270
271 procedure TSelectSQLParser.AnalyseLine(const Line: string);
272 var Symbol: TSQLSymbol;
273 begin
274 while true do
275 begin
276 if FState = stError then
277 raise Exception.Create('Entered Error State');
278 Symbol := GetSymbol(Line,FIndex);
279 if (FState = stInParam) and (Symbol <> sqString) then
280 raise Exception.Create(sBadParameter);
281
282 case Symbol of
283 sqSpace:
284 if not (FState in [stInComment,stInCommentLine]) then
285 AddToSQL(' ');
286
287 sqColon:
288 if not (FState in [stInComment,stInCommentLine]) then
289 begin
290 AddToSQL(':');
291 if not (FState in [stInSingleQuotes,stInDoubleQuotes]) then
292 SetState(stInParam);
293 end;
294
295 sqSemiColon:
296 if not (FState in [stInComment,stInCommentLine]) then
297 case FState of
298 stInWhere,stInGroupBy,
299 stInHaving,stInPlan,stInFrom:
300 begin
301 FState := stDone;
302 Exit
303 end;
304
305 stInSingleQuotes, stInDoubleQuotes:
306 AddToSQL(';');
307
308 else
309 raise Exception.Create('Unexpected ";"')
310 end;
311
312 sqAsterisk:
313 if not (FState in [stInComment,stInCommentLine]) then
314 AddToSQL('*');
315
316 sqForwardSlash:
317 if not (FState in [stInComment,stInCommentLine]) then
318 AddToSQL('/');
319
320 sqOpenBracket:
321 if not (FState in [stInComment,stInCommentLine]) then
322 begin
323 if FNested = 0 then
324 case FState of
325 stInSelect,
326 stNestedSelect:
327 SetState(stNestedSelect);
328
329 stInFrom,
330 stNestedFrom:
331 SetState(stNestedFrom);
332
333 stInWhere,
334 stNestedWhere:
335 SetState(stNestedWhere);
336
337 stInGroupBy,
338 stNestedGroupBy:
339 SetState(stNestedGroupBy);
340
341 stCTE3:
342 begin
343 FState := stCTEClosed;
344 SetState(stInCTE);
345 end;
346 end;
347 if (FNested > 0 ) or (FState <> stInCTE) then
348 AddToSQL('(');
349 Inc(FNested);
350 end;
351
352 sqCloseBracket:
353 if not (FState in [stInComment,stInCommentLine]) then
354 begin
355 Dec(FNested);
356 if (FNested > 0) or (FState <> stInCTE) then
357 AddToSQL(')');
358 if FNested = 0 then
359 begin
360 if FState = stInCTE then
361 FState := PopState
362 else
363 if FState in [stNestedSelect,stNestedFrom,stNestedWhere,stNestedGroupBy] then
364 FState := PopState;
365 end;
366 if FState = stCTEClosed then
367 AddCTE;
368 end;
369
370 sqComma:
371 if FState = stCTEClosed then
372 FState := stCTE
373 else
374 AddToSQL(',');
375
376 sqCommentStart:
377 if not (FState in [stInComment,stInCommentLine]) then
378 SetState(stInComment);
379
380 sqCommentEnd:
381 if FState = stInComment then
382 FState := PopState
383 else
384 FState := stError;
385
386 sqCommentLine:
387 if not (FState in [stInComment,stInCommentLine]) then
388 SetState(stInCommentLine);
389
390 sqSingleQuotes:
391 if not (FState in [stInComment,stInCommentLine]) then
392 begin
393 case FState of
394 stInSingleQuotes:
395 begin
396 FState := PopState;
397 AddToSQL(FLiteral)
398 end;
399 stInDoubleQuotes:
400 {Ignore};
401 else
402 begin
403 FLiteral := '';
404 SetState(stInSingleQuotes)
405 end
406 end;
407 AddToSQL('''')
408 end;
409
410 sqDoubleQuotes:
411 if not (FState in [stInComment,stInCommentLine]) then
412 begin
413 case FState of
414 stInSingleQuotes:
415 {Ignore};
416 stInDoubleQuotes:
417 begin
418 FState := PopState;
419 AddToSQL(FLiteral)
420 end;
421 else
422 begin
423 FLiteral := '';
424 SetState(stInDoubleQuotes)
425 end
426 end;
427 AddToSQL('"')
428 end;
429
430 sqString:
431 if not (FState in [stInComment,stInCommentLine]) then
432 begin
433 if FState = stInParam then
434 begin
435 FState := PopState;
436 ParamList.Add(FString)
437 end
438 else
439 if FState in [stCTE, stCTE1] then
440 FState := stCTE2;
441 AddToSQL(FString)
442 end;
443
444 sqEOL:
445 begin
446 case FState of
447 stInCommentLine:
448 FState := PopState;
449 stInDoubleQuotes,
450 stInSingleQuotes:
451 Begin
452 FLiteral := FLiteral + #$0A;
453 Exit;
454 End;
455 end;
456 AddToSQL(' ');
457 Exit;
458 end;
459
460 sqSelect:
461 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stNestedSelect,stInCTE] then
462 AddToSql(FString)
463 else
464 FState := stInSelect;
465
466 sqFrom:
467 if FState = stInSelect then
468 FState := stInFrom
469 else
470 AddToSql(FString);
471 { if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,
472 stNestedGroupBy,stNestedSelect] then
473 AddToSql(FString)
474 else
475 FState := stInFrom;}
476
477 sqGroup:
478 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
479 AddToSql(FString)
480 else
481 FState := stInGroup;
482
483 sqWhere:
484 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stNestedSelect,stInCTE] then
485 AddToSql(FString)
486 else
487 FState := stInWhere;
488
489 sqHaving:
490 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
491 AddToSql(FString)
492 else
493 FState := stInHaving;
494
495 sqPlan:
496 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
497 AddToSql(FString)
498 else
499 FState := stInPlan;
500
501 sqOrder:
502 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere] then
503 AddToSql(FString)
504 else
505 FState := stInOrder;
506
507 sqUnion:
508 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
509 AddToSql(FString)
510 else
511 begin
512 FState := stUnion;
513 Exit
514 end;
515
516 sqAll:
517 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
518 AddToSql(FString)
519 else
520 if (FState = stInit) and FAllowUnionAll and not FUnionAll then
521 FUnionAll := true
522 else
523 raise Exception.Create('Unexpected symbol "all"');
524
525 sqBy:
526 case FState of
527 stInGroup:
528 FState := stInGroupBy;
529 stInOrder:
530 FState := stInOrderBy;
531 stNestedFrom,stNestedWhere,stInCTE,
532 stInSingleQuotes,
533 stInDoubleQuotes:
534 AddToSql(FString);
535 else
536 raise Exception.CreateFmt(sBadBy,[Line])
537 end;
538
539 sqWith:
540 if FState = stInit then
541 begin
542 FState := stCTE;
543 InitCTE;
544 end
545 else
546 raise Exception.Create('Unexpected symbol "with"');
547
548 sqRecursive:
549 if FState = stCTE then
550 begin
551 FCTE.Recursive := true;
552 FState := stCTE1
553 end
554 else
555 raise Exception.Create('Unexpected symbol "recursive"');
556
557 sqAs:
558 if FState = stCTE2 then
559 FState := stCTE3
560 else
561 AddToSQL('as');
562
563 else
564 raise Exception.Create(sBadSymbol);
565 end
566 end
567 end;
568
569 procedure TSelectSQLParser.AnalyseSQL(Lines: TStrings);
570 var I: integer;
571 begin
572 try
573 for I := FStartLine to Lines.Count - 1 do
574 try
575 AnalyseLine(Lines[I]);
576 case FState of
577 stDone:
578 break;
579 stUnion:
580 begin
581 if FIndex > length(Lines[I]) then
582 if I+1 < Lines.Count then
583 FUnion := TSelectSQLParser.Create(Lines,I+1,1)
584 else
585 raise Exception.Create(sIncomplete)
586 else
587 FUnion := TSelectSQLParser.Create(Lines,I,FIndex);
588 Exit
589 end;
590 end;
591 FIndex := 1;
592 except on E: Exception do
593 raise Exception.CreateFmt(sBadSQL,[Lines[I],E.Message])
594 end;
595 finally
596 FOriginalWhereClause := WhereClause;
597 FOriginalHavingClause := HavingClause;
598 FOriginalOrderByClause := OrderByClause
599 end;
600 end;
601
602 procedure TSelectSQLParser.InitCTE;
603 begin
604 with FCTE do
605 begin
606 Recursive := false;
607 Name := '';
608 Text := '';
609 end;
610 end;
611
612 procedure TSelectSQLParser.AddCTE;
613 var cte: PCTEDef;
614 begin
615 new(cte);
616 cte^.Name := FCTE.Name;
617 cte^.Recursive := FCTE.Recursive;
618 cte^.Text := FCTE.Text;
619 FCTEs.add(cte);
620 InitCTE;
621 end;
622
623 function TSelectSQLParser.Check4ReservedWord(const Text: string): TSQLSymbol;
624 begin
625 Result := sqString;
626 if CompareText(Text,'select') = 0 then
627 Result := sqSelect
628 else
629 if CompareText(Text,'from') = 0 then
630 Result := sqFrom
631 else
632 if CompareText(Text,'where') = 0 then
633 Result := sqWhere
634 else
635 if CompareText(Text,'group') = 0 then
636 Result := sqGroup
637 else
638 if CompareText(Text,'by') = 0 then
639 Result := sqBy
640 else
641 if CompareText(Text,'having') = 0 then
642 Result := sqHaving
643 else
644 if CompareText(Text,'plan') = 0 then
645 Result := sqPlan
646 else
647 if CompareText(Text,'union') = 0 then
648 Result := sqUnion
649 else
650 if CompareText(Text,'all') = 0 then
651 Result := sqAll
652 else
653 if CompareText(Text,'order') = 0 then
654 Result := sqOrder
655 else
656 if CompareText(Text,'with') = 0 then
657 Result := sqWith
658 else
659 if CompareText(Text,'recursive') = 0 then
660 Result := sqRecursive
661 else
662 if CompareText(Text,'as') = 0 then
663 Result := sqAs
664 end;
665
666 constructor TSelectSQLParser.Create(aDataSet: TDataSet; SQLText: TStrings);
667 begin
668 FDataSet := aDataSet;
669 Create(SQLText,0,1)
670 end;
671
672 constructor TSelectSQLParser.Create(aDataSet: TDataSet; const SQLText: string);
673 var Lines: TStringList;
674 begin
675 Lines := TStringList.Create;
676 try
677 Lines.Text := SQLText;
678 Create(aDataSet,Lines)
679 finally
680 Lines.Free
681 end
682 end;
683
684 constructor TSelectSQLParser.Create(SQLText: TStrings; StartLine,
685 StartIndex: integer);
686 begin
687 inherited Create;
688 FParamList := TStringList.Create;
689 FCTEs := TList.Create;
690 FLastSymbol := sqNone;
691 FState := stInit;
692 FStartLine := StartLine;
693 FIndex := StartIndex;
694 FAllowUnionAll := true;
695 AnalyseSQL(SQLText);
696 end;
697
698 procedure TSelectSQLParser.Changed;
699 begin
700 if assigned(FOnSQLChanging) and not FDestroying then
701 OnSQLChanging(self)
702 end;
703
704 function TSelectSQLParser.GetNextSymbol(C: char): TSQLSymbol;
705 begin
706 case C of
707 ' ',#9:
708 Result := sqSpace;
709 ';':
710 Result := sqSemiColon;
711 '"':
712 Result := sqDoubleQuotes;
713 '''':
714 Result := sqSingleQuotes;
715 '/':
716 Result := sqForwardSlash;
717 '*':
718 Result := sqAsterisk;
719 '(':
720 Result := sqOpenBracket;
721 ')':
722 Result := sqCloseBracket;
723 ':':
724 Result := sqColon;
725 ',':
726 Result := sqComma;
727 else
728 begin
729 Result := sqString;
730 FLastChar := C
731 end
732 end
733 end;
734
735 function TSelectSQLParser.GetSymbol(const Line: string; var index: integer): TSQLSymbol;
736 begin
737 Result := FLastSymbol;
738 if Result = sqString then
739 FString := FLastChar;
740 FLastSymbol := sqNone;
741
742 while (index <= Length(Line)) and (FLastSymbol = sqNone) do
743 begin
744 FLastSymbol := GetNextSymbol(Line[index]);
745 {combine if possible}
746 case Result of
747 sqNone:
748 begin
749 Result := FLastSymbol;
750 if FLastSymbol = sqString then
751 FString := FLastChar;
752 FLastSymbol := sqNone
753 end;
754
755 sqSpace:
756 if FLastSymbol = sqSpace then
757 FLastSymbol := sqNone;
758
759 sqForwardSlash:
760 if FLastSymbol = sqAsterisk then
761 begin
762 Result := sqCommentStart;
763 FLastSymbol := sqNone
764 end
765 else
766 if FLastSymbol = sqForwardSlash then
767 begin
768 Result := sqCommentLine;
769 FLastSymbol := sqNone
770 end;
771
772 sqAsterisk:
773 if FLastSymbol = sqForwardSlash then
774 begin
775 Result := sqCommentEnd;
776 FLastSymbol := sqNone
777 end;
778
779 sqString:
780 if FLastSymbol = sqString then
781 begin
782 FString := FString + FLastChar;
783 FLastSymbol := sqNone
784 end;
785 end;
786 Inc(index)
787 end;
788
789 if (Result = sqString) and not (FState in [stInComment,stInCommentLine, stInSingleQuotes,stInDoubleQuotes])then
790 Result := Check4ReservedWord(FString);
791
792 if (index > Length(Line)) then
793 begin
794 if (Result = sqNone) then
795 Result := sqEOL
796 else
797 if (FLastSymbol = sqNone) and (Result <> sqEOL) then
798 FLastSymbol := sqEOL;
799 end;
800
801 end;
802
803 function TSelectSQLParser.GetSQlText: string;
804 var SQL: TStringList;
805 I: integer;
806 begin
807 SQL := TStringList.Create;
808 try
809 for I := 0 to CTECount - 1 do
810 begin
811 if I = 0 then
812 begin
813 if CTEs[I]^.Recursive then
814 SQL.Add('WITH RECURSIVE ' + CTEs[I]^.Name + ' AS (' + CTES[I]^.Text + ')')
815 else
816 SQL.Add('WITH ' + CTEs[I]^.Name + ' AS (' + CTES[I]^.Text +')')
817 end
818 else
819 begin
820 SQL.Add(',');
821 SQL.Add(CTEs[I]^.Name + ' AS (' + CTES[I]^.Text +')')
822 end
823 end;
824 if CTECount > 0 then
825 SQL.Add('');
826 SQL.Add('SELECT ' + SelectClause + #13#10' FROM ' + FromClause);
827 if WhereClause <> '' then
828 SQL.Add('Where ' + WhereClause);
829 if GroupClause <> '' then
830 SQL.Add('GROUP BY ' + GroupClause);
831 if HavingClause <> '' then
832 SQL.Add('HAVING ' + HavingClause);
833 if PlanClause <> '' then
834 SQL.Add('PLAN ' + PlanClause);
835 if OrderByClause <> '' then
836 SQL.Add('ORDER BY ' + OrderByClause);
837 if Union <> nil then
838 begin
839 if Union.UnionAll then
840 SQL.Add('UNION ALL')
841 else
842 SQL.Add('UNION');
843 SQL.Add(Union.SQLText)
844 end;
845 Result := SQL.Text
846 finally
847 SQL.Free
848 end
849 end;
850
851 function TSelectSQLParser.PopState: TSQLStates;
852 begin
853 if FStackIndex = 0 then
854 raise Exception.Create(sStackUnderFlow);
855 Dec(FStackIndex);
856 Result := FStack[FStackIndex]
857 end;
858
859 procedure TSelectSQLParser.SetState(AState: TSQLStates);
860 begin
861 if FStackIndex > 16 then
862 raise Exception.Create(sStackOverFlow);
863 FStack[FStackIndex] := FState;
864 Inc(FStackIndex);
865 FState := AState
866 end;
867
868 procedure TSelectSQLParser.SetSelectClause(const Value: string);
869 begin
870 if Union <> nil then Union.SelectClause := Value;
871 FSelectClause := Value;
872 Changed
873 end;
874
875 procedure TSelectSQLParser.SetFromClause(const Value: string);
876 begin
877 if Union <> nil then
878 Union.FromClause := Value
879 else
880 FFromClause := Value;
881 Changed
882 end;
883
884 procedure TSelectSQLParser.SetGroupClause(const Value: string);
885 begin
886 if Union <> nil then
887 Union.GroupClause := Value
888 else
889 FGroupClause := Value;
890 Changed
891 end;
892
893 procedure TSelectSQLParser.SetOrderByClause(const Value: string);
894 begin
895 if Union <> nil then
896 Union.OrderByClause := Value
897 else
898 FOrderByClause := Value;
899 Changed
900 end;
901
902 procedure TSelectSQLParser.DropUnion;
903 begin
904 if FUnion <> nil then
905 begin
906 FUnion.Free;
907 FUnion := nil;
908 Changed
909 end
910 end;
911
912 function TSelectSQLParser.GetFieldPosition(AliasName: string): integer;
913 begin
914 if assigned(FDataSet) and (FDataSet is TIBCustomDataset) then
915 Result := TIBCustomDataset(FDataSet).GetFieldPosition(AliasName)
916 else
917 Result := 0;
918 end;
919
920 procedure TSelectSQLParser.ResetWhereClause;
921 begin
922 FWhereClause := FOriginalWhereClause;
923 if Union <> nil then
924 Union.ResetWhereClause;
925 Changed
926 end;
927
928 procedure TSelectSQLParser.ResetHavingClause;
929 begin
930 FHavingClause := FOriginalHavingClause;
931 if Union <> nil then
932 Union.ResetHavingClause;
933 Changed
934 end;
935
936 procedure TSelectSQLParser.ResetOrderByClause;
937 begin
938 FOrderbyClause := FOriginalOrderByClause;
939 if Union <> nil then
940 Union.ResetOrderByClause;
941 Changed
942 end;
943
944 procedure TSelectSQLParser.Reset;
945 begin
946 ResetWhereClause;
947 ResetHavingClause;
948 ResetOrderByClause
949 end;
950
951 destructor TSelectSQLParser.Destroy;
952 begin
953 FDestroying := true;
954 DropUnion;
955 if FParamList <> nil then FParamList.Free;
956 if FCTEs <> nil then
957 begin
958 CTEClear;
959 FCTEs.Free;
960 end;
961 inherited;
962 end;
963
964 end.
965
966