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 229 by tony, Tue Apr 10 13:32:36 2018 UTC vs.
Revision 412 by tony, Mon Jul 17 14:08:12 2023 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;
73  TOnProgressEvent = procedure (Sender: TObject; Reset: boolean; value: integer) of object;
74
75  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    );
86
87 type
39  
40 <  { TSymbolStream }
40 >  { TSQLStatementReader }
41  
42 <  {A simple lookahead one parser to process a text stream as a stream of symbols.
43 <   This is an abstract object, subclassed for different sources.}
44 <
45 <  TSymbolStream = class
42 >  TSQLStatementReader = class(TSQLXMLReader)
43 >  private
44 >    type
45 >      TSQLState = (stDefault, stInStmt, stInBlock, stInArrayDim, stInDeclare);
46    private
47 <    FNextSymbol: TSQLSymbol;
47 >    FDatabase: TIBDatabase;
48 >    FHasBegin: boolean;
49      FOnNextLine: TOnNextLine;
98    FOnProgressEvent: TOnProgressEvent;
50      FTerminator: char;
51 <    FLastChar: char;
101 <    FIndex: integer;
102 <    FLine: string;
103 <    FString: string;
104 <    FXMLTag: TXMLTag;
105 <    FXMLMode: integer;
51 >    FTransaction: TIBTransaction;
52    protected
53 <    FNextStatement: boolean;
54 <    function GetErrorPrefix: string; virtual; abstract;
55 <    function GetNextSymbol(C: char): TSQLSymbol;
110 <    function FindTag(tag: string; var xmlTag: TXMLTag): boolean;
111 <    function GetNextLine(var Line: string):boolean; virtual; abstract;
53 >    procedure EchoNextLine(aLine: string);
54 >    function GetAttachment: IAttachment; override;
55 >    function GetTransaction: ITransaction; override;
56    public
57      constructor Create;
58 <    procedure ShowError(msg: string; params: array of const);
59 <    function GetSymbol: TSQLSymbol; virtual;
60 <    procedure NextStatement;
117 <    property SymbolValue: string read FString;
118 <    property Terminator: char read FTerminator write FTerminator;
119 <    property XMLTag: TXMLTag read FXMLTag;
58 >    function GetNextStatement(var stmt: string) : boolean; virtual;
59 >    property HasBegin: boolean read FHasBegin;
60 >    property Terminator: char read FTerminator write FTerminator default DefaultTerminator;
61      property OnNextLine: TOnNextLine read FOnNextLine write FOnNextLine;
62 <    property OnProgressEvent: TOnProgressEvent read FOnProgressEvent write FOnProgressEvent; {Progress Bar Support}
62 >    property Database: TIBDatabase read FDatabase write FDatabase;
63 >    property Transaction: TIBTransaction read FTransaction write FTransaction;
64    end;
65  
124  { TBatchSymbolStream }
66  
67 <  {This symbol stream supports non-interactive parsing of a text file, stream or
67 >  { TBatchSQLStatementReader }
68 >
69 >  {This SQL Reader supports non-interactive parsing of a text file, stream or
70     lines of text.}
71  
72 <  TBatchSymbolStream = class(TSymbolStream)
72 >  TBatchSQLStatementReader = class(TSQLStatementReader)
73    private
74 <    FLines: TStrings;
74 >    FInStream: TStream;
75 >    FOwnsInStream: boolean;
76      FLineIndex: integer;
77 +    FIndex: integer;
78 +    FCurLine: string;
79    protected
80 <    function GetErrorPrefix: string; override;
81 <    function GetNextLine(var Line: string):boolean; override;
80 >    function GetChar: AnsiChar; override;
81 >    function GetErrorPrefix: AnsiString; override;
82    public
83 <    constructor Create;
138 <    destructor Destroy; override;
83 >    procedure Reset; override;
84      procedure SetStreamSource(Lines: TStrings); overload;
85      procedure SetStreamSource(S: TStream); overload;
86      procedure SetStreamSource(FileName: string); overload;
87 +    procedure SetStringStreamSource(S: string);
88    end;
89  
90 <  { TInteractiveSymbolStream }
90 >  { TInteractiveSQLStatementReader }
91  
92 <  {This symbol stream supports interactive parsing of commands and
92 >  {This SQL reader supports interactive parsing of commands and
93     SQL statements entered at a console}
94  
95 <  TInteractiveSymbolStream = class(TSymbolStream)
95 >  TInteractiveSQLStatementReader = class(TSQLStatementReader)
96    private
97      FPrompt: string;
98      FContinuePrompt: string;
99      FTerminated: boolean;
100 +    FLine: string;
101 +    FLineIndex: integer;
102 +    FNextStatement: boolean;
103 +    function GetNextLine(var Line: string):boolean;
104    protected
105 <    function GetErrorPrefix: string; override;
106 <    function GetNextLine(var Line: string):boolean; override;
105 >    function GetChar: AnsiChar; override;
106 >    function GetErrorPrefix: AnsiString; override;
107    public
108      constructor Create(aPrompt: string='SQL>'; aContinue: string = 'CON>');
109 <    function GetSymbol: TSQLSymbol; override;
109 >    function GetNextStatement(var stmt: string) : boolean; override;
110      property Terminated: boolean read FTerminated write FTerminated;
111    end;
112  
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
113    TGetParamValue = procedure(Sender: TObject; ParamName: string; var BlobID: TISC_QUAD) of object;
114    TLogEvent = procedure(Sender: TObject; Msg: string) of Object;
115    TOnSelectSQL = procedure (Sender: TObject; SQLText: string) of object;
# Line 272 | Line 131 | type
131    TCustomIBXScript = class(TComponent)
132    private
133      FEcho: boolean;
134 <    FIBXMLProcessor: TIBXMLProcessor;
276 <    FIBSQLProcessor: TIBSQLProcessor;
134 >    FSQLReader: TSQLStatementReader;
135      FDatabase: TIBDatabase;
136      FDataOutputFormatter: TIBCustomDataOutput;
137      FIgnoreCreateDatabase: boolean;
# Line 302 | Line 160 | type
160      procedure SetShowPerformanceStats(AValue: boolean);
161      procedure SetTransaction(AValue: TIBTransaction);
162    protected
305    FSymbolStream: TSymbolStream;
163      procedure Add2Log(const Msg: string; IsError: boolean=true); virtual;
164      procedure ExecSQL(stmt: string);
165      procedure EchoNextLine(Sender: TObject; Line: string);
166      procedure Notification(AComponent: TComponent; Operation: TOperation); override;
167      function ProcessStatement(stmt: string): boolean; virtual;
168      function ProcessStream: boolean;
169 +    procedure SetSQLStatementReader(SQLStatementReader: TSQLStatementReader);
170    public
171      constructor Create(aOwner: TComponent); override;
172      destructor Destroy; override;
173      procedure DefaultSelectSQLHandler(aSQLText: string);
174 +    property SQLStatementReader: TSQLStatementReader read FSQLReader;
175    published
176      property Database: TIBDatabase read FDatabase write SetDatabase;
177      property DataOutputFormatter: TIBCustomDataOutput read FDataOutputFormatter
# Line 419 | Line 278 | type
278      function ExecSQLScript(sql: string): boolean;
279    end;
280  
422 function StringToHex(octetString: string; MaxLineLength: integer=0): string; overload;
423 procedure StringToHex(octetString: string; TextOut: TStrings; MaxLineLength: integer=0); overload;
424
281  
282   resourcestring
283    sInvalidSetStatement = 'Invalid %s Statement - %s';
284 +  sInvalidCharacterSet = 'Unrecognised character set name - "%s"';
285 +  sOnLineError = 'On Line %d Character %d: ';
286  
287   implementation
288  
289   uses Sysutils, RegExpr;
290  
291   resourcestring
434  sTerminatorUnknownState = 'Statement Terminator in unexpected state (%d)';
292    sNoSelectSQL = 'Select SQL Statements are not supported';
436  sStackUnderflow = 'Stack Underflow';
293    sNoParamQueries =  'Parameterised Queries are not supported';
438  sStackOverFlow = 'Stack Overflow';
294    sResolveQueryParam =  'Resolving Query Parameter: %s';
440  sXMLStackUnderflow = 'XML Stack Underflow';
441  sInvalidEndTag = 'XML End Tag Mismatch - %s';
442  sXMLStackOverFlow = 'XML Stack Overflow';
443  sErrorState = 'Entered Error State';
444  sXMLError = 'Invalid XML (%c)';
445  sXMLAttributeError = 'Unexpected attribute - "%s" = "%s"';
446  sInvalidBoundsList = 'Invalid array bounds list - "%s"';
447  sBinaryBlockMustbeEven = 'Binary block must have an even number of characters';
448  sInvalidCharacterSet = 'Unrecognised character set name - "%s"';
449  sOnLineError = 'On Line %d Character %d: ';
450  sArrayIndexError = 'Array Index Error (%d)';
451  sBlobIndexError = 'Blob Index Error (%d)';
295    sStatementError = 'Error processing SQL statement: %s %s - for statement "%s"';
296  
297 < function StringToHex(octetString: string; MaxLineLength: integer): string; overload;
297 > { TSQLStatementReader }
298  
299 <  function ToHex(aValue: byte): string;
300 <  const
301 <    HexChars: array [0..15] of char = '0123456789ABCDEF';
302 <  begin
303 <    Result := HexChars[aValue shr 4] +
461 <               HexChars[(aValue and $0F)];
462 <  end;
299 > procedure TSQLStatementReader.EchoNextLine(aLine: string);
300 > begin
301 >  if assigned(FOnNextLine) then
302 >    OnNextLine(self,aLine);
303 > end;
304  
305 < var i, j: integer;
305 > function TSQLStatementReader.GetAttachment: IAttachment;
306   begin
307 <  i := 1;
308 <  Result := '';
468 <  if MaxLineLength = 0 then
469 <  while i <= Length(octetString) do
470 <  begin
471 <    Result += ToHex(byte(octetString[i]));
472 <    Inc(i);
473 <  end
307 >  if FDatabase <> nil then
308 >    Result := FDatabase.Attachment
309    else
310 <  while i <= Length(octetString) do
311 <  begin
312 <      for j := 1 to MaxLineLength do
310 >    Result := nil;
311 > end;
312 >
313 > function TSQLStatementReader.GetTransaction: ITransaction;
314 > begin
315 >  if FTransaction <> nil then
316 >    Result := FTransaction.TransactionIntf
317 >  else
318 >    Result := nil;
319 > end;
320 >
321 > constructor TSQLStatementReader.Create;
322 > begin
323 >  inherited Create;
324 >  Terminator := DefaultTerminator;
325 > end;
326 >
327 > function TSQLStatementReader.GetNextStatement(var stmt: string): boolean;
328 > var State: TSQLState;
329 >    Nested: integer;
330 >    token: TSQLTokens;
331 >    EndOfStatement: boolean;
332 > begin
333 >  FHasBegin := false;
334 >  EndOfStatement := false;
335 >  Nested := 0;
336 >  stmt := '';
337 >  State := stDefault;
338 >  while not EOF and not EndOfStatement do
339 >  begin
340 >    token := GetNextToken;
341 > //    writeln(token,' ',TokenText,' ',Terminator);
342 >    case State of
343 >    stDefault:
344 >      {ignore everything before a reserved word}
345 >      if (token <= high(TSQLReservedWords)) or (token = sqltIdentifier) then
346 >        begin
347 >          State := stInStmt;
348 >          stmt += TokenText;
349 >        end;
350 >
351 >    stInStmt:
352 >       begin
353 >        case token of
354 >          sqltBegin:
355 >          begin
356 >            FHasBegin := true;
357 >            State := stInBlock;
358 >            Nested := 1;
359 >            stmt += TokenText;
360 >          end;
361 >
362 >          sqltDeclare:
363 >            begin
364 >              State := stInDeclare;
365 >              stmt += TokenText;
366 >            end;
367 >
368 >          sqltOpenSquareBracket:
369 >             begin
370 >               State := stInArrayDim;
371 >               stmt += TokenText;
372 >             end;
373 >
374 >          sqltComment:
375 >            stmt += '/*' + TokenText + '*/';
376 >
377 >          sqltCommentLine:
378 >            stmt += '/*' + TokenText + ' */' + LineEnding;
379 >
380 >          sqltQuotedString:
381 >            stmt += '''' + SQLSafeString(TokenText) + '''';
382 >
383 >          sqltIdentifierInDoubleQuotes:
384 >            stmt += '"' + TokenText + '"';
385 >
386 >          sqltEOL:
387 >            stmt += LineEnding;
388 >
389 >          else
390 >            begin
391 >              if (tokentext = Terminator) and (Nested = 0) then
392 >              begin
393 >                EndOfStatement := true;
394 >                State := stDefault;
395 >              end
396 >              else
397 >                stmt += TokenText;
398 >            end;
399 >          end;
400 >        end;
401 >
402 >    {ignore begin..end blocks for Terminator detection }
403 >
404 >    stInBlock:
405        begin
406 <        if i > Length(octetString) then
407 <          Exit
406 >        case token of
407 >        sqltBegin:
408 >          begin
409 >            Inc(Nested);
410 >            stmt += TokenText;
411 >          end;
412 >
413 >        sqltEnd:
414 >          begin
415 >            Dec(Nested);
416 >            stmt += TokenText;
417 >            if Nested = 0 then
418 >            begin
419 >              State := stDefault;
420 >              EndOfStatement := true;
421 >            end;
422 >          end;
423 >
424 >        sqltCase:
425 >          {case constructs can appear within select statement in nested blocks.
426 >           We need to match the case constructs END token in order to parse the
427 >           block correctly. This is a simple parser and the only objective is
428 >           to determine the correct end of block. We therefore do not check to
429 >           ensure that the next end properly matches the case. The CASE is thus
430 >           treated the same as BEGIN. The Firebird SQL Parser will flag any errors
431 >           due to mismatched CASE/BEGIN END}
432 >          begin
433 >            Inc(Nested);
434 >            stmt += TokenText;
435 >          end;
436 >
437 >        sqltComment:
438 >          stmt += '/*' + TokenText + '*/';
439 >
440 >        sqltCommentLine:
441 >          stmt += '/* ' + TokenText + ' */' + LineEnding;
442 >
443 >        sqltQuotedString:
444 >          stmt += '''' + SQLSafeString(TokenText) + '''';
445 >
446 >        sqltIdentifierInDoubleQuotes:
447 >          stmt += '"' + TokenText + '"';
448 >
449 >        sqltEOL:
450 >          stmt += LineEnding;
451 >
452          else
453 <          Result += ToHex(byte(octetString[i]));
454 <        inc(i);
453 >          stmt += TokenText;
454 >        end;
455        end;
485      Result += LineEnding;
486  end;
487 end;
456  
457 < procedure StringToHex(octetString: string; TextOut: TStrings; MaxLineLength: integer); overload;
458 < begin
459 <    TextOut.Add(StringToHex(octetString,MaxLineLength));
457 >      {ignore array dimensions for Terminator detection }
458 >
459 >    stInArrayDim:
460 >      begin
461 >        case token of
462 >
463 >        sqltComment:
464 >          stmt += '/*' + TokenText + '*/';
465 >
466 >        sqltCommentLine:
467 >          stmt += '/* ' + TokenText + ' */' + LineEnding;
468 >
469 >        sqltCloseSquareBracket:
470 >        begin
471 >          stmt += TokenText;
472 >          State := stInStmt;
473 >        end;
474 >
475 >        sqltEOL:
476 >          stmt += LineEnding;
477 >
478 >        else
479 >          stmt += TokenText;
480 >        end;
481 >      end;
482 >
483 >    {ignore Declare statement for terminator - semi-colon terminates declaration}
484 >
485 >    stInDeclare:
486 >      begin
487 >        case token of
488 >
489 >        sqltComment:
490 >          stmt += '/*' + TokenText + '*/';
491 >
492 >        sqltCommentLine:
493 >          stmt += '/* ' + TokenText + ' */' + LineEnding;
494 >
495 >        sqltQuotedString:
496 >          stmt += '''' + SQLSafeString(TokenText) + '''';  {exists some DECLARE with cursor having SELECT ...\... rc.rdb$constraint_type = 'PRIMARY KEY');}
497 >
498 >        sqltIdentifierInDoubleQuotes:
499 >          stmt += '"' + TokenText + '"';
500 >
501 >       sqltSemiColon:
502 >          begin
503 >            State := stInStmt;
504 >            stmt += TokenText;
505 >          end;
506 >
507 >        sqltEOL:
508 >          stmt += LineEnding;
509 >
510 >        else
511 >          stmt += TokenText;
512 >        end;
513 >      end;
514 >    end;
515 > //    writeln(stmt);
516 >  end;
517 >  Result := stmt <> '';
518   end;
519  
520  
# Line 498 | Line 524 | end;
524   constructor TIBXScript.Create(aOwner: TComponent);
525   begin
526    inherited Create(aOwner);
527 <  FSymbolStream := TBatchSymbolStream.Create;
502 <  FSymbolStream.OnNextLine := @EchoNextLine;
527 >  SetSQLStatementReader(TBatchSQLStatementReader.Create);
528   end;
529  
530   function TIBXScript.PerformUpdate(SQLFile: string; aAutoDDL: boolean): boolean;
# Line 517 | Line 542 | end;
542  
543   function TIBXScript.RunScript(SQLFile: string): boolean;
544   begin
545 <  TBatchSymbolStream(FSymbolStream).SetStreamSource(SQLFile);
545 >  TBatchSQLStatementReader(FSQLReader).SetStreamSource(SQLFile);
546    Result := ProcessStream;
547   end;
548  
549   function TIBXScript.RunScript(SQLStream: TStream): boolean;
550   begin
551 <  TBatchSymbolStream(FSymbolStream).SetStreamSource(SQLStream);
551 >  TBatchSQLStatementReader(FSQLReader).SetStreamSource(SQLStream);
552    Result := ProcessStream;
553   end;
554  
555   function TIBXScript.RunScript(SQLLines: TStrings): boolean;
556   begin
557 <  TBatchSymbolStream(FSymbolStream).SetStreamSource(SQLLines);
557 >  TBatchSQLStatementReader(FSQLReader).SetStreamSource(SQLLines);
558    Result := ProcessStream;
559   end;
560  
561   function TIBXScript.ExecSQLScript(sql: string): boolean;
537 var s: TStringList;
562   begin
563 <  s := TStringList.Create;
564 <  try
541 <    s.Text := sql;
542 <    TBatchSymbolStream(FSymbolStream).SetStreamSource(s);
543 <    Result := ProcessStream;
544 <  finally
545 <    s.Free;
546 <  end;
563 >  TBatchSQLStatementReader(FSQLReader).SetStringStreamSource(sql);
564 >  Result := ProcessStream;
565   end;
566  
567   { TCustomIBXScript }
# Line 579 | Line 597 | begin
597     FISQL.SQL.Text := stmt;
598     FISQL.Transaction := GetTransaction;
599     FISQL.Transaction.Active := true;
600 <   FISQL.ParamCheck := not FIBSQLProcessor.HasBegin; {Probably PSQL}
600 > //   FISQL.ParamCheck := not FSQLReader.HasBegin; {Probably PSQL}
601     FISQL.Prepare;
602     FISQL.Statement.EnableStatistics(ShowPerformanceStats);
603  
# Line 618 | Line 636 | end;
636  
637   function TCustomIBXScript.GetOnProgressEvent: TOnProgressEvent;
638   begin
639 <  Result := FSymbolStream.OnProgressEvent;
639 >  Result := FSQLReader.OnProgressEvent;
640   end;
641  
642   function TCustomIBXScript.GetTransaction: TIBTransaction;
# Line 651 | Line 669 | begin
669   if not (csLoading in ComponentState) and (FDatabase = AValue) then Exit;
670   FDatabase := AValue;
671   FISQL.Database := AValue;
654 FIBXMLProcessor.Database := AValue;
672   FInternalTransaction.Active := false;
673   FInternalTransaction.DefaultDatabase := AValue;
674   end;
# Line 668 | Line 685 | end;
685  
686   procedure TCustomIBXScript.SetOnProgressEvent(AValue: TOnProgressEvent);
687   begin
688 <  FSymbolStream.OnProgressEvent := AValue;
688 >  FSQLReader.OnProgressEvent := AValue;
689   end;
690  
691   procedure TCustomIBXScript.SetParamValue(SQLVar: ISQLParam);
692   var BlobID: TISC_QUAD;
693      ix: integer;
694   begin
695 <  if (SQLVar.SQLType = SQL_BLOB) and (Pos(ibx_blob,SQLVar.Name) = 1) then
695 >  if (SQLVar.SQLType = SQL_BLOB) and (Pos(TSQLXMLReader.ibx_blob,SQLVar.Name) = 1) then
696    begin
697 <    ix := StrToInt(system.copy(SQLVar.Name,length(ibx_blob)+1,length(SQLVar.Name)-length(ibx_blob)));
698 <    SQLVar.AsBlob := FIBXMLProcessor.BlobData[ix].BlobIntf;
697 >    ix := StrToInt(system.copy(SQLVar.Name,length(TSQLXMLReader.ibx_blob)+1,length(SQLVar.Name)-length(TSQLXMLReader.ibx_blob)));
698 >    SQLVar.AsBlob := FSQLReader.BlobData[ix].BlobIntf;
699      Exit;
700    end
701    else
702 <  if (SQLVar.SQLType = SQL_ARRAY) and (Pos(ibx_array,SQLVar.Name) = 1) then
702 >  if (SQLVar.SQLType = SQL_ARRAY) and (Pos(TSQLXMLReader.ibx_array,SQLVar.Name) = 1) then
703    begin
704 <    ix := StrToInt(system.copy(SQLVar.Name,length(ibx_array)+1,length(SQLVar.Name)-length(ibx_array)));
705 <    SQLVar.AsArray := FIBXMLProcessor.ArrayData[ix].ArrayIntf;
704 >    ix := StrToInt(system.copy(SQLVar.Name,length(TSQLXMLReader.ibx_array)+1,length(SQLVar.Name)-length(TSQLXMLReader.ibx_array)));
705 >    SQLVar.AsArray := FSQLReader.ArrayData[ix].ArrayIntf;
706      Exit;
707    end;
708  
# Line 714 | Line 731 | function TCustomIBXScript.ProcessStream:
731   var stmt: string;
732   begin
733    Result := false;
734 <  while FIBSQLProcessor.GetNextStatement(FSymbolStream,stmt) do
734 >  FSQLReader.Database := Database;
735 >  if FTransaction = nil then
736 >    FSQLReader.Transaction := FInternalTransaction
737 >  else
738 >    FSQLReader.Transaction := FTransaction;
739 >  while FSQLReader.GetNextStatement(stmt) do
740    try
741 < //    writeln('stmt = ',stmt);
742 <    if trim(stmt) = '' then continue;
741 >    stmt := trim(stmt);
742 > //    writeln('stmt = "',stmt,'"');
743 >    if stmt = '' then continue;
744      if not ProcessStatement(stmt) then
745        ExecSQL(stmt);
746  
# Line 725 | Line 748 | begin
748        begin
749          with GetTransaction do
750            if InTransaction then Rollback;
751 <        FSymbolStream.Terminator := DefaultTerminator;
751 >        FSQLReader.Terminator := TSQLStatementReader.DefaultTerminator;
752          if assigned(OnErrorLog) then
753          begin
754 <          Add2Log(Format(sStatementError,[FSymbolStream.GetErrorPrefix,
754 >          Add2Log(Format(sStatementError,[FSQLReader.GetErrorPrefix,
755                               E.Message,stmt]),true);
756                               if StopOnFirstError then Exit;
757          end
# Line 739 | Line 762 | begin
762    Result := true;
763   end;
764  
765 + procedure TCustomIBXScript.SetSQLStatementReader(
766 +  SQLStatementReader: TSQLStatementReader);
767 + begin
768 +  FSQLReader := SQLStatementReader;
769 +  FSQLReader.OnNextLine := @EchoNextLine;
770 + end;
771 +
772   function TCustomIBXScript.ProcessStatement(stmt: string): boolean;
773   var command: string;
774  
# Line 850 | Line 880 | var  RegexObj: TRegExpr;
880       LoginPrompt: boolean;
881   begin
882    Result := false;
883 <  Terminator := FSymbolStream.Terminator;
883 >  Terminator := FSQLReader.Terminator;
884    RegexObj := TRegExpr.Create;
885    try
886      {process create database}
# Line 868 | Line 898 | begin
898          OnCreateDatabase(self,FileName);
899        stmt := 'CREATE DATABASE ''' + FileName + '''' + system.copy(stmt,RegexObj.MatchPos[3], RegexObj.MatchLen[3]);
900        UpdateUserPassword;
901 <      FDatabase.Connected := false;
901 >      if FDatabase.Connected then
902 >        FDatabase.Dropdatabase;
903        FDatabase.CreateDatabase(stmt);
873      FDatabase.Connected := false;
874      ExtractUserInfo;
875      FDatabase.Connected := true;
904        Result := true;
905        Exit;
906      end;
# Line 920 | Line 948 | begin
948      RegexObj.Expression := '^ *SET +TERM +(.) *(\' + Terminator + '|)';
949      if RegexObj.Exec(stmt) then
950      begin
951 <       FSymbolStream.Terminator := RegexObj.Match[1][1];
951 >       FSQLReader.Terminator := RegexObj.Match[1][1];
952         Result := true;
953         Exit;
954      end;
# Line 946 | Line 974 | begin
974      begin
975        command := AnsiUpperCase(RegexObj.Match[1]);
976        param := trim(RegexObj.Match[2]);
977 +      if command = 'GENERATOR' then
978 +      begin
979 +        Result := false;
980 +        Exit;
981 +      end;
982        if command = 'AUTODDL' then
983          AutoDDL := ((RegexObj.MatchLen[2] = 0) and not AutoDDL) or
984                     (RegexObj.MatchLen[2] > 0) and Toggle(param)
# Line 985 | Line 1018 | begin
1018        begin
1019          if assigned(DataOutputFormatter) then
1020            DataOutputFormatter.SetCommand(command,param,stmt,Result);
1021 <        if not Result and assigned(OnSetStatement) then
1022 <          OnSetStatement(self,command,param,stmt,Result)
1023 <        else
1024 <          raise Exception.CreateFmt(sInvalidSetStatement, [command,stmt]);
1021 >        if not Result then
1022 >        begin
1023 >          if assigned(OnSetStatement) then
1024 >            OnSetStatement(self,command,param,stmt,Result)
1025 >          else
1026 >            raise Exception.CreateFmt(sInvalidSetStatement, [command,stmt]);
1027 >        end;
1028          Exit;
1029        end;
1030        Result := true;
# Line 1004 | Line 1040 | procedure TCustomIBXScript.SetTransactio
1040   begin
1041    if FTransaction = AValue then Exit;
1042    FTransaction := AValue;
1007  FIBXMLProcessor.Transaction := AValue;
1043   end;
1044  
1045   constructor TCustomIBXScript.Create(aOwner: TComponent);
# Line 1015 | Line 1050 | begin
1050    FAutoDDL := true;
1051    FISQL := TIBSQL.Create(self);
1052    FISQL.ParamCheck := true;
1018  FIBXMLProcessor := TIBXMLProcessor.Create;
1019  FIBSQLProcessor := TIBSQLProcessor.Create(FIBXMLProcessor);
1053    FInternalTransaction := TIBTransaction.Create(self);
1054    FInternalTransaction.Params.Clear;
1055    FInternalTransaction.Params.Add('concurrency');
# Line 1025 | Line 1058 | end;
1058  
1059   destructor TCustomIBXScript.Destroy;
1060   begin
1061 <  if FIBSQLProcessor <> nil then FIBSQLProcessor.Free;
1029 <  if FIBXMLProcessor <> nil then FIBXMLProcessor.Free;
1030 <  if FSymbolStream <> nil then FSymbolStream.Free;
1061 >  if FSQLReader <> nil then FSQLReader.Free;
1062    if FISQL <> nil then FISQL.Free;
1063    if FInternalTransaction <> nil then FInternalTransaction.Free;
1064    inherited Destroy;
# Line 1038 | Line 1069 | begin
1069    if assigned(DataOutputFormatter) then
1070      DataOutputFormatter.DataOut(aSQLText,@Add2Log)
1071    else
1072 <    FSymbolStream.ShowError(sNoSelectSQL,[nil]);
1042 < end;
1043 <
1044 < { TIBSQLProcessor }
1045 <
1046 < procedure TIBSQLProcessor.AddToSQL(const Symbol: string);
1047 < begin
1048 <  FSQLText := FSQLText +  Symbol;
1049 < //  writeln('SQL = ',FSQLText);
1072 >    FSQLReader.ShowError(sNoSelectSQL);
1073   end;
1074  
1075 < procedure TIBSQLProcessor.SetState(AState: TSQLStates);
1053 < begin
1054 <  if FStackIndex > 16 then
1055 <    FSymbolStream.ShowError(sStackOverFlow,[nil]);
1056 <  FStack[FStackIndex] := FState;
1057 <  Inc(FStackIndex);
1058 <  FState := AState
1059 < end;
1060 <
1061 < function TIBSQLProcessor.PopState: TSQLStates;
1062 < begin
1063 <  if FStackIndex = 0 then
1064 <    FSymbolStream.ShowError(sStackUnderflow,[nil]);
1065 <  Dec(FStackIndex);
1066 <  Result := FStack[FStackIndex]
1067 < end;
1075 > { TInteractiveSQLStatementReader }
1076  
1077 < constructor TIBSQLProcessor.Create(XMLProcessor: TIBXMLProcessor);
1077 > function TInteractiveSQLStatementReader.GetErrorPrefix: AnsiString;
1078   begin
1079 <  inherited Create;
1072 <  FXMLProcessor := XMLProcessor;
1079 >  Result := '';
1080   end;
1081  
1082 < function TIBSQLProcessor.GetNextStatement(SymbolStream: TSymbolStream;
1076 <  var stmt: string): boolean;
1077 < var Symbol: TSQLSymbol;
1078 <    NonSpace: boolean;
1079 <    Done: boolean;
1082 > function TInteractiveSQLStatementReader.GetNextLine(var Line: string): boolean;
1083   begin
1084 <  FSQLText := '';
1085 <  FState := stInit;
1086 <  FHasBegin := false;
1087 <  FSymbolStream := SymbolStream;
1088 <  FXMLProcessor.NextStatement;
1089 <  SymbolStream.NextStatement;
1087 <
1088 <  Result := true;
1089 <  Done := false;
1090 <  NonSpace := false;
1091 <  while not Done do
1092 <  with SymbolStream do
1084 >  if FNextStatement then
1085 >    write(FPrompt)
1086 >  else
1087 >    write(FContinuePrompt);
1088 >  Result := not system.EOF;
1089 >  if Result then
1090    begin
1091 <    if FState = stError then
1092 <      ShowError(sErrorState,[nil]);
1096 <    Symbol := GetSymbol;
1097 < //    writeln('Symbol = ',Symbol,' Value = ',SymbolValue);
1098 <    if not (Symbol in [' ',sqEOL]) then
1099 <      NonSpace := true;
1100 <
1101 <    case Symbol of
1102 <    sqTag:
1103 <      begin
1104 <        if FState in [stInSQL,stNested] then
1105 <          AddToSQL(FXMLProcessor.AnalyseXML(SymbolStream));
1106 <      end;
1107 <
1108 <    sqTerminator:
1109 <        case FState of
1110 <        stInit: {ignore empty statement};
1111 <
1112 <        stInSQL:
1113 <            Done := true;
1114 <
1115 <       stNested:
1116 <         AddToSQL(Terminator);
1117 <
1118 <       stInDeclaration:
1119 <         begin
1120 <           FState := PopState;
1121 <           AddToSQL(Terminator);
1122 <         end;
1123 <
1124 <       else
1125 <         ShowError(sTerminatorUnknownState,[FState]);
1126 <       end;
1127 <
1128 <    ';':
1129 <        begin
1130 <          if FState = stInDeclaration then
1131 <            FState := PopState;
1132 <          AddToSQL(';');
1133 <        end;
1134 <
1135 <    '*':
1136 <      begin
1137 <       AddToSQL('*');
1138 <       if FState =  stInit then
1139 <          FState := stInSQL
1140 <      end;
1141 <
1142 <    '/':
1143 <      begin
1144 <       AddToSQL('/');
1145 <       if FState =  stInit then
1146 <          FState := stInSQL
1147 <      end;
1148 <
1149 <    sqComment,
1150 <    sqQuotedString,
1151 <    sqDoubleQuotedString:
1152 <      if FState <> stInit then
1153 <        AddToSQL(SymbolValue);
1154 <
1155 <    sqCommentLine:
1156 <      if FState <> stInit then
1157 <      AddToSQL(SymbolValue + LineEnding);
1158 <
1159 <    sqEnd:
1160 <      begin
1161 <        AddToSQL(SymbolValue);
1162 <        case FState of
1163 <        stNested:
1164 <          begin
1165 <            if FNested = 0 then
1166 <            begin
1167 <              FState := PopState;
1168 <              if not FInCase then
1169 <              begin
1170 <                FState := stInit;
1171 <                Done := true;
1172 <              end
1173 <              else
1174 <                FInCase := false;
1175 <            end
1176 <           else
1177 <              Dec(FNested)
1178 <          end;
1179 <          {Otherwise ignore}
1180 <        end
1181 <      end;
1182 <
1183 <    sqBegin:
1184 <      begin
1185 <        FHasBegin := true;
1186 <        AddToSQL(SymbolValue);
1187 <        case FState of
1188 <        stNested:
1189 <          Inc(FNested);
1190 <
1191 <        stInSQL,
1192 <        stInit:
1193 <          SetState(stNested);
1194 <        end
1195 <      end;
1196 <
1197 <    sqCase:
1198 <    begin
1199 <      AddToSQL(SymbolValue);
1200 <      case FState of
1201 <      stNested:
1202 <        Inc(FNested);
1203 <
1204 <      stInSQL,
1205 <      stInit:
1206 <        begin
1207 <          FInCase := true;
1208 <          SetState(stNested);
1209 <        end;
1210 <      end
1211 <    end;
1212 <
1213 <    sqDeclare:
1214 <      begin
1215 <        AddToSQL(SymbolValue);
1216 <        if FState in [stInit,stInSQL] then
1217 <          SetState(stInDeclaration)
1218 <      end;
1219 <
1220 <    sqString:
1221 <      begin
1222 <        AddToSQL(SymbolValue);
1223 <        if FState = stInit then
1224 <          FState := stInSQL
1225 <      end;
1226 <
1227 <    sqEOL:
1228 <      begin
1229 <        case FState of
1230 <        stInit:
1231 <          {Do nothing};
1232 <        else
1233 <          if NonSpace then AddToSQL(LineEnding);
1234 <        end;
1235 <      end;
1236 <
1237 <    sqEOF:
1238 <      begin
1239 <        Done := true;
1240 <        Result := trim(FSQLText) <> '';
1241 <      end
1242 <    else
1243 <    if FState <> stInit then
1244 <      AddToSQL(Symbol);
1245 <    end
1246 <  end;
1247 <  stmt := FSQLText;
1248 < //  writeln('stmt = ',stmt);
1249 < end;
1250 <
1251 < { TIBXMLProcessor }
1252 <
1253 < procedure TIBXMLProcessor.EndXMLTag(xmltag: TXMLTag);
1254 < begin
1255 <  if FXMLTagIndex = 0 then
1256 <    FSymbolStream.ShowError(sXMLStackUnderflow,[nil]);
1257 <  if xmltag <> FXMLTagStack[FXMLTagIndex] then
1258 <    FSymbolStream.ShowError(sInvalidEndTag,[FSymbolStream.SymbolValue]);
1259 <
1260 <  case FXMLTagStack[FXMLTagIndex] of
1261 <  xtBlob:
1262 <    FBlobData[FCurrentBlob].BlobIntf.Close;
1263 <
1264 <  xtArray:
1265 <    FArrayData[FCurrentArray].ArrayIntf.SaveChanges;
1266 <
1267 <  xtElt:
1268 <    Dec(FArrayData[FCurrentArray].CurrentRow);
1269 <  end;
1270 <  Dec(FXMLTagIndex);
1271 < end;
1272 <
1273 < procedure TIBXMLProcessor.EnterTag;
1274 < var aCharSetID: integer;
1275 < begin
1276 <  case FXMLTagStack[FXMLTagIndex] of
1277 <  xtBlob:
1278 <    begin
1279 <      Database.Connected := true;
1280 <      Transaction.Active := true;
1281 <      FBlobData[FCurrentBlob].BlobIntf := Database.Attachment.CreateBlob(
1282 <        Transaction.TransactionIntf,FBlobData[FCurrentBlob].SubType);
1283 <    end;
1284 <
1285 <  xtArray:
1286 <    with FArrayData[FCurrentArray] do
1287 <    begin
1288 <      Database.Connected := true;
1289 <      Transaction.Active := true;
1290 <      Database.Attachment.CharSetName2CharSetID(CharSet,aCharSetID);
1291 <      SetLength(Index,dim);
1292 <      ArrayIntf := Database.Attachment.CreateArray(
1293 <                     Transaction.TransactionIntf,
1294 <                     Database.Attachment.CreateArrayMetaData(SQLType,
1295 <                       relationName,columnName,Scale,Size,
1296 <                       aCharSetID,dim,bounds)
1297 <                     );
1298 <    end;
1091 >    readln(Line);
1092 >    EchoNextLine(Line);
1093    end;
1094   end;
1095  
1096 < function TIBXMLProcessor.GetArrayData(index: integer): TArrayData;
1096 > function TInteractiveSQLStatementReader.GetChar: AnsiChar;
1097   begin
1098 <  if (index < 0) or (index > ArrayDataCount) then
1099 <    FSymbolStream.ShowError(sArrayIndexError,[index]);
1100 <  Result := FArrayData[index];
1101 < end;
1308 <
1309 < function TIBXMLProcessor.GetArrayDataCount: integer;
1310 < begin
1311 <  Result := Length(FArrayData);
1312 < end;
1313 <
1314 < function TIBXMLProcessor.GetBlobData(index: integer): TBlobData;
1315 < begin
1316 <  if (index < 0) or (index > BlobDataCount) then
1317 <    FSymbolStream.ShowError(sBlobIndexError,[index]);
1318 <  Result := FBlobData[index];
1319 < end;
1320 <
1321 < function TIBXMLProcessor.GetBlobDataCount: integer;
1322 < begin
1323 <  Result := Length(FBlobData);
1324 < end;
1325 <
1326 < procedure TIBXMLProcessor.ProcessTagValue(tagValue: string);
1327 <
1328 <  function nibble(hex: char): byte;
1329 <  begin
1330 <    case hex of
1331 <    '0': Result := 0;
1332 <    '1': Result := 1;
1333 <    '2': Result := 2;
1334 <    '3': Result := 3;
1335 <    '4': Result := 4;
1336 <    '5': Result := 5;
1337 <    '6': Result := 6;
1338 <    '7': Result := 7;
1339 <    '8': Result := 8;
1340 <    '9': Result := 9;
1341 <    'a','A': Result := 10;
1342 <    'b','B': Result := 11;
1343 <    'c','C': Result := 12;
1344 <    'd','D': Result := 13;
1345 <    'e','E': Result := 14;
1346 <    'f','F': Result := 15;
1347 <    end;
1348 <  end;
1349 <
1350 <  procedure RemoveWhiteSpace(var hexData: string);
1351 <  var i: integer;
1098 >  if Terminated then
1099 >    Result := #0
1100 >  else
1101 >  if FLineIndex > Length(FLine) then
1102    begin
1103 <    {Remove White Space}
1104 <    i := 1;
1105 <    while i <= length(hexData) do
1106 <    begin
1107 <      case hexData[i] of
1358 <      ' ',#9,#10,#13:
1359 <        begin
1360 <          if i < Length(hexData) then
1361 <            Move(hexData[i+1],hexData[i],Length(hexData)-i);
1362 <          SetLength(hexData,Length(hexData)-1);
1363 <        end;
1364 <      else
1365 <        Inc(i);
1366 <      end;
1367 <    end;
1368 <  end;
1369 <
1370 <  procedure WriteToBlob(hexData: string);
1371 <  var i,j : integer;
1372 <      blength: integer;
1373 <      P: PChar;
1103 >    Result := LF;
1104 >    FLineIndex := 0;
1105 >  end
1106 >  else
1107 >  if FLineIndex = 0 then
1108    begin
1109 <    RemoveWhiteSpace(hexData);
1110 <    if odd(length(hexData)) then
1377 <      FSymbolStream.ShowError(sBinaryBlockMustbeEven,[nil]);
1378 <    blength := Length(hexData) div 2;
1379 <    IBAlloc(FBlobBuffer,0,blength);
1380 <    j := 1;
1381 <    P := FBlobBuffer;
1382 <    for i := 1 to blength do
1383 <    begin
1384 <      P^ := char((nibble(hexData[j]) shl 4) or nibble(hexdata[j+1]));
1385 <      Inc(j,2);
1386 <      Inc(P);
1387 <    end;
1388 <    FBlobData[FCurrentBlob].BlobIntf.Write(FBlobBuffer^,blength);
1389 <  end;
1390 <
1391 < begin
1392 <  if tagValue = '' then Exit;
1393 <  case FXMLTagStack[FXMLTagIndex] of
1394 <  xtBlob:
1395 <    WriteToBlob(tagValue);
1396 <
1397 <  xtElt:
1398 <    with FArrayData[FCurrentArray] do
1399 <      ArrayIntf.SetAsString(index,tagValue);
1400 <
1401 <  end;
1402 < end;
1403 <
1404 < procedure TIBXMLProcessor.StartXMLTag(xmltag: TXMLTag);
1405 < begin
1406 <  if FXMLTagIndex > 19 then
1407 <    FSymbolStream.ShowError(sXMLStackOverFlow,[nil]);
1408 <  Inc(FXMLTagIndex);
1409 <  FXMLTagStack[FXMLTagIndex] := xmltag;
1410 <  case xmltag of
1411 <  xtBlob:
1412 <    begin
1413 <      Inc(FCurrentBlob);
1414 <      SetLength(FBlobData,FCurrentBlob+1);
1415 <      FBlobData[FCurrentBlob].BlobIntf := nil;
1416 <      FBlobData[FCurrentBlob].SubType := 0;
1417 <    end;
1418 <
1419 <  xtArray:
1420 <    begin
1421 <      Inc(FCurrentArray);
1422 <      SetLength(FArrayData,FCurrentArray+1);
1423 <      with FArrayData[FCurrentArray] do
1424 <      begin
1425 <        ArrayIntf := nil;
1426 <        SQLType := 0;
1427 <        dim := 0;
1428 <        Size := 0;
1429 <        Scale := 0;
1430 <        CharSet := 'NONE';
1431 <        SetLength(Index,0);
1432 <        CurrentRow := -1;
1433 <      end;
1434 <    end;
1435 <
1436 <  xtElt:
1437 <    with FArrayData[FCurrentArray] do
1438 <      Inc(CurrentRow);
1439 <
1440 <  end;
1441 < end;
1442 <
1443 < procedure TIBXMLProcessor.ProcessAttributeValue(attrValue: string);
1444 < begin
1445 <  case FXMLTagStack[FXMLTagIndex] of
1446 <  xtBlob:
1447 <    if FAttributeName = 'subtype' then
1448 <      FBlobData[FCurrentBlob].SubType := StrToInt(attrValue)
1449 <    else
1450 <      FSymbolStream.ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
1451 <
1452 <  xtArray:
1453 <    if FAttributeName = 'sqltype' then
1454 <      FArrayData[FCurrentArray].SQLType := StrToInt(attrValue)
1455 <    else
1456 <    if FAttributeName = 'relation_name' then
1457 <      FArrayData[FCurrentArray].relationName := attrValue
1109 >    if not GetNextLine(FLine) then
1110 >      Result := #0
1111      else
1112 <    if FAttributeName = 'column_name' then
1113 <      FArrayData[FCurrentArray].columnName := attrValue
1112 >    if Length(FLine) = 0 then
1113 >      Result := LF
1114      else
1462    if FAttributeName = 'dim' then
1463      FArrayData[FCurrentArray].Dim := StrToInt(attrValue)
1464    else
1465    if FAttributeName = 'length' then
1466      FArrayData[FCurrentArray].Size := StrToInt(attrValue)
1467    else
1468    if FAttributeName = 'scale' then
1469      FArrayData[FCurrentArray].Scale := StrToInt(attrValue)
1470    else
1471    if FAttributeName = 'charset' then
1472      FArrayData[FCurrentArray].CharSet := attrValue
1473    else
1474    if FAttributeName = 'bounds' then
1475      ProcessBoundsList(attrValue)
1476    else
1477      FSymbolStream.ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
1478
1479  xtElt:
1480    if FAttributeName = 'ix' then
1481      with FArrayData[FCurrentArray] do
1482        Index[CurrentRow] :=  StrToInt(attrValue)
1483     else
1484        FSymbolStream.ShowError(sXMLAttributeError,[FAttributeName,attrValue]);
1485  end;
1486 end;
1487
1488 procedure TIBXMLProcessor.ProcessBoundsList(boundsList: string);
1489 var list: TStringList;
1490    i,j: integer;
1491 begin
1492  list := TStringList.Create;
1493  try
1494    list.Delimiter := ',';
1495    list.DelimitedText := boundsList;
1496    with FArrayData[FCurrentArray] do
1115      begin
1116 <      if dim <> list.Count then
1117 <        FSymbolStream.ShowError(sInvalidBoundsList,[boundsList]);
1118 <      SetLength(bounds,dim);
1119 <      for i := 0 to list.Count - 1 do
1120 <      begin
1121 <        j := Pos(':',list[i]);
1122 <        if j = 0 then
1123 <          raise Exception.CreateFmt(sInvalidBoundsList,[boundsList]);
1506 <        bounds[i].LowerBound := StrToInt(system.copy(list[i],1,j-1));
1507 <        bounds[i].UpperBound := StrToInt(system.copy(list[i],j+1,length(list[i])-j));
1508 <      end;
1509 <    end;
1510 <  finally
1511 <    list.Free;
1116 >      Result := FLine[1];
1117 >      FLineIndex := 2;
1118 >    end
1119 >  end
1120 >  else
1121 >  begin
1122 >    Result := FLine[FLineIndex];
1123 >    Inc(FLineIndex);
1124    end;
1125   end;
1126  
1127 < constructor TIBXMLProcessor.Create;
1127 > constructor TInteractiveSQLStatementReader.Create(aPrompt: string; aContinue: string);
1128   begin
1129    inherited Create;
1130 <  NextStatement;
1130 >  FPrompt := aPrompt;
1131 >  FLineIndex := 0;
1132 >  FNextStatement := true;
1133 >  FContinuePrompt := aContinue;
1134   end;
1135  
1136 < destructor TIBXMLProcessor.Destroy;
1136 > function TInteractiveSQLStatementReader.GetNextStatement(var stmt: string
1137 >  ): boolean;
1138   begin
1139 <  FreeMem(FBlobBuffer);
1140 <  inherited Destroy;
1139 >  Result := inherited GetNextStatement(stmt);
1140 >  FNextStatement := Result;
1141   end;
1142  
1143 < function TIBXMLProcessor.AnalyseXML(SymbolStream: TSymbolStream): string;
1528 < var Symbol: TSQLSymbol;
1529 <    Done: boolean;
1530 <    XMLString: string;
1531 < begin
1532 <  Result := '';
1533 <  XMLString := '';
1534 <  Done := false;
1535 <  FState := stInTag;
1536 <  FSymbolStream := SymbolStream;
1537 <  with SymbolStream do
1538 <  begin
1539 <    StartXMLTag(XMLTag);
1540 <    while not Done do
1541 <    with SymbolStream do
1542 <    begin
1543 <      Symbol := GetSymbol;
1544 <
1545 <      case Symbol of
1546 <      sqEOL:
1547 <      case FState of
1548 <      stQuotedAttributeValue,
1549 <      stTagged:
1550 <         XMLString += LineEnding;
1551 <      end;
1552 <
1553 <      ' ',sqTab:
1554 <        case FState of
1555 <        stQuotedAttributeValue,
1556 <        stTagged:
1557 <           XMLString += ' ';
1558 <        end;
1559 <
1560 <      ';':
1561 <        case FState of
1562 <        stQuotedAttributeValue,
1563 <        stTagged:
1564 <           XMLString += ';';
1565 <        else
1566 <          ShowError(sXMLError,[Symbol]);
1567 <        end;
1568 <
1569 <      '''':
1570 <        case FState of
1571 <        stQuotedAttributeValue,
1572 <        stTagged:
1573 <           XMLString += '''';
1574 <        else
1575 <          ShowError(sXMLError,[Symbol]);
1576 <        end;
1577 <
1578 <      '*':
1579 <        case FState of
1580 <        stQuotedAttributeValue,
1581 <        stTagged:
1582 <           XMLString += '*';
1583 <        else
1584 <          ShowError(sXMLError,[Symbol]);
1585 <        end;
1586 <
1587 <      '/':
1588 <        case FState of
1589 <        stQuotedAttributeValue,
1590 <        stTagged:
1591 <           XMLString += '/';
1592 <        else
1593 <          ShowError(sXMLError,[Symbol]);
1594 <        end;
1595 <
1596 <      '>':
1597 <        case FState of
1598 <        stEndTag:
1599 <            case XMLTag of
1600 <            xtBlob:
1601 <              begin
1602 <                Result := ':' + Format(ibx_blob+'%d',[FCurrentBlob]);
1603 <                Done := true;
1604 <              end;
1605 <            xtArray:
1606 <              begin
1607 <                Result := ':' + Format(ibx_array+'%d',[FCurrentArray]);
1608 <                Done := true;
1609 <              end;
1610 <            else
1611 <              FState := stTagged;
1612 <          end;
1613 <
1614 <        stInTag:
1615 <          begin
1616 <            XMLString := '';
1617 <            FState := stTagged;
1618 <            EnterTag;
1619 <          end;
1143 > { TBatchSQLStatementReader }
1144  
1145 <        stQuotedAttributeValue,
1622 <        stTagged:
1623 <          XMLString += '>';
1624 <
1625 <        else
1626 <          ShowError(sXMLError,[Symbol]);
1627 <        end;
1628 <
1629 <      sqTag:
1630 <        if FState = stTagged then
1631 <        begin
1632 <          FState := stInTag;
1633 <          StartXMLTag(XMLTag)
1634 <        end
1635 <        else
1636 <          ShowError(sXMLError,[Symbol]);
1637 <
1638 <      sqEndTag:
1639 <        if FState = stTagged then
1640 <        begin
1641 <          ProcessTagValue(XMLString);
1642 <          EndXMLTag(XMLTag);
1643 <          FState := stEndTag;
1644 <        end
1645 <        else
1646 <          ShowError(sXMLError,[Symbol]);
1647 <
1648 <      '=':
1649 <        case FState of
1650 <        stAttribute:
1651 <          FState := stAttributeValue;
1652 <
1653 <        stQuotedAttributeValue,
1654 <        stTagged:
1655 <          XMLString += '=';
1656 <
1657 <        else
1658 <          ShowError(sXMLError,[Symbol]);
1659 <        end;
1660 <
1661 <      '"':
1662 <        case FState of
1663 <        stAttributeValue:
1664 <          begin
1665 <            XMLString := '';
1666 <            FState := stQuotedAttributeValue;
1667 <          end;
1668 <
1669 <        stQuotedAttributeValue:
1670 <          begin
1671 <            ProcessAttributeValue(XMLString);
1672 <            FState := stInTag;
1673 <          end;
1674 <
1675 <        stTagged:
1676 <          XMLString += '"';
1677 <
1678 <        else
1679 <          ShowError(sXMLError,[Symbol]);
1680 <        end;
1681 <
1682 <      sqString:
1683 <        case FState of
1684 <        stInTag: {attribute name}
1685 <          begin
1686 <            FAttributeName := SymbolValue;
1687 <            FState := stAttribute;
1688 <          end;
1689 <
1690 <        stAttributeValue:
1691 <          begin
1692 <            ProcessAttributeValue(FString);
1693 <            FState := stInTag;
1694 <          end;
1695 <
1696 <        stQuotedAttributeValue,
1697 <        stTagged:
1698 <           XMLString += SymbolValue;
1699 <
1700 <        else
1701 <          ShowError(sXMLError,[Symbol]);
1702 <        end;
1703 <      else
1704 <        ShowError(sXMLError,[Symbol]);
1705 <      end
1706 <    end;
1707 <  end;
1708 < end;
1709 <
1710 < procedure TIBXMLProcessor.NextStatement;
1145 > function TBatchSQLStatementReader.GetChar: AnsiChar;
1146   begin
1147 <  FXMLTagIndex := 0;
1148 <  SetLength(FBlobData,0);
1149 <  FCurrentBlob := -1;
1150 <  SetLength(FArrayData,0);
1716 <  FCurrentArray := -1;
1717 < end;
1718 <
1719 < class function TIBXMLProcessor.FormatBlob(Field: ISQLData): string;
1720 < var TextOut: TStrings;
1721 < begin
1722 <  TextOut := TStringList.Create;
1723 <  try
1724 <    TextOut.Add(Format('<blob subtype="%d">',[Field.getSubtype]));
1725 <    StringToHex(Field.AsString,TextOut,BlobLineLength);
1726 <    TextOut.Add('</blob>');
1727 <    Result := TextOut.Text;
1728 <  finally
1729 <    TextOut.Free;
1730 <  end;
1731 < end;
1732 <
1733 < class function TIBXMLProcessor.FormatArray(Database: TIBDatabase; ar: IArray
1734 <  ): string;
1735 < var index: array of integer;
1736 <    TextOut: TStrings;
1737 <
1738 <    procedure AddElements(dim: integer; indent:string = ' ');
1739 <    var i: integer;
1740 <        recurse: boolean;
1147 >  if not EOF and assigned(FInStream) and not (FInStream.Position = FInStream.Size) then
1148 >  begin
1149 >    Result := char(FInStream.ReadByte);
1150 >    if Result = LF then
1151      begin
1152 <      SetLength(index,dim+1);
1153 <      recurse := dim < ar.GetDimensions - 1;
1154 <      with ar.GetBounds[dim] do
1155 <      for i := LowerBound to UpperBound do
1156 <      begin
1157 <        index[dim] := i;
1158 <        if recurse then
1159 <        begin
1160 <          TextOut.Add(Format('%s<elt id="%d">',[indent,i]));
1751 <          AddElements(dim+1,indent + ' ');
1752 <          TextOut.Add('</elt>');
1753 <        end
1754 <        else
1755 <        if ((ar.GetSQLType = SQL_TEXT) or (ar.GetSQLType = SQL_VARYING)) and
1756 <           (ar.GetCharSetID = 1) then
1757 <           TextOut.Add(Format('%s<elt ix="%d">%s</elt>',[indent,i,StringToHex(ar.GetAsString(index))]))
1758 <        else
1759 <          TextOut.Add(Format('%s<elt ix="%d">%s</elt>',[indent,i,ar.GetAsString(index)]));
1760 <      end;
1761 <    end;
1762 <
1763 < var
1764 <    s: string;
1765 <    bounds: TArrayBounds;
1766 <    i: integer;
1767 <    boundsList: string;
1768 < begin
1769 <  TextOut := TStringList.Create;
1770 <  try
1771 <    s := Format('<array dim = "%d" sqltype = "%d" length = "%d" relation_name = "%s" column_name = "%s"',
1772 <                                [ar.GetDimensions,ar.GetSQLType,ar.GetSize,
1773 <                                 ar.GetTableName,ar.GetColumnName]);
1774 <    case ar.GetSQLType of
1775 <    SQL_DOUBLE, SQL_FLOAT, SQL_LONG, SQL_SHORT, SQL_D_FLOAT, SQL_INT64:
1776 <       s += Format(' scale = "%d"',[ ar.GetScale]);
1777 <    SQL_TEXT,
1778 <    SQL_VARYING:
1779 <      s += Format(' charset = "%s"',[Database.Attachment.GetCharsetName(ar.GetCharSetID)]);
1780 <    end;
1781 <    bounds := ar.GetBounds;
1782 <    boundsList := '';
1783 <    for i := 0 to length(bounds) - 1 do
1152 >      EchoNextLine(FCurLine);
1153 >      FCurLine := '';
1154 >      if assigned(OnProgressEvent) then
1155 >        OnProgressEvent(self,false,FIndex+1);
1156 >      Inc(FLineIndex);
1157 >      FIndex := 1;
1158 >    end
1159 >    else
1160 >    if Result <> CR then
1161      begin
1162 <      if i <> 0 then boundsList += ',';
1163 <      boundsList += Format('%d:%d',[bounds[i].LowerBound,bounds[i].UpperBound]);
1162 >      FCurLine += Result;
1163 >      Inc(FIndex);
1164      end;
1165 <    s += Format(' bounds="%s"',[boundsList]);
1789 <    s += '>';
1790 <    TextOut.Add(s);
1791 <
1792 <    SetLength(index,0);
1793 <    AddElements(0);
1794 <    TextOut.Add('</array>');
1795 <    Result := TextOut.Text;
1796 <  finally
1797 <    TextOut.Free;
1798 <  end;
1799 < end;
1800 <
1801 < { TInteractiveSymbolStream }
1802 <
1803 < function TInteractiveSymbolStream.GetErrorPrefix: string;
1804 < begin
1805 <  Result := '';
1806 < end;
1807 <
1808 < function TInteractiveSymbolStream.GetNextLine(var Line: string): boolean;
1809 < begin
1810 <  if FNextStatement then
1811 <    write(FPrompt)
1812 <  else
1813 <    write(FContinuePrompt);
1814 <  Result := not EOF;
1815 <  if Result then
1816 <    readln(Line);
1817 < end;
1818 <
1819 < constructor TInteractiveSymbolStream.Create(aPrompt: string; aContinue: string);
1820 < begin
1821 <  inherited Create;
1822 <  FPrompt := aPrompt;
1823 <  FContinuePrompt := aContinue;
1824 < end;
1825 <
1826 < function TInteractiveSymbolStream.GetSymbol: TSQLSymbol;
1827 < begin
1828 <  if Terminated then
1829 <    Result := sqEOF
1165 >  end
1166    else
1167 <    Result := inherited GetSymbol;
1167 >    Result := #0;
1168   end;
1169  
1170 < { TBatchSymbolStream }
1835 <
1836 < function TBatchSymbolStream.GetErrorPrefix: string;
1170 > function TBatchSQLStatementReader.GetErrorPrefix: AnsiString;
1171   begin
1172    Result := Format(sOnLineError,[FLineIndex,FIndex]);
1173   end;
1174  
1175 < function TBatchSymbolStream.GetNextLine(var Line: string): boolean;
1842 < begin
1843 <  Result := FLineIndex < FLines.Count;
1844 <  if Result then
1845 <  begin
1846 <    Line := FLines[FLineIndex];
1847 < //    writeln('Next Line = ',Line);
1848 <    Inc(FLineIndex);
1849 <    if assigned(OnProgressEvent) then
1850 <      OnProgressEvent(self,false,1);
1851 <  end;
1852 < end;
1853 <
1854 < constructor TBatchSymbolStream.Create;
1855 < begin
1856 <  inherited Create;
1857 <  FLines := TStringList.Create;
1858 < end;
1859 <
1860 < destructor TBatchSymbolStream.Destroy;
1861 < begin
1862 <  if assigned(FLines) then FLines.Free;
1863 <  inherited Destroy;
1864 < end;
1865 <
1866 < procedure TBatchSymbolStream.SetStreamSource(Lines: TStrings);
1175 > procedure TBatchSQLStatementReader.Reset;
1176   begin
1177 <  FLineIndex := 0;
1178 <  FLines.Assign(Lines);
1177 >  inherited Reset;
1178 >  if FOwnsInStream and assigned(FInStream) then
1179 >    FInStream.Free;
1180 >  FInStream := nil;
1181 >  FOwnsInStream := false;
1182 >  FLineIndex := 1;
1183 >  FIndex := 1;
1184 >  FCurLine := '';
1185 > end;
1186 >
1187 > procedure TBatchSQLStatementReader.SetStreamSource(Lines: TStrings);
1188 > begin
1189 >  Reset;
1190 >  FInStream := TMemoryStream.Create;
1191 >  FOwnsInStream := true;
1192 >  Lines.SaveToStream(FInStream);
1193 >  FInStream.Position := 0;
1194    if assigned(OnProgressEvent) then
1195 <    OnProgressEvent(self,true,FLines.Count);
1195 >    OnProgressEvent(self,true,FInStream.Size);
1196   end;
1197  
1198 < procedure TBatchSymbolStream.SetStreamSource(S: TStream);
1198 > procedure TBatchSQLStatementReader.SetStreamSource(S: TStream);
1199   begin
1200 <  FLineIndex := 0;
1201 <  FLines.LoadFromStream(S);
1200 >  Reset;
1201 >  FInStream := S;
1202    if assigned(OnProgressEvent) then
1203 <    OnProgressEvent(self,true,FLines.Count);
1203 >    OnProgressEvent(self,true,S.Size - S.Position);
1204   end;
1205  
1206 < procedure TBatchSymbolStream.SetStreamSource(FileName: string);
1206 > procedure TBatchSQLStatementReader.SetStreamSource(FileName: string);
1207   begin
1208 <  FLineIndex := 0;
1209 <  FLines.LoadFromFile(FileName);
1208 >  Reset;
1209 >  FInStream := TFileStream.Create(FileName,fmShareCompat);
1210 >  FOwnsInStream := true;
1211    if assigned(OnProgressEvent) then
1212 <    OnProgressEvent(self,true,FLines.Count);
1888 < end;
1889 <
1890 < { TSymbolStream }
1891 <
1892 < function TSymbolStream.GetNextSymbol(C: char): TSQLSymbol;
1893 < begin
1894 <  Result := sqNone;
1895 <  if C = FTerminator then
1896 <    Result := sqTerminator
1897 <  else
1898 <  case C of
1899 <  #0..#8,#10..#31,' ':
1900 <    Result := ' ';
1901 <
1902 <  #9,';','"','''','/','-',
1903 <  '*','=','>','<',',':
1904 <    Result := C;
1905 <  else
1906 <    begin
1907 <      Result := sqString;
1908 <      FLastChar := C
1909 <    end
1910 <  end;
1911 < end;
1912 <
1913 < function TSymbolStream.FindTag(tag: string; var xmlTag: TXMLTag): boolean;
1914 < var i: integer;
1915 < begin
1916 <  Result := false;
1917 <  for i := 0 to Length(XMLTagDefs) - 1 do
1918 <    if XMLTagDefs[i].TagValue = tag then
1919 <    begin
1920 <      xmlTag := XMLTagDefs[i].XMLTag;
1921 <      Result := true;
1922 <      break;
1923 <    end;
1924 < end;
1925 <
1926 < constructor TSymbolStream.Create;
1927 < begin
1928 <  inherited;
1929 <  FTerminator := DefaultTerminator;
1930 <  NextStatement;
1931 < end;
1932 <
1933 < procedure TSymbolStream.ShowError(msg: string; params: array of const);
1934 < begin
1935 <  raise EIBClientError.CreateFmt(GetErrorPrefix + msg,params);
1936 < end;
1937 <
1938 < function TSymbolStream.GetSymbol: TSQLSymbol;
1939 < var
1940 <    DelimitedText: string;
1941 <    CurState: (gsNone,gsInComment,gsInSingleQuotes,gsInDoubleQuotes);
1942 < begin
1943 <  Result := sqNone;
1944 <  CurState := gsNone;
1945 <  DelimitedText := '';
1946 <  if FNextSymbol <> sqNone then
1947 <  begin
1948 <    Result := FNextSymbol;
1949 <    if Result = sqString then
1950 <      FString := FLastChar
1951 <    else
1952 <      FString := '';
1953 <    FNextSymbol := sqNone
1954 <  end;
1955 <
1956 <  while FNextSymbol = sqNone do {find the next symbol}
1957 <  begin
1958 <    if FIndex > Length(FLine) then
1959 <    begin
1960 <      FNextSymbol := sqEOL;
1961 <      FIndex := 0;
1962 <    end
1963 <    else
1964 <    begin
1965 <      if FIndex = 0 then
1966 <      begin
1967 <        if not GetNextLine(FLine) then
1968 <        begin
1969 <          Result := sqEOF;
1970 <          FNextSymbol := sqNone;
1971 <          Exit;
1972 <        end;
1973 <        FIndex := 1;
1974 <        FNextStatement := false;
1975 <        if assigned(OnNextLine) then
1976 <          OnNextLine(self,FLine);
1977 <        if CurState <> gsNone then
1978 <          DelimitedText += LineEnding;
1979 <        if Length(FLine) = 0 then
1980 <          continue;
1981 <      end;
1982 <      if CurState <> gsNone then
1983 <        DelimitedText += FLine[FIndex];
1984 <      FNextSymbol := GetNextSymbol(FLine[FIndex]);
1985 <      Inc(FIndex);
1986 <    end;
1987 <
1988 <    case CurState of
1989 <    gsNone:
1990 <      begin
1991 <        {combine if possible}
1992 <        case Result of
1993 <        sqNone:
1994 <          begin
1995 <            Result := FNextSymbol;
1996 <            if FNextSymbol = sqString then
1997 <              FString := FLastChar;
1998 <            FNextSymbol := sqNone
1999 <          end;
2000 <
2001 <        '/':
2002 <          if FXMLMode > 0 then
2003 <            break
2004 <          else
2005 <          if FNextSymbol = '*' then
2006 <          begin
2007 <            CurState := gsInComment;
2008 <            DelimitedText := '/*';
2009 <            Result := sqNone;
2010 <            FNextSymbol := sqNone
2011 <          end
2012 <          else
2013 <          if FNextSymbol = '/' then
2014 <          begin
2015 <            FString := '/*' + system.copy(FLine,FIndex,length(FLine)- FIndex + 1) + ' */';
2016 <            Result := sqCommentLine;
2017 <            FIndex := 0;
2018 <            FNextSymbol := sqNone
2019 <          end;
2020 <
2021 <        '-':
2022 <          if FXMLMode > 0 then
2023 <            break
2024 <          else
2025 <          if FNextSymbol = '-' then
2026 <          begin
2027 <            FString := '--' + system.copy(FLine,FIndex,length(FLine)- FIndex + 1) ;
2028 <            Result := sqCommentLine;
2029 <            FIndex := 0;
2030 <            FNextSymbol := sqNone
2031 <          end;
2032 <
2033 <        '<':
2034 <          if (FXMLMode > 0) and (FNextSymbol = '/') then
2035 <          begin
2036 <            Result := sqEndTag;
2037 <            FString := '';
2038 <            FNextSymbol := sqNone
2039 <          end
2040 <          else
2041 <          if FNextSymbol = sqString then
2042 <          begin
2043 <            Result := sqTag;
2044 <            FString := FLastChar;
2045 <            FNextSymbol := sqNone
2046 <          end;
2047 <
2048 <        '''':
2049 <        if FXMLMode > 0 then
2050 <          break
2051 <        else
2052 <        if FNextSymbol = '''' then
2053 <        begin
2054 <          Result := sqQuotedString;
2055 <          FString := '''''';
2056 <          FNextSymbol := sqNone
2057 <        end
2058 <        else
2059 <        begin
2060 <          CurState := gsInSingleQuotes;
2061 <          DelimitedText := '''';
2062 <          if FNextSymbol = sqEOL then
2063 <            DelimitedText += LineEnding
2064 <          else
2065 <            DelimitedText += FLine[FIndex-1];
2066 <          Result := sqNone;
2067 <          FNextSymbol := sqNone
2068 <        end;
2069 <
2070 <        '"':
2071 <        if FXMLMode > 0 then
2072 <          break
2073 <        else
2074 <        begin
2075 <          CurState := gsInDoubleQuotes;
2076 <          DelimitedText := '"';
2077 <          if FNextSymbol = sqEOL then
2078 <            DelimitedText += LineEnding
2079 <          else
2080 <            DelimitedText += FLine[FIndex-1];
2081 <          Result := sqNone;
2082 <          FNextSymbol := sqNone
2083 <        end;
2084 <
2085 <        sqTag,
2086 <        sqEndTag,
2087 <        sqString:
2088 <          if FNextSymbol = sqString then
2089 <          begin
2090 <            FString := FString + FLastChar;
2091 <            FNextSymbol := sqNone
2092 <          end;
2093 <        end
2094 <      end;
2095 <
2096 <    {Check for state exit condition}
2097 <    gsInSingleQuotes:
2098 <      if Result = '''' then
2099 <      begin
2100 <         CurState := gsNone;
2101 <         if FNextSymbol = sqEOL then
2102 <           FString := DelimitedText
2103 <         else
2104 <           FString := system.copy(DelimitedText,1,Length(DelimitedText)-1);
2105 <         Result := sqQuotedString;
2106 <       end;
2107 <
2108 <    gsInDoubleQuotes:
2109 <      if Result = '"' then
2110 <      begin
2111 <         CurState := gsNone;
2112 <         if FNextSymbol = sqEOL then
2113 <           FString := DelimitedText
2114 <         else
2115 <           FString := system.copy(DelimitedText,1,Length(DelimitedText)-1);
2116 <         Result := sqDoubleQuotedString;
2117 <       end;
2118 <
2119 <    gsInComment:
2120 <    if (Result = '*') and (FNextSymbol = '/') then
2121 <      begin
2122 <        CurState := gsNone;
2123 <        FString := DelimitedText;
2124 <        Result := sqComment;
2125 <        FNextSymbol := sqNone
2126 <      end;
2127 <
2128 <    end;
2129 <
2130 <    if (CurState <> gsNone) and (FNextSymbol <> sqNone) then
2131 <    begin
2132 <      Result := FNextSymbol;
2133 <      FNextSymbol := sqNone;
2134 <    end;
2135 <  end;
2136 <
2137 <  if (Result = sqTag) and (FNextSymbol <> sqNone) then
2138 <  begin
2139 <    if FindTag(FString,FXMLTag) then
2140 <      Inc(FXMLMode)
2141 <    else
2142 <      Result := sqString;
2143 <  end
2144 <  else
2145 <  if (Result = sqEndTag) and (FNextSymbol <> sqNone) then
2146 <  begin
2147 <    if FindTag(FString,FXMLTag) then
2148 <      Dec(FXMLMode)
2149 <    else
2150 <      Result := sqString;
2151 <  end;
2152 <
2153 <  if (FXMLMode = 0) and (Result = sqString) and (FString <> '') then
2154 <  begin
2155 <       if CompareText(FString,'begin') = 0 then
2156 <         Result := sqBegin
2157 <       else
2158 <       if CompareText(FString,'end') = 0 then
2159 <         Result := sqEnd
2160 <       else
2161 <       if CompareText(FString,'declare') = 0 then
2162 <         Result := sqDeclare
2163 <       else
2164 <       if CompareText(FString,'case') = 0 then
2165 <         Result := sqCase
2166 <  end;
2167 < //  writeln(Result,',',FString);
1212 >    OnProgressEvent(self,true,FInStream.Size);
1213   end;
1214  
1215 < procedure TSymbolStream.NextStatement;
1215 > procedure TBatchSQLStatementReader.SetStringStreamSource(S: string);
1216   begin
1217 <  FXMLTag := xtNone;
1218 <  FNextStatement := true;
1217 >  Reset;
1218 >  FInStream := TStringStream.Create(S);
1219 >  FOwnsInStream := true;
1220 >  if assigned(OnProgressEvent) then
1221 >    OnProgressEvent(self,true,FInStream.Size);
1222   end;
1223  
1224   end.

Comparing ibx/trunk/runtime/nongui/ibxscript.pas (property svn:eol-style):
Revision 229 by tony, Tue Apr 10 13:32:36 2018 UTC vs.
Revision 412 by tony, Mon Jul 17 14:08:12 2023 UTC

# Line 0 | Line 1
1 + native

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines