ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/ibxscript.pas
Revision: 37
Committed: Mon Feb 15 14:44:25 2016 UTC (8 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 21025 byte(s)
Log Message:
Committing updates for Release R1-4-0

File Contents

# User Rev Content
1 tony 37 (*
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 ibxscript;
27    
28     {$mode objfpc}{$H+}
29    
30     interface
31    
32     uses Classes, IBDatabase, IBSQL, IBHeader;
33    
34     type
35     TSQLSymbol = (sqNone,sqSpace,sqSemiColon,sqSingleQuotes,sqDoubleQuotes,
36     sqEnd,sqBegin,sqCommit,sqRollback,sqString,sqCommentStart,
37     sqCommentEnd,sqCommentLine,sqAsterisk,sqForwardSlash,
38     sqDeclare,sqEOL,sqTerminator, sqReconnect);
39    
40     TSQLStates = (stInit, stError, stInSQL, stNested,stInSingleQuotes,
41     stInDoubleQuotes, stInComment, stInCommentLine,
42     stInDeclaration, stInCommit, stInReconnect);
43    
44     TGetParamValue = procedure(Sender: TObject; ParamName: string; var BlobID: TISC_QUAD) of object;
45     TLogEvent = procedure(Sender: TObject; Msg: string) of Object;
46     TOnProgressEvent = procedure (Sender: TObject; Reset: boolean; value: integer) of object;
47     TOnSelectSQL = procedure (Sender: TObject; SQLText: string) of object;
48    
49     {
50     TIBXScript: runs an SQL script in the specified file or stream. The text is parsed
51     into SQL statements which are executed in turn. The intention is to be ISQL
52     compatible but with extensions:
53    
54     * SET TERM and Set AutoDDL are both supported
55    
56     * New Command: RECONNECT. Performs a commit followed by disconnecting and
57     reconnecting to the database.
58    
59     * Procedure Bodies (BEGIN .. END blocks) are self-delimiting and do not need
60     an extra terminator. If a terminator is present, this is treated as an
61     empty statement. The result is ISQL compatible, but does not require the
62     use of SET TERM.
63    
64     * DML statements may have arguments in IBX format (e.g UPDATE MYTABLE Set data = :mydata).
65     Arguments are valid only for BLOB columns and are resolved using the GetParamValue
66     event. This returns the blobid to be used. A typical use of the event is to
67     read binary data from a file, save it in a blob stream and return the blob id.
68    
69     Select SQL statements are not directly supported but can be handled by an external
70     handler (OnSelectSQL event). If the handler is not present then an exception
71     is raised if a Select SQL statement is found.
72    
73     Properties:
74    
75     * Database: Link to TIBDatabase component
76     * Transaction: Link to Transaction. Defaults to internaltransaction (concurrency, wait)
77     * Echo: boolean. When true, all SQL statements are echoed to log
78     * StopOnFirstError: boolean. When true the script engine terminates on the first
79     SQL Error.
80     * IgnoreGrants: When true, grant statements are silently discarded. This can be
81     useful when applying a script using the Embedded Server.
82    
83    
84     Events:
85    
86     * GetParamValue: called when an SQL parameter is found (in PSQL :name format).
87     This is only called for blob fields. Handler should return the BlobID to be
88     used as the parameter value. If not present an exception is raised when a
89     parameter is found.
90     * OnOutputLog: Called to write SQL Statements to the log (stdout)
91     * OnErrorLog: Called to write all other messages to the log (stderr)
92     * OnProgressEvent: Progress bar support. If Reset is true the value is maximum
93     value of progress bar. Otherwise called to step progress bar.
94     * OnSelectSQL: handler for select SQL statements. If not present, select SQL
95     statements result in an exception.
96    
97     The PerformUpdate function is used to execute an SQL Script and may be called
98     multiple times.
99     }
100    
101    
102     { TIBXScript }
103    
104     TIBXScript = class(TComponent)
105     private
106     FDatabase: TIBDatabase;
107     FEcho: boolean;
108     FIgnoreGrants: boolean;
109     FOnErrorLog: TLogEvent;
110     FOnProgressEvent: TOnProgressEvent;
111     FOnSelectSQL: TOnSelectSQL;
112     FStopOnFirstError: boolean;
113     FTransaction: TIBTransaction;
114     FInternalTransaction: TIBTransaction;
115     FState: TSQLStates;
116     FString: string;
117     FISQL: TIBSQL;
118     FLastSymbol: TSQLSymbol;
119     FNested: integer;
120     FLastChar: char;
121     FSQLText: string;
122     FHasBegin: boolean;
123     FStack: array [0..16] of TSQLStates;
124     FStackindex: integer;
125     FGetParamValue: TGetParamValue;
126     FOnOutputLog: TLogEvent;
127     FTerminator: char;
128     FAutoDDL: boolean;
129     procedure Add2Log(const Msg: string; IsError: boolean=true);
130     procedure AddToSQL(const Symbol: string);
131     function AnalyseSQL(Lines: TStringList): boolean;
132     procedure AnalyseLine(const Line: string);
133     procedure DoCommit;
134     procedure DoReconnect;
135     procedure ExecSQL;
136     function GetNextSymbol(C: char): TSQLSymbol;
137     function GetSymbol(const Line: string; var index: integer): TSQLSymbol;
138     function GetTransaction: TIBTransaction;
139     procedure SetDatabase(AValue: TIBDatabase);
140     procedure SetParamValue(SQLVar: TIBXSQLVAR);
141     procedure SetState(AState: TSQLStates);
142     procedure ClearStatement;
143     function PopState: TSQLStates;
144     function ProcessSetStatement(stmt: string): boolean;
145     public
146     constructor Create(aOwner: TComponent); override;
147     destructor Destroy; override;
148     function PerformUpdate(const SQLFile: string; AutoDDL: boolean): boolean; overload;
149     function PerformUpdate(const SQLStream: TStream; AutoDDL: boolean): boolean; overload;
150     published
151     property Database: TIBDatabase read FDatabase write SetDatabase;
152     property Echo: boolean read FEcho write FEcho default true; {Echo Input to Log}
153     property IgnoreGrants: boolean read FIgnoreGrants write FIgnoreGrants;
154     property Transaction: TIBTransaction read FTransaction write FTransaction;
155     property StopOnFirstError: boolean read FStopOnFirstError write FStopOnFirstError default true;
156     property GetParamValue: TGetParamValue read FGetParamValue write FGetParamValue; {resolve parameterized queries}
157     property OnOutputLog: TLogEvent read FOnOutputLog write FOnOutputLog; {Log handler}
158     property OnErrorLog: TLogEvent read FOnErrorLog write FOnErrorLog;
159     property OnProgressEvent: TOnProgressEvent read FOnProgressEvent write FOnProgressEvent; {Progress Bar Support}
160     property OnSelectSQL: TOnSelectSQL read FOnSelectSQL write FOnSelectSQL; {Handle Select SQL Statements}
161     end;
162    
163     implementation
164    
165     uses Sysutils, IB, RegExpr;
166    
167     resourcestring
168     sTerminatorUnknownState = 'Statement Terminator in unexpected state (%d)';
169     sUnterminatedString = 'Unterminated string';
170     sUnknownSymbol = 'Unknown Symbol %d';
171     sNoSelectSQL = 'Select SQL Statements are not supported';
172     sStackUnderflow = 'Stack Underflow';
173     sInvalidAutoDDL = 'Invalid AUTODDL Statement - %s';
174     sNoParamQueries = 'Parameterised Queries are not supported';
175     sStackOverFlow = 'Stack Overflow';
176     sResolveQueryParam = 'Resolving Query Parameter: %s';
177     sNoCommit = 'Commit not allowed here';
178     sNoReconnect = 'Reconnect not allowed here';
179    
180     { TIBXScript }
181    
182     procedure TIBXScript.Add2Log(const Msg: string; IsError: boolean);
183     begin
184     if IsError then
185     begin
186     if assigned(OnErrorLog) then OnErrorLog(self,Msg)
187     end
188     else
189     if assigned(FOnOutputLog) then FOnOutputLog(self,Msg)
190     end;
191    
192     procedure TIBXScript.AddToSQL(const Symbol: string);
193     begin
194     FSQLText := FSQLText + Symbol
195     end;
196    
197     procedure TIBXScript.AnalyseLine(const Line: string);
198     var index: integer;
199     Symbol: TSQLSymbol;
200     NonSpace: boolean;
201     begin
202     index := 1;
203     NonSpace := false;
204     while true do
205     begin
206     if FState = stError then
207     raise Exception.Create('Entered Error State');
208     Symbol := GetSymbol(Line,index);
209     if not (Symbol in [sqSpace,sqEOL]) then
210     NonSpace := true;
211     case Symbol of
212     sqSpace:
213     if not (FState in [stInComment,stInCommentLine]) then
214     AddToSQL(' ');
215    
216     sqTerminator:
217     if not (FState in [stInComment,stInCommentLine]) then
218     case FState of
219     stInit: {ignore empty statement};
220    
221     stInSQL:
222     ExecSQL;
223    
224     stInCommit:
225     DoCommit;
226    
227     stInReconnect:
228     DoReconnect;
229    
230     stNested, stInSingleQuotes, stInDoubleQuotes:
231     AddToSQL(FTerminator);
232    
233     stInDeclaration:
234     begin
235     FState := PopState;
236     AddToSQL(FTerminator);
237     end;
238    
239     else
240     raise Exception.CreateFmt(sTerminatorUnknownState,[FState]);
241     end;
242    
243     sqSemiColon:
244     begin
245     if FState = stInDeclaration then
246     FState := PopState;
247     AddToSQL(';');
248     end;
249    
250     sqAsterisk:
251     if not (FState in [stInComment,stInCommentLine]) then
252     begin
253     AddToSQL('*');
254     if FState = stInit then
255     FState := stInSQL
256     end;
257    
258     sqForwardSlash:
259     if not (FState in [stInComment,stInCommentLine]) then
260     begin
261     AddToSQL('/');
262     if FState = stInit then
263     FState := stInSQL
264     end;
265    
266     sqCommentStart:
267     if not (FState in [stInComment,stInCommentLine]) then
268     SetState(stInComment);
269    
270     sqCommentEnd:
271     if FState = stInComment then
272     begin
273     AddToSQL('/* ' + Trim(FString) + ' */');
274     FState := PopState
275     end
276     else
277     FState := stError;
278    
279     sqCommentLine:
280     if not (FState in [stInComment,stInCommentLine]) then
281     SetState(stInCommentLine);
282    
283     sqSingleQuotes:
284     if not (FState in [stInComment,stInCommentLine]) then
285     begin
286     case FState of
287     stInSingleQuotes:
288     FState := PopState;
289     stInDoubleQuotes:
290     {Ignore};
291     else
292     SetState(stInSingleQuotes)
293     end;
294     AddToSQL('''')
295     end;
296    
297     sqDoubleQuotes:
298     if not (FState in [stInComment,stInCommentLine]) then
299     begin
300     case FState of
301     stInSingleQuotes:
302     {Ignore};
303     stInDoubleQuotes:
304     FState := PopState;
305     else
306     SetState(stInDoubleQuotes)
307     end;
308     AddToSQL('"')
309     end;
310    
311     sqEnd:
312     if not (FState in [stInComment,stInCommentLine]) then
313     begin
314     AddToSQL(FString);
315     case FState of
316     stInSingleQuotes,
317     stInDoubleQuotes:
318     {Ignore};
319     stNested:
320     begin
321     if FNested = 0 then
322     begin
323     PopState;
324     FState := stInit;
325     ExecSQL
326     end
327     else
328     Dec(FNested)
329     end;
330     {Otherwise ignore}
331     end
332     end;
333    
334     sqBegin:
335     if not (FState in [stInComment,stInCommentLine]) then
336     begin
337     FHasBegin := true;
338     AddToSQL(FString);
339     case FState of
340     stInSingleQuotes,
341     stInDoubleQuotes:
342     {Ignore};
343     stNested:
344     Inc(FNested);
345    
346     stInSQL,
347     stInit:
348     SetState(stNested);
349     end
350     end;
351    
352     sqDeclare:
353     if not (FState in [stInComment,stInCommentLine]) then
354     begin
355     AddToSQL(FString);
356     if FState in [stInit,stInSQL] then
357     SetState(stInDeclaration)
358     end;
359    
360     sqCommit:
361     if not (FState in [stInComment,stInCommentLine]) then
362     begin
363     if FState = stInit then
364     FState := stInCommit
365     else
366     raise Exception.Create(sNoCommit)
367     end;
368    
369     sqReconnect:
370     if not (FState in [stInComment,stInCommentLine]) then
371     begin
372     if FState = stInit then
373     FState := stInReconnect
374     else
375     raise Exception.Create(sNoReconnect)
376     end;
377    
378     sqString:
379     if not (FState in [stInComment,stInCommentLine]) then
380     begin
381     AddToSQL(FString);
382     if FState = stInit then
383     FState := stInSQL
384     end;
385    
386     sqEOL:
387     begin
388     case FState of
389     stInCommentLine:
390     begin
391     AddToSQL('/* ' + Trim(FString) + ' */');
392     FState := PopState;
393     end;
394     stInDoubleQuotes,
395     stInSingleQuotes:
396     raise Exception.Create(sUnterminatedString);
397     end;
398     if NonSpace then AddToSQL(#13#10);
399     Exit;
400     end;
401     else
402     raise Exception.CreateFmt(sUnknownSymbol,[Symbol]);
403     end
404     end
405     end;
406    
407     function TIBXScript.AnalyseSQL(Lines: TStringList): boolean;
408     var I: integer;
409     begin
410     Result := true;
411     ClearStatement;
412     FLastSymbol := sqNone;
413     for I := 0 to Lines.Count - 1 do
414     begin
415     if Echo then Add2Log(Lines[I],false);
416     if assigned(OnProgressEvent) then
417     OnProgressEvent(self,false,1);
418     try
419     AnalyseLine(Lines[I]);
420     except on E:Exception do
421     begin
422     Add2Log(E.Message);
423     Result := false;
424     if StopOnFirstError then Exit;
425     ClearStatement;
426     FLastSymbol := sqNone;
427     end
428     end;
429     end;
430     if FState <> stInit then
431     AnalyseLine(';');
432     Result := (FStackIndex = 0) and (FState = stInit)
433     end;
434    
435     constructor TIBXScript.Create(aOwner: TComponent);
436     begin
437     inherited;
438     FStopOnFirstError := true;
439     FEcho := true;
440     FState := stInit;
441     FISQL := TIBSQL.Create(self);
442     FISQL.ParamCheck := true;
443     FInternalTransaction := TIBTransaction.Create(self);
444     FInternalTransaction.Params.Clear;
445     FInternalTransaction.Params.Add('concurrency');
446     FInternalTransaction.Params.Add('wait');
447     ClearStatement;
448     end;
449    
450     destructor TIBXScript.Destroy;
451     begin
452     if FISQL <> nil then FISQL.Free;
453     if FInternalTransaction <> nil then FInternalTransaction.Free;
454     inherited;
455     end;
456    
457     procedure TIBXScript.DoCommit;
458     begin
459     with GetTransaction do
460     if InTransaction then Commit;
461     if not GetTransaction.InTransaction then
462     GetTransaction.StartTransaction;
463     ClearStatement;
464     end;
465    
466     procedure TIBXScript.DoReconnect;
467     begin
468     with GetTransaction do
469     if InTransaction then Commit;
470     Database.Connected := false;
471     Database.Connected := true;
472     if not GetTransaction.InTransaction then
473     GetTransaction.StartTransaction;
474     ClearStatement;
475     end;
476    
477     procedure TIBXScript.ExecSQL;
478     var DDL: boolean;
479     I: integer;
480     begin
481     if FSQLText <> '' then
482     begin
483     if ProcessSetStatement(FSQLText) then {Handle Set Statement}
484     begin
485     ClearStatement;
486     Exit;
487     end;
488    
489     FISQL.SQL.Text := FSQLText;
490     FISQL.Transaction := GetTransaction;
491     with FISQL.Transaction do
492     if not InTransaction then StartTransaction;
493     FISQL.ParamCheck := not FHasBegin; {Probably PSQL}
494     FISQL.Prepare;
495     if FISQL.SQLType in [SQLInsert, SQLUpdate, SQLDelete] then
496     begin
497     {Interpret parameters}
498     for I := 0 to FISQL.Params.Count - 1 do
499     SetParamValue(FISQL.Params[I]);
500     end;
501    
502     if FISQL.SQLType = SQLSelect then
503     begin
504     if assigned(OnSelectSQL) then
505     OnSelectSQL(self,FSQLText)
506     else
507     raise Exception.Create(sNoSelectSQL);
508     end
509     else
510     begin
511     DDL := FISQL.SQLType = SQLDDL;
512     if not DDL or not FIgnoreGrants or (Pos('GRANT',AnsiUpperCase(Trim(FSQLText))) <> 1) then
513     FISQL.ExecQuery;
514     if FAutoDDL and DDL then
515     FISQL.Transaction.Commit;
516     FISQL.Close;
517     end;
518     FISQL.SQL.Clear;
519     ClearStatement;
520     end
521     end;
522    
523    
524    
525     function TIBXScript.GetNextSymbol(C: char): TSQLSymbol;
526     begin
527     if C = FTerminator then
528     Result := sqTerminator
529     else
530     case C of
531     ' ',#9:
532     Result := sqSpace;
533     ';':
534     Result := sqSemiColon;
535     '"':
536     Result := sqDoubleQuotes;
537     '''':
538     Result := sqSingleQuotes;
539     '/':
540     Result := sqForwardSlash;
541     '*':
542     Result := sqAsterisk;
543     else
544     begin
545     Result := sqString;
546     FLastChar := C
547     end
548     end;
549     end;
550    
551     function TIBXScript.GetSymbol(const Line: string; var index: integer): TSQLSymbol;
552     begin
553     Result := sqNone;
554     if FLastSymbol <> sqNone then
555     begin
556     Result := FLastSymbol;
557     if Result = sqString then
558     FString := FLastChar;
559     FLastSymbol := sqNone
560     end;
561    
562     while (index <= Length(Line)) and (FLastSymbol = sqNone) do
563     begin
564     FLastSymbol := GetNextSymbol(Line[index]);
565     {combine if possible}
566     case Result of
567     sqNone:
568     begin
569     Result := FLastSymbol;
570     if FLastSymbol = sqString then
571     FString := FLastChar;
572     FLastSymbol := sqNone
573     end;
574    
575     sqForwardSlash:
576     if FLastSymbol = sqAsterisk then
577     begin
578     Result := sqCommentStart;
579     FLastSymbol := sqNone
580     end
581     else
582     if FLastSymbol = sqForwardSlash then
583     begin
584     Result := sqCommentLine;
585     FLastSymbol := sqNone
586     end;
587    
588     sqAsterisk:
589     if FLastSymbol = sqForwardSlash then
590     begin
591     Result := sqCommentEnd;
592     FLastSymbol := sqNone
593     end;
594    
595     sqString:
596     if FLastSymbol = sqString then
597     begin
598     FString := FString + FLastChar;
599     FLastSymbol := sqNone
600     end;
601     end;
602     Inc(index)
603     end;
604    
605     if (index > Length(Line)) then
606     if Result = sqNone then
607     Result := sqEOL
608     else
609     if (FLastSymbol = sqNone) and (Result <> sqEOL) then
610     FLastSymbol := sqEOL;
611    
612     if Result = sqString then
613     begin
614     if FString <> '' then
615     if CompareText(FString,'begin') = 0 then
616     Result := sqBegin
617     else
618     if CompareText(FString,'end') = 0 then
619     Result := sqEnd
620     else
621     if CompareText(FString,'declare') = 0 then
622     Result := sqDeclare
623     else
624     if CompareText(FString,'commit') = 0 then
625     Result := sqCommit
626     else
627     if CompareText(FString,'reconnect') = 0 then
628     Result := sqReconnect;
629     end
630     end;
631    
632     function TIBXScript.GetTransaction: TIBTransaction;
633     begin
634     if FTransaction = nil then
635     Result := FInternalTransaction
636     else
637     Result := FTransaction;
638     end;
639    
640     procedure TIBXScript.SetDatabase(AValue: TIBDatabase);
641     begin
642     if FDatabase = AValue then Exit;
643     FDatabase := AValue;
644     FISQL.Database := AValue;
645     FInternalTransaction.DefaultDatabase := AValue;
646     end;
647    
648     function TIBXScript.PerformUpdate(const SQLFile: string;
649     AutoDDL: boolean): boolean;
650     var F: TFileStream;
651     begin
652     F := TFileStream.Create(SQLFile,fmOpenRead or fmShareDenyNone);
653     try
654     Result := PerformUpdate(F,AutoDDL)
655     finally
656     F.Free
657     end;
658     end;
659    
660     function TIBXScript.PerformUpdate(const SQLStream: TStream; AutoDDL: boolean): boolean;
661     var Lines: TStringList;
662     FNotConnected: boolean;
663     begin
664     FTerminator := ';';
665     FAutoDDL := AutoDDL;
666     FNotConnected := not Database.Connected;
667     Database.Connected := true;
668     try
669     Lines := TStringList.Create;
670     Lines.LoadFromStream(SQLStream);
671     try
672     if assigned(OnProgressEvent) then
673     OnProgressEvent(self,true,Lines.Count);
674    
675     Result := AnalyseSQL(Lines)
676     finally
677     Lines.Free
678     end;
679     except on E:Exception do
680     begin
681     Add2Log(E.Message);
682     with GetTransaction do
683     if InTransaction then Rollback;
684     Result := false
685     end
686     end;
687     with GetTransaction do
688     if InTransaction then Commit;
689     if FNotConnected then
690     Database.Connected := false;
691     end;
692    
693     function TIBXScript.PopState: TSQLStates;
694     begin
695     if FStackIndex = 0 then
696     raise Exception.Create(sStackUnderflow);
697     Dec(FStackIndex);
698     Result := FStack[FStackIndex]
699     end;
700    
701     function TIBXScript.ProcessSetStatement(stmt: string): boolean;
702     var RegexObj: TRegExpr;
703     begin
704     Result := false;
705     RegexObj := TRegExpr.Create;
706     try
707     {Process Set Term}
708     RegexObj.Expression := 'SET +TERM +(.) *(\' + FTerminator + '|)';
709     if RegexObj.Exec(AnsiUpperCase(stmt)) then
710     begin
711     FTerminator := RegexObj.Match[1][1];
712     Result := true;
713     Exit;
714     end;
715    
716     {Process AutoDDL}
717     RegexObj.Expression := 'SET +AUTODDL +([a-zA-Z]+) *(\' + FTerminator + '|)';
718     if RegexObj.Exec(AnsiUpperCase(stmt)) then
719     begin
720     if AnsiUpperCase(RegexObj.Match[1]) = 'ON' then
721     FAutoDDL := true
722     else
723     if AnsiUpperCase(RegexObj.Match[1]) = 'OFF' then
724     FAutoDDL := false
725     else
726     raise Exception.CreateFmt(sInvalidAutoDDL, [RegexObj.Match[0]]);
727    
728     Result := true;
729     end;
730     finally
731     RegexObj.Free;
732     end;
733     end;
734    
735    
736     procedure TIBXScript.SetParamValue(SQLVar: TIBXSQLVAR);
737     var BlobID: TISC_QUAD;
738     begin
739     if assigned(FGetParamValue) and (SQLVar.SQLType = SQL_BLOB) then
740     begin
741     Add2Log(Format(sResolveQueryParam,[SQLVar.Name]));
742     GetParamValue(self,SQLVar.Name,BlobID);
743     if (BlobID.gds_quad_high = 0) and (BlobID.gds_quad_low = 0) then
744     SQLVar.Clear
745     else
746     SQLVar.AsQuad := BlobID
747     end
748     else
749     raise Exception.Create(sNoParamQueries);
750     end;
751    
752     procedure TIBXScript.SetState(AState: TSQLStates);
753     begin
754     if FStackIndex > 16 then
755     raise Exception.Create(sStackOverFlow);
756     FStack[FStackIndex] := FState;
757     Inc(FStackIndex);
758     FState := AState
759     end;
760    
761     procedure TIBXScript.ClearStatement;
762     begin
763     FSQLText := '';
764     FState := stInit;
765     FHasBegin := false;
766     FLastChar := ' ';
767     FLastSymbol := sqNone;
768     end;
769    
770     end.
771