ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBSQLParser.pas
Revision: 27
Committed: Tue Apr 14 13:10:23 2015 UTC (9 years, 7 months ago) by tony
Content type: text/x-pascal
File size: 25534 byte(s)
Log Message:
Committing updates for Release R1-2-3

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 raise Exception.Create(sNoEndToThis);
452 end;
453 AddToSQL(' ');
454 Exit;
455 end;
456
457 sqSelect:
458 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stNestedSelect,stInCTE] then
459 AddToSql(FString)
460 else
461 FState := stInSelect;
462
463 sqFrom:
464 if FState = stInSelect then
465 FState := stInFrom
466 else
467 AddToSql(FString);
468 { if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,
469 stNestedGroupBy,stNestedSelect] then
470 AddToSql(FString)
471 else
472 FState := stInFrom;}
473
474 sqGroup:
475 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
476 AddToSql(FString)
477 else
478 FState := stInGroup;
479
480 sqWhere:
481 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stNestedSelect,stInCTE] then
482 AddToSql(FString)
483 else
484 FState := stInWhere;
485
486 sqHaving:
487 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
488 AddToSql(FString)
489 else
490 FState := stInHaving;
491
492 sqPlan:
493 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
494 AddToSql(FString)
495 else
496 FState := stInPlan;
497
498 sqOrder:
499 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere] then
500 AddToSql(FString)
501 else
502 FState := stInOrder;
503
504 sqUnion:
505 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
506 AddToSql(FString)
507 else
508 begin
509 FState := stUnion;
510 Exit
511 end;
512
513 sqAll:
514 if FState in [stInSingleQuotes,stInDoubleQuotes,stNestedFrom,stNestedWhere,stInCTE] then
515 AddToSql(FString)
516 else
517 if (FState = stInit) and FAllowUnionAll and not FUnionAll then
518 FUnionAll := true
519 else
520 raise Exception.Create('Unexpected symbol "all"');
521
522 sqBy:
523 case FState of
524 stInGroup:
525 FState := stInGroupBy;
526 stInOrder:
527 FState := stInOrderBy;
528 stNestedFrom,stNestedWhere,stInCTE,
529 stInSingleQuotes,
530 stInDoubleQuotes:
531 AddToSql(FString);
532 else
533 raise Exception.CreateFmt(sBadBy,[Line])
534 end;
535
536 sqWith:
537 if FState = stInit then
538 begin
539 FState := stCTE;
540 InitCTE;
541 end
542 else
543 raise Exception.Create('Unexpected symbol "with"');
544
545 sqRecursive:
546 if FState = stCTE then
547 begin
548 FCTE.Recursive := true;
549 FState := stCTE1
550 end
551 else
552 raise Exception.Create('Unexpected symbol "recursive"');
553
554 sqAs:
555 if FState = stCTE2 then
556 FState := stCTE3
557 else
558 AddToSQL('as');
559
560 else
561 raise Exception.Create(sBadSymbol);
562 end
563 end
564 end;
565
566 procedure TSelectSQLParser.AnalyseSQL(Lines: TStrings);
567 var I: integer;
568 begin
569 try
570 for I := FStartLine to Lines.Count - 1 do
571 try
572 AnalyseLine(Lines[I]);
573 case FState of
574 stDone:
575 break;
576 stUnion:
577 begin
578 if FIndex > length(Lines[I]) then
579 if I+1 < Lines.Count then
580 FUnion := TSelectSQLParser.Create(Lines,I+1,1)
581 else
582 raise Exception.Create(sIncomplete)
583 else
584 FUnion := TSelectSQLParser.Create(Lines,I,FIndex);
585 Exit
586 end;
587 end;
588 FIndex := 1;
589 except on E: Exception do
590 raise Exception.CreateFmt(sBadSQL,[Lines[I],E.Message])
591 end;
592 finally
593 FOriginalWhereClause := WhereClause;
594 FOriginalHavingClause := HavingClause;
595 FOriginalOrderByClause := OrderByClause
596 end;
597 end;
598
599 procedure TSelectSQLParser.InitCTE;
600 begin
601 with FCTE do
602 begin
603 Recursive := false;
604 Name := '';
605 Text := '';
606 end;
607 end;
608
609 procedure TSelectSQLParser.AddCTE;
610 var cte: PCTEDef;
611 begin
612 new(cte);
613 cte^.Name := FCTE.Name;
614 cte^.Recursive := FCTE.Recursive;
615 cte^.Text := FCTE.Text;
616 FCTEs.add(cte);
617 InitCTE;
618 end;
619
620 function TSelectSQLParser.Check4ReservedWord(const Text: string): TSQLSymbol;
621 begin
622 Result := sqString;
623 if CompareText(Text,'select') = 0 then
624 Result := sqSelect
625 else
626 if CompareText(Text,'from') = 0 then
627 Result := sqFrom
628 else
629 if CompareText(Text,'where') = 0 then
630 Result := sqWhere
631 else
632 if CompareText(Text,'group') = 0 then
633 Result := sqGroup
634 else
635 if CompareText(Text,'by') = 0 then
636 Result := sqBy
637 else
638 if CompareText(Text,'having') = 0 then
639 Result := sqHaving
640 else
641 if CompareText(Text,'plan') = 0 then
642 Result := sqPlan
643 else
644 if CompareText(Text,'union') = 0 then
645 Result := sqUnion
646 else
647 if CompareText(Text,'all') = 0 then
648 Result := sqAll
649 else
650 if CompareText(Text,'order') = 0 then
651 Result := sqOrder
652 else
653 if CompareText(Text,'with') = 0 then
654 Result := sqWith
655 else
656 if CompareText(Text,'recursive') = 0 then
657 Result := sqRecursive
658 else
659 if CompareText(Text,'as') = 0 then
660 Result := sqAs
661 end;
662
663 constructor TSelectSQLParser.Create(aDataSet: TDataSet; SQLText: TStrings);
664 begin
665 FDataSet := aDataSet;
666 Create(SQLText,0,1)
667 end;
668
669 constructor TSelectSQLParser.Create(aDataSet: TDataSet; const SQLText: string);
670 var Lines: TStringList;
671 begin
672 Lines := TStringList.Create;
673 try
674 Lines.Text := SQLText;
675 Create(aDataSet,Lines)
676 finally
677 Lines.Free
678 end
679 end;
680
681 constructor TSelectSQLParser.Create(SQLText: TStrings; StartLine,
682 StartIndex: integer);
683 begin
684 inherited Create;
685 FParamList := TStringList.Create;
686 FCTEs := TList.Create;
687 FLastSymbol := sqNone;
688 FState := stInit;
689 FStartLine := StartLine;
690 FIndex := StartIndex;
691 FAllowUnionAll := true;
692 AnalyseSQL(SQLText);
693 end;
694
695 procedure TSelectSQLParser.Changed;
696 begin
697 if assigned(FOnSQLChanging) and not FDestroying then
698 OnSQLChanging(self)
699 end;
700
701 function TSelectSQLParser.GetNextSymbol(C: char): TSQLSymbol;
702 begin
703 case C of
704 ' ',#9:
705 Result := sqSpace;
706 ';':
707 Result := sqSemiColon;
708 '"':
709 Result := sqDoubleQuotes;
710 '''':
711 Result := sqSingleQuotes;
712 '/':
713 Result := sqForwardSlash;
714 '*':
715 Result := sqAsterisk;
716 '(':
717 Result := sqOpenBracket;
718 ')':
719 Result := sqCloseBracket;
720 ':':
721 Result := sqColon;
722 ',':
723 Result := sqComma;
724 else
725 begin
726 Result := sqString;
727 FLastChar := C
728 end
729 end
730 end;
731
732 function TSelectSQLParser.GetSymbol(const Line: string; var index: integer): TSQLSymbol;
733 begin
734 Result := FLastSymbol;
735 if Result = sqString then
736 FString := FLastChar;
737 FLastSymbol := sqNone;
738
739 while (index <= Length(Line)) and (FLastSymbol = sqNone) do
740 begin
741 FLastSymbol := GetNextSymbol(Line[index]);
742 {combine if possible}
743 case Result of
744 sqNone:
745 begin
746 Result := FLastSymbol;
747 if FLastSymbol = sqString then
748 FString := FLastChar;
749 FLastSymbol := sqNone
750 end;
751
752 sqSpace:
753 if FLastSymbol = sqSpace then
754 FLastSymbol := sqNone;
755
756 sqForwardSlash:
757 if FLastSymbol = sqAsterisk then
758 begin
759 Result := sqCommentStart;
760 FLastSymbol := sqNone
761 end
762 else
763 if FLastSymbol = sqForwardSlash then
764 begin
765 Result := sqCommentLine;
766 FLastSymbol := sqNone
767 end;
768
769 sqAsterisk:
770 if FLastSymbol = sqForwardSlash then
771 begin
772 Result := sqCommentEnd;
773 FLastSymbol := sqNone
774 end;
775
776 sqString:
777 if FLastSymbol = sqString then
778 begin
779 FString := FString + FLastChar;
780 FLastSymbol := sqNone
781 end;
782 end;
783 Inc(index)
784 end;
785
786 if (Result = sqString) and not (FState in [stInComment,stInCommentLine])then
787 Result := Check4ReservedWord(FString);
788
789 if (index > Length(Line)) then
790 if Result = sqNone then
791 Result := sqEOL
792 else
793 if (FLastSymbol = sqNone) and (Result <> sqEOL) then
794 FLastSymbol := sqEOL;
795
796 end;
797
798 function TSelectSQLParser.GetSQlText: string;
799 var SQL: TStringList;
800 I: integer;
801 begin
802 SQL := TStringList.Create;
803 try
804 for I := 0 to CTECount - 1 do
805 begin
806 if I = 0 then
807 begin
808 if CTEs[I]^.Recursive then
809 SQL.Add('WITH RECURSIVE ' + CTEs[I]^.Name + ' AS (' + CTES[I]^.Text + ')')
810 else
811 SQL.Add('WITH ' + CTEs[I]^.Name + ' AS (' + CTES[I]^.Text +')')
812 end
813 else
814 begin
815 SQL.Add(',');
816 SQL.Add(CTEs[I]^.Name + ' AS (' + CTES[I]^.Text +')')
817 end
818 end;
819 if CTECount > 0 then
820 SQL.Add('');
821 SQL.Add('SELECT ' + SelectClause + #13#10' FROM ' + FromClause);
822 if WhereClause <> '' then
823 SQL.Add('Where ' + WhereClause);
824 if GroupClause <> '' then
825 SQL.Add('GROUP BY ' + GroupClause);
826 if HavingClause <> '' then
827 SQL.Add('HAVING ' + HavingClause);
828 if PlanClause <> '' then
829 SQL.Add('PLAN ' + PlanClause);
830 if OrderByClause <> '' then
831 SQL.Add('ORDER BY ' + OrderByClause);
832 if Union <> nil then
833 begin
834 if Union.UnionAll then
835 SQL.Add('UNION ALL')
836 else
837 SQL.Add('UNION');
838 SQL.Add(Union.SQLText)
839 end;
840 Result := SQL.Text
841 finally
842 SQL.Free
843 end
844 end;
845
846 function TSelectSQLParser.PopState: TSQLStates;
847 begin
848 if FStackIndex = 0 then
849 raise Exception.Create(sStackUnderFlow);
850 Dec(FStackIndex);
851 Result := FStack[FStackIndex]
852 end;
853
854 procedure TSelectSQLParser.SetState(AState: TSQLStates);
855 begin
856 if FStackIndex > 16 then
857 raise Exception.Create(sStackOverFlow);
858 FStack[FStackIndex] := FState;
859 Inc(FStackIndex);
860 FState := AState
861 end;
862
863 procedure TSelectSQLParser.SetSelectClause(const Value: string);
864 begin
865 if Union <> nil then Union.SelectClause := Value;
866 FSelectClause := Value;
867 Changed
868 end;
869
870 procedure TSelectSQLParser.SetFromClause(const Value: string);
871 begin
872 if Union <> nil then
873 Union.FromClause := Value
874 else
875 FFromClause := Value;
876 Changed
877 end;
878
879 procedure TSelectSQLParser.SetGroupClause(const Value: string);
880 begin
881 if Union <> nil then
882 Union.GroupClause := Value
883 else
884 FGroupClause := Value;
885 Changed
886 end;
887
888 procedure TSelectSQLParser.SetOrderByClause(const Value: string);
889 begin
890 if Union <> nil then
891 Union.OrderByClause := Value
892 else
893 FOrderByClause := Value;
894 Changed
895 end;
896
897 procedure TSelectSQLParser.DropUnion;
898 begin
899 if FUnion <> nil then
900 begin
901 FUnion.Free;
902 FUnion := nil;
903 Changed
904 end
905 end;
906
907 function TSelectSQLParser.GetFieldPosition(AliasName: string): integer;
908 begin
909 if assigned(FDataSet) and (FDataSet is TIBCustomDataset) then
910 Result := TIBCustomDataset(FDataSet).GetFieldPosition(AliasName)
911 else
912 Result := 0;
913 end;
914
915 procedure TSelectSQLParser.ResetWhereClause;
916 begin
917 FWhereClause := FOriginalWhereClause;
918 if Union <> nil then
919 Union.ResetWhereClause;
920 Changed
921 end;
922
923 procedure TSelectSQLParser.ResetHavingClause;
924 begin
925 FHavingClause := FOriginalHavingClause;
926 if Union <> nil then
927 Union.ResetHavingClause;
928 Changed
929 end;
930
931 procedure TSelectSQLParser.ResetOrderByClause;
932 begin
933 FOrderbyClause := FOriginalOrderByClause;
934 if Union <> nil then
935 Union.ResetOrderByClause;
936 Changed
937 end;
938
939 procedure TSelectSQLParser.Reset;
940 begin
941 ResetWhereClause;
942 ResetHavingClause;
943 ResetOrderByClause
944 end;
945
946 destructor TSelectSQLParser.Destroy;
947 begin
948 FDestroying := true;
949 DropUnion;
950 if FParamList <> nil then FParamList.Free;
951 if FCTEs <> nil then
952 begin
953 CTEClear;
954 FCTEs.Free;
955 end;
956 inherited;
957 end;
958
959 end.
960
961