ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/ibxscript.pas
(Generate patch)

Comparing ibx/trunk/runtime/nongui/ibxscript.pas (file contents):
Revision 227 by tony, Mon Apr 9 10:31:10 2018 UTC vs.
Revision 348 by tony, Wed Oct 6 09:38:14 2021 UTC

# Line 31 | Line 31 | unit ibxscript;
31  
32   interface
33  
34 < uses Classes, IBDatabase,  IBSQL, IB, IBDataOutput;
35 <
36 < const
37 <  ibx_blob = 'IBX_BLOB';
38 <  ibx_array = 'IBX_ARRAY';
39 <
40 <  BlobLineLength = 40;
41 <
42 <  DefaultTerminator = ';';
43 <
44 <  {Non-character symbols}
45 <  sqNone                 = #0;
46 <  sqEnd                  = #1;
47 <  sqBegin                = #2;
48 <  sqString               = #3;
49 <  sqComment              = #4;
50 <  sqCase                 = #5;
51 <  sqDeclare              = #6;
52 <  sqCommentLine          = #7;
53 <  sqEOL                  = #8;
54 <  sqTab                  = #9;
55 <  sqTerminator           = #10;
56 <  sqEOF                  = #11;
57 <  sqTag                  = #12;
58 <  sqEndTag               = #13;
59 <  sqQuotedString         = #14;
60 <  sqDoubleQuotedString   = #15;
34 > uses Classes, IBDatabase,  IBSQL, IB, IBDataOutput, IBUtils;
35  
36   type
63  TSQLSymbol = char;
64
65  TSQLStates =  (stInit, stError, stInSQL, stNested,  stInDeclaration);
66
67  TXMLStates =  (stInTag,stAttribute,stAttributeValue,stQuotedAttributeValue,
68                 stTagged,stEndTag);
69
70  TXMLTag    =   (xtNone,xtBlob,xtArray,xtElt);
37  
38    TOnNextLine = procedure(Sender: TObject; Line: string) of object;
39    TOnProgressEvent = procedure (Sender: TObject; Reset: boolean; value: integer) of object;
40  
41 <  TXMLTagDef = record
76 <    XMLTag: TXMLTag;
77 <    TagValue: string;
78 <  end;
79 <
80 < const
81 <  XMLTagDefs: array [0..2] of TXMLTagDef = (
82 <    (XMLTag: xtBlob;   TagValue: 'blob'),
83 <    (XMLTag: xtArray;  TagValue: 'array'),
84 <    (XMLTag: xtElt;    TagValue: 'elt')
85 <    );
41 >  { TSQLXMLReader }
42  
43 < type
43 >  TSQLXMLReader = class(TSQLTokeniser)
44 >  private
45 >    type
46 >      TXMLStates =  (stNoXML, stInTag,stInTagBody,
47 >                     stAttribute,stAttributeValue,stQuotedAttributeValue,
48 >                     stInEndTag, stInEndTagBody,
49 >                     stXMLData);
50 >
51 >      TXMLTag    =   (xtNone,xtBlob,xtArray,xtElt);
52 >
53 >      TXMLTagDef = record
54 >        XMLTag: TXMLTag;
55 >        TagValue: string;
56 >      end;
57 >
58 >    const
59 >      XMLTagDefs: array [xtBlob..xtElt] of TXMLTagDef = (
60 >        (XMLTag: xtBlob;   TagValue: 'blob'),
61 >        (XMLTag: xtArray;  TagValue: 'array'),
62 >        (XMLTag: xtElt;    TagValue: 'elt')
63 >        );
64 >      MaxXMLTags = 20;
65 >      BlobLineLength = 40;
66  
67 <  { TSymbolStream }
67 >  public
68 >    const
69 >      ibx_blob = 'IBX_BLOB';
70 >      ibx_array = 'IBX_ARRAY';
71 >
72 >    type
73 >      TBlobData = record
74 >        BlobIntf: IBlob;
75 >        SubType: cardinal;
76 >      end;
77 >
78 >      TArrayData = record
79 >        ArrayIntf: IArray;
80 >        SQLType: cardinal;
81 >        relationName: string;
82 >        columnName: string;
83 >        dim: cardinal;
84 >        Size: cardinal;
85 >        Scale: integer;
86 >        CharSet: string;
87 >        bounds: TArrayBounds;
88 >        CurrentRow: integer;
89 >        Index: array of integer;
90 >      end;
91 >
92 >   private
93 >     FDatabase: TIBDatabase;
94 >     FOnProgressEvent: TOnProgressEvent;
95 >     FTransaction: TIBTransaction;
96 >     FXMLState: TXMLStates;
97 >     FXMLTagStack: array [1..MaxXMLTags] of TXMLTag;
98 >     FXMLTagIndex: integer;
99 >     FAttributeName: string;
100 >     FXMLData: string;
101 >     FBlobData: array of TBlobData;
102 >     FCurrentBlob: integer;
103 >     FBlobBuffer: PChar;
104 >     FArrayData: array of TArrayData;
105 >     FCurrentArray: integer;
106 >     FXMLString: string;
107 >     function FindTag(tag: string; var xmlTag: TXMLTag): boolean;
108 >     function GetArrayData(index: integer): TArrayData;
109 >     function GetArrayDataCount: integer;
110 >     function GetBlobData(index: integer): TBlobData;
111 >     function GetBlobDataCount: integer;
112 >     function GetTagName(xmltag: TXMLTag): string;
113 >     procedure ProcessAttributeValue(attrValue: string);
114 >     procedure ProcessBoundsList(boundsList: string);
115 >     procedure ProcessTagValue(tagValue: string);
116 >     procedure XMLTagInit(xmltag: TXMLTag);
117 >     function XMLTagEnd(var xmltag: TXMLTag): boolean;
118 >     procedure XMLTagEnter;
119 >   protected
120 >     function GetErrorPrefix: string; virtual; abstract;
121 >     function TokenFound(var token: TSQLTokens): boolean; override;
122 >     procedure Reset; override;
123 >     procedure ShowError(msg: string; params: array of const); virtual; overload;
124 >     procedure ShowError(msg: string); overload;
125 >   public
126 >     constructor Create;
127 >     procedure FreeDataObjects;
128 >     class function FormatBlob(Field: ISQLData): string;
129 >     class function FormatArray(Database: TIBDatabase; ar: IArray): string;
130 >     property BlobData[index: integer]: TBlobData read GetBlobData;
131 >     property BlobDataCount: integer read GetBlobDataCount;
132 >     property ArrayData[index: integer]: TArrayData read GetArrayData;
133 >     property ArrayDataCount: integer read GetArrayDataCount;
134 >     property Database: TIBDatabase read FDatabase write FDatabase;
135 >     property Transaction: TIBTransaction read FTransaction write FTransaction;
136 >     property OnProgressEvent: TOnProgressEvent read FOnProgressEvent write FOnProgressEvent; {Progress Bar Support}
137 >  end;
138  
139 <  {A simple lookahead one parser to process a text stream as a stream of symbols.
92 <   This is an abstract object, subclassed for different sources.}
139 >  { TSQLStatementReader }
140  
141 <  TSymbolStream = class
141 >  TSQLStatementReader = class(TSQLXMLReader)
142    private
143 <    FNextSymbol: TSQLSymbol;
143 >    type
144 >      TSQLState = (stDefault, stInStmt, stInBlock, stInArrayDim, stInDeclare);
145 >  private
146 >    FHasBegin: boolean;
147      FOnNextLine: TOnNextLine;
98    FOnProgressEvent: TOnProgressEvent;
148      FTerminator: char;
100    FLastChar: char;
101    FIndex: integer;
102    FLine: string;
103    FString: string;
104    FXMLTag: TXMLTag;
105    FXMLMode: integer;
149    protected
150 <    FNextStatement: boolean;
108 <    function GetErrorPrefix: string; virtual; abstract;
109 <    function GetNextSymbol(C: char): TSQLSymbol;
110 <    function FindTag(tag: string; var xmlTag: TXMLTag): boolean;
111 <    function GetNextLine(var Line: string):boolean; virtual; abstract;
150 >    procedure EchoNextLine(aLine: string);
151    public
152      constructor Create;
153 <    procedure ShowError(msg: string; params: array of const);
154 <    function GetSymbol: TSQLSymbol; virtual;
155 <    procedure NextStatement;
117 <    property SymbolValue: string read FString;
118 <    property Terminator: char read FTerminator write FTerminator;
119 <    property XMLTag: TXMLTag read FXMLTag;
153 >    function GetNextStatement(var stmt: string) : boolean; virtual;
154 >    property HasBegin: boolean read FHasBegin;
155 >    property Terminator: char read FTerminator write FTerminator default DefaultTerminator;
156      property OnNextLine: TOnNextLine read FOnNextLine write FOnNextLine;
121    property OnProgressEvent: TOnProgressEvent read FOnProgressEvent write FOnProgressEvent; {Progress Bar Support}
157    end;
158  
124  { TBatchSymbolStream }
159  
160 <  {This symbol stream supports non-interactive parsing of a text file, stream or
160 >  { TBatchSQLStatementReader }
161 >
162 >  {This SQL Reader supports non-interactive parsing of a text file, stream or
163     lines of text.}
164  
165 <  TBatchSymbolStream = class(TSymbolStream)
165 >  TBatchSQLStatementReader = class(TSQLStatementReader)
166    private
167 <    FLines: TStrings;
167 >    FInStream: TStream;
168 >    FOwnsInStream: boolean;
169      FLineIndex: integer;
170 +    FIndex: integer;
171 +    FCurLine: string;
172    protected
173 +    function GetChar: char; override;
174      function GetErrorPrefix: string; override;
135    function GetNextLine(var Line: string):boolean; override;
175    public
176 <    constructor Create;
138 <    destructor Destroy; override;
176 >    procedure Reset; override;
177      procedure SetStreamSource(Lines: TStrings); overload;
178      procedure SetStreamSource(S: TStream); overload;
179      procedure SetStreamSource(FileName: string); overload;
180 +    procedure SetStringStreamSource(S: string);
181    end;
182  
183 <  { TInteractiveSymbolStream }
183 >  { TInteractiveSQLStatementReader }
184  
185 <  {This symbol stream supports interactive parsing of commands and
185 >  {This SQL reader supports interactive parsing of commands and
186     SQL statements entered at a console}
187  
188 <  TInteractiveSymbolStream = class(TSymbolStream)
188 >  TInteractiveSQLStatementReader = class(TSQLStatementReader)
189    private
190      FPrompt: string;
191      FContinuePrompt: string;
192      FTerminated: boolean;
193 +    FLine: string;
194 +    FLineIndex: integer;
195 +    FNextStatement: boolean;
196 +    function GetNextLine(var Line: string):boolean;
197    protected
198 +    function GetChar: char; override;
199      function GetErrorPrefix: string; override;
156    function GetNextLine(var Line: string):boolean; override;
200    public
201      constructor Create(aPrompt: string='SQL>'; aContinue: string = 'CON>');
202 <    function GetSymbol: TSQLSymbol; override;
202 >    function GetNextStatement(var stmt: string) : boolean; override;
203      property Terminated: boolean read FTerminated write FTerminated;
204    end;
205  
163  TBlobData = record
164    BlobIntf: IBlob;
165    SubType: cardinal;
166  end;
167
168  TArrayData = record
169    ArrayIntf: IArray;
170    SQLType: cardinal;
171    relationName: string;
172    columnName: string;
173    dim: cardinal;
174    Size: cardinal;
175    Scale: integer;
176    CharSet: string;
177    bounds: TArrayBounds;
178    CurrentRow: integer;
179    Index: array of integer;
180  end;
181
182  { TIBXMLProcessor }
183
184  {This is a simple XML parser that parses the output of a symbol stream as XML
185   structured data, recognising tags, attributes and data. The tags are given in
186   the table XMLTagDefs. The BlobData and ArrayData properties return blob and
187   array data decoded from the XML stream.}
188
189  TIBXMLProcessor = class
190  private
191    FDatabase: TIBDatabase;
192    FSymbolStream: TSymbolStream;
193    FState: TXMLStates;
194    FTransaction: TIBTransaction;
195    FXMLTagStack: array [1..20] of TXMLTag;
196    FXMLTagIndex: integer;
197    FAttributeName: string;
198    FBlobData: array of TBlobData;
199    FCurrentBlob: integer;
200    FArrayData: array of TArrayData;
201    FCurrentArray: integer;
202    FBlobBuffer: PChar;
203    procedure EndXMLTag(xmltag: TXMLTag);
204    procedure EnterTag;
205    function GetArrayData(index: integer): TArrayData;
206    function GetArrayDataCount: integer;
207    function GetBlobData(index: integer): TBlobData;
208    function GetBlobDataCount: integer;
209    procedure ProcessTagValue(tagValue: string);
210    procedure StartXMLTag(xmltag: TXMLTag);
211    procedure ProcessAttributeValue(attrValue: string);
212    procedure ProcessBoundsList(boundsList: string);
213  public
214    constructor Create;
215    destructor Destroy; override;
216    function AnalyseXML(SymbolStream: TSymbolStream): string;
217    procedure NextStatement;
218    class function FormatBlob(Field: ISQLData): string;
219    class function FormatArray(Database: TIBDatabase; ar: IArray): string;
220    property BlobData[index: integer]: TBlobData read GetBlobData;
221    property BlobDataCount: integer read GetBlobDataCount;
222    property ArrayData[index: integer]: TArrayData read GetArrayData;
223    property ArrayDataCount: integer read GetArrayDataCount;
224    property Database: TIBDatabase read FDatabase write FDatabase;
225    property Transaction: TIBTransaction read FTransaction write FTransaction;
226  end;
227
228  { TIBSQLProcessor }
229
230  {This parses a symbol stream into SQL statements. If embedded XML is found then
231   this is processed by the supplied XMLProcessor. The HasBegin property provides
232   a simple way to recognised stored procedure DDL, and "Execute As" statements.}
233
234  TIBSQLProcessor = class
235  private
236    FSQLText: string;
237    FState: TSQLStates;
238    FStack: array [0..16] of TSQLStates;
239    FStackindex: integer;
240    FHasBegin: boolean;
241    FInCase: boolean;
242    FNested: integer;
243    FXMLProcessor: TIBXMLProcessor;
244    FSymbolStream: TSymbolStream;
245    procedure AddToSQL(const Symbol: string);
246    procedure SetState(AState: TSQLStates);
247    function PopState: TSQLStates;
248  public
249    constructor Create(XMLProcessor: TIBXMLProcessor);
250    function GetNextStatement(SymbolStream: TSymbolStream; var stmt: string) : boolean;
251    property HasBegin: boolean read FHasBegin;
252  end;
253
206    TGetParamValue = procedure(Sender: TObject; ParamName: string; var BlobID: TISC_QUAD) of object;
207    TLogEvent = procedure(Sender: TObject; Msg: string) of Object;
208    TOnSelectSQL = procedure (Sender: TObject; SQLText: string) of object;
# Line 272 | Line 224 | type
224    TCustomIBXScript = class(TComponent)
225    private
226      FEcho: boolean;
227 <    FIBXMLProcessor: TIBXMLProcessor;
276 <    FIBSQLProcessor: TIBSQLProcessor;
227 >    FSQLReader: TSQLStatementReader;
228      FDatabase: TIBDatabase;
229      FDataOutputFormatter: TIBCustomDataOutput;
230      FIgnoreCreateDatabase: boolean;
# Line 293 | Line 244 | type
244      FAutoDDL: boolean;
245      procedure DoCommit;
246      procedure DoReconnect;
296    procedure ExecSQL(stmt: string);
247      function GetOnProgressEvent: TOnProgressEvent;
248      function GetTransaction: TIBTransaction;
249      procedure SetDatabase(AValue: TIBDatabase);
# Line 303 | Line 253 | type
253      procedure SetShowPerformanceStats(AValue: boolean);
254      procedure SetTransaction(AValue: TIBTransaction);
255    protected
306    FSymbolStream: TSymbolStream;
256      procedure Add2Log(const Msg: string; IsError: boolean=true); virtual;
257 +    procedure ExecSQL(stmt: string);
258      procedure EchoNextLine(Sender: TObject; Line: string);
259      procedure Notification(AComponent: TComponent; Operation: TOperation); override;
260      function ProcessStatement(stmt: string): boolean; virtual;
261      function ProcessStream: boolean;
262 +    procedure SetSQLStatementReader(SQLStatementReader: TSQLStatementReader);
263    public
264      constructor Create(aOwner: TComponent); override;
265      destructor Destroy; override;
266      procedure DefaultSelectSQLHandler(aSQLText: string);
267 +    property SQLStatementReader: TSQLStatementReader read FSQLReader;
268    published
269      property Database: TIBDatabase read FDatabase write SetDatabase;
270      property DataOutputFormatter: TIBCustomDataOutput read FDataOutputFormatter
# Line 431 | Line 383 | implementation
383   uses Sysutils, RegExpr;
384  
385   resourcestring
434  sTerminatorUnknownState = 'Statement Terminator in unexpected state (%d)';
386    sNoSelectSQL = 'Select SQL Statements are not supported';
436  sStackUnderflow = 'Stack Underflow';
387    sNoParamQueries =  'Parameterised Queries are not supported';
438  sStackOverFlow = 'Stack Overflow';
388    sResolveQueryParam =  'Resolving Query Parameter: %s';
389    sXMLStackUnderflow = 'XML Stack Underflow';
390    sInvalidEndTag = 'XML End Tag Mismatch - %s';
391 +  sBadEndTagClosing = 'XML End Tag incorrectly closed';
392    sXMLStackOverFlow = 'XML Stack Overflow';
443  sErrorState = 'Entered Error State';
444  sXMLError = 'Invalid XML (%c)';
393    sXMLAttributeError = 'Unexpected attribute - "%s" = "%s"';
394    sInvalidBoundsList = 'Invalid array bounds list - "%s"';
395    sBinaryBlockMustbeEven = 'Binary block must have an even number of characters';
# Line 450 | Line 398 | resourcestring
398    sArrayIndexError = 'Array Index Error (%d)';
399    sBlobIndexError = 'Blob Index Error (%d)';
400    sStatementError = 'Error processing SQL statement: %s %s - for statement "%s"';
401 +  sNotInArray = 'elt tag found but not in an XML array tag';
402 +  sNoDatabase = 'Missing database for xml tag import';
403 +  sNoTransaction = 'Missing transaction for xml tag import';
404  
405   function StringToHex(octetString: string; MaxLineLength: integer): string; overload;
406  
# Line 491 | Line 442 | begin
442      TextOut.Add(StringToHex(octetString,MaxLineLength));
443   end;
444  
445 + { TSQLStatementReader }
446 +
447 + procedure TSQLStatementReader.EchoNextLine(aLine: string);
448 + begin
449 +  if assigned(FOnNextLine) then
450 +    OnNextLine(self,aLine);
451 + end;
452 +
453 + constructor TSQLStatementReader.Create;
454 + begin
455 +  inherited Create;
456 +  Terminator := DefaultTerminator;
457 + end;
458 +
459 + function TSQLStatementReader.GetNextStatement(var stmt: string): boolean;
460 + var State: TSQLState;
461 +    Nested: integer;
462 +    token: TSQLTokens;
463 +    EndOfStatement: boolean;
464 + begin
465 +  FHasBegin := false;
466 +  Result := false;
467 +  EndOfStatement := false;
468 +  Nested := 0;
469 +  stmt := '';
470 +  State := stDefault;
471 +  while not EOF and not EndOfStatement do
472 +  begin
473 +    token := GetNextToken;
474 + //    writeln(token,' ',TokenText,' ',Terminator);
475 +    case State of
476 +    stDefault:
477 +      {ignore everything before a reserved word}
478 +      if (token <= high(TSQLReservedWords)) or (token = sqltIdentifier) then
479 +        begin
480 +          State := stInStmt;
481 +          stmt += TokenText;
482 +        end;
483 +
484 +    stInStmt:
485 +       begin
486 +        case token of
487 +          sqltBegin:
488 +          begin
489 +            FHasBegin := true;
490 +            State := stInBlock;
491 +            Nested := 1;
492 +            stmt += TokenText;
493 +          end;
494 +
495 +          sqltDeclare:
496 +            begin
497 +              State := stInDeclare;
498 +              stmt += TokenText;
499 +            end;
500 +
501 +          sqltOpenSquareBracket:
502 +             begin
503 +               State := stInArrayDim;
504 +               stmt += TokenText;
505 +             end;
506 +
507 +          sqltComment:
508 +            stmt += '/*' + TokenText + '*/';
509 +
510 +          sqltCommentLine:
511 +            stmt += '/*' + TokenText + ' */' + LineEnding;
512 +
513 +          sqltQuotedString:
514 +            stmt += '''' + SQLSafeString(TokenText) + '''';
515 +
516 +          sqltIdentifierInDoubleQuotes:
517 +            stmt += '"' + TokenText + '"';
518 +
519 +          sqltCR: {ignore};
520 +
521 +          sqltEOL:
522 +            stmt += LineEnding;
523 +
524 +          else
525 +            begin
526 +              if (tokentext = Terminator) and (Nested = 0) then
527 +              begin
528 +                EndOfStatement := true;
529 +                State := stDefault;
530 +              end
531 +              else
532 +                stmt += TokenText;
533 +            end;
534 +          end;
535 +        end;
536 +
537 +    {ignore begin..end blocks for Terminator detection }
538 +
539 +    stInBlock:
540 +      begin
541 +        case token of
542 +        sqltBegin:
543 +          begin
544 +            Inc(Nested);
545 +            stmt += TokenText;
546 +          end;
547 +
548 +        sqltEnd:
549 +          begin
550 +            Dec(Nested);
551 +            stmt += TokenText;
552 +            if Nested = 0 then
553 +            begin
554 +              State := stDefault;
555 +              EndOfStatement := true;
556 +            end;
557 +          end;
558 +
559 +        sqltCase:
560 +          {case constructs can appear within select statement in nested blocks.
561 +           We need to match the case constructs END token in order to parse the
562 +           block correctly. This is a simple parser and the only objective is
563 +           to determine the correct end of block. We therefore do not check to
564 +           ensure that the next end properly matches the case. The CASE is thus
565 +           treated the same as BEGIN. The Firebird SQL Parser will flag any errors
566 +           due to mismatched CASE/BEGIN END}
567 +          begin
568 +            Inc(Nested);
569 +            stmt += TokenText;
570 +          end;
571 +
572 +        sqltComment:
573 +          stmt += '/*' + TokenText + '*/';
574 +
575 +        sqltCommentLine:
576 +          stmt += '/* ' + TokenText + ' */' + LineEnding;
577 +
578 +        sqltQuotedString:
579 +          stmt += '''' + SQLSafeString(TokenText) + '''';
580 +
581 +        sqltIdentifierInDoubleQuotes:
582 +          stmt += '"' + TokenText + '"';
583 +
584 +        sqltCR: {ignore};
585 +
586 +        sqltEOL:
587 +          stmt += LineEnding;
588 +
589 +        else
590 +          stmt += TokenText;
591 +        end;
592 +      end;
593 +
594 +      {ignore array dimensions for Terminator detection }
595 +
596 +    stInArrayDim:
597 +      begin
598 +        case token of
599 +
600 +        sqltComment:
601 +          stmt += '/*' + TokenText + '*/';
602 +
603 +        sqltCommentLine:
604 +          stmt += '/* ' + TokenText + ' */' + LineEnding;
605 +
606 +        sqltCloseSquareBracket:
607 +        begin
608 +          stmt += TokenText;
609 +          State := stInStmt;
610 +        end;
611 +
612 +        sqltCR: {ignore};
613 +
614 +        sqltEOL:
615 +          stmt += LineEnding;
616 +
617 +        else
618 +          stmt += TokenText;
619 +        end;
620 +      end;
621 +
622 +    {ignore Declare statement for terminator - semi-colon terminates declaration}
623 +
624 +    stInDeclare:
625 +      begin
626 +        case token of
627 +
628 +        sqltComment:
629 +          stmt += '/*' + TokenText + '*/';
630 +
631 +        sqltCommentLine:
632 +          stmt += '/* ' + TokenText + ' */' + LineEnding;
633 +
634 +        sqltSemiColon:
635 +          begin
636 +            State := stInStmt;
637 +            stmt += TokenText;
638 +          end;
639 +
640 +        sqltCR: {ignore};
641 +
642 +        sqltEOL:
643 +          stmt += LineEnding;
644 +
645 +        else
646 +          stmt += TokenText;
647 +        end;
648 +      end;
649 +    end;
650 +  end;
651 +  Result := stmt <> '';
652 + end;
653 +
654 + { TSQLXMLReader }
655 +
656 + function TSQLXMLReader.FindTag(tag: string; var xmlTag: TXMLTag): boolean;
657 + var i: TXMLTag;
658 + begin
659 +  Result := false;
660 +  for i := xtBlob to xtElt do
661 +    if XMLTagDefs[i].TagValue = tag then
662 +    begin
663 +      xmlTag := XMLTagDefs[i].XMLTag;
664 +      Result := true;
665 +      break;
666 +    end;
667 + end;
668 +
669 + function TSQLXMLReader.GetArrayData(index: integer): TArrayData;
670 + begin
671 +  if (index < 0) or (index > ArrayDataCount) then
672 +    ShowError(sArrayIndexError,[index]);
673 +  Result := FArrayData[index];
674 + end;
675 +
676 + function TSQLXMLReader.GetArrayDataCount: integer;
677 + begin
678 +  Result := Length(FArrayData);
679 + end;
680 +
681 + function TSQLXMLReader.GetBlobData(index: integer): TBlobData;
682 + begin
683 +  if (index < 0) or (index > BlobDataCount) then
684 +    ShowError(sBlobIndexError,[index]);
685 +  Result := FBlobData[index];
686 + end;
687 +
688 + function TSQLXMLReader.GetBlobDataCount: integer;
689 + begin
690 +  Result := Length(FBlobData);
691 + end;
692 +
693 + function TSQLXMLReader.GetTagName(xmltag: TXMLTag): string;
694 + var i: TXMLTag;
695 + begin
696 +  Result := 'unknown';
697 +  for i := xtBlob to xtElt do
698 +    if XMLTagDefs[i].XMLTag = xmltag then
699 +    begin
700 +      Result := XMLTagDefs[i].TagValue;
701 +      Exit;
702 +    end;
703 + end;
704 +
705 + procedure TSQLXMLReader.ProcessAttributeValue(attrValue: string);
706 + begin
707 +  case FXMLTagStack[FXMLTagIndex] of
708 +  xtBlob:
709 +    if FAttributeName = 'subtype' then
710 +      FBlobData[FCurrentBlob].SubType := StrToInt(attrValue)
711 +    else
712 +      ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
713 +
714 +  xtArray:
715 +    if FAttributeName = 'sqltype' then
716 +      FArrayData[FCurrentArray].SQLType := StrToInt(attrValue)
717 +    else
718 +    if FAttributeName = 'relation_name' then
719 +      FArrayData[FCurrentArray].relationName := attrValue
720 +    else
721 +    if FAttributeName = 'column_name' then
722 +      FArrayData[FCurrentArray].columnName := attrValue
723 +    else
724 +    if FAttributeName = 'dim' then
725 +      FArrayData[FCurrentArray].Dim := StrToInt(attrValue)
726 +    else
727 +    if FAttributeName = 'length' then
728 +      FArrayData[FCurrentArray].Size := StrToInt(attrValue)
729 +    else
730 +    if FAttributeName = 'scale' then
731 +      FArrayData[FCurrentArray].Scale := StrToInt(attrValue)
732 +    else
733 +    if FAttributeName = 'charset' then
734 +      FArrayData[FCurrentArray].CharSet := attrValue
735 +    else
736 +    if FAttributeName = 'bounds' then
737 +      ProcessBoundsList(attrValue)
738 +    else
739 +      ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
740 +
741 +  xtElt:
742 +    if FAttributeName = 'ix' then
743 +      with FArrayData[FCurrentArray] do
744 +        Index[CurrentRow] :=  StrToInt(attrValue)
745 +     else
746 +        ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
747 +  end;
748 + end;
749 +
750 + procedure TSQLXMLReader.ProcessBoundsList(boundsList: string);
751 + var list: TStringList;
752 +    i,j: integer;
753 + begin
754 +  list := TStringList.Create;
755 +  try
756 +    list.Delimiter := ',';
757 +    list.DelimitedText := boundsList;
758 +    with FArrayData[FCurrentArray] do
759 +    begin
760 +      if dim <> list.Count then
761 +        ShowError(sInvalidBoundsList,[boundsList]);
762 +      SetLength(bounds,dim);
763 +      for i := 0 to list.Count - 1 do
764 +      begin
765 +        j := Pos(':',list[i]);
766 +        if j = 0 then
767 +          raise Exception.CreateFmt(sInvalidBoundsList,[boundsList]);
768 +        bounds[i].LowerBound := StrToInt(system.copy(list[i],1,j-1));
769 +        bounds[i].UpperBound := StrToInt(system.copy(list[i],j+1,length(list[i])-j));
770 +      end;
771 +    end;
772 +  finally
773 +    list.Free;
774 +  end;
775 + end;
776 +
777 + procedure TSQLXMLReader.ProcessTagValue(tagValue: string);
778 +
779 +  function nibble(hex: char): byte;
780 +  begin
781 +    case hex of
782 +    '0': Result := 0;
783 +    '1': Result := 1;
784 +    '2': Result := 2;
785 +    '3': Result := 3;
786 +    '4': Result := 4;
787 +    '5': Result := 5;
788 +    '6': Result := 6;
789 +    '7': Result := 7;
790 +    '8': Result := 8;
791 +    '9': Result := 9;
792 +    'a','A': Result := 10;
793 +    'b','B': Result := 11;
794 +    'c','C': Result := 12;
795 +    'd','D': Result := 13;
796 +    'e','E': Result := 14;
797 +    'f','F': Result := 15;
798 +    end;
799 +  end;
800 +
801 +  procedure RemoveWhiteSpace(var hexData: string);
802 +  var i: integer;
803 +  begin
804 +    {Remove White Space}
805 +    i := 1;
806 +    while i <= length(hexData) do
807 +    begin
808 +      case hexData[i] of
809 +      ' ',#9,#10,#13:
810 +        begin
811 +          if i < Length(hexData) then
812 +            Move(hexData[i+1],hexData[i],Length(hexData)-i);
813 +          SetLength(hexData,Length(hexData)-1);
814 +        end;
815 +      else
816 +        Inc(i);
817 +      end;
818 +    end;
819 +  end;
820 +
821 +  procedure WriteToBlob(hexData: string);
822 +  var i,j : integer;
823 +      blength: integer;
824 +      P: PChar;
825 +  begin
826 +    RemoveWhiteSpace(hexData);
827 +    if odd(length(hexData)) then
828 +      ShowError(sBinaryBlockMustbeEven,[nil]);
829 +    blength := Length(hexData) div 2;
830 +    IBAlloc(FBlobBuffer,0,blength);
831 +    j := 1;
832 +    P := FBlobBuffer;
833 +    for i := 1 to blength do
834 +    begin
835 +      P^ := char((nibble(hexData[j]) shl 4) or nibble(hexdata[j+1]));
836 +      Inc(j,2);
837 +      Inc(P);
838 +    end;
839 +    FBlobData[FCurrentBlob].BlobIntf.Write(FBlobBuffer^,blength);
840 +  end;
841 +
842 + begin
843 +  if tagValue = '' then Exit;
844 +  case FXMLTagStack[FXMLTagIndex] of
845 +  xtBlob:
846 +    WriteToBlob(tagValue);
847 +
848 +  xtElt:
849 +    with FArrayData[FCurrentArray] do
850 +      ArrayIntf.SetAsString(index,tagValue);
851 +
852 +  end;
853 + end;
854 +
855 + procedure TSQLXMLReader.XMLTagInit(xmltag: TXMLTag);
856 + begin
857 +  if FXMLTagIndex > MaxXMLTags then
858 +    ShowError(sXMLStackOverFlow,[nil]);
859 +  Inc(FXMLTagIndex);
860 +  FXMLTagStack[FXMLTagIndex] := xmltag;
861 +  FXMLString := '';
862 +
863 +  case xmltag of
864 +  xtBlob:
865 +    begin
866 +      Inc(FCurrentBlob);
867 +      SetLength(FBlobData,FCurrentBlob+1);
868 +      FBlobData[FCurrentBlob].BlobIntf := nil;
869 +      FBlobData[FCurrentBlob].SubType := 0;
870 +    end;
871 +
872 +  xtArray:
873 +    begin
874 +      Inc(FCurrentArray);
875 +      SetLength(FArrayData,FCurrentArray+1);
876 +      with FArrayData[FCurrentArray] do
877 +      begin
878 +        ArrayIntf := nil;
879 +        SQLType := 0;
880 +        dim := 0;
881 +        Size := 0;
882 +        Scale := 0;
883 +        CharSet := 'NONE';
884 +        SetLength(Index,0);
885 +        CurrentRow := -1;
886 +      end;
887 +    end;
888 +
889 +  xtElt:
890 +      with FArrayData[FCurrentArray] do
891 +        Inc(CurrentRow)
892 +  end;
893 + end;
894 +
895 + function TSQLXMLReader.XMLTagEnd(var xmltag: TXMLTag): boolean;
896 + begin
897 +  if FXMLTagIndex = 0 then
898 +    ShowError(sXMLStackUnderflow,[nil]);
899 +
900 +  xmlTag := FXMLTagStack[FXMLTagIndex];
901 +  case FXMLTagStack[FXMLTagIndex] of
902 +  xtBlob:
903 +    FBlobData[FCurrentBlob].BlobIntf.Close;
904 +
905 +  xtArray:
906 +    FArrayData[FCurrentArray].ArrayIntf.SaveChanges;
907 +
908 +  xtElt:
909 +    Dec(FArrayData[FCurrentArray].CurrentRow);
910 +  end;
911 +  Dec(FXMLTagIndex);
912 +  Result := FXMLTagIndex = 0;
913 + end;
914 +
915 + procedure TSQLXMLReader.XMLTagEnter;
916 + var aCharSetID: integer;
917 + begin
918 +  if Database = nil then
919 +    ShowError(sNoDatabase);
920 +  if Transaction = nil then
921 +    ShowError(sNoTransaction);
922 +  case FXMLTagStack[FXMLTagIndex] of
923 +  xtBlob:
924 +    begin
925 +      Database.Connected := true;
926 +      Transaction.Active := true;
927 +      FBlobData[FCurrentBlob].BlobIntf := Database.Attachment.CreateBlob(
928 +        Transaction.TransactionIntf,FBlobData[FCurrentBlob].SubType);
929 +    end;
930 +
931 +  xtArray:
932 +    with FArrayData[FCurrentArray] do
933 +    begin
934 +      Database.Connected := true;
935 +      Transaction.Active := true;
936 +      Database.Attachment.CharSetName2CharSetID(CharSet,aCharSetID);
937 +      SetLength(Index,dim);
938 +      ArrayIntf := Database.Attachment.CreateArray(
939 +                     Transaction.TransactionIntf,
940 +                     Database.Attachment.CreateArrayMetaData(SQLType,
941 +                       relationName,columnName,Scale,Size,
942 +                       aCharSetID,dim,bounds)
943 +                     );
944 +    end;
945 +  end;
946 + end;
947 +
948 + {This is where the XML tags are identified and the token stream modified in
949 + consequence}
950 +
951 + function TSQLXMLReader.TokenFound(var token: TSQLTokens): boolean;
952 +
953 + procedure NotAnXMLTag;
954 + begin
955 +   begin
956 +     if FXMLTagIndex = 0 then
957 +     {nothing to do with XML so go back to processing SQL}
958 +     begin
959 +       QueueToken(token);
960 +       ReleaseQueue(token);
961 +       FXMLState := stNoXML
962 +     end
963 +     else
964 +     begin
965 +       {Not an XML tag, so just push back to XML Data}
966 +       FXMLState := stXMLData;
967 +       FXMLString += GetQueuedText;
968 +       ResetQueue;
969 +     end;
970 +   end;
971 + end;
972 +
973 + var XMLTag: TXMLTag;
974 + begin
975 +  Result := inherited TokenFound(token);
976 +  if not Result then Exit;
977 +
978 +  case FXMLState of
979 +  stNoXML:
980 +    if token = sqltLT then
981 +    begin
982 +      ResetQueue;
983 +      QueueToken(token); {save in case this is not XML}
984 +      FXMLState := stInTag;
985 +    end;
986 +
987 +  stInTag:
988 +    {Opening '<' found, now looking for tag name or end tag marker}
989 +    case token of
990 +    sqltIdentifier:
991 +      begin
992 +        if FindTag(TokenText,XMLTag) then
993 +        begin
994 +          XMLTagInit(XMLTag);
995 +          QueueToken(token);
996 +          FXMLState := stInTagBody;
997 +        end
998 +        else
999 +          NotAnXMLTag;
1000 +      end;
1001 +
1002 +    sqltForwardSlash:
1003 +      FXMLState := stInEndTag;
1004 +
1005 +    else
1006 +      NotAnXMLTag;
1007 +    end {case token};
1008 +
1009 +  stInTagBody:
1010 +    {Tag name found. Now looking for attribute or closing '>'}
1011 +    case token of
1012 +    sqltIdentifier:
1013 +      begin
1014 +        FAttributeName := TokenText;
1015 +        QueueToken(token);
1016 +        FXMLState := stAttribute;
1017 +      end;
1018 +
1019 +    sqltGT:
1020 +      begin
1021 +        ResetQueue;
1022 +        XMLTagEnter;
1023 +        FXMLState := stXMLData;
1024 +      end;
1025 +
1026 +    sqltSpace,
1027 +    sqltCR, sqltEOL:
1028 +      QueueToken(token);
1029 +
1030 +    else
1031 +      NotAnXMLTag;
1032 +    end {case token};
1033 +
1034 +  stAttribute:
1035 +    {Attribute name found. Must be followed by an '=', a '>' or another tag name}
1036 +    case token of
1037 +      sqltEquals:
1038 +      begin
1039 +        QueueToken(token);
1040 +        FXMLState := stAttributeValue;
1041 +      end;
1042 +
1043 +      sqltSpace,
1044 +      sqltCR, sqltEOL:
1045 +        QueueToken(token);
1046 +
1047 +      sqltIdentifier:
1048 +        begin
1049 +          ProcessAttributeValue('');
1050 +          FAttributeName := TokenText;
1051 +          QueueToken(token);
1052 +          FXMLState := stAttribute;
1053 +        end;
1054 +
1055 +      sqltGT:
1056 +        begin
1057 +          ProcessAttributeValue('');
1058 +          ResetQueue;
1059 +          XMLTagEnter;
1060 +          FXMLState := stXMLData;
1061 +        end;
1062 +
1063 +      else
1064 +        NotAnXMLTag;
1065 +    end; {case token}
1066 +
1067 +  stAttributeValue:
1068 +    {Looking for attribute value as a single identifier or a double quoted value}
1069 +    case token of
1070 +    sqltIdentifier,sqltIdentifierInDoubleQuotes:
1071 +      begin
1072 +        ProcessAttributeValue(TokenText);
1073 +        QueueToken(token);
1074 +        FXMLState := stInTagBody;
1075 +      end;
1076 +
1077 +    sqltSpace,
1078 +    sqltCR, sqltEOL:
1079 +      QueueToken(token);
1080 +
1081 +    else
1082 +      NotAnXMLTag;
1083 +    end; {case token}
1084 +
1085 +  stXMLData:
1086 +    if token = sqltLT then
1087 +    begin
1088 +      QueueToken(token); {save in case this is not XML}
1089 +      FXMLState := stInTag;
1090 +    end
1091 +    else
1092 +      FXMLString += TokenText;
1093 +
1094 +  stInEndTag:
1095 +    {Opening '</' found, now looking for tag name}
1096 +    case token of
1097 +    sqltIdentifier:
1098 +      begin
1099 +        if FindTag(TokenText,XMLTag) and (XMLTag = FXMLTagStack[FXMLTagIndex]) then
1100 +        begin
1101 +          QueueToken(token);
1102 +          FXMLState := stInEndTagBody;
1103 +        end
1104 +        else
1105 +          ShowError(sInvalidEndTag,[TokenText]);
1106 +      end;
1107 +    else
1108 +      NotAnXMLTag;
1109 +    end {case token};
1110 +
1111 +  stInEndTagBody:
1112 +  {End tag name found, now looping for closing '>'}
1113 +    case Token of
1114 +    sqltGT:
1115 +      begin
1116 +        ProcessTagValue(FXMLString);
1117 +        if XMLTagEnd(XMLTag) then
1118 +        begin
1119 +          ResetQueue;
1120 +          QueueToken(sqltColon,':');
1121 +          case XMLTag of
1122 +            xtBlob:
1123 +              QueueToken(sqltIdentifier,Format(ibx_blob+'%d',[FCurrentBlob]));
1124 +
1125 +            xtArray:
1126 +              QueueToken(sqltIdentifier, Format(ibx_array+'%d',[FCurrentArray]));
1127 +          end;
1128 +          ReleaseQueue(token);
1129 +          FXMLState := stNoXML;
1130 +       end
1131 +       else
1132 +         FXMLState := stXMLData;
1133 +      end;
1134 +
1135 +    sqltSpace,
1136 +    sqltCR, sqltEOL:
1137 +      QueueToken(token);
1138 +
1139 +    else
1140 +      ShowError(sBadEndTagClosing);
1141 +    end; {case token}
1142 +
1143 +  end {Case FState};
1144 +
1145 +  {Only allow token to be returned if not processing an XML tag}
1146 +
1147 +  Result := FXMLState = stNoXML;
1148 + end;
1149 +
1150 + procedure TSQLXMLReader.ShowError(msg: string; params: array of const);
1151 + begin
1152 +  raise EIBClientError.CreateFmt(GetErrorPrefix + msg,params);
1153 + end;
1154 +
1155 + procedure TSQLXMLReader.ShowError(msg: string);
1156 + begin
1157 +  ShowError(msg,[nil]);
1158 + end;
1159 +
1160 + constructor TSQLXMLReader.Create;
1161 + begin
1162 +  inherited;
1163 +  FXMLState := stNoXML;
1164 + end;
1165 +
1166 + procedure TSQLXMLReader.FreeDataObjects;
1167 + begin
1168 +  FXMLTagIndex := 0;
1169 +  SetLength(FBlobData,0);
1170 +  FCurrentBlob := -1;
1171 +  SetLength(FArrayData,0);
1172 +  FCurrentArray := -1;
1173 + end;
1174 +
1175 + class function TSQLXMLReader.FormatBlob(Field: ISQLData): string;
1176 + var TextOut: TStrings;
1177 + begin
1178 +  TextOut := TStringList.Create;
1179 +  try
1180 +    TextOut.Add(Format('<blob subtype="%d">',[Field.getSubtype]));
1181 +    StringToHex(Field.AsString,TextOut,BlobLineLength);
1182 +    TextOut.Add('</blob>');
1183 +    Result := TextOut.Text;
1184 +  finally
1185 +    TextOut.Free;
1186 +  end;
1187 + end;
1188 +
1189 + class function TSQLXMLReader.FormatArray(Database: TIBDatabase; ar: IArray
1190 +  ): string;
1191 + var index: array of integer;
1192 +    TextOut: TStrings;
1193 +
1194 +    procedure AddElements(dim: integer; indent:string = ' ');
1195 +    var i: integer;
1196 +        recurse: boolean;
1197 +    begin
1198 +      SetLength(index,dim+1);
1199 +      recurse := dim < ar.GetDimensions - 1;
1200 +      with ar.GetBounds[dim] do
1201 +      for i := LowerBound to UpperBound do
1202 +      begin
1203 +        index[dim] := i;
1204 +        if recurse then
1205 +        begin
1206 +          TextOut.Add(Format('%s<elt id="%d">',[indent,i]));
1207 +          AddElements(dim+1,indent + ' ');
1208 +          TextOut.Add('</elt>');
1209 +        end
1210 +        else
1211 +        if ((ar.GetSQLType = SQL_TEXT) or (ar.GetSQLType = SQL_VARYING)) and
1212 +           (ar.GetCharSetID = 1) then
1213 +           TextOut.Add(Format('%s<elt ix="%d">%s</elt>',[indent,i,StringToHex(ar.GetAsString(index))]))
1214 +        else
1215 +          TextOut.Add(Format('%s<elt ix="%d">%s</elt>',[indent,i,ar.GetAsString(index)]));
1216 +      end;
1217 +    end;
1218 +
1219 + var
1220 +    s: string;
1221 +    bounds: TArrayBounds;
1222 +    i: integer;
1223 +    boundsList: string;
1224 + begin
1225 +  TextOut := TStringList.Create;
1226 +  try
1227 +    if ar.GetCharSetWidth = 0 then
1228 +      s := Format('<array dim = "%d" sqltype = "%d" length = "%d" relation_name = "%s" column_name = "%s"',
1229 +                              [ar.GetDimensions,ar.GetSQLType,ar.GetSize,
1230 +                               ar.GetTableName,ar.GetColumnName])
1231 +    else
1232 +      s := Format('<array dim = "%d" sqltype = "%d" length = "%d" relation_name = "%s" column_name = "%s"',
1233 +                                [ar.GetDimensions,ar.GetSQLType,ar.GetSize div ar.GetCharSetWidth,
1234 +                                 ar.GetTableName,ar.GetColumnName]);
1235 +    case ar.GetSQLType of
1236 +    SQL_DOUBLE, SQL_FLOAT, SQL_LONG, SQL_SHORT, SQL_D_FLOAT, SQL_INT64:
1237 +       s += Format(' scale = "%d"',[ ar.GetScale]);
1238 +    SQL_TEXT,
1239 +    SQL_VARYING:
1240 +      s += Format(' charset = "%s"',[Database.Attachment.GetCharsetName(ar.GetCharSetID)]);
1241 +    end;
1242 +    bounds := ar.GetBounds;
1243 +    boundsList := '';
1244 +    for i := 0 to length(bounds) - 1 do
1245 +    begin
1246 +      if i <> 0 then boundsList += ',';
1247 +      boundsList += Format('%d:%d',[bounds[i].LowerBound,bounds[i].UpperBound]);
1248 +    end;
1249 +    s += Format(' bounds="%s"',[boundsList]);
1250 +    s += '>';
1251 +    TextOut.Add(s);
1252 +
1253 +    SetLength(index,0);
1254 +    AddElements(0);
1255 +    TextOut.Add('</array>');
1256 +    Result := TextOut.Text;
1257 +  finally
1258 +    TextOut.Free;
1259 +  end;       end;
1260 +
1261 + procedure TSQLXMLReader.Reset;
1262 + begin
1263 +  inherited Reset;
1264 +  FreeDataObjects;
1265 +  FXMLString := '';
1266 +  FreeMem(FBlobBuffer);
1267 + end;
1268 +
1269  
1270  
1271   { TIBXScript }
# Line 498 | Line 1273 | end;
1273   constructor TIBXScript.Create(aOwner: TComponent);
1274   begin
1275    inherited Create(aOwner);
1276 <  FSymbolStream := TBatchSymbolStream.Create;
502 <  FSymbolStream.OnNextLine := @EchoNextLine;
1276 >  SetSQLStatementReader(TBatchSQLStatementReader.Create);
1277   end;
1278  
1279   function TIBXScript.PerformUpdate(SQLFile: string; aAutoDDL: boolean): boolean;
# Line 517 | Line 1291 | end;
1291  
1292   function TIBXScript.RunScript(SQLFile: string): boolean;
1293   begin
1294 <  TBatchSymbolStream(FSymbolStream).SetStreamSource(SQLFile);
1294 >  TBatchSQLStatementReader(FSQLReader).SetStreamSource(SQLFile);
1295    Result := ProcessStream;
1296   end;
1297  
1298   function TIBXScript.RunScript(SQLStream: TStream): boolean;
1299   begin
1300 <  TBatchSymbolStream(FSymbolStream).SetStreamSource(SQLStream);
1300 >  TBatchSQLStatementReader(FSQLReader).SetStreamSource(SQLStream);
1301    Result := ProcessStream;
1302   end;
1303  
1304   function TIBXScript.RunScript(SQLLines: TStrings): boolean;
1305   begin
1306 <  TBatchSymbolStream(FSymbolStream).SetStreamSource(SQLLines);
1306 >  TBatchSQLStatementReader(FSQLReader).SetStreamSource(SQLLines);
1307    Result := ProcessStream;
1308   end;
1309  
1310   function TIBXScript.ExecSQLScript(sql: string): boolean;
537 var s: TStringList;
1311   begin
1312 <  s := TStringList.Create;
1313 <  try
541 <    s.Text := sql;
542 <    TBatchSymbolStream(FSymbolStream).SetStreamSource(s);
543 <    Result := ProcessStream;
544 <  finally
545 <    s.Free;
546 <  end;
1312 >  TBatchSQLStatementReader(FSQLReader).SetStringStreamSource(sql);
1313 >  Result := ProcessStream;
1314   end;
1315  
1316   { TCustomIBXScript }
# Line 561 | Line 1328 | end;
1328   procedure TCustomIBXScript.DoCommit;
1329   begin
1330    with GetTransaction do
564  begin
1331      if InTransaction then Commit;
566    Active := true;
567  end;
1332   end;
1333  
1334   procedure TCustomIBXScript.DoReconnect;
571 var LoginPrompt: boolean;
1335   begin
1336    with GetTransaction do
1337      if InTransaction then Commit;
1338 <  LoginPrompt := Database.LoginPrompt;
576 <  Database.LoginPrompt := false;
577 <  Database.Connected := false;
578 <  Database.Connected := true;
579 <  Database.LoginPrompt := LoginPrompt;
580 <  GetTransaction.Active := true;
1338 >  Database.Reconnect;
1339   end;
1340  
1341   procedure TCustomIBXScript.ExecSQL(stmt: string);
# Line 588 | Line 1346 | begin
1346     FISQL.SQL.Text := stmt;
1347     FISQL.Transaction := GetTransaction;
1348     FISQL.Transaction.Active := true;
1349 <   FISQL.ParamCheck := not FIBSQLProcessor.HasBegin; {Probably PSQL}
1349 > //   FISQL.ParamCheck := not FSQLReader.HasBegin; {Probably PSQL}
1350     FISQL.Prepare;
1351     FISQL.Statement.EnableStatistics(ShowPerformanceStats);
1352  
# Line 627 | Line 1385 | end;
1385  
1386   function TCustomIBXScript.GetOnProgressEvent: TOnProgressEvent;
1387   begin
1388 <  Result := FSymbolStream.OnProgressEvent;
1388 >  Result := FSQLReader.OnProgressEvent;
1389   end;
1390  
1391   function TCustomIBXScript.GetTransaction: TIBTransaction;
# Line 660 | Line 1418 | begin
1418   if not (csLoading in ComponentState) and (FDatabase = AValue) then Exit;
1419   FDatabase := AValue;
1420   FISQL.Database := AValue;
1421 < FIBXMLProcessor.Database := AValue;
1421 > FSQLReader.Database := AValue;
1422   FInternalTransaction.Active := false;
1423   FInternalTransaction.DefaultDatabase := AValue;
1424   end;
# Line 677 | Line 1435 | end;
1435  
1436   procedure TCustomIBXScript.SetOnProgressEvent(AValue: TOnProgressEvent);
1437   begin
1438 <  FSymbolStream.OnProgressEvent := AValue;
1438 >  FSQLReader.OnProgressEvent := AValue;
1439   end;
1440  
1441   procedure TCustomIBXScript.SetParamValue(SQLVar: ISQLParam);
1442   var BlobID: TISC_QUAD;
1443      ix: integer;
1444   begin
1445 <  if (SQLVar.SQLType = SQL_BLOB) and (Pos(ibx_blob,SQLVar.Name) = 1) then
1445 >  if (SQLVar.SQLType = SQL_BLOB) and (Pos(TSQLXMLReader.ibx_blob,SQLVar.Name) = 1) then
1446    begin
1447 <    ix := StrToInt(system.copy(SQLVar.Name,length(ibx_blob)+1,length(SQLVar.Name)-length(ibx_blob)));
1448 <    SQLVar.AsBlob := FIBXMLProcessor.BlobData[ix].BlobIntf;
1447 >    ix := StrToInt(system.copy(SQLVar.Name,length(TSQLXMLReader.ibx_blob)+1,length(SQLVar.Name)-length(TSQLXMLReader.ibx_blob)));
1448 >    SQLVar.AsBlob := FSQLReader.BlobData[ix].BlobIntf;
1449      Exit;
1450    end
1451    else
1452 <  if (SQLVar.SQLType = SQL_ARRAY) and (Pos(ibx_array,SQLVar.Name) = 1) then
1452 >  if (SQLVar.SQLType = SQL_ARRAY) and (Pos(TSQLXMLReader.ibx_array,SQLVar.Name) = 1) then
1453    begin
1454 <    ix := StrToInt(system.copy(SQLVar.Name,length(ibx_array)+1,length(SQLVar.Name)-length(ibx_array)));
1455 <    SQLVar.AsArray := FIBXMLProcessor.ArrayData[ix].ArrayIntf;
1454 >    ix := StrToInt(system.copy(SQLVar.Name,length(TSQLXMLReader.ibx_array)+1,length(SQLVar.Name)-length(TSQLXMLReader.ibx_array)));
1455 >    SQLVar.AsArray := FSQLReader.ArrayData[ix].ArrayIntf;
1456      Exit;
1457    end;
1458  
# Line 723 | Line 1481 | function TCustomIBXScript.ProcessStream:
1481   var stmt: string;
1482   begin
1483    Result := false;
1484 <  while FIBSQLProcessor.GetNextStatement(FSymbolStream,stmt) do
1484 >  while FSQLReader.GetNextStatement(stmt) do
1485    try
1486 +    stmt := trim(stmt);
1487   //    writeln('stmt = ',stmt);
1488 <    if trim(stmt) = '' then continue;
1488 >    if stmt = '' then continue;
1489      if not ProcessStatement(stmt) then
1490        ExecSQL(stmt);
1491  
# Line 734 | Line 1493 | begin
1493        begin
1494          with GetTransaction do
1495            if InTransaction then Rollback;
1496 <        FSymbolStream.Terminator := DefaultTerminator;
1496 >        FSQLReader.Terminator := TSQLStatementReader.DefaultTerminator;
1497          if assigned(OnErrorLog) then
1498          begin
1499 <          Add2Log(Format(sStatementError,[FSymbolStream.GetErrorPrefix,
1499 >          Add2Log(Format(sStatementError,[FSQLReader.GetErrorPrefix,
1500                               E.Message,stmt]),true);
1501                               if StopOnFirstError then Exit;
1502          end
# Line 748 | Line 1507 | begin
1507    Result := true;
1508   end;
1509  
1510 + procedure TCustomIBXScript.SetSQLStatementReader(
1511 +  SQLStatementReader: TSQLStatementReader);
1512 + begin
1513 +  FSQLReader := SQLStatementReader;
1514 +  FSQLReader.OnNextLine := @EchoNextLine;
1515 +  FSQLReader.Transaction := FInternalTransaction;
1516 + end;
1517 +
1518   function TCustomIBXScript.ProcessStatement(stmt: string): boolean;
1519   var command: string;
753    ucStmt: string;
1520  
1521    function Toggle(aValue: string): boolean;
1522    begin
# Line 771 | Line 1537 | var command: string;
1537      try
1538        RegexObj.ModifierG := false; {turn off greedy matches}
1539        RegexObj.Expression := ' +USER +''(.+)''';
1540 <      if RegexObj.Exec(ucStmt) then
1540 >      if RegexObj.Exec(stmt) then
1541          FDatabase.Params.Values['user_name'] := RegexObj.Match[1];
1542  
1543        RegexObj.Expression := ' +PASSWORD +''(.+)''';
1544 <      if RegexObj.Exec(ucStmt) then
1544 >      if RegexObj.Exec(stmt) then
1545          FDatabase.Params.Values['password'] :=
1546                      system.copy(stmt,RegexObj.MatchPos[1],RegexObj.MatchLen[1]);
1547      finally
# Line 790 | Line 1556 | var command: string;
1556      RegexObj := TRegExpr.Create;
1557      try
1558        RegexObj.ModifierG := false; {turn off greedy matches}
1559 +      RegexObj.ModifierI := true; {case insensitive}
1560        RegexObj.Expression := '^ *CONNECT +''(.*)''';
1561 <      if RegexObj.Exec(ucStmt) then
1561 >      if RegexObj.Exec(stmt) then
1562        begin
1563          FDatabase.DatabaseName := system.copy(stmt,RegexObj.MatchPos[1],RegexObj.MatchLen[1]);
1564        end;
1565  
1566        RegexObj.Expression := ' +ROLE +''(.+)''';
1567 <      if RegexObj.Exec(ucStmt) then
1567 >      if RegexObj.Exec(stmt) then
1568          FDatabase.Params.Values['sql_role_name'] := RegexObj.Match[1]
1569        else
1570        with FDatabase.Params do
# Line 805 | Line 1572 | var command: string;
1572          Delete(IndexOfName('sql_role_name'));
1573  
1574        RegexObj.Expression := ' +CACHE +([0-9]+)';
1575 <      if RegexObj.Exec(ucStmt) then
1575 >      if RegexObj.Exec(stmt) then
1576          FDatabase.Params.Values['cache_manager'] := RegexObj.Match[1]
1577        else
1578        with FDatabase.Params do
# Line 822 | Line 1589 | var command: string;
1589      RegexObj := TRegExpr.Create;
1590      try
1591        RegexObj.ModifierG := false; {turn off greedy matches}
1592 +      RegexObj.ModifierI := true; {case insensitive}
1593        RegexObj.Expression := '^ *CREATE +(DATABASE|SCHEMA) +(''.*'') +USER +''(.+)''';
1594 <      if not RegexObj.Exec(ucStmt) and (FDatabase.Params.IndexOfName('user_name') <> -1) then
1594 >      if not RegexObj.Exec(stmt) and (FDatabase.Params.IndexOfName('user_name') <> -1) then
1595        begin
1596          RegexObj.Expression := '^ *CREATE +(DATABASE|SCHEMA) +(''.*'')';
1597 <        if RegexObj.Exec(ucStmt) then
1597 >        if RegexObj.Exec(stmt) then
1598          begin
1599            system.Insert(' USER ''' + FDatabase.Params.Values['user_name'] +'''',stmt,
1600                   RegexObj.MatchPos[2] + RegexObj.MatchLen[2]);
833          ucStmt := AnsiUpperCase(stmt);
1601          end;
1602        end;
1603  
1604        RegexObj.Expression := '^ *CREATE +(DATABASE|SCHEMA) +''.*'' +USER +''.+'' PASSWORD +''(.+)''';
1605 <      if not RegexObj.Exec(ucStmt) and (FDatabase.Params.IndexOfName('password') <> -1) then
1605 >      if not RegexObj.Exec(stmt) and (FDatabase.Params.IndexOfName('password') <> -1) then
1606        begin
1607          RegexObj.Expression := '^ *CREATE +(DATABASE|SCHEMA) +''.*'' +(USER +''.+'')';
1608 <        if RegexObj.Exec(ucStmt) then
1608 >        if RegexObj.Exec(stmt) then
1609          begin
1610            system.Insert(' PASSWORD ''' + FDatabase.Params.Values['password'] +'''',stmt,
1611                   RegexObj.MatchPos[2] + RegexObj.MatchLen[2]);
845          ucStmt := AnsiUpperCase(stmt);
1612          end;
1613        end;
1614      finally
# Line 860 | Line 1626 | var  RegexObj: TRegExpr;
1626       LoginPrompt: boolean;
1627   begin
1628    Result := false;
1629 <  ucStmt := AnsiUpperCase(stmt);
864 <  Terminator := FSymbolStream.Terminator;
1629 >  Terminator := FSQLReader.Terminator;
1630    RegexObj := TRegExpr.Create;
1631    try
1632      {process create database}
1633 +    RegexObj.ModifierI := true; {case insensitive}
1634      RegexObj.Expression := '^ *CREATE +(DATABASE|SCHEMA) +''(.*)''(.*)(\' + Terminator + '|)';
1635 <    if RegexObj.Exec(ucStmt) then
1635 >    if RegexObj.Exec(stmt) then
1636      begin
1637        if IgnoreCreateDatabase then
1638        begin
# Line 877 | Line 1643 | begin
1643        if assigned(FOnCreateDatabase) then
1644          OnCreateDatabase(self,FileName);
1645        stmt := 'CREATE DATABASE ''' + FileName + '''' + system.copy(stmt,RegexObj.MatchPos[3], RegexObj.MatchLen[3]);
880      ucStmt := AnsiUpperCase(stmt);
1646        UpdateUserPassword;
1647 <      FDatabase.Connected := false;
1647 >      if FDatabase.Connected then
1648 >        FDatabase.Dropdatabase;
1649        FDatabase.CreateDatabase(stmt);
884      FDatabase.Connected := false;
885      ExtractUserInfo;
886      DoReconnect;
1650        Result := true;
1651        Exit;
1652      end;
1653  
1654      {process connect statement}
1655      RegexObj.Expression := '^ *CONNECT +.*(\' + Terminator + '|)';
1656 <    if RegexObj.Exec(ucStmt) then
1656 >    if RegexObj.Exec(stmt) then
1657      begin
1658        ExtractConnectInfo;
1659 <      DoReconnect;
1659 >      FDatabase.Connected := false;
1660 >      FDatabase.Connected := true;
1661        Result := true;
1662        Exit;
1663      end;
1664  
1665      {Process Drop Database}
1666      RegexObj.Expression := '^ *DROP +DATABASE *(\' + Terminator + '|)';
1667 <    if RegexObj.Exec(ucStmt) then
1667 >    if RegexObj.Exec(stmt) then
1668      begin
1669        FDatabase.DropDatabase;
1670        Result := true;
# Line 909 | Line 1673 | begin
1673  
1674      {process commit statement}
1675      RegexObj.Expression := '^ *COMMIT *(\' + Terminator + '|)';
1676 <    if RegexObj.Exec(ucStmt) then
1676 >    if RegexObj.Exec(stmt) then
1677      begin
1678        DoCommit;
1679        Result := true;
# Line 918 | Line 1682 | begin
1682  
1683      {process Reconnect statement}
1684      RegexObj.Expression := '^ *RECONNECT *(\' + Terminator + '|)';
1685 <    if RegexObj.Exec(ucStmt) then
1685 >    if RegexObj.Exec(stmt) then
1686      begin
1687        DoReconnect;
1688        Result := true;
# Line 928 | Line 1692 | begin
1692  
1693      {Process Set Term}
1694      RegexObj.Expression := '^ *SET +TERM +(.) *(\' + Terminator + '|)';
1695 <    if RegexObj.Exec(ucStmt) then
1695 >    if RegexObj.Exec(stmt) then
1696      begin
1697 <       FSymbolStream.Terminator := RegexObj.Match[1][1];
1697 >       FSQLReader.Terminator := RegexObj.Match[1][1];
1698         Result := true;
1699         Exit;
1700      end;
1701  
1702      {process Set SQL Dialect}
1703      RegexObj.Expression := '^ *SET +SQL +DIALECT +([0-9]) *(\' + Terminator + '|)';
1704 <    if RegexObj.Exec(ucStmt) then
1704 >    if RegexObj.Exec(stmt) then
1705      begin
1706        n := StrToInt(RegexObj.Match[1]);
1707        if Database.SQLDialect <> n then
# Line 952 | Line 1716 | begin
1716  
1717      {Process Remaining Set statements}
1718      RegexObj.Expression := '^ *SET +([A-Z]+)( +[A-Z0-9]+|) *(\' + Terminator + '|)';
1719 <    if RegexObj.Exec(ucStmt) then
1719 >    if RegexObj.Exec(stmt) then
1720      begin
1721        command := AnsiUpperCase(RegexObj.Match[1]);
1722        param := trim(RegexObj.Match[2]);
1723 +      if command = 'GENERATOR' then
1724 +      begin
1725 +        Result := false;
1726 +        Exit;
1727 +      end;
1728        if command = 'AUTODDL' then
1729          AutoDDL := ((RegexObj.MatchLen[2] = 0) and not AutoDDL) or
1730                     (RegexObj.MatchLen[2] > 0) and Toggle(param)
# Line 995 | Line 1764 | begin
1764        begin
1765          if assigned(DataOutputFormatter) then
1766            DataOutputFormatter.SetCommand(command,param,stmt,Result);
1767 <        if not Result and assigned(OnSetStatement) then
1768 <          OnSetStatement(self,command,param,stmt,Result)
1769 <        else
1770 <          raise Exception.CreateFmt(sInvalidSetStatement, [command,stmt]);
1767 >        if not Result then
1768 >        begin
1769 >          if assigned(OnSetStatement) then
1770 >            OnSetStatement(self,command,param,stmt,Result)
1771 >          else
1772 >            raise Exception.CreateFmt(sInvalidSetStatement, [command,stmt]);
1773 >        end;
1774          Exit;
1775        end;
1776        Result := true;
# Line 1014 | Line 1786 | procedure TCustomIBXScript.SetTransactio
1786   begin
1787    if FTransaction = AValue then Exit;
1788    FTransaction := AValue;
1789 <  FIBXMLProcessor.Transaction := AValue;
1789 >  if FTransaction = nil then
1790 >    FSQLReader.Transaction := FInternalTransaction
1791 >  else
1792 >    FSQLReader.Transaction := FTransaction;
1793   end;
1794  
1795   constructor TCustomIBXScript.Create(aOwner: TComponent);
# Line 1025 | Line 1800 | begin
1800    FAutoDDL := true;
1801    FISQL := TIBSQL.Create(self);
1802    FISQL.ParamCheck := true;
1028  FIBXMLProcessor := TIBXMLProcessor.Create;
1029  FIBSQLProcessor := TIBSQLProcessor.Create(FIBXMLProcessor);
1803    FInternalTransaction := TIBTransaction.Create(self);
1804    FInternalTransaction.Params.Clear;
1805    FInternalTransaction.Params.Add('concurrency');
# Line 1035 | Line 1808 | end;
1808  
1809   destructor TCustomIBXScript.Destroy;
1810   begin
1811 <  if FIBSQLProcessor <> nil then FIBSQLProcessor.Free;
1039 <  if FIBXMLProcessor <> nil then FIBXMLProcessor.Free;
1040 <  if FSymbolStream <> nil then FSymbolStream.Free;
1811 >  if FSQLReader <> nil then FSQLReader.Free;
1812    if FISQL <> nil then FISQL.Free;
1813    if FInternalTransaction <> nil then FInternalTransaction.Free;
1814    inherited Destroy;
# Line 1048 | Line 1819 | begin
1819    if assigned(DataOutputFormatter) then
1820      DataOutputFormatter.DataOut(aSQLText,@Add2Log)
1821    else
1822 <    FSymbolStream.ShowError(sNoSelectSQL,[nil]);
1822 >    FSQLReader.ShowError(sNoSelectSQL);
1823   end;
1824  
1825 < { TIBSQLProcessor }
1825 > { TInteractiveSQLStatementReader }
1826  
1827 < procedure TIBSQLProcessor.AddToSQL(const Symbol: string);
1827 > function TInteractiveSQLStatementReader.GetErrorPrefix: string;
1828   begin
1829 <  FSQLText := FSQLText +  Symbol;
1059 < //  writeln('SQL = ',FSQLText);
1060 < end;
1061 <
1062 < procedure TIBSQLProcessor.SetState(AState: TSQLStates);
1063 < begin
1064 <  if FStackIndex > 16 then
1065 <    FSymbolStream.ShowError(sStackOverFlow,[nil]);
1066 <  FStack[FStackIndex] := FState;
1067 <  Inc(FStackIndex);
1068 <  FState := AState
1069 < end;
1070 <
1071 < function TIBSQLProcessor.PopState: TSQLStates;
1072 < begin
1073 <  if FStackIndex = 0 then
1074 <    FSymbolStream.ShowError(sStackUnderflow,[nil]);
1075 <  Dec(FStackIndex);
1076 <  Result := FStack[FStackIndex]
1077 < end;
1078 <
1079 < constructor TIBSQLProcessor.Create(XMLProcessor: TIBXMLProcessor);
1080 < begin
1081 <  inherited Create;
1082 <  FXMLProcessor := XMLProcessor;
1829 >  Result := '';
1830   end;
1831  
1832 < function TIBSQLProcessor.GetNextStatement(SymbolStream: TSymbolStream;
1086 <  var stmt: string): boolean;
1087 < var Symbol: TSQLSymbol;
1088 <    NonSpace: boolean;
1089 <    Done: boolean;
1832 > function TInteractiveSQLStatementReader.GetNextLine(var Line: string): boolean;
1833   begin
1834 <  FSQLText := '';
1835 <  FState := stInit;
1836 <  FHasBegin := false;
1837 <  FSymbolStream := SymbolStream;
1838 <  FXMLProcessor.NextStatement;
1839 <  SymbolStream.NextStatement;
1097 <
1098 <  Result := true;
1099 <  Done := false;
1100 <  NonSpace := false;
1101 <  while not Done do
1102 <  with SymbolStream do
1834 >  if FNextStatement then
1835 >    write(FPrompt)
1836 >  else
1837 >    write(FContinuePrompt);
1838 >  Result := not system.EOF;
1839 >  if Result then
1840    begin
1841 <    if FState = stError then
1842 <      ShowError(sErrorState,[nil]);
1106 <    Symbol := GetSymbol;
1107 < //    writeln('Symbol = ',Symbol,' Value = ',SymbolValue);
1108 <    if not (Symbol in [' ',sqEOL]) then
1109 <      NonSpace := true;
1110 <
1111 <    case Symbol of
1112 <    sqTag:
1113 <      begin
1114 <        if FState in [stInSQL,stNested] then
1115 <          AddToSQL(FXMLProcessor.AnalyseXML(SymbolStream));
1116 <      end;
1117 <
1118 <    sqTerminator:
1119 <        case FState of
1120 <        stInit: {ignore empty statement};
1121 <
1122 <        stInSQL:
1123 <            Done := true;
1124 <
1125 <       stNested:
1126 <         AddToSQL(Terminator);
1127 <
1128 <       stInDeclaration:
1129 <         begin
1130 <           FState := PopState;
1131 <           AddToSQL(Terminator);
1132 <         end;
1133 <
1134 <       else
1135 <         ShowError(sTerminatorUnknownState,[FState]);
1136 <       end;
1137 <
1138 <    ';':
1139 <        begin
1140 <          if FState = stInDeclaration then
1141 <            FState := PopState;
1142 <          AddToSQL(';');
1143 <        end;
1144 <
1145 <    '*':
1146 <      begin
1147 <       AddToSQL('*');
1148 <       if FState =  stInit then
1149 <          FState := stInSQL
1150 <      end;
1151 <
1152 <    '/':
1153 <      begin
1154 <       AddToSQL('/');
1155 <       if FState =  stInit then
1156 <          FState := stInSQL
1157 <      end;
1158 <
1159 <    sqComment,
1160 <    sqQuotedString,
1161 <    sqDoubleQuotedString:
1162 <      if FState <> stInit then
1163 <        AddToSQL(SymbolValue);
1164 <
1165 <    sqCommentLine:
1166 <      if FState <> stInit then
1167 <      AddToSQL(SymbolValue + LineEnding);
1168 <
1169 <    sqEnd:
1170 <      begin
1171 <        AddToSQL(SymbolValue);
1172 <        case FState of
1173 <        stNested:
1174 <          begin
1175 <            if FNested = 0 then
1176 <            begin
1177 <              FState := PopState;
1178 <              if not FInCase then
1179 <              begin
1180 <                FState := stInit;
1181 <                Done := true;
1182 <              end
1183 <              else
1184 <                FInCase := false;
1185 <            end
1186 <           else
1187 <              Dec(FNested)
1188 <          end;
1189 <          {Otherwise ignore}
1190 <        end
1191 <      end;
1192 <
1193 <    sqBegin:
1194 <      begin
1195 <        FHasBegin := true;
1196 <        AddToSQL(SymbolValue);
1197 <        case FState of
1198 <        stNested:
1199 <          Inc(FNested);
1200 <
1201 <        stInSQL,
1202 <        stInit:
1203 <          SetState(stNested);
1204 <        end
1205 <      end;
1206 <
1207 <    sqCase:
1208 <    begin
1209 <      AddToSQL(SymbolValue);
1210 <      case FState of
1211 <      stNested:
1212 <        Inc(FNested);
1213 <
1214 <      stInSQL,
1215 <      stInit:
1216 <        begin
1217 <          FInCase := true;
1218 <          SetState(stNested);
1219 <        end;
1220 <      end
1221 <    end;
1222 <
1223 <    sqDeclare:
1224 <      begin
1225 <        AddToSQL(SymbolValue);
1226 <        if FState in [stInit,stInSQL] then
1227 <          SetState(stInDeclaration)
1228 <      end;
1229 <
1230 <    sqString:
1231 <      begin
1232 <        AddToSQL(SymbolValue);
1233 <        if FState = stInit then
1234 <          FState := stInSQL
1235 <      end;
1236 <
1237 <    sqEOL:
1238 <      begin
1239 <        case FState of
1240 <        stInit:
1241 <          {Do nothing};
1242 <        else
1243 <          if NonSpace then AddToSQL(LineEnding);
1244 <        end;
1245 <      end;
1246 <
1247 <    sqEOF:
1248 <      begin
1249 <        Done := true;
1250 <        Result := trim(FSQLText) <> '';
1251 <      end
1252 <    else
1253 <    if FState <> stInit then
1254 <      AddToSQL(Symbol);
1255 <    end
1256 <  end;
1257 <  stmt := FSQLText;
1258 < //  writeln('stmt = ',stmt);
1259 < end;
1260 <
1261 < { TIBXMLProcessor }
1262 <
1263 < procedure TIBXMLProcessor.EndXMLTag(xmltag: TXMLTag);
1264 < begin
1265 <  if FXMLTagIndex = 0 then
1266 <    FSymbolStream.ShowError(sXMLStackUnderflow,[nil]);
1267 <  if xmltag <> FXMLTagStack[FXMLTagIndex] then
1268 <    FSymbolStream.ShowError(sInvalidEndTag,[FSymbolStream.SymbolValue]);
1269 <
1270 <  case FXMLTagStack[FXMLTagIndex] of
1271 <  xtBlob:
1272 <    FBlobData[FCurrentBlob].BlobIntf.Close;
1273 <
1274 <  xtArray:
1275 <    FArrayData[FCurrentArray].ArrayIntf.SaveChanges;
1276 <
1277 <  xtElt:
1278 <    Dec(FArrayData[FCurrentArray].CurrentRow);
1279 <  end;
1280 <  Dec(FXMLTagIndex);
1281 < end;
1282 <
1283 < procedure TIBXMLProcessor.EnterTag;
1284 < var aCharSetID: integer;
1285 < begin
1286 <  case FXMLTagStack[FXMLTagIndex] of
1287 <  xtBlob:
1288 <    begin
1289 <      Database.Connected := true;
1290 <      Transaction.Active := true;
1291 <      FBlobData[FCurrentBlob].BlobIntf := Database.Attachment.CreateBlob(
1292 <        Transaction.TransactionIntf,FBlobData[FCurrentBlob].SubType);
1293 <    end;
1294 <
1295 <  xtArray:
1296 <    with FArrayData[FCurrentArray] do
1297 <    begin
1298 <      Database.Connected := true;
1299 <      Transaction.Active := true;
1300 <      Database.Attachment.CharSetName2CharSetID(CharSet,aCharSetID);
1301 <      SetLength(Index,dim);
1302 <      ArrayIntf := Database.Attachment.CreateArray(
1303 <                     Transaction.TransactionIntf,
1304 <                     Database.Attachment.CreateArrayMetaData(SQLType,
1305 <                       relationName,columnName,Scale,Size,
1306 <                       aCharSetID,dim,bounds)
1307 <                     );
1308 <    end;
1841 >    readln(Line);
1842 >    EchoNextLine(Line);
1843    end;
1844   end;
1845  
1846 < function TIBXMLProcessor.GetArrayData(index: integer): TArrayData;
1313 < begin
1314 <  if (index < 0) or (index > ArrayDataCount) then
1315 <    FSymbolStream.ShowError(sArrayIndexError,[index]);
1316 <  Result := FArrayData[index];
1317 < end;
1318 <
1319 < function TIBXMLProcessor.GetArrayDataCount: integer;
1320 < begin
1321 <  Result := Length(FArrayData);
1322 < end;
1323 <
1324 < function TIBXMLProcessor.GetBlobData(index: integer): TBlobData;
1846 > function TInteractiveSQLStatementReader.GetChar: char;
1847   begin
1848 <  if (index < 0) or (index > BlobDataCount) then
1849 <    FSymbolStream.ShowError(sBlobIndexError,[index]);
1850 <  Result := FBlobData[index];
1851 < end;
1330 <
1331 < function TIBXMLProcessor.GetBlobDataCount: integer;
1332 < begin
1333 <  Result := Length(FBlobData);
1334 < end;
1335 <
1336 < procedure TIBXMLProcessor.ProcessTagValue(tagValue: string);
1337 <
1338 <  function nibble(hex: char): byte;
1339 <  begin
1340 <    case hex of
1341 <    '0': Result := 0;
1342 <    '1': Result := 1;
1343 <    '2': Result := 2;
1344 <    '3': Result := 3;
1345 <    '4': Result := 4;
1346 <    '5': Result := 5;
1347 <    '6': Result := 6;
1348 <    '7': Result := 7;
1349 <    '8': Result := 8;
1350 <    '9': Result := 9;
1351 <    'a','A': Result := 10;
1352 <    'b','B': Result := 11;
1353 <    'c','C': Result := 12;
1354 <    'd','D': Result := 13;
1355 <    'e','E': Result := 14;
1356 <    'f','F': Result := 15;
1357 <    end;
1358 <  end;
1359 <
1360 <  procedure RemoveWhiteSpace(var hexData: string);
1361 <  var i: integer;
1848 >  if Terminated then
1849 >    Result := #0
1850 >  else
1851 >  if FLineIndex > Length(FLine) then
1852    begin
1853 <    {Remove White Space}
1854 <    i := 1;
1855 <    while i <= length(hexData) do
1856 <    begin
1857 <      case hexData[i] of
1368 <      ' ',#9,#10,#13:
1369 <        begin
1370 <          if i < Length(hexData) then
1371 <            Move(hexData[i+1],hexData[i],Length(hexData)-i);
1372 <          SetLength(hexData,Length(hexData)-1);
1373 <        end;
1374 <      else
1375 <        Inc(i);
1376 <      end;
1377 <    end;
1378 <  end;
1379 <
1380 <  procedure WriteToBlob(hexData: string);
1381 <  var i,j : integer;
1382 <      blength: integer;
1383 <      P: PChar;
1853 >    Result := LF;
1854 >    FLineIndex := 0;
1855 >  end
1856 >  else
1857 >  if FLineIndex = 0 then
1858    begin
1859 <    RemoveWhiteSpace(hexData);
1860 <    if odd(length(hexData)) then
1387 <      FSymbolStream.ShowError(sBinaryBlockMustbeEven,[nil]);
1388 <    blength := Length(hexData) div 2;
1389 <    IBAlloc(FBlobBuffer,0,blength);
1390 <    j := 1;
1391 <    P := FBlobBuffer;
1392 <    for i := 1 to blength do
1393 <    begin
1394 <      P^ := char((nibble(hexData[j]) shl 4) or nibble(hexdata[j+1]));
1395 <      Inc(j,2);
1396 <      Inc(P);
1397 <    end;
1398 <    FBlobData[FCurrentBlob].BlobIntf.Write(FBlobBuffer^,blength);
1399 <  end;
1400 <
1401 < begin
1402 <  if tagValue = '' then Exit;
1403 <  case FXMLTagStack[FXMLTagIndex] of
1404 <  xtBlob:
1405 <    WriteToBlob(tagValue);
1406 <
1407 <  xtElt:
1408 <    with FArrayData[FCurrentArray] do
1409 <      ArrayIntf.SetAsString(index,tagValue);
1410 <
1411 <  end;
1412 < end;
1413 <
1414 < procedure TIBXMLProcessor.StartXMLTag(xmltag: TXMLTag);
1415 < begin
1416 <  if FXMLTagIndex > 19 then
1417 <    FSymbolStream.ShowError(sXMLStackOverFlow,[nil]);
1418 <  Inc(FXMLTagIndex);
1419 <  FXMLTagStack[FXMLTagIndex] := xmltag;
1420 <  case xmltag of
1421 <  xtBlob:
1422 <    begin
1423 <      Inc(FCurrentBlob);
1424 <      SetLength(FBlobData,FCurrentBlob+1);
1425 <      FBlobData[FCurrentBlob].BlobIntf := nil;
1426 <      FBlobData[FCurrentBlob].SubType := 0;
1427 <    end;
1428 <
1429 <  xtArray:
1430 <    begin
1431 <      Inc(FCurrentArray);
1432 <      SetLength(FArrayData,FCurrentArray+1);
1433 <      with FArrayData[FCurrentArray] do
1434 <      begin
1435 <        ArrayIntf := nil;
1436 <        SQLType := 0;
1437 <        dim := 0;
1438 <        Size := 0;
1439 <        Scale := 0;
1440 <        CharSet := 'NONE';
1441 <        SetLength(Index,0);
1442 <        CurrentRow := -1;
1443 <      end;
1444 <    end;
1445 <
1446 <  xtElt:
1447 <    with FArrayData[FCurrentArray] do
1448 <      Inc(CurrentRow);
1449 <
1450 <  end;
1451 < end;
1452 <
1453 < procedure TIBXMLProcessor.ProcessAttributeValue(attrValue: string);
1454 < begin
1455 <  case FXMLTagStack[FXMLTagIndex] of
1456 <  xtBlob:
1457 <    if FAttributeName = 'subtype' then
1458 <      FBlobData[FCurrentBlob].SubType := StrToInt(attrValue)
1459 <    else
1460 <      FSymbolStream.ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
1461 <
1462 <  xtArray:
1463 <    if FAttributeName = 'sqltype' then
1464 <      FArrayData[FCurrentArray].SQLType := StrToInt(attrValue)
1465 <    else
1466 <    if FAttributeName = 'relation_name' then
1467 <      FArrayData[FCurrentArray].relationName := attrValue
1468 <    else
1469 <    if FAttributeName = 'column_name' then
1470 <      FArrayData[FCurrentArray].columnName := attrValue
1471 <    else
1472 <    if FAttributeName = 'dim' then
1473 <      FArrayData[FCurrentArray].Dim := StrToInt(attrValue)
1474 <    else
1475 <    if FAttributeName = 'length' then
1476 <      FArrayData[FCurrentArray].Size := StrToInt(attrValue)
1477 <    else
1478 <    if FAttributeName = 'scale' then
1479 <      FArrayData[FCurrentArray].Scale := StrToInt(attrValue)
1859 >    if not GetNextLine(FLine) then
1860 >      Result := #0
1861      else
1862 <    if FAttributeName = 'charset' then
1863 <      FArrayData[FCurrentArray].CharSet := attrValue
1483 <    else
1484 <    if FAttributeName = 'bounds' then
1485 <      ProcessBoundsList(attrValue)
1862 >    if Length(FLine) = 0 then
1863 >      Result := LF
1864      else
1487      FSymbolStream.ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
1488
1489  xtElt:
1490    if FAttributeName = 'ix' then
1491      with FArrayData[FCurrentArray] do
1492        Index[CurrentRow] :=  StrToInt(attrValue)
1493     else
1494        FSymbolStream.ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
1495  end;
1496 end;
1497
1498 procedure TIBXMLProcessor.ProcessBoundsList(boundsList: string);
1499 var list: TStringList;
1500    i,j: integer;
1501 begin
1502  list := TStringList.Create;
1503  try
1504    list.Delimiter := ',';
1505    list.DelimitedText := boundsList;
1506    with FArrayData[FCurrentArray] do
1865      begin
1866 <      if dim <> list.Count then
1867 <        FSymbolStream.ShowError(sInvalidBoundsList,[boundsList]);
1868 <      SetLength(bounds,dim);
1869 <      for i := 0 to list.Count - 1 do
1870 <      begin
1871 <        j := Pos(':',list[i]);
1872 <        if j = 0 then
1873 <          raise Exception.CreateFmt(sInvalidBoundsList,[boundsList]);
1516 <        bounds[i].LowerBound := StrToInt(system.copy(list[i],1,j-1));
1517 <        bounds[i].UpperBound := StrToInt(system.copy(list[i],j+1,length(list[i])-j));
1518 <      end;
1519 <    end;
1520 <  finally
1521 <    list.Free;
1866 >      Result := FLine[1];
1867 >      FLineIndex := 2;
1868 >    end
1869 >  end
1870 >  else
1871 >  begin
1872 >    Result := FLine[FLineIndex];
1873 >    Inc(FLineIndex);
1874    end;
1875   end;
1876  
1877 < constructor TIBXMLProcessor.Create;
1877 > constructor TInteractiveSQLStatementReader.Create(aPrompt: string; aContinue: string);
1878   begin
1879    inherited Create;
1880 <  NextStatement;
1880 >  FPrompt := aPrompt;
1881 >  FLineIndex := 0;
1882 >  FNextStatement := true;
1883 >  FContinuePrompt := aContinue;
1884   end;
1885  
1886 < destructor TIBXMLProcessor.Destroy;
1886 > function TInteractiveSQLStatementReader.GetNextStatement(var stmt: string
1887 >  ): boolean;
1888   begin
1889 <  FreeMem(FBlobBuffer);
1890 <  inherited Destroy;
1889 >  Result := inherited GetNextStatement(stmt);
1890 >  FNextStatement := Result;
1891   end;
1892  
1893 < function TIBXMLProcessor.AnalyseXML(SymbolStream: TSymbolStream): string;
1538 < var Symbol: TSQLSymbol;
1539 <    Done: boolean;
1540 <    XMLString: string;
1541 < begin
1542 <  Result := '';
1543 <  XMLString := '';
1544 <  Done := false;
1545 <  FState := stInTag;
1546 <  FSymbolStream := SymbolStream;
1547 <  with SymbolStream do
1548 <  begin
1549 <    StartXMLTag(XMLTag);
1550 <    while not Done do
1551 <    with SymbolStream do
1552 <    begin
1553 <      Symbol := GetSymbol;
1554 <
1555 <      case Symbol of
1556 <      sqEOL:
1557 <      case FState of
1558 <      stQuotedAttributeValue,
1559 <      stTagged:
1560 <         XMLString += LineEnding;
1561 <      end;
1562 <
1563 <      ' ',sqTab:
1564 <        case FState of
1565 <        stQuotedAttributeValue,
1566 <        stTagged:
1567 <           XMLString += ' ';
1568 <        end;
1893 > { TBatchSQLStatementReader }
1894  
1895 <      ';':
1571 <        case FState of
1572 <        stQuotedAttributeValue,
1573 <        stTagged:
1574 <           XMLString += ';';
1575 <        else
1576 <          ShowError(sXMLError,[Symbol]);
1577 <        end;
1578 <
1579 <      '''':
1580 <        case FState of
1581 <        stQuotedAttributeValue,
1582 <        stTagged:
1583 <           XMLString += '''';
1584 <        else
1585 <          ShowError(sXMLError,[Symbol]);
1586 <        end;
1587 <
1588 <      '*':
1589 <        case FState of
1590 <        stQuotedAttributeValue,
1591 <        stTagged:
1592 <           XMLString += '*';
1593 <        else
1594 <          ShowError(sXMLError,[Symbol]);
1595 <        end;
1596 <
1597 <      '/':
1598 <        case FState of
1599 <        stQuotedAttributeValue,
1600 <        stTagged:
1601 <           XMLString += '/';
1602 <        else
1603 <          ShowError(sXMLError,[Symbol]);
1604 <        end;
1605 <
1606 <      '>':
1607 <        case FState of
1608 <        stEndTag:
1609 <            case XMLTag of
1610 <            xtBlob:
1611 <              begin
1612 <                Result := ':' + Format(ibx_blob+'%d',[FCurrentBlob]);
1613 <                Done := true;
1614 <              end;
1615 <            xtArray:
1616 <              begin
1617 <                Result := ':' + Format(ibx_array+'%d',[FCurrentArray]);
1618 <                Done := true;
1619 <              end;
1620 <            else
1621 <              FState := stTagged;
1622 <          end;
1623 <
1624 <        stInTag:
1625 <          begin
1626 <            XMLString := '';
1627 <            FState := stTagged;
1628 <            EnterTag;
1629 <          end;
1630 <
1631 <        stQuotedAttributeValue,
1632 <        stTagged:
1633 <          XMLString += '>';
1634 <
1635 <        else
1636 <          ShowError(sXMLError,[Symbol]);
1637 <        end;
1638 <
1639 <      sqTag:
1640 <        if FState = stTagged then
1641 <        begin
1642 <          FState := stInTag;
1643 <          StartXMLTag(XMLTag)
1644 <        end
1645 <        else
1646 <          ShowError(sXMLError,[Symbol]);
1647 <
1648 <      sqEndTag:
1649 <        if FState = stTagged then
1650 <        begin
1651 <          ProcessTagValue(XMLString);
1652 <          EndXMLTag(XMLTag);
1653 <          FState := stEndTag;
1654 <        end
1655 <        else
1656 <          ShowError(sXMLError,[Symbol]);
1657 <
1658 <      '=':
1659 <        case FState of
1660 <        stAttribute:
1661 <          FState := stAttributeValue;
1662 <
1663 <        stQuotedAttributeValue,
1664 <        stTagged:
1665 <          XMLString += '=';
1666 <
1667 <        else
1668 <          ShowError(sXMLError,[Symbol]);
1669 <        end;
1670 <
1671 <      '"':
1672 <        case FState of
1673 <        stAttributeValue:
1674 <          begin
1675 <            XMLString := '';
1676 <            FState := stQuotedAttributeValue;
1677 <          end;
1678 <
1679 <        stQuotedAttributeValue:
1680 <          begin
1681 <            ProcessAttributeValue(XMLString);
1682 <            FState := stInTag;
1683 <          end;
1684 <
1685 <        stTagged:
1686 <          XMLString += '"';
1687 <
1688 <        else
1689 <          ShowError(sXMLError,[Symbol]);
1690 <        end;
1691 <
1692 <      sqString:
1693 <        case FState of
1694 <        stInTag: {attribute name}
1695 <          begin
1696 <            FAttributeName := SymbolValue;
1697 <            FState := stAttribute;
1698 <          end;
1699 <
1700 <        stAttributeValue:
1701 <          begin
1702 <            ProcessAttributeValue(FString);
1703 <            FState := stInTag;
1704 <          end;
1705 <
1706 <        stQuotedAttributeValue,
1707 <        stTagged:
1708 <           XMLString += SymbolValue;
1709 <
1710 <        else
1711 <          ShowError(sXMLError,[Symbol]);
1712 <        end;
1713 <      else
1714 <        ShowError(sXMLError,[Symbol]);
1715 <      end
1716 <    end;
1717 <  end;
1718 < end;
1719 <
1720 < procedure TIBXMLProcessor.NextStatement;
1895 > function TBatchSQLStatementReader.GetChar: char;
1896   begin
1897 <  FXMLTagIndex := 0;
1898 <  SetLength(FBlobData,0);
1899 <  FCurrentBlob := -1;
1900 <  SetLength(FArrayData,0);
1726 <  FCurrentArray := -1;
1727 < end;
1728 <
1729 < class function TIBXMLProcessor.FormatBlob(Field: ISQLData): string;
1730 < var TextOut: TStrings;
1731 < begin
1732 <  TextOut := TStringList.Create;
1733 <  try
1734 <    TextOut.Add(Format('<blob subtype="%d">',[Field.getSubtype]));
1735 <    StringToHex(Field.AsString,TextOut,BlobLineLength);
1736 <    TextOut.Add('</blob>');
1737 <    Result := TextOut.Text;
1738 <  finally
1739 <    TextOut.Free;
1740 <  end;
1741 < end;
1742 <
1743 < class function TIBXMLProcessor.FormatArray(Database: TIBDatabase; ar: IArray
1744 <  ): string;
1745 < var index: array of integer;
1746 <    TextOut: TStrings;
1747 <
1748 <    procedure AddElements(dim: integer; indent:string = ' ');
1749 <    var i: integer;
1750 <        recurse: boolean;
1897 >  if not EOF and assigned(FInStream) and not (FInStream.Position = FInStream.Size) then
1898 >  begin
1899 >    Result := char(FInStream.ReadByte);
1900 >    if Result = LF then
1901      begin
1902 <      SetLength(index,dim+1);
1903 <      recurse := dim < ar.GetDimensions - 1;
1904 <      with ar.GetBounds[dim] do
1905 <      for i := LowerBound to UpperBound do
1906 <      begin
1907 <        index[dim] := i;
1908 <        if recurse then
1909 <        begin
1760 <          TextOut.Add(Format('%s<elt id="%d">',[indent,i]));
1761 <          AddElements(dim+1,indent + ' ');
1762 <          TextOut.Add('</elt>');
1763 <        end
1764 <        else
1765 <        if ((ar.GetSQLType = SQL_TEXT) or (ar.GetSQLType = SQL_VARYING)) and
1766 <           (ar.GetCharSetID = 1) then
1767 <           TextOut.Add(Format('%s<elt ix="%d">%s</elt>',[indent,i,StringToHex(ar.GetAsString(index))]))
1768 <        else
1769 <          TextOut.Add(Format('%s<elt ix="%d">%s</elt>',[indent,i,ar.GetAsString(index)]));
1770 <      end;
1771 <    end;
1772 <
1773 < var
1774 <    s: string;
1775 <    bounds: TArrayBounds;
1776 <    i: integer;
1777 <    boundsList: string;
1778 < begin
1779 <  TextOut := TStringList.Create;
1780 <  try
1781 <    s := Format('<array dim = "%d" sqltype = "%d" length = "%d" relation_name = "%s" column_name = "%s"',
1782 <                                [ar.GetDimensions,ar.GetSQLType,ar.GetSize,
1783 <                                 ar.GetTableName,ar.GetColumnName]);
1784 <    case ar.GetSQLType of
1785 <    SQL_DOUBLE, SQL_FLOAT, SQL_LONG, SQL_SHORT, SQL_D_FLOAT, SQL_INT64:
1786 <       s += Format(' scale = "%d"',[ ar.GetScale]);
1787 <    SQL_TEXT,
1788 <    SQL_VARYING:
1789 <      s += Format(' charset = "%s"',[Database.Attachment.GetCharsetName(ar.GetCharSetID)]);
1790 <    end;
1791 <    bounds := ar.GetBounds;
1792 <    boundsList := '';
1793 <    for i := 0 to length(bounds) - 1 do
1902 >      EchoNextLine(FCurLine);
1903 >      FCurLine := '';
1904 >      if assigned(OnProgressEvent) then
1905 >        OnProgressEvent(self,false,FIndex+1);
1906 >      Inc(FLineIndex);
1907 >      FIndex := 1;
1908 >    end
1909 >    else
1910      begin
1911 <      if i <> 0 then boundsList += ',';
1912 <      boundsList += Format('%d:%d',[bounds[i].LowerBound,bounds[i].UpperBound]);
1911 >      FCurLine += Result;
1912 >      Inc(FIndex);
1913      end;
1914 <    s += Format(' bounds="%s"',[boundsList]);
1799 <    s += '>';
1800 <    TextOut.Add(s);
1801 <
1802 <    SetLength(index,0);
1803 <    AddElements(0);
1804 <    TextOut.Add('</array>');
1805 <    Result := TextOut.Text;
1806 <  finally
1807 <    TextOut.Free;
1808 <  end;
1809 < end;
1810 <
1811 < { TInteractiveSymbolStream }
1812 <
1813 < function TInteractiveSymbolStream.GetErrorPrefix: string;
1814 < begin
1815 <  Result := '';
1816 < end;
1817 <
1818 < function TInteractiveSymbolStream.GetNextLine(var Line: string): boolean;
1819 < begin
1820 <  if FNextStatement then
1821 <    write(FPrompt)
1822 <  else
1823 <    write(FContinuePrompt);
1824 <  Result := not EOF;
1825 <  if Result then
1826 <    readln(Line);
1827 < end;
1828 <
1829 < constructor TInteractiveSymbolStream.Create(aPrompt: string; aContinue: string);
1830 < begin
1831 <  inherited Create;
1832 <  FPrompt := aPrompt;
1833 <  FContinuePrompt := aContinue;
1834 < end;
1835 <
1836 < function TInteractiveSymbolStream.GetSymbol: TSQLSymbol;
1837 < begin
1838 <  if Terminated then
1839 <    Result := sqEOF
1914 >  end
1915    else
1916 <    Result := inherited GetSymbol;
1916 >    Result := #0;
1917   end;
1918  
1919 < { TBatchSymbolStream }
1845 <
1846 < function TBatchSymbolStream.GetErrorPrefix: string;
1919 > function TBatchSQLStatementReader.GetErrorPrefix: string;
1920   begin
1921    Result := Format(sOnLineError,[FLineIndex,FIndex]);
1922   end;
1923  
1924 < function TBatchSymbolStream.GetNextLine(var Line: string): boolean;
1852 < begin
1853 <  Result := FLineIndex < FLines.Count;
1854 <  if Result then
1855 <  begin
1856 <    Line := FLines[FLineIndex];
1857 < //    writeln('Next Line = ',Line);
1858 <    Inc(FLineIndex);
1859 <    if assigned(OnProgressEvent) then
1860 <      OnProgressEvent(self,false,1);
1861 <  end;
1862 < end;
1863 <
1864 < constructor TBatchSymbolStream.Create;
1924 > procedure TBatchSQLStatementReader.Reset;
1925   begin
1926 <  inherited Create;
1927 <  FLines := TStringList.Create;
1928 < end;
1929 <
1930 < destructor TBatchSymbolStream.Destroy;
1931 < begin
1932 <  if assigned(FLines) then FLines.Free;
1933 <  inherited Destroy;
1934 < end;
1935 <
1936 < procedure TBatchSymbolStream.SetStreamSource(Lines: TStrings);
1937 < begin
1938 <  FLineIndex := 0;
1939 <  FLines.Assign(Lines);
1926 >  inherited Reset;
1927 >  if FOwnsInStream and assigned(FInStream) then
1928 >    FInStream.Free;
1929 >  FInStream := nil;
1930 >  FOwnsInStream := false;
1931 >  FLineIndex := 1;
1932 >  FIndex := 1;
1933 >  FCurLine := '';
1934 > end;
1935 >
1936 > procedure TBatchSQLStatementReader.SetStreamSource(Lines: TStrings);
1937 > begin
1938 >  Reset;
1939 >  FInStream := TMemoryStream.Create;
1940 >  FOwnsInStream := true;
1941 >  Lines.SaveToStream(FInStream);
1942 >  FInStream.Position := 0;
1943    if assigned(OnProgressEvent) then
1944 <    OnProgressEvent(self,true,FLines.Count);
1944 >    OnProgressEvent(self,true,FInStream.Size);
1945   end;
1946  
1947 < procedure TBatchSymbolStream.SetStreamSource(S: TStream);
1947 > procedure TBatchSQLStatementReader.SetStreamSource(S: TStream);
1948   begin
1949 <  FLineIndex := 0;
1950 <  FLines.LoadFromStream(S);
1949 >  Reset;
1950 >  FInStream := S;
1951    if assigned(OnProgressEvent) then
1952 <    OnProgressEvent(self,true,FLines.Count);
1952 >    OnProgressEvent(self,true,S.Size - S.Position);
1953   end;
1954  
1955 < procedure TBatchSymbolStream.SetStreamSource(FileName: string);
1955 > procedure TBatchSQLStatementReader.SetStreamSource(FileName: string);
1956   begin
1957 <  FLineIndex := 0;
1958 <  FLines.LoadFromFile(FileName);
1957 >  Reset;
1958 >  FInStream := TFileStream.Create(FileName,fmShareCompat);
1959 >  FOwnsInStream := true;
1960    if assigned(OnProgressEvent) then
1961 <    OnProgressEvent(self,true,FLines.Count);
1898 < end;
1899 <
1900 < { TSymbolStream }
1901 <
1902 < function TSymbolStream.GetNextSymbol(C: char): TSQLSymbol;
1903 < begin
1904 <  Result := sqNone;
1905 <  if C = FTerminator then
1906 <    Result := sqTerminator
1907 <  else
1908 <  case C of
1909 <  #0..#8,#10..#31,' ':
1910 <    Result := ' ';
1911 <
1912 <  #9,';','"','''','/','-',
1913 <  '*','=','>','<',',':
1914 <    Result := C;
1915 <  else
1916 <    begin
1917 <      Result := sqString;
1918 <      FLastChar := C
1919 <    end
1920 <  end;
1921 < end;
1922 <
1923 < function TSymbolStream.FindTag(tag: string; var xmlTag: TXMLTag): boolean;
1924 < var i: integer;
1925 < begin
1926 <  Result := false;
1927 <  for i := 0 to Length(XMLTagDefs) - 1 do
1928 <    if XMLTagDefs[i].TagValue = tag then
1929 <    begin
1930 <      xmlTag := XMLTagDefs[i].XMLTag;
1931 <      Result := true;
1932 <      break;
1933 <    end;
1934 < end;
1935 <
1936 < constructor TSymbolStream.Create;
1937 < begin
1938 <  inherited;
1939 <  FTerminator := DefaultTerminator;
1940 <  NextStatement;
1941 < end;
1942 <
1943 < procedure TSymbolStream.ShowError(msg: string; params: array of const);
1944 < begin
1945 <  raise EIBClientError.CreateFmt(GetErrorPrefix + msg,params);
1946 < end;
1947 <
1948 < function TSymbolStream.GetSymbol: TSQLSymbol;
1949 < var
1950 <    DelimitedText: string;
1951 <    CurState: (gsNone,gsInComment,gsInSingleQuotes,gsInDoubleQuotes);
1952 < begin
1953 <  Result := sqNone;
1954 <  CurState := gsNone;
1955 <  DelimitedText := '';
1956 <  if FNextSymbol <> sqNone then
1957 <  begin
1958 <    Result := FNextSymbol;
1959 <    if Result = sqString then
1960 <      FString := FLastChar
1961 <    else
1962 <      FString := '';
1963 <    FNextSymbol := sqNone
1964 <  end;
1965 <
1966 <  while FNextSymbol = sqNone do {find the next symbol}
1967 <  begin
1968 <    if FIndex > Length(FLine) then
1969 <    begin
1970 <      FNextSymbol := sqEOL;
1971 <      FIndex := 0;
1972 <    end
1973 <    else
1974 <    begin
1975 <      if FIndex = 0 then
1976 <      begin
1977 <        if not GetNextLine(FLine) then
1978 <        begin
1979 <          Result := sqEOF;
1980 <          FNextSymbol := sqNone;
1981 <          Exit;
1982 <        end;
1983 <        FIndex := 1;
1984 <        FNextStatement := false;
1985 <        if assigned(OnNextLine) then
1986 <          OnNextLine(self,FLine);
1987 <        if CurState <> gsNone then
1988 <          DelimitedText += LineEnding;
1989 <        if Length(FLine) = 0 then
1990 <          continue;
1991 <      end;
1992 <      if CurState <> gsNone then
1993 <        DelimitedText += FLine[FIndex];
1994 <      FNextSymbol := GetNextSymbol(FLine[FIndex]);
1995 <      Inc(FIndex);
1996 <    end;
1997 <
1998 <    case CurState of
1999 <    gsNone:
2000 <      begin
2001 <        {combine if possible}
2002 <        case Result of
2003 <        sqNone:
2004 <          begin
2005 <            Result := FNextSymbol;
2006 <            if FNextSymbol = sqString then
2007 <              FString := FLastChar;
2008 <            FNextSymbol := sqNone
2009 <          end;
2010 <
2011 <        '/':
2012 <          if FXMLMode > 0 then
2013 <            break
2014 <          else
2015 <          if FNextSymbol = '*' then
2016 <          begin
2017 <            CurState := gsInComment;
2018 <            DelimitedText := '/*';
2019 <            Result := sqNone;
2020 <            FNextSymbol := sqNone
2021 <          end
2022 <          else
2023 <          if FNextSymbol = '/' then
2024 <          begin
2025 <            FString := '/*' + system.copy(FLine,FIndex,length(FLine)- FIndex + 1) + ' */';
2026 <            Result := sqCommentLine;
2027 <            FIndex := 0;
2028 <            FNextSymbol := sqNone
2029 <          end;
2030 <
2031 <        '-':
2032 <          if FXMLMode > 0 then
2033 <            break
2034 <          else
2035 <          if FNextSymbol = '-' then
2036 <          begin
2037 <            FString := '--' + system.copy(FLine,FIndex,length(FLine)- FIndex + 1) ;
2038 <            Result := sqCommentLine;
2039 <            FIndex := 0;
2040 <            FNextSymbol := sqNone
2041 <          end;
2042 <
2043 <        '<':
2044 <          if (FXMLMode > 0) and (FNextSymbol = '/') then
2045 <          begin
2046 <            Result := sqEndTag;
2047 <            FString := '';
2048 <            FNextSymbol := sqNone
2049 <          end
2050 <          else
2051 <          if FNextSymbol = sqString then
2052 <          begin
2053 <            Result := sqTag;
2054 <            FString := FLastChar;
2055 <            FNextSymbol := sqNone
2056 <          end;
2057 <
2058 <        '''':
2059 <        if FXMLMode > 0 then
2060 <          break
2061 <        else
2062 <        if FNextSymbol = '''' then
2063 <        begin
2064 <          Result := sqQuotedString;
2065 <          FString := '''''';
2066 <          FNextSymbol := sqNone
2067 <        end
2068 <        else
2069 <        begin
2070 <          CurState := gsInSingleQuotes;
2071 <          DelimitedText := '''';
2072 <          if FNextSymbol = sqEOL then
2073 <            DelimitedText += LineEnding
2074 <          else
2075 <            DelimitedText += FLine[FIndex-1];
2076 <          Result := sqNone;
2077 <          FNextSymbol := sqNone
2078 <        end;
2079 <
2080 <        '"':
2081 <        if FXMLMode > 0 then
2082 <          break
2083 <        else
2084 <        begin
2085 <          CurState := gsInDoubleQuotes;
2086 <          DelimitedText := '"';
2087 <          if FNextSymbol = sqEOL then
2088 <            DelimitedText += LineEnding
2089 <          else
2090 <            DelimitedText += FLine[FIndex-1];
2091 <          Result := sqNone;
2092 <          FNextSymbol := sqNone
2093 <        end;
2094 <
2095 <        sqTag,
2096 <        sqEndTag,
2097 <        sqString:
2098 <          if FNextSymbol = sqString then
2099 <          begin
2100 <            FString := FString + FLastChar;
2101 <            FNextSymbol := sqNone
2102 <          end;
2103 <        end
2104 <      end;
2105 <
2106 <    {Check for state exit condition}
2107 <    gsInSingleQuotes:
2108 <      if Result = '''' then
2109 <      begin
2110 <         CurState := gsNone;
2111 <         if FNextSymbol = sqEOL then
2112 <           FString := DelimitedText
2113 <         else
2114 <           FString := system.copy(DelimitedText,1,Length(DelimitedText)-1);
2115 <         Result := sqQuotedString;
2116 <       end;
2117 <
2118 <    gsInDoubleQuotes:
2119 <      if Result = '"' then
2120 <      begin
2121 <         CurState := gsNone;
2122 <         if FNextSymbol = sqEOL then
2123 <           FString := DelimitedText
2124 <         else
2125 <           FString := system.copy(DelimitedText,1,Length(DelimitedText)-1);
2126 <         Result := sqDoubleQuotedString;
2127 <       end;
2128 <
2129 <    gsInComment:
2130 <    if (Result = '*') and (FNextSymbol = '/') then
2131 <      begin
2132 <        CurState := gsNone;
2133 <        FString := DelimitedText;
2134 <        Result := sqComment;
2135 <        FNextSymbol := sqNone
2136 <      end;
2137 <
2138 <    end;
2139 <
2140 <    if (CurState <> gsNone) and (FNextSymbol <> sqNone) then
2141 <    begin
2142 <      Result := FNextSymbol;
2143 <      FNextSymbol := sqNone;
2144 <    end;
2145 <  end;
2146 <
2147 <  if (Result = sqTag) and (FNextSymbol <> sqNone) then
2148 <  begin
2149 <    if FindTag(FString,FXMLTag) then
2150 <      Inc(FXMLMode)
2151 <    else
2152 <      Result := sqString;
2153 <  end
2154 <  else
2155 <  if (Result = sqEndTag) and (FNextSymbol <> sqNone) then
2156 <  begin
2157 <    if FindTag(FString,FXMLTag) then
2158 <      Dec(FXMLMode)
2159 <    else
2160 <      Result := sqString;
2161 <  end;
2162 <
2163 <  if (FXMLMode = 0) and (Result = sqString) and (FString <> '') then
2164 <  begin
2165 <       if CompareText(FString,'begin') = 0 then
2166 <         Result := sqBegin
2167 <       else
2168 <       if CompareText(FString,'end') = 0 then
2169 <         Result := sqEnd
2170 <       else
2171 <       if CompareText(FString,'declare') = 0 then
2172 <         Result := sqDeclare
2173 <       else
2174 <       if CompareText(FString,'case') = 0 then
2175 <         Result := sqCase
2176 <  end;
2177 < //  writeln(Result,',',FString);
1961 >    OnProgressEvent(self,true,FInStream.Size);
1962   end;
1963  
1964 < procedure TSymbolStream.NextStatement;
1964 > procedure TBatchSQLStatementReader.SetStringStreamSource(S: string);
1965   begin
1966 <  FXMLTag := xtNone;
1967 <  FNextStatement := true;
1966 >  Reset;
1967 >  FInStream := TStringStream.Create(S);
1968 >  FOwnsInStream := true;
1969 >  if assigned(OnProgressEvent) then
1970 >    OnProgressEvent(self,true,FInStream.Size);
1971   end;
1972  
1973   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines