ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/ibxscript.pas
Revision: 39
Committed: Tue May 17 08:14:52 2016 UTC (7 years, 11 months ago) by tony
Content type: text/x-pascal
File size: 21084 byte(s)
Log Message:
Committing updates for Release R1-4-1

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