home *** CD-ROM | disk | FTP | other *** search
/ PC World 2000 October / PCWorld_2000-10_cd2.bin / Borland / interbase / IBConsole_src.ZIP / ibconsole / zluSQL.pas < prev    next >
Pascal/Delphi Source File  |  2000-07-24  |  42KB  |  1,455 lines

  1. {
  2.  * The contents of this file are subject to the InterBase Public License
  3.  * Version 1.0 (the "License"); you may not use this file except in
  4.  * compliance with the License.
  5.  * 
  6.  * You may obtain a copy of the License at http://www.Inprise.com/IPL.html.
  7.  * 
  8.  * Software distributed under the License is distributed on an "AS IS"
  9.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  10.  * the License for the specific language governing rights and limitations
  11.  * under the License.  The Original Code was created by Inprise
  12.  * Corporation and its predecessors.
  13.  * 
  14.  * Portions created by Inprise Corporation are Copyright (C) Inprise
  15.  * Corporation. All Rights Reserved.
  16.  * 
  17.  * Contributor(s): ______________________________________.
  18. }
  19.  
  20. unit zluSQL;
  21.  
  22. interface
  23.  
  24. uses
  25.   Classes, Windows, SysUtils, Registry, Dialogs, Forms, Messages, Controls,
  26.   IBDatabase, IBCustomDataset, IBDatabaseInfo;
  27.  
  28. type
  29.   TISQLExceptionCode = (eeInitialization, eeInvDialect, eeFOpen, eeParse,
  30.                         eeCreate, eeConnect, eeStatement,
  31.                         eeCommit, eeRollback, eeDDL, eeDML, eeQuery, eeFree,
  32.                         eeUnsupt);
  33.  
  34.   EISQLException = class(Exception)
  35.   private
  36.     FExceptionCode: TISQLExceptionCode;
  37.     FErrorCode: Integer;
  38.     FExceptionData: String;
  39.   public
  40.     constructor Create (ErrorCode: Integer;
  41.                         ExceptionData: String;
  42.                         ExceptionCode: TISQLExceptionCode;
  43.                         Msg: String);
  44.   published
  45.     property ExceptionCode: TISQLExceptionCode read FExceptionCode;
  46.     property ExceptionData: String read FExceptionData;
  47.     property ErrorCode: Integer read FErrorCode;
  48.   end;
  49.  
  50.   TISQLAction = (actInput, actOutput, actCount, actEcho, actList, actNames, actPlan,
  51.                  actStats, actTime, actUnk, actUnSup, actDialect, actAutoDDL);
  52.  
  53.   TSQLEvent = (evntDrop, evntAlter, evntConnect, evntCreate, evntTransaction,
  54.                evntUnk, evntDialect, evntRows, evntISQL);
  55.  
  56.   TSQLSubEvent = (seTable, seTrigger, seProcedure, seDomain, seFilter, seFunction,
  57.                   seException, seDatabase, seUnk, seView, seRole, seGenerator,
  58.                   seDialect1, seDialect2, seDialect3, seAutoDDL);
  59.  
  60.   TDataOutput = procedure (const Info: String) of object;
  61.  
  62.   TISQLEvent = procedure (const ISQLEvent: TSQLEvent; const ISQLSubEvent: TSQLSubEvent;
  63.                           const Data: variant; const Database: TIBDatabase) of object;
  64.  
  65.   TQueryStats = record
  66.     Plan,
  67.     Query: String;
  68.     TimePrepare,
  69.     TimeExecute: TDateTime;
  70.     Rows: Integer;
  71.     Buffers,
  72.     Reads,
  73.     Writes,
  74.     Fetches,
  75.     MaxMem,
  76.     StartMem,
  77.     EndMem: longint;
  78.   end;
  79.  
  80.   TIBSQLObj = class (TComponent)
  81.   private
  82.     FStatistics: TStringList;
  83.     FQuery: TStrings;
  84.     FDatabase: TIBDatabase;
  85.     FDataSet: TIBDataset;
  86.     FDataOutput: TDataOutput;
  87.     FISQLEvent: TISQLEvent;
  88.     FProgressEvent: TNotifyEvent;
  89.     FDefaultTransIDX,
  90.     FDDLTransIDX,
  91.     FStatements: integer;
  92.     FStatsOn,
  93.     FCanceled,
  94.     FAutoDDL: boolean;
  95.     FQueryStats: TQueryStats;
  96.     FDBInfo: TIBDatabaseInfo;
  97.  
  98.     procedure GetEvent(const Query: String; out event: TSQLEvent;
  99.       out sEvent: TSQLSubEvent);
  100.     function ParseSQL(const InputSQL: TStringList; out Data: TStringList;
  101.       const TermChar: String): boolean;
  102.     function ParseDBCreate (const InputSQL: String; out DBName: String;
  103.       out Params: String): boolean;
  104.     function ParseDBConnect (const InputSQL: String; out DBName: String;
  105.       out Params: TStringList): boolean;
  106.     function ParseClientDialect (const InputSQL: String): Integer;
  107.     function IsISQLCommand (const InputSQL: String; out Data: Variant): TISQLAction;
  108.     function ParseOnOff (const InputSQL, Item: String): Boolean;
  109.  
  110.   published
  111.     property DefaultTransIDX: integer read FDefaultTransIDX write FDefaultTransIDX;
  112.     property DDLTransIDX: integer read FDDLTransIDX write FDDLTransIDX;    
  113.     property AutoDDL: boolean read FAutoDDL write FAutoDDL;
  114.     property Query: TStrings read FQuery write FQuery;
  115.     property Database: TIBDatabase read FDatabase write FDatabase;
  116.     property Dataset: TIBDataSet read FDataset write FDataset;
  117.     property Statements: Integer read FStatements;
  118.     property Statistics: boolean read FStatsOn write FStatsOn;
  119.     property StatisticsList: TStringList read FStatistics;
  120.     property OnDataOutput: TDataOutput read FDataOutput write FDataOutput;
  121.     property OnISQLEvent: TISQLEvent read FISQLEvent write FISQLEvent;
  122.     property OnQueryProgress: TNotifyEvent read FProgressEvent write FProgressEvent;
  123.  
  124.   public
  125.     constructor Create (AComponent: TComponent); override;
  126.     destructor Destroy; override;
  127.     procedure DoISQL;
  128.     procedure DoPrepare;
  129.     procedure Cancel;
  130.   end;
  131.  
  132. implementation
  133.  
  134. uses
  135.   IBSql, stdctrls, zluGlobal, dmuMain, IBHeader, frmuMessage;
  136.  
  137. procedure TIBSQLObj.GetEvent (const Query: String; out event: TSQLEvent; out sEvent: TSQLSubEvent);
  138. var
  139.   str: String;
  140. begin
  141.   str := AnsiUpperCase(Query);
  142.   if Pos ('CREATE', Str) = 1 then
  143.   begin
  144.     event := evntCreate;
  145.     Delete (str, 1, Length('CREATE')+1);
  146.   end
  147.   else
  148.   begin
  149.     if Pos ('DROP', str) = 1 then
  150.     begin
  151.       event := evntDrop;
  152.       Delete (str, 1, Length ('DROP')+1);
  153.     end
  154.     else
  155.     begin
  156.       if Pos ('ALTER', str) = 1 then
  157.       begin
  158.         event := evntALTER;
  159.         Delete (str, 1, Length ('ALTER')+1);
  160.       end
  161.       else
  162.       begin
  163.         event := evntUnk;
  164.         sEvent := seUnk;
  165.         exit;
  166.       end;
  167.     end
  168.   end;
  169.  
  170.   if Pos ('VIEW', str) = 1 then
  171.   begin
  172.     sEvent := seView;
  173.     exit;
  174.   end;
  175.   if Pos ('TABLE', str) = 1 then
  176.   begin
  177.     sEvent := seTable;
  178.     exit;
  179.   end;
  180.   if Pos ('PROCEDURE', str) = 1 then
  181.   begin
  182.     sEvent := seProcedure;
  183.     exit;
  184.   end;
  185.   if Pos ('DOMAIN', str) = 1 then
  186.   begin
  187.     sEvent := seDomain;
  188.     exit;
  189.   end;
  190.   if Pos ('EXTERNAL', str) = 1 then
  191.   begin
  192.     sEvent := seFunction;
  193.     exit;
  194.   end;
  195.   if Pos ('FILTER', str) = 1 then
  196.   begin
  197.     sEvent := seFilter;
  198.     exit;
  199.   end;
  200.   if Pos ('EXCEPTION', str) = 1 then
  201.   begin
  202.     sEvent := seException;
  203.     exit;
  204.   end;
  205.   if Pos ('ROLE', str) = 1 then
  206.   begin
  207.     sEvent := seRole;
  208.     exit;
  209.   end;
  210.   if Pos ('GENERATOR', str) = 1 then
  211.   begin
  212.     sEvent := seGenerator;
  213.     exit;
  214.   end;
  215.   if Pos ('TRIGGER', str) = 1 then
  216.   begin
  217.     sEvent := seTrigger;
  218.     exit;
  219.   end;
  220.   sEvent := seUnk;
  221. end;
  222.  
  223.  
  224. function TIBSQLObj.ParseSQL(const InputSQL: TStringList; out Data: TStringList;
  225.   const TermChar: string): boolean;
  226.  
  227. var
  228.   lTermLen,
  229.   lDelimPos,
  230.   lLineLen,
  231.   lSrc_Cnt: Integer;
  232.  
  233.   done,
  234.   inStatement,
  235.   inComment: boolean;
  236.  
  237.   lTmp,
  238.   lLine,
  239.   lTermChar: String;
  240.  
  241.  
  242. begin
  243.   lTermChar := TermChar;
  244.   inComment := false;
  245.   lSrc_Cnt := 0;
  246.   result := true;
  247.   done := false;
  248.   while not done and (lSrc_Cnt < InputSQL.Count) do
  249.   begin
  250.     Application.ProcessMessages;
  251.     { If the line is blank, skip it }
  252.     if Length (Trim (InputSQL.Strings[lSrc_Cnt])) = 0 then
  253.     begin
  254.       Inc (lSrc_Cnt);
  255.       Continue;
  256.     end;
  257.  
  258.     { If the current line contains only a close comment, append it to the last
  259.       line }
  260.     if Trim(InputSQL.Strings[lSrc_Cnt]) = '*/' then
  261.     begin
  262.       Data.Strings[Data.Count-1] := Format('%s%s', [Data.Strings[Data.Count-1],
  263.         InputSQL.Strings[lSrc_Cnt]]);
  264.       Inc(lSrc_Cnt);
  265.       Continue;
  266.     end;
  267.  
  268.     { Next, check to see if the line is a comment or starts one.
  269.       If it does, then remove it }
  270.     lDelimPos := AnsiPos('/*', Trim(InputSQL.Strings[lSrc_Cnt]));
  271.     if  lDelimPos = 1 then
  272.     begin
  273.       { since this line contains a comment character, save anything to the
  274.         left of the comment (if applicable) }
  275.       if AnsiPos('*/', InputSQL.Strings[lSrc_Cnt]) = 0 then
  276.         inComment := true;
  277.  
  278.       { If the line above started a comment, then keep going until the
  279.         comment is completed }
  280.       while inComment do
  281.       begin
  282.         repeat
  283.           Application.ProcessMessages;
  284.           Inc (lSrc_Cnt);
  285.           if (lSrc_Cnt = InputSQL.Count) then
  286.           begin
  287.             result := false;
  288.             done := true;
  289.             continue;
  290.           end;
  291.         until (AnsiPos('*/', InputSQL.Strings[lSrc_Cnt]) <> 0);
  292.  
  293.         { if there is something after the comment, save it }
  294.         lDelimPos := AnsiPos('*/', InputSQL.Strings[lSrc_Cnt]);
  295.         lLine := Format('%s %s', [lLine, Copy (InputSQL.Strings[lSrc_cnt],
  296.           lDelimPos+2, Length(InputSQL.Strings[lSrc_Cnt]))]);
  297.         inComment := false;
  298.       end;
  299.       Inc (lSrc_Cnt);
  300.       Continue;
  301.     end;
  302.  
  303.     { Is the delimiter being changed? }
  304.     if AnsiPos ('SET TERM', AnsiUpperCase(Trim(InputSQL.Strings[lSrc_Cnt]))) = 1 then
  305.     begin
  306.       lTmp := Trim(InputSQL.Strings[lSrc_Cnt]);
  307.       Delete (lTmp, 1, Length('SET TERM'));
  308.       lTmp := Trim(lTmp);
  309.       lDelimPos := Pos (lTermChar, lTmp);
  310.       Delete (lTmp, lDelimPos, Length(lTermChar));
  311.       lTermChar := Trim(lTmp);
  312.       lTmp := '';
  313.       Inc (lSrc_Cnt);
  314.       Continue;
  315.     end;
  316.  
  317.     { If the delimiter is at the end of the line, or if a comment exists
  318.        after the delimiter, then this is an entire statement.  If there is
  319.        a comment after the delimiter, remove it }
  320.     lDelimPos := AnsiPos (lTermChar, Trim(InputSQL.Strings[lSrc_Cnt]));
  321.     lTermLen := Length (lTermChar);
  322.     lLineLen := Length (Trim(InputSQL.Strings[lSrc_Cnt]));
  323.     
  324.     if (lDelimPos = (lLineLen - (lTermLen - 1))) or
  325.        (AnsiPos ('/*', InputSQL.Strings[lSrc_Cnt]) > lDelimPos) then
  326.     begin
  327.       lLine := Trim(Format('%s %s',[lLine, InputSQL.Strings[lSrc_Cnt]]));
  328.       Delete (lLine, lDelimPos, lTermLen);
  329.  
  330.       { Make sure that the line isn't blank after removing the terminator.
  331.         Some case tools print too many termination characters }
  332.       if Length(Trim(lLine)) <> 0 then
  333.          Data.Append (Trim(lLine));
  334.       lLine := '';
  335.       Inc(lSrc_Cnt);
  336.     end
  337.     else
  338.     begin
  339.       { This statement spans multiple lines, so concatenate the lines into 1
  340.         adding a CR/LF between the lines to ensure that the source looks
  341.         as it was added }
  342.       inStatement := true;
  343.       lLine := Format('%s %s',[lLine, InputSQL.Strings[lSrc_Cnt]]);
  344.       repeat
  345.         Application.ProcessMessages;
  346.         Inc (lSrc_Cnt);
  347.         if (lSrc_Cnt = InputSQL.Count) then
  348.         begin
  349.           result := true;
  350.           done := true;
  351.           instatement := false;
  352.           continue;
  353.         end;
  354.         { Blank line }
  355.         if Length (Trim (InputSQL.Strings[lSrc_Cnt])) = 0 then
  356.           Continue;
  357.  
  358.         lDelimPos := AnsiPos (lTermChar, InputSQL.Strings[lSrc_Cnt]);
  359.         lLineLen := Length (InputSQL.Strings[lSrc_Cnt]);
  360.         if (lDelimPos = (lLineLen - (lTermLen - 1))) or
  361.            ((lDelimPos > 0) and
  362.             (AnsiPos ('/*', InputSQL.Strings[lSrc_Cnt]) > lDelimPos)) then
  363.           inStatement := false;
  364.         lLine := lLine + #13#10 + InputSQL.Strings[lSrc_Cnt];
  365.       until not inStatement;
  366.  
  367.       { Remove the termination character }
  368.       lDelimPos := AnsiPos (lTermChar, lLine);
  369.       Delete (lLine, lDelimPos, lTermLen);
  370.  
  371.       { Make sure that the line isn't blank after removing the terminator.
  372.         Some case tools print too many termination characters }
  373.       if Length(Trim(lLine)) <> 0 then
  374.         Data.Append (Trim(lLine));
  375.  
  376.       lLine := '';
  377.       Inc (lSrc_Cnt);
  378.     end;
  379.   end; { while }
  380. end;
  381.  
  382. function TIBSQLObj.ParseDBCreate (const InputSQL: String; out DBName: String; out Params: String): boolean;
  383. var
  384.   lCnt,
  385.   lDelimPos,
  386.   lStartPos: Integer;
  387.  
  388.   lLine,
  389.   lTemp,
  390.   QuoteChar: String;
  391.  
  392. begin
  393.  
  394.   lLine := Trim(InputSQL);
  395.  
  396.   (* Get the database name *)
  397.   (* A database can be created using CREATE DATABASE or CREATE SCHEMA *)
  398.   if Pos ('CREATE DATABASE', AnsiUpperCase(lLine)) = 1 then
  399.     Delete (lLine, 1, Length ('CREATE DATABASE '))
  400.   else
  401.     Delete (lLine, 1, Length ('CREATE SCHEMA '));
  402.  
  403.   QuoteChar := Copy (lLine, 1, 1);
  404.  
  405.   lStartPos := 1;
  406.   lDelimPos := 1;
  407.  
  408.   repeat
  409.     Application.ProcessMessages;
  410.     Delete (lLine, lStartPos, lDelimPos);
  411.     lDelimPos := Pos (QuoteChar, lLine);
  412.     lTemp := Copy (lLine, lStartPos, lDelimPos-1);
  413.     DBName := DBName + lTemp;
  414.   until (lLine[lDelimPos+1] in [#0, #13, #10, ' ']) or
  415.         (lDelimPos = 0);
  416.  
  417.   if lDelimPos = 0 then
  418.   begin
  419.     result := false;
  420.     exit;
  421.   end;
  422.  
  423.   (* Remove the database name from the line *)
  424.   Delete (lLine, lStartPos, lDelimPos);
  425.  
  426.   Params := ' '+lLine;
  427.  
  428.   for lCnt := 1 to Length(Params) do
  429.     if Params[lCnt] = '"' then Params[lCnt] := '''';
  430.  
  431.   result := true;
  432. end;
  433.  
  434. function TIBSQLObj.ParseDBConnect (const InputSQL: String; out DBName: String; out Params: TStringList): boolean;
  435. var
  436.   lDelimPos: Integer;
  437.  
  438.   lLine,
  439.   lTemp,
  440.   lParam,
  441.   QuoteChar: String;
  442.  
  443.   isaRole: boolean;
  444. begin
  445.  
  446.   lLine := Trim(InputSQL);
  447.   isaRole := false;
  448.   (* Get the database name *)
  449.   Delete (lLine, 1, Length ('CONNECT '));
  450.   QuoteChar := Copy (lLine, 1, 1);
  451.  
  452.   (* Connect statements don't require a quoted database name *)
  453.   if (QuoteChar = '''') or (QuoteChar = '"') then
  454.   begin
  455.     lDelimPos := 1;
  456.  
  457.     repeat
  458.       Application.ProcessMessages;
  459.       lLine := Trim(lLine);
  460.       Delete (lLine, 1, lDelimPos);
  461.       lDelimPos := Pos (QuoteChar, lLine);
  462.       lTemp := Copy (lLine, 1, lDelimPos-1);
  463.       DBName := DBName + lTemp;
  464.     until (lLine[lDelimPos+1] in [#0, #13, #10, ' ']) or
  465.           (lDelimPos = 0);
  466.  
  467.     if lDelimPos = 0 then
  468.     begin
  469.       result := false;
  470.       exit;
  471.     end;
  472.     (* Remove the database name from the line *)
  473.     Delete (lLine, 1, lDelimPos);
  474.   end
  475.   else
  476.   begin
  477.     lDelimPos := Pos (' ', lLine);
  478.  
  479.     (* If there are no spaces on the line, then the only thing is the database
  480.      * name
  481.      *)
  482.     if lDelimPos = 0 then
  483.     begin
  484.       DBName := lLine;
  485.       result := true;
  486.       exit;
  487.     end;
  488.     DBName := Copy (lLine, 1, lDelimPos-1);
  489.     Delete (lLine, 1, lDelimPos);
  490.   end;
  491.  
  492.   (* Get the parameters for connecting *)
  493.   lLine := Trim(lLine);
  494.   while Length (lLine) > 0 do
  495.   begin
  496.     Application.ProcessMessages;
  497.     lDelimPos := Pos (' ', lLine);
  498.     if lDelimPos = 0 then
  499.     begin
  500.       lTemp := Copy (lLine, 1, Length(lLine));
  501.       lParam := lParam + ' ' + lTemp;
  502.       Params.Append (lParam);
  503.       break;
  504.     end;
  505.     lTemp := Copy (lLine, 1, lDelimPos-1);
  506.  
  507.     if AnsiSameText (lTemp, 'USER') or AnsiSameText (lTemp, 'USERNAME') then
  508.       lParam := 'USER_NAME='
  509.     else
  510.     begin
  511.       if AnsiSameText (lTemp, 'PASS') or AnsiSameText (lTemp, 'PASSWORD') then
  512.         lParam := 'PASSWORD='
  513.       else
  514.       begin
  515.         if AnsiSameText (lTemp, 'ROLE') then
  516.         begin
  517.           lParam := 'SQL_ROLE_NAME=';
  518.           isaRole := true;
  519.         end
  520.         else
  521.           if AnsiSameText (lTemp, 'CACHE') then
  522.             lParam := 'NUM_BUFFERS='
  523.       end;
  524.     end;
  525.  
  526.     Delete (lLine, 1, lDelimPos);
  527.     lLine := Trim(lLine);
  528.     lDelimPos := Pos (' ', lLine);
  529.     if lDelimPos = 0 then
  530.       lDelimPos := Length(lLine)+1;
  531.  
  532.     lTemp := Copy (lLine, 1, lDelimPos-1);
  533.     QuoteChar := Copy (lTemp, 1, 1);
  534.     if (not isaRole) and
  535.        ((QuoteChar = '''') or
  536.         (QuoteChar = '"')) then
  537.     begin
  538.       { Delete the first quote }
  539.       Delete (lTemp, 1, 1);
  540.       { Delete the second }
  541.       Delete (lTemp, Pos(QuoteChar, lTemp), 1);
  542.     end;
  543.  
  544.     Delete (lLine, 1, lDelimPos);
  545.     lLine := Trim(lLine);
  546.     lParam := lParam + lTemp;
  547.     Params.Append (lParam);
  548.     Continue;
  549.   end;
  550.  
  551.   result := true;
  552. end;
  553.  
  554. function TIBSQLObj.ParseClientDialect (const InputSQL: String): Integer;
  555. var
  556.   lLine: String;
  557. begin
  558.   lLine := Trim(InputSQL);
  559.   Delete (lLine, 1, Length ('SET SQL DIALECT '));
  560.   result := StrToInt(lLine);
  561. end;
  562.  
  563. function TIBSQLObj.ParseOnOff (const InputSQL, Item: String): Boolean;
  564. var
  565.   lLine: String;
  566. begin
  567.   lLine := Trim(AnsiUpperCase(InputSQL));
  568.   Delete (lLine, 1, Length (Item+' '));
  569.  
  570.   if Pos ('ON', lLine) = 1 then
  571.     result := true
  572.   else
  573.     if Pos ('OFF', lLine) = 1 then
  574.       result := false
  575.     else
  576.       result := true;  (* default *)
  577. end;
  578.  
  579. function TIBSQLObj.IsISQLCommand (const InputSQL: String; out Data: Variant): TISQLAction;
  580. var
  581.   lLine: String;
  582.  
  583. begin
  584.  
  585.   lLine := Trim(AnsiUpperCase(InputSQL));
  586.  
  587.   if (Pos ('BLOBDUMP', lLine) = 1) or
  588.      (Pos ('EDIT', lLine) = 1) or
  589.      (Pos ('EXIT', lLine) = 1) or
  590.      (Pos ('HELP', lLine) = 1) or
  591.      (Pos ('QUIT', lLine) = 1) or
  592.      (Pos ('SHOW ',lLine) = 1) then
  593.   begin
  594.      result := actUnSup;
  595.      exit;
  596.   end;
  597.  
  598.   if (Pos ('IN ', lLine) = 1) or
  599.      (Pos ('INPUT', lLine) = 1) or
  600.      (Pos ('OUT ', lLine) = 1) or
  601.      (Pos ('OUTPUT', lLine) = 1) then
  602.   begin
  603.     if (Pos ('I', lLine) = 1) then
  604.       result := actInput
  605.     else
  606.       result := actOutput;
  607.  
  608.     (* grab the filename to read or output to *)
  609.     Data := Copy (lLine, Pos (' ', lLine), Length(lLine));
  610.     Data := Trim(Data);
  611.     exit;
  612.   end;
  613.  
  614.   if Pos('SET COUNT', lLine) = 1 then
  615.   begin
  616.     Data := ParseOnOff (lLine, 'SET COUNT');
  617.     result := actCount;
  618.     exit;
  619.   end;
  620.  
  621.   if Pos('SET ECHO', lLine) = 1 then
  622.   begin
  623.     Data := ParseOnOff (lLine, 'SET ECHO');
  624.     result := actEcho;
  625.     exit;
  626.   end;
  627.  
  628.   if Pos('SET LIST', lLine) = 1 then
  629.   begin
  630.     Data := ParseOnOff (lLine, 'SET LIST');
  631.     result := actList;
  632.     exit;
  633.   end;
  634.  
  635.   if Pos('SET NAMES', lLine) = 1 then
  636.   begin
  637.     result := actNames;
  638.  
  639.     (* Grab the character set name *)
  640.     Data := Copy (lLine, Length('SET NAMES '), Length(lLine));
  641.     Data := Trim(Data);
  642.     exit;
  643.   end;
  644.  
  645.   if Pos('SET PLAN', lLine) = 1 then
  646.   begin
  647.     Data := ParseOnOff (lLine, 'SET PLAN');
  648.     result := actPlan;
  649.     exit;
  650.   end;
  651.  
  652.   if Pos('SET STATS', lLine) = 1 then
  653.   begin
  654.     Data := ParseOnOff (lLine, 'SET STATS');
  655.     result := actStats;
  656.     exit;
  657.   end;
  658.  
  659.   if Pos('SET TIME', lLine) = 1 then
  660.   begin
  661.     Data := ParseOnOff (lLine, 'SET TIME');
  662.     result := actTime;
  663.     exit;
  664.   end;
  665.  
  666.     (* Setting the Client dialect *)
  667.   if Pos ('SET SQL DIALECT', lLine) = 1 then
  668.   begin
  669.     Data := ParseClientDialect (lLine);
  670.     Data := Trim(Data);
  671.     result := actDialect;
  672.     exit;
  673.   end;
  674.  
  675.   if (Pos ('SET AUTODDL', lLine) = 1) or
  676.      (Pos ('SET AUTO', lLine) = 1) then
  677.   begin
  678.     if Pos ('DDL', lLine) <> 0 then
  679.       Data := ParseOnOff (lLine, 'SET AUTODDL')
  680.     else
  681.       Data := ParseOnOff (lLine, 'SET AUTO');
  682.     result := actAutoDDL;
  683.     exit;
  684.   end;
  685.  
  686.   result := actUnk;
  687. end;
  688.  
  689. procedure TIBSQLObj.DoISQL;
  690. var
  691.   ISQLObj: TIBSQLObj;
  692.   retval,
  693.   lCnt: integer;
  694.  
  695.   evnt: TSQLEvent;
  696.   sEvent: TSQLSubEvent;
  697.  
  698.   Hour,
  699.   Min,
  700.   Sec,
  701.   MSec: Word;
  702.  
  703.   Tmp,
  704.   Params,
  705.   DBName: String;
  706.  
  707.   NewSource : TStringList;
  708.  
  709.   Data,
  710.   ParamList,
  711.   Source: TStringList;
  712.  
  713.   TmpTransaction,
  714.   DDLTransaction,
  715.   QryTransaction: TIBTransaction;
  716.  
  717.   TmpQuery,
  718.   DDLQuery,
  719.   IBQuery: TIBSQL;
  720.  
  721.   ISQLValue: Variant;
  722.   ISQLAction: TISQLAction;
  723.  
  724.   done,
  725.   show_Plan,
  726.   show_Stats,
  727.   show_List,
  728.   show_Count,
  729.   show_Echo,
  730.   show_time,
  731.   Output: boolean;
  732.  
  733.   ClientDialect: Integer;
  734.   CharSet: String;
  735.  
  736.   InputFile: TextFile;
  737.   TmpTime: TDateTime;
  738.  
  739.   StartSecs,
  740.   EndSecs : Comp;
  741.  
  742. begin
  743.   TmpQuery := nil;
  744.   DDLQuery := nil;
  745.   IBQuery := nil;
  746.   DDLTransaction := nil;
  747.   TmpTransaction := nil;
  748.   Data := TStringList.Create;
  749.   Source := TStringList.Create;
  750.   Source.AddStrings (FQuery);
  751.  
  752.   (* Defaults *)
  753.   ClientDialect := gAppSettings[DEFAULT_DIALECT].Setting;
  754.   CharSet := gAppSettings[CHARACTER_SET].Setting;
  755.  
  756.   if not ParseSQL (Source, Data, gAppSettings[ISQL_TERMINATOR].Setting) then
  757.     raise EISQLException.Create (ERR_ISQL_ERROR, '', eeParse, 'Unable to parse script');
  758.  
  759.   FStatements := Data.Count - 1;
  760.   Source.Free;
  761.  
  762.   try
  763.     (* If the database was already assigned and attached to,
  764.      * then create the components for the database
  765.      *)
  766.     if Assigned(Database.Handle) then
  767.     begin
  768. //      QryTransaction := Database.Transactions[FDefaultTransIDX];
  769.       DDLTransaction := Database.Transactions[FDDLTransIDX];
  770.       TmpTransaction := TIBTransaction.Create (nil);
  771.  
  772.       IBQuery := TIBSQL.Create (nil);
  773.       DDLQuery:= TIBSQL.Create (nil);
  774.       TmpQuery:= TIBSQL.Create (nil);
  775.  
  776.       TmpTransaction.DefaultDatabase := FDatabase;
  777.  
  778.       with IBQuery do
  779.       begin
  780.         Database := FDatabase;
  781.         Transaction := Database.Transactions[FDefaultTransIDX];
  782.         ParamCheck := false;
  783.       end;
  784.  
  785.       with TmpQuery do
  786.       begin
  787.         Database := FDatabase;
  788.         Transaction := TmpTransaction;
  789.         ParamCheck := false;
  790.       end;
  791.  
  792.       with DDLQuery do
  793.       begin
  794.         Database := FDatabase;
  795.         Transaction := Database.Transactions[FDDLTransIDX];
  796.         ParamCheck := false;
  797.       end;
  798.     end;
  799.   except on E: Exception do
  800.   begin
  801.     raise EISQLException.Create (ERR_ISQL_ERROR, '', eeInitialization, E.Message);
  802.   end;
  803.   end;
  804.  
  805.   (* Go through the String list excecuting the information line by line.
  806.    * Each statement is executed against the currently connected database
  807.    * and server (if there is one).
  808.    *)
  809.   for lCnt := 0 to Data.Count-1 do
  810.   begin
  811.     if FCanceled then
  812.       break;
  813.  
  814.     Application.ProcessMessages;
  815. //    dlgProgress.DoStep;
  816.  
  817.     if Assigned(FProgressEvent) then
  818.       OnQueryProgress(self);
  819.  
  820.     (* Is this an ISQL Command? *)
  821.     IsqlAction := IsISQLCommand (Data.Strings[lCnt], IsqlValue);
  822.  
  823.     case ISQlAction of
  824.       actInput:
  825.       begin
  826.         try
  827.           AssignFile(InputFile, IsqlValue);
  828.           Reset (InputFile);
  829.           NewSource := TStringList.Create;
  830.           while not SeekEof(InputFile) do
  831.           begin
  832.             Readln(InputFile, Tmp);
  833.             NewSource.Append(Tmp);
  834.           end;
  835.         except on E: Exception do
  836.           raise EISQLException.Create (ERR_FOPEN, ISQLValue, eeFopen, E.Message);
  837.         end;
  838.         ISQLObj := TIBSQLObj.Create (self);
  839.         with ISQLObj do
  840.         begin
  841.           DefaultTransIDX := FDefaultTransIDX;
  842.           DDLTransIDX := FDDLTransIDX;
  843.           AutoDDL := FAutoDDL;
  844.           Query := NewSource;
  845.           Database := Self.FDatabase;
  846.           DataSet := Self.FDataSet;
  847.           OnDataOutput := Self.FDataOutput;
  848.           OnISQLEvent := Self.OnISQLEvent;
  849.           DoIsql;
  850.           Free;
  851.           NewSource.Free;          
  852.         end;
  853.         Continue;
  854.       end;
  855.       actOutput:
  856.       begin
  857.         Output := True;
  858.       end;
  859.       actCount:
  860.       begin
  861.         show_Count := ISQLValue;
  862.         continue;
  863.       end;
  864.  
  865.       actEcho:
  866.       begin
  867.         show_Echo := ISQLValue;
  868.         continue;
  869.       end;
  870.  
  871.       actList:
  872.       begin
  873.         show_List := ISQLValue;
  874.         continue;
  875.       end;
  876.  
  877.       actNames:
  878.       begin
  879.         Charset := ISQLValue;
  880.         continue;
  881.       end;
  882.  
  883.       actPlan:
  884.       begin
  885.         show_Plan := ISQLValue;
  886.         continue;
  887.       end;
  888.  
  889.       actStats:
  890.       begin
  891.         show_Stats := ISQLValue;
  892.         continue;
  893.       end;
  894.  
  895.       actTime:
  896.       begin
  897.         show_time := ISQLValue;
  898.         continue;
  899.       end;
  900.  
  901.       actDialect:
  902.       begin
  903.         ClientDialect := ISQLValue;
  904.         if Assigned (Database) then
  905.         try
  906.           Database.SQLDialect := ClientDialect;
  907.           if Assigned (OnISQLEvent) then
  908.             case ClientDialect of
  909.               1: OnISQLEvent (evntDialect, seDialect1, ClientDialect, Database);
  910.               2: OnISQLEvent (evntDialect, seDialect2, ClientDialect, Database);
  911.               3: OnISQLEvent (evntDialect, seDialect3, ClientDialect, Database);
  912.             end;
  913.         except
  914.           on E: Exception do
  915.           begin
  916.             raise EISQLException.Create (ERR_ISQL_ERROR, IntToStr(ClientDialect),
  917.                     eeInvDialect, E.Message);
  918.           end;
  919.         end;
  920.         continue;
  921.       end;
  922.  
  923.       actAutoDDL:
  924.       begin
  925.         FAutoDDL := ISQLValue;
  926.         if Assigned (OnISQLEvent) then
  927.           OnISQLEvent (evntISQL, seAutoDDL, FAutoDDL, Database);
  928.  
  929.         continue;
  930.       end;
  931.  
  932.       actUnSup:
  933.         Continue;
  934. //        raise EISQLException.Create (ERR_ISQL_ERROR, '', eeUnsupt, Data.Strings[lCnt]);
  935.     end;
  936.     (* Does this line drop a database *)
  937.     if (Pos ('DROP DATABASE', AnsiUpperCase(Data.Strings[lCnt])) = 1) then
  938.     begin
  939.       FDatabase.DropDatabase;
  940.       if Assigned (OnISQLEvent) then
  941.         OnISQLEvent (evntDrop, seDatabase, FDatabase.DatabaseName, Database);
  942.       continue;
  943.     end;
  944.  
  945.     (* Does this line create a database *)
  946.     if (Pos ('CREATE DATABASE', AnsiUpperCase(Data.Strings[lCnt])) = 1) or
  947.        (Pos ('CREATE SCHEMA', AnsiUpperCase(Data.Strings[lCnt])) = 1) then
  948.     begin
  949.       if not ParseDBCreate (Data.Strings[lCnt], DBName, Params) then
  950.         raise EISQLException.Create (ERR_ISQL_ERROR, '', eeParse, 'An error occured parsing CREATE statement.')
  951.       else
  952.       begin
  953.         try
  954.           try
  955.             FDatabase.CheckInactive;
  956.           except
  957.             on E: Exception do
  958.             begin
  959.               if FDatabase.Transactions[FDefaultTransIDX].InTransaction then
  960.                 raise EISQLException.Create (ERR_ISQL_ERROR, DBName, eeConnect,
  961.                   E.Message+#13#10'Commit or Rollback the current transaction')
  962.               else
  963.                 FDatabase.Close;
  964.             end;
  965.           end;
  966.  
  967.           FDatabase.DatabaseName := DBName;
  968.           FDatabase.SQLDialect := ClientDialect;
  969.           FDatabase.Params.Text := Params;
  970.  
  971.           FDatabase.LoginPrompt := false;
  972.           FDatabase.CreateDatabase;
  973.  
  974.           QryTransaction := TIBTransaction.Create (nil);
  975.           DDLTransaction := TIBTransaction.Create (nil);
  976.           TmpTransaction := TIBTransaction.Create (nil);
  977.  
  978.           FDefaultTransIDX := FDatabase.AddTransaction (QryTransaction);
  979.           FDDLTransIDX := FDatabase.AddTransaction (DDLTransaction);
  980.           FDatabase.AddTransaction (TmpTransaction);
  981.  
  982.           QryTransaction.DefaultDatabase := FDatabase;
  983.           DDLTransaction.DefaultDatabase := FDatabase;
  984.           TmpTransaction.DefaultDatabase := FDatabase;
  985.  
  986.           IBQuery := TIBSQL.Create (nil);
  987.           DDLQuery:= TIBSQL.Create (nil);
  988.           TmpQuery:= TIBSQL.Create (nil);
  989.  
  990.           with IBQuery do
  991.           begin
  992.             Database := FDatabase;
  993.             Transaction := QryTransaction;
  994.             ParamCheck := false;
  995.           end;
  996.  
  997.           with TmpQuery do
  998.           begin
  999.             Database := FDatabase;
  1000.             Transaction := TmpTransaction;
  1001.             ParamCheck := false;
  1002.           end;
  1003.  
  1004.           with DDLQuery do
  1005.           begin
  1006.             Database := FDatabase;
  1007.             Transaction := DDLTransaction;
  1008.             ParamCheck := false;
  1009.           end;
  1010.  
  1011.           FDatabase.Open;
  1012.           if Assigned (OnISQLEvent) then
  1013.             OnISQLEvent (evntCreate, seDatabase, DBName, FDatabase);
  1014.         except
  1015.           on E: Exception do
  1016.           raise EISQLException.Create (ERR_ISQL_ERROR, DBName, eeCreate, E.Message);
  1017.         end;
  1018.       end;
  1019.       Continue;
  1020.     end;
  1021.  
  1022.     (* Does this line connect to a database *)
  1023.     if Pos ('CONNECT', AnsiUpperCase(Data.Strings[lCnt])) = 1 then
  1024.     begin
  1025.       ParamList := TStringList.Create;
  1026.       if not ParseDBConnect (Data.Strings[lCnt], DBName, ParamList) then
  1027.         raise EISQLException.Create (ERR_ISQL_ERROR, '', eeParse, 'An error occured parsing CONNECT statement.')
  1028.       else
  1029.       begin
  1030.         try
  1031.           try
  1032.             FDatabase.CheckInactive;
  1033.           except
  1034.             on E: Exception do
  1035.             begin
  1036.               if FDatabase.Transactions[FDefaultTransIDX].InTransaction then
  1037.                 raise EISQLException.Create (ERR_ISQL_ERROR, DBName, eeConnect,
  1038.                   E.Message+#13#10'Commit or Rollback the current transaction')
  1039.               else
  1040.                 FDatabase.Close;
  1041.             end;
  1042.           end;
  1043.           FDatabase.DatabaseName := DBName;
  1044.           if (charset <> '') and (charset <> 'NONE') then
  1045.             ParamList.Add ('lc_ctype='+charset);
  1046.           FDatabase.Params.Text := ParamList.Text;
  1047.           FDatabase.Params.Add(Format('isc_dpb_sql_dialect=%d',[ClientDialect]));
  1048.           ParamList.Free;
  1049.           FDatabase.LoginPrompt := false;
  1050.           FDatabase.SQLDialect := ClientDialect;
  1051.           FDatabase.Open;
  1052.  
  1053.           QryTransaction := TIBTransaction.Create (nil);
  1054.           DDLTransaction := TIBTransaction.Create (nil);
  1055.           TmpTransaction := TIBTransaction.Create (nil);
  1056.  
  1057.           FDefaultTransIDX := FDatabase.AddTransaction (QryTransaction);
  1058.           FDDLTransIDX := FDatabase.AddTransaction (DDLTransaction);
  1059.           FDatabase.AddTransaction (TmpTransaction);
  1060.  
  1061.           IBQuery := TIBSQL.Create (nil);
  1062.           DDLQuery:= TIBSQL.Create (nil);
  1063.           TmpQuery:= TIBSQL.Create (nil);
  1064.  
  1065.           QryTransaction.DefaultDatabase := FDatabase;
  1066.           DDLTransaction.DefaultDatabase := FDatabase;
  1067.           TmpTransaction.DefaultDatabase := FDatabase;
  1068.  
  1069.           with IBQuery do
  1070.           begin
  1071.             Database := FDatabase;
  1072.  
  1073.             if Assigned (FDataset) then
  1074.               Transaction := FDataset.Transaction
  1075.             else
  1076.               Transaction := QryTransaction;
  1077.             ParamCheck := false;
  1078.           end;
  1079.  
  1080.           with TmpQuery do
  1081.           begin
  1082.             Database := FDatabase;
  1083.             Transaction := TmpTransaction;
  1084.             ParamCheck := false;
  1085.           end;
  1086.  
  1087.           with DDLQuery do
  1088.           begin
  1089.             Database := FDatabase;
  1090.             Transaction := DDLTransaction;
  1091.             ParamCheck := false;
  1092.           end;
  1093.  
  1094.           if Assigned (OnISQLEvent) then
  1095.             OnISQLEvent (evntConnect, seDatabase, DBName, FDatabase);
  1096.         except
  1097.           on E: Exception do
  1098.             raise EISQLException.Create (ERR_ISQL_ERROR, DBName, eeConnect, E.Message);
  1099.         end;
  1100.       end;
  1101.       Continue;
  1102.     end;
  1103.  
  1104.     (* If it isn't any of the above, then execute the statement *)
  1105.     if not Assigned (IBQuery) then
  1106.       raise EISQLException.Create (ERR_ISQL_ERROR, Data.Strings[lCnt], eeStatement, 'No active connection');
  1107.  
  1108.     with IBQuery do
  1109.     begin
  1110.       Close;
  1111.       SQL.Clear;
  1112.       SQL.Add (Data.Strings[lCnt]);
  1113.       TmpQuery.SQL.Clear;
  1114.       TmpQuery.SQL.Add (Data.Strings[lCnt]);
  1115.       (* See if the statement is valid *)
  1116.       with TmpQuery do
  1117.       begin
  1118.         try
  1119.           if TmpTransaction.InTransaction then
  1120.             TmpTransaction.Commit;
  1121.           TmpTransaction.StartTransaction;
  1122.           Prepare;
  1123.           Close;
  1124.         except on E: Exception do
  1125.           begin
  1126.             TmpTransaction.Commit;
  1127.             Close;
  1128.             raise EISQLException.Create (ERR_ISQL_ERROR, Data.Strings[lCnt], eeStatement, E.Message);
  1129.           end;
  1130.         end;
  1131.         TmpTransaction.Commit;
  1132.       end;
  1133.  
  1134.       case TmpQuery.SQLType of
  1135.         SQLCommit:
  1136.         begin
  1137.           try
  1138.             if Assigned(Dataset) then
  1139.               Transaction := Dataset.Transaction;
  1140.  
  1141.             if Transaction.InTransaction then
  1142.               Transaction.Commit;
  1143.  
  1144.             if DDLTransaction.InTransaction then
  1145.               DDLTransaction.Commit;
  1146.  
  1147.             if Assigned(OnISQLEvent) then
  1148.               OnISQLEvent (evntTransaction, seUnk, false, Database);
  1149.               
  1150.           except on E: Exception do
  1151.             raise EISQLException.Create (ERR_ISQL_ERROR, Data.Strings[lCnt], eeCommit, E.Message);
  1152.           end;
  1153.         end;
  1154.  
  1155.         SQLRollback:
  1156.         begin
  1157.           try
  1158.             if Assigned(Dataset) then
  1159.               Transaction := Dataset.Transaction;
  1160.           
  1161.             if Transaction.InTransaction then
  1162.               Transaction.Rollback;
  1163.  
  1164.             if DDLTransaction.InTransaction then
  1165.               DDLTransaction.Rollback;
  1166.  
  1167.             if Assigned(OnISQLEvent) then
  1168.               OnISQLEvent (evntTransaction, seUnk, false, Database);
  1169.  
  1170.           except on E: Exception do
  1171.             raise EISQLException.Create (ERR_ISQL_ERROR, Data.Strings[lCnt], eeRollback, E.Message);
  1172.           end;
  1173.         end;
  1174.  
  1175.         SQLDDL, SQLSetGenerator:
  1176.         begin
  1177.           (* Use a different IBQuery since DDL can be set to autocommit *)
  1178.           DDLQuery.SQL.Clear;
  1179.           DDLQuery.SQL := SQL;
  1180.           with DDLQuery do begin
  1181.             try
  1182.               if not DDLTransaction.InTransaction then
  1183.                 DDLTransaction.StartTransaction;
  1184.               Prepare;
  1185.               ExecQuery;
  1186.               if FAutoDDL then
  1187.                 DDLTransaction.Commit;
  1188.  
  1189.               if Assigned (OnISQLEvent) then
  1190.               begin
  1191.                 GetEvent (DDLQuery.SQL.Strings[0], evnt, sevent);
  1192.                 OnISQLEvent (evnt, sEvent, DBName, Database);
  1193.               end;
  1194.                Close;
  1195.             except on E: Exception do
  1196.             begin
  1197.               Close;
  1198.               if FAutoDDL then
  1199.                 DDLTransaction.Rollback;
  1200.               raise EISQLException.Create (ERR_ISQL_ERROR, Data.Strings[lCnt], eeDDL, E.Message);
  1201.             end;
  1202.             end;
  1203.           end;
  1204.         end;
  1205.  
  1206.         SQLDelete, SQLInsert, SQLUpdate:
  1207.         begin
  1208.           try
  1209.             if Assigned(Dataset) then
  1210.               Transaction := Dataset.Transaction;
  1211.               
  1212.             if not Transaction.InTransaction then
  1213.               Transaction.StartTransaction;
  1214.  
  1215.             StartSecs := 0;
  1216.             if FStatsOn then
  1217.             begin
  1218.               FDBInfo.Database := FDatabase;
  1219.               FQueryStats.StartMem := FDBInfo.CurrentMemory;
  1220.               FQueryStats.Query := SQL.Text;
  1221.               StartSecs := TimeStampToMSecs(DateTimeToTimeStamp(Time));
  1222.             end;
  1223.  
  1224.             Prepare;
  1225.  
  1226.             if FStatsOn then
  1227.             begin
  1228.               EndSecs := TimeStampToMSecs(DateTimeToTimeStamp(Time));
  1229.               TmpTime := TimeStampToDateTime(MSecsToTimeStamp(EndSecs - StartSecs));
  1230.               FQueryStats.TimePrepare := TmpTime;
  1231.               StartSecs := TimeStampToMSecs(DateTimeToTimeStamp(Time));
  1232.             end;
  1233.  
  1234.             ExecQuery;
  1235.  
  1236.             FQueryStats.Plan := Plan;
  1237.             FQueryStats.Rows := IBQuery.RowsAffected;
  1238.  
  1239.             if FStatsOn then
  1240.             begin
  1241.               EndSecs := TimeStampToMSecs(DateTimeToTimeStamp(Time));
  1242.               TmpTime := TimeStampToDateTime(MSecsToTimeStamp(EndSecs - StartSecs));
  1243.               FQueryStats.TimeExecute := TmpTime;
  1244.               FQueryStats.EndMem := FDBInfo.CurrentMemory;
  1245.               FQueryStats.MaxMem := FDBInfo.MaxMemory;
  1246.               FQueryStats.Buffers := FDBInfo.NumBuffers;
  1247.               FQueryStats.Reads := FDBInfo.Reads;
  1248.               FQueryStats.Writes := FDBInfo.Writes;
  1249.             end;
  1250.  
  1251.             if Assigned (OnISQLEvent) then
  1252.               OnISQLEvent(evntRows, seUnk, RowsAffected, Database);
  1253.  
  1254.             Close;
  1255.           except on E: Exception do
  1256.           begin
  1257.             Close;
  1258.             raise EISQLException.Create (ERR_ISQL_ERROR, Data.Strings[lCnt], eeDML, E.Message);
  1259.           end;
  1260.           end;
  1261.         end;
  1262.  
  1263.         SQLSelect, SQLSelectForUpdate, SQLExecProcedure:
  1264.         begin
  1265.           try
  1266.             if Assigned (DataSet) then
  1267.             begin
  1268.               if not Transaction.InTransaction then
  1269.                 Transaction.StartTransaction;
  1270.  
  1271.               DataSet.Close;
  1272.               DataSet.SelectSQL.Text := SQL.Text;
  1273.  
  1274.               StartSecs := 0;
  1275.               if FStatsOn then
  1276.               begin
  1277.                 FDBInfo.Database := FDatabase;
  1278.                 FQueryStats.StartMem := FDBInfo.CurrentMemory;
  1279.                 FQueryStats.Query := SQL.Text;
  1280.                 StartSecs := TimeStampToMSecs(DateTimeToTimeStamp(Time));
  1281.               end;
  1282.  
  1283.               DataSet.Prepare;
  1284.  
  1285.               if FStatsOn then
  1286.               begin
  1287.                 EndSecs := TimeStampToMSecs(DateTimeToTimeStamp(Time));
  1288.                 TmpTime := TimeStampToDateTime(MSecsToTimeStamp(EndSecs - StartSecs));
  1289.                 FQueryStats.TimePrepare := TmpTime;
  1290.                 StartSecs := TimeStampToMSecs(DateTimeToTimeStamp(Time));
  1291.               end;
  1292.  
  1293.               DataSet.Open;
  1294.               DataSet.FetchAll;
  1295.               FQueryStats.Plan := Dataset.QSelect.Plan;
  1296.               FQueryStats.Rows := Dataset.RecordCount;
  1297.  
  1298.               if FStatsOn then
  1299.               begin
  1300.                 EndSecs := TimeStampToMSecs(DateTimeToTimeStamp(Time));
  1301.                 TmpTime := TimeStampToDateTime(MSecsToTimeStamp(EndSecs - StartSecs));
  1302.                 FQueryStats.TimeExecute := TmpTime;
  1303.                 FQueryStats.EndMem := FDBInfo.CurrentMemory;
  1304.                 FQueryStats.MaxMem := FDBInfo.MaxMemory;
  1305.                 FQueryStats.Buffers := FDBInfo.NumBuffers;
  1306.                 FQueryStats.Reads := FDBInfo.Reads;
  1307.                 FQueryStats.Writes := FDBInfo.Writes;
  1308.               end;
  1309.             end
  1310.             else
  1311.             begin
  1312.               if not Transaction.InTransaction then
  1313.                 Transaction.StartTransaction;
  1314.               Prepare;
  1315.               ExecQuery;
  1316.               Close;
  1317.             end;
  1318.           except
  1319.             on E: Exception do
  1320.               begin
  1321.                 Close;
  1322.                 raise EISQLException.Create (ERR_ISQL_ERROR, Data.Strings[lCnt], eeQuery, E.Message);
  1323.               end;
  1324.           end;
  1325.         end;
  1326.       end;
  1327.     end;
  1328.   end;
  1329.   
  1330.   try
  1331.     with FQueryStats do
  1332.     begin
  1333.       DecodeTime(TimeExecute, Hour, Min, Sec, MSec);
  1334.       FStatistics.Add(Format('Execution Time (hh:mm:ss.ssss)%s%.2d:%.2d:%.2d.%.4d',[DEL, Hour, Min, Sec, MSec]));
  1335.       DecodeTime(TimePrepare, Hour, Min, Sec, MSec);
  1336.       FStatistics.Add(Format('Prepare Time (hh:mm:ss.ssss)%s%.2d:%.2d:%.2d:%.4d',[DEL,Hour, Min, Sec, MSec]));
  1337.       FStatistics.Add(Format('Starting Memory%s%d',[DEL,StartMem]));
  1338.       FStatistics.Add(Format('Current Memory%s%d',[DEL,EndMem]));
  1339.       FStatistics.Add(Format('Delta Memory%s%d',[DEL,EndMem-StartMem]));
  1340.       FStatistics.Add(Format('Number of Buffers%s%d',[DEL, Buffers]));
  1341.       FStatistics.Add(Format('Reads%s%d',[DEL,Reads]));
  1342.       FStatistics.Add(Format('Writes%s%d',[DEL,Writes]));
  1343.       if Length(Plan) > 0 then
  1344.         FStatistics.Add(Format('Plan%s%s',[DEL, Plan]))
  1345.       else
  1346.         FStatistics.Add(Format('Plan%s%s',[DEL, 'Not Available']));
  1347.       FStatistics.Add(Format('Records Fetched%s%d',[DEL, Rows]));
  1348.     end;
  1349.     IBQuery.Free;
  1350.     FDatabase.RemoveTransaction (FDatabase.FindTransaction(TmpTransaction));
  1351.     TmpTransaction.Free;
  1352.   except
  1353.   on E: Exception do
  1354.     begin
  1355.       raise EISQLException.Create (ERR_ISQL_ERROR, '', eeFree, E.Message);
  1356.     end;
  1357.   end;
  1358. end;
  1359.  
  1360. procedure TIBSQLObj.Cancel;
  1361. begin
  1362.   FCanceled := true;
  1363. end;
  1364.  
  1365. constructor TIBSQLObj.Create(AComponent: TComponent);
  1366. begin
  1367.   inherited;
  1368.   FAutoDDL := true;
  1369.   FDBInfo := TIBDatabaseInfo.Create(nil);
  1370.   FStatistics := TStringList.create;
  1371. end;
  1372.  
  1373. procedure TIBSQLObj.DoPrepare;
  1374. var
  1375.   Data,
  1376.   Source: TStringList;
  1377.  
  1378.   lQry: TIBSql;
  1379.   lTrans: TIBTransaction;
  1380.  
  1381.   lCnt: integer;
  1382.  
  1383. begin
  1384.   Data := TStringList.Create;
  1385.   Source := TStringList.Create;
  1386.   Source.AddStrings (FQuery);
  1387.  
  1388.   (* Defaults *)
  1389.   if not ParseSQL (Source, Data,  gAppSettings[ISQL_TERMINATOR].Setting) then
  1390.     raise EISQLException.Create (ERR_ISQL_ERROR, '', eeParse, 'Unable to parse script');
  1391.   Source.Free;
  1392.  
  1393.   { Prepare each line and post results }
  1394.   lQry := TIBSql.Create(self);
  1395.   lTrans := TIBTransaction.Create(self);
  1396.   lTrans.DefaultDatabase := FDatabase;
  1397.   try
  1398.     with lQry do
  1399.     begin
  1400.       Transaction := lTrans;
  1401.       Database := FDatabase;
  1402.     end;
  1403.  
  1404.     for lCnt := 0 to Data.Count-1 do
  1405.     begin
  1406.       with lQry do
  1407.       begin
  1408.         SQL.Text := Data.Strings[lCnt];
  1409.         Transaction.StartTransaction;
  1410.  
  1411.         try
  1412.           Prepare;
  1413.         except on E: Exception do
  1414.           raise EISQLException.Create (ERR_ISQL_ERROR, Data.Strings[lCnt], eeStatement, E.Message);
  1415.         end;
  1416.  
  1417.         if Assigned (OnDataOutput) then
  1418.         begin
  1419.           OnDataOutput (Format('Statement: %s',[Data.Strings[lCnt]]));
  1420.           if Length(Plan) > 0 then
  1421.             OnDataOutput (Format('%s', [Plan]))
  1422.           else
  1423.             OnDataOutput ('Not Available')
  1424.         end;
  1425.  
  1426.         Transaction.Commit;
  1427.         Close;
  1428.       end;
  1429.     end;
  1430.   finally
  1431.     lQry.Free;
  1432.     lTrans.Free;
  1433.   end;
  1434. end;
  1435.  
  1436. { EISQLException }
  1437. constructor EISQLException.Create(ErrorCode: Integer;
  1438.   ExceptionData: String; ExceptionCode: TISQLExceptionCode; Msg: String);
  1439. begin
  1440.   inherited Create (Msg);
  1441.   FExceptionData := ExceptionData;
  1442.   FExceptionCode := ExceptionCode;
  1443.   FErrorCode:= ErrorCode;
  1444. end;
  1445.  
  1446. destructor TIBSQLObj.Destroy;
  1447. begin
  1448.   FDBInfo.Free;
  1449.   FStatistics.Free;
  1450.   inherited;
  1451. end;
  1452.  
  1453. end.
  1454.  
  1455.