home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / delphi / kolekce / d6 / RX275D6.ZIP / Units / Rxquery.pas < prev    next >
Pascal/Delphi Source File  |  2001-06-24  |  28KB  |  1,079 lines

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1995, 1996 AO ROSNO             }
  6. {         Copyright (c) 1997 Master-Bank                }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit RxQuery;
  11.  
  12. {$I RX.INC}
  13. {$P+,W-,R-}
  14.  
  15. interface
  16.  
  17. uses Bde, Windows, RTLConsts,  Classes, SysUtils, DB, DBTables, rxStrUtils, BdeUtils;
  18.  
  19. {.$DEFINE DEBUG}
  20.  
  21. const
  22.   DefaultMacroChar = '%';
  23.   DefaultTermChar  = '/';
  24.  
  25. { TRxQuery }
  26.  
  27. type
  28.   TQueryOpenStatus = (qsOpened, qsExecuted, qsFailed);
  29.  
  30.   TRxQuery = class(TQuery)
  31.   private
  32.     FDisconnectExpected: Boolean;
  33.     FSaveQueryChanged: TNotifyEvent;
  34.     FMacroChar: Char;
  35.     FMacros: TParams;
  36.     FSQLPattern: TStrings;
  37.     FStreamPatternChanged: Boolean;
  38.     FPatternChanged: Boolean;
  39.     FOpenStatus: TQueryOpenStatus;
  40. {$IFNDEF WIN32}
  41.     FParamCheck: Boolean;
  42. {$ENDIF}
  43.     function GetMacros: TParams;
  44.     procedure SetMacros(Value: TParams);
  45.     procedure SetSQL(Value: TStrings);
  46.     procedure PatternChanged(Sender: TObject);
  47.     procedure QueryChanged(Sender: TObject);
  48.     procedure RecreateMacros;
  49.     procedure CreateMacros(List: TParams; const Value: PChar);
  50.     procedure Expand(Query: TStrings);
  51.     function GetMacroCount: Word;
  52.     procedure SetMacroChar(Value: Char);
  53.     function GetRealSQL: TStrings;
  54. {$IFDEF DEBUG}
  55.     procedure SetRealSQL(Value: TStrings);
  56. {$ENDIF DEBUG}
  57.   protected
  58. {$IFDEF RX_D3}
  59.     procedure InternalFirst; override;
  60.     function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  61. {$ENDIF}
  62.     procedure Loaded; override;
  63.     function CreateHandle: HDBICur; override;
  64.     procedure OpenCursor {$IFDEF RX_D3} (InfoQuery: Boolean) {$ENDIF}; override;
  65.     procedure Disconnect; override;
  66. {$IFDEF RX_D5}
  67.   protected
  68.     { IProviderSupport }
  69.     procedure PSExecute; override;
  70.     function PSGetDefaultOrder: TIndexDef; override;
  71.     function PSGetTableName: string; override;
  72. {$ENDIF}
  73.   public
  74.     constructor Create(AOwner: TComponent); override;
  75.     destructor Destroy; override;
  76.     procedure ExpandMacros;
  77.     procedure ExecSQL;
  78.     procedure Prepare;
  79.     procedure OpenOrExec(ChangeLive: Boolean);
  80.     procedure ExecDirect;
  81.     function MacroByName(const Value: string): TParam;
  82. {$IFNDEF RX_D3}
  83.     function IsEmpty: Boolean;
  84. {$ENDIF RX_D3}
  85.     property MacroCount: Word read GetMacroCount;
  86.     property OpenStatus: TQueryOpenStatus read FOpenStatus;
  87. {$IFNDEF DEBUG}
  88.     property RealSQL: TStrings read GetRealSQL;
  89. {$ENDIF DEBUG}
  90.   published
  91. {$IFNDEF WIN32}
  92.     property ParamCheck: Boolean read FParamCheck write FParamCheck default True;
  93. {$ENDIF}
  94.     property MacroChar: Char read FMacroChar write SetMacroChar default DefaultMacroChar;
  95.     property SQL: TStrings read FSQLPattern write SetSQL;
  96. {$IFDEF DEBUG}
  97.     property RealSQL: TStrings read GetRealSQL write SetRealSQL stored False;
  98. {$ENDIF DEBUG}
  99.     property Macros: TParams read GetMacros write SetMacros;
  100.   end;
  101.  
  102. {$IFDEF WIN32}
  103.  
  104. { TRxQueryThread }
  105.  
  106.   TRunQueryMode = (rqOpen, rqExecute, rqExecDirect, rqOpenOrExec);
  107.  
  108.   TRxQueryThread = class(TThread)
  109.   private
  110.     FData: TBDEDataSet;
  111.     FMode: TRunQueryMode;
  112.     FPrepare: Boolean;
  113.     FException: TObject;
  114.     procedure DoHandleException;
  115.   protected
  116.     procedure ModeError; virtual;
  117.     procedure DoTerminate; override;
  118.     procedure Execute; override;
  119.     procedure HandleException; virtual;
  120.   public
  121.     constructor Create(Data: TBDEDataSet; RunMode: TRunQueryMode;
  122.       Prepare, CreateSuspended: Boolean);
  123.   end;
  124.  
  125. {$ENDIF WIN32}
  126.  
  127. { TSQLScript }
  128.  
  129.   TScriptAction = (saFail, saAbort, saRetry, saIgnore, saContinue);
  130.  
  131.   TScriptErrorEvent = procedure(Sender: TObject; E: EDatabaseError;
  132.     LineNo, StatementNo: Integer; var Action: TScriptAction) of object;
  133.  
  134.   TSQLScript = class(TComponent)
  135.   private
  136.     FSQL: TStrings;
  137.     FParams: TParams;
  138.     FQuery: TRxQuery;
  139.     FTransaction: Boolean;
  140.     FSemicolonTerm: Boolean;
  141.     FIgnoreParams: Boolean;
  142.     FTerm: Char;
  143.     FBeforeExec: TNotifyEvent;
  144.     FAfterExec: TNotifyEvent;
  145.     FOnScriptError: TScriptErrorEvent;
  146. {$IFDEF WIN32}
  147.     function GetSessionName: string;
  148.     procedure SetSessionName(const Value: string);
  149.     function GetDBSession: TSession;
  150.     function GetText: string;
  151. {$ENDIF WIN32}
  152. {$IFDEF RX_D4}
  153.     procedure ReadParamData(Reader: TReader);
  154.     procedure WriteParamData(Writer: TWriter);
  155. {$ENDIF RX_D4}
  156.     function GetDatabase: TDatabase;
  157.     function GetDatabaseName: string;
  158.     procedure SetDatabaseName(const Value: string);
  159.     procedure CreateParams(List: TParams; const Value: PChar);
  160.     procedure QueryChanged(Sender: TObject);
  161.     procedure SetQuery(Value: TStrings);
  162.     procedure SetParamsList(Value: TParams);
  163.     function GetParamsCount: Cardinal;
  164.   protected
  165. {$IFDEF RX_D4}
  166.     procedure DefineProperties(Filer: TFiler); override;
  167. {$ENDIF RX_D4}
  168.     procedure CheckExecQuery(LineNo, StatementNo: Integer);
  169.     procedure ExecuteScript(StatementNo: Integer); virtual;
  170.   public
  171.     constructor Create(AOwner: TComponent); override;
  172.     destructor Destroy; override;
  173.     procedure ExecSQL;
  174.     procedure ExecStatement(StatementNo: Integer);
  175.     function ParamByName(const Value: string): TParam;
  176. {$IFDEF WIN32}
  177.     property DBSession: TSession read GetDBSession;
  178.     property Text: string read GetText;
  179. {$ELSE}
  180.     function GetText: PChar;
  181. {$ENDIF WIN32}
  182.     property Database: TDatabase read GetDatabase;
  183.     property ParamCount: Cardinal read GetParamsCount;
  184.   published
  185.     property DatabaseName: string read GetDatabaseName write SetDatabaseName;
  186.     property IgnoreParams: Boolean read FIgnoreParams write FIgnoreParams default False;
  187.     property SemicolonTerm: Boolean read FSemicolonTerm write FSemicolonTerm default True;
  188. {$IFDEF WIN32}
  189.     property SessionName: string read GetSessionName write SetSessionName;
  190. {$ENDIF WIN32}
  191.     property Term: Char read FTerm write FTerm default DefaultTermChar;
  192.     property SQL: TStrings read FSQL write SetQuery;
  193.     property Params: TParams read FParams write SetParamsList {$IFDEF RX_D4} stored False {$ENDIF};
  194.     property Transaction: Boolean read FTransaction write FTransaction;
  195.     property BeforeExec: TNotifyEvent read FBeforeExec write FBeforeExec;
  196.     property AfterExec: TNotifyEvent read FAfterExec write FAfterExec;
  197.     property OnScriptError: TScriptErrorEvent read FOnScriptError write FOnScriptError;
  198.   end;
  199.  
  200. const
  201.   dbfExecScript = dbfTable;
  202.  
  203. procedure CreateQueryParams(List: TParams; const Value: PChar; Macro: Boolean;
  204.   SpecialChar: Char; Delims: TCharSet);
  205.  
  206. implementation
  207.  
  208. uses DBUtils, Consts, DBConsts, Forms {$IFDEF RX_D3}, BDEConst {$ENDIF}
  209.   {$IFNDEF WIN32}, Str16 {$ENDIF}, VclUtils;
  210.  
  211. { Parse SQL utility routines }
  212.  
  213. function NameDelimiter(C: Char; Delims: TCharSet): Boolean;
  214. begin
  215.   Result := (C in [' ', ',', ';', ')', #13, #10]) or (C in Delims);
  216. end;
  217.  
  218. function IsLiteral(C: Char): Boolean;
  219. begin
  220.   Result := C in ['''', '"'];
  221. end;
  222.  
  223. procedure CreateQueryParams(List: TParams; const Value: PChar; Macro: Boolean;
  224.   SpecialChar: Char; Delims: TCharSet);
  225. var
  226.   CurPos, StartPos: PChar;
  227.   CurChar: Char;
  228.   Literal: Boolean;
  229.   EmbeddedLiteral: Boolean;
  230.   Name: string;
  231.  
  232.   function StripLiterals(Buffer: PChar): string;
  233.   var
  234.     Len: Word;
  235.     TempBuf: PChar;
  236.  
  237.     procedure StripChar(Value: Char);
  238.     begin
  239.       if TempBuf^ = Value then
  240.         StrMove(TempBuf, TempBuf + 1, Len - 1);
  241.       if TempBuf[StrLen(TempBuf) - 1] = Value then
  242.         TempBuf[StrLen(TempBuf) - 1] := #0;
  243.     end;
  244.  
  245.   begin
  246.     Len := StrLen(Buffer) + 1;
  247.     TempBuf := AllocMem(Len);
  248.     Result := '';
  249.     try
  250.       StrCopy(TempBuf, Buffer);
  251.       StripChar('''');
  252.       StripChar('"');
  253.       Result := StrPas(TempBuf);
  254.     finally
  255.       FreeMem(TempBuf, Len);
  256.     end;
  257.   end;
  258.  
  259. begin
  260.   if SpecialChar = #0 then Exit;
  261.   CurPos := Value;
  262.   Literal := False;
  263.   EmbeddedLiteral := False;
  264.   repeat
  265.     CurChar := CurPos^;
  266.     if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ <> SpecialChar) then
  267.     begin
  268.       StartPos := CurPos;
  269.       while (CurChar <> #0) and (Literal or not NameDelimiter(CurChar, Delims)) do begin
  270.         Inc(CurPos);
  271.         CurChar := CurPos^;
  272.         if IsLiteral(CurChar) then begin
  273.           Literal := Literal xor True;
  274.           if CurPos = StartPos + 1 then EmbeddedLiteral := True;
  275.         end;
  276.       end;
  277.       CurPos^ := #0;
  278.       if EmbeddedLiteral then begin
  279.         Name := StripLiterals(StartPos + 1);
  280.         EmbeddedLiteral := False;
  281.       end
  282.       else Name := StrPas(StartPos + 1);
  283.       if Assigned(List) then begin
  284. {$IFDEF RX_D4}
  285.         if List.FindParam(Name) = nil then begin
  286. {$ENDIF RX_D4}
  287.           if Macro then
  288.             List.CreateParam(ftString, Name, ptInput).AsString := TrueExpr
  289.           else List.CreateParam(ftUnknown, Name, ptUnknown);
  290. {$IFDEF RX_D4}
  291.         end;
  292. {$ENDIF RX_D4}
  293.       end;
  294.       CurPos^ := CurChar;
  295.       StartPos^ := '?';
  296.       Inc(StartPos);
  297.       StrMove(StartPos, CurPos, StrLen(CurPos) + 1);
  298.       CurPos := StartPos;
  299.     end
  300.     else if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ = SpecialChar) then
  301.       StrMove(CurPos, CurPos + 1, StrLen(CurPos) + 1)
  302.     else if IsLiteral(CurChar) then Literal := Literal xor True;
  303.     Inc(CurPos);
  304.   until CurChar = #0;
  305. end;
  306.  
  307. { TRxQuery }
  308.  
  309. constructor TRxQuery.Create(AOwner: TComponent);
  310. begin
  311.   inherited Create(AOwner);
  312. {$IFNDEF WIN32}
  313.   FParamCheck := True;
  314. {$ENDIF WIN32}
  315.   FOpenStatus := qsFailed;
  316.   FSaveQueryChanged := TStringList(inherited SQL).OnChange;
  317.   TStringList(inherited SQL).OnChange := QueryChanged;
  318.   FMacroChar := DefaultMacroChar;
  319.   FSQLPattern := TStringList.Create;
  320.   TStringList(SQL).OnChange := PatternChanged;
  321.   FMacros := TParams.Create{$IFDEF RX_D4}(Self){$ENDIF};
  322. end;
  323.  
  324. destructor TRxQuery.Destroy;
  325. begin
  326.   Destroying;
  327.   Disconnect;
  328.   FMacros.Free;
  329.   FSQLPattern.Free;
  330.   inherited Destroy;
  331. end;
  332.  
  333. procedure TRxQuery.Loaded;
  334. begin
  335.   inherited Loaded;
  336.   GetMacros; {!! trying this way}
  337. end;
  338.  
  339. {$IFDEF RX_D3}
  340.  
  341. procedure TRxQuery.InternalFirst;
  342. begin
  343.   if not (UniDirectional and BOF) then
  344.     inherited InternalFirst;
  345. end;
  346.  
  347. function TRxQuery.GetRecord(Buffer: PChar; GetMode: TGetMode;
  348.   DoCheck: Boolean): TGetResult;
  349. begin
  350.   //!!!!!!
  351.   if UniDirectional and (GetMode in [gmPrior, gmNext]) then DoCheck := False;
  352.   Result := inherited GetRecord(Buffer, GetMode, DoCheck);
  353. end;
  354.  
  355. {$ENDIF}
  356.  
  357. function TRxQuery.CreateHandle: HDBICur;
  358. begin
  359.   FOpenStatus := qsFailed;
  360.   Result := inherited CreateHandle;
  361.   if Result = nil then FOpenStatus := qsExecuted
  362.   else FOpenStatus := qsOpened;
  363. end;
  364.  
  365. procedure TRxQuery.OpenCursor;
  366. begin
  367.   ExpandMacros;
  368.   inherited OpenCursor{$IFDEF RX_D3}(InfoQuery){$ENDIF};
  369. end;
  370.  
  371. procedure TRxQuery.ExecSQL;
  372. begin
  373.   ExpandMacros;
  374.   inherited ExecSQL;
  375. end;
  376.  
  377. procedure TRxQuery.Prepare;
  378. begin
  379.   ExpandMacros;
  380.   inherited Prepare;
  381. end;
  382.  
  383. procedure TRxQuery.OpenOrExec(ChangeLive: Boolean);
  384.  
  385.   procedure TryOpen;
  386.   begin
  387.     try
  388.       Open;
  389.     except
  390.       if OpenStatus <> qsExecuted then raise;
  391.     end;
  392.   end;
  393.  
  394. begin
  395.   try
  396.     TryOpen;
  397.   except
  398.     on E: EDatabaseError do
  399.       if RequestLive and ChangeLive then begin
  400.         RequestLive := False;
  401.         try
  402.           TryOpen;
  403.         except
  404.           on E: EDatabaseError do
  405.             if OpenStatus <> qsOpened then
  406.               ExecDirect
  407.             else begin
  408.               FOpenStatus := qsFailed;
  409.               raise;
  410.             end;
  411.           else raise;
  412.         end;
  413.       end
  414.       else begin
  415.         if OpenStatus <> qsOpened then
  416.           ExecDirect
  417.         else begin
  418.           FOpenStatus := qsFailed;
  419.           raise;
  420.         end;
  421.       end;
  422.     else raise;
  423.   end;
  424. end;
  425.  
  426. procedure TRxQuery.ExecDirect;
  427. {$IFNDEF WIN32}
  428. var
  429.   P: PChar;
  430. {$ENDIF}
  431. begin
  432.   CheckInactive;
  433.   SetDBFlag(dbfExecSQL, True);
  434.   try
  435.     if SQL.Count > 0 then begin
  436.       FOpenStatus := qsFailed;
  437. {$IFDEF WIN32}
  438.       Check(DbiQExecDirect(DBHandle, qryLangSQL, PChar(inherited SQL.Text),
  439.         nil));
  440. {$ELSE}
  441.       P := inherited SQL.GetText;
  442.       try
  443.         Check(DbiQExecDirect(DBHandle, qryLangSQL, P, nil));
  444.       finally
  445.         StrDispose(P);
  446.       end;
  447. {$ENDIF WIN32}
  448.       FOpenStatus := qsExecuted;
  449.     end
  450.     else _DBError(SEmptySQLStatement);
  451.   finally
  452.     SetDBFlag(dbfExecSQL, False);
  453.   end;
  454. end;
  455.  
  456. procedure TRxQuery.Disconnect;
  457. var
  458.   Strings: TStrings;
  459.   Event1, Event2: TNotifyEvent;
  460. begin
  461.   inherited Disconnect;
  462.   if (csDestroying in ComponentState) then Exit;
  463.   Strings := inherited SQL;
  464.   Event1 := TStringList(Strings).OnChange;
  465.   Event2 := QueryChanged;
  466.   if @Event1 <> @Event2 then begin
  467.     if not FDisconnectExpected then SQL := inherited SQL;
  468.     TStringList(inherited SQL).OnChange := QueryChanged;
  469.   end;
  470. end;
  471.  
  472. procedure TRxQuery.SetMacroChar(Value: Char);
  473. begin
  474.   if Value <> FMacroChar then begin
  475.     FMacroChar := Value;
  476.     RecreateMacros;
  477.   end;
  478. end;
  479.  
  480. function TRxQuery.GetMacros: TParams;
  481. begin
  482.   if FStreamPatternChanged then begin
  483.     FStreamPatternChanged := False;
  484.     PatternChanged(nil);
  485.   end;
  486.   Result := FMacros;
  487. end;
  488.  
  489. procedure TRxQuery.SetMacros(Value: TParams);
  490. begin
  491.   FMacros.AssignValues(Value);
  492. end;
  493.  
  494. procedure TRxQuery.SetSQL(Value: TStrings);
  495. begin
  496.   inherited Disconnect;
  497.   TStringList(FSQLPattern).OnChange := nil;
  498.   FSQLPattern.Assign(Value);
  499.   TStringList(FSQLPattern).OnChange := PatternChanged;
  500.   PatternChanged(nil);
  501. end;
  502.  
  503. procedure TRxQuery.PatternChanged(Sender: TObject);
  504. begin
  505.   if (csLoading in ComponentState) then begin
  506.     FStreamPatternChanged := True;
  507.     Exit;
  508.   end;
  509.   inherited Disconnect;
  510.   RecreateMacros;
  511.   FPatternChanged := True;
  512.   try
  513.     ExpandMacros;
  514.   finally
  515.     FPatternChanged := False;
  516.   end;
  517. end;
  518.  
  519. procedure TRxQuery.QueryChanged(Sender: TObject);
  520. {$IFNDEF WIN32}
  521. var
  522.   List: TParams;
  523.   SaveParams: Boolean;
  524. {$ENDIF}
  525. begin
  526. {$IFDEF WIN32}
  527.   FSaveQueryChanged(Sender);
  528. {$ELSE}
  529.   SaveParams := not (ParamCheck or (csDesigning in ComponentState));
  530.   if SaveParams then List := TParams.Create{$IFDEF RX_D4}(Self){$ENDIF};
  531.   try
  532.     if SaveParams then List.Assign(Params);
  533.     FSaveQueryChanged(Sender);
  534.     if SaveParams then Params.Assign(List);
  535.   finally
  536.     if SaveParams then List.Free;
  537.   end;
  538. {$ENDIF WIN32}
  539.   if not FDisconnectExpected then begin
  540.     SQL := inherited SQL;
  541.   end;
  542. end;
  543.  
  544. procedure TRxQuery.ExpandMacros;
  545. var
  546.   ExpandedSQL: TStringList;
  547. begin
  548.   if not FPatternChanged and not FStreamPatternChanged and
  549.     (MacroCount = 0) then Exit;
  550.   ExpandedSQL := TStringList.Create;
  551.   try
  552.     Expand(ExpandedSQL);
  553.     FDisconnectExpected := True;
  554.     try
  555.       inherited SQL := ExpandedSQL;
  556.     finally
  557.       FDisconnectExpected := False;
  558.     end;
  559.   finally
  560.     ExpandedSQL.Free;
  561.   end;
  562. end;
  563.  
  564. procedure TRxQuery.RecreateMacros;
  565. var
  566.   List: TParams;
  567. {$IFNDEF WIN32}
  568.   P: PChar;
  569. {$ENDIF}
  570. begin
  571. {$IFDEF RX_D4}
  572.   if not (csReading in ComponentState) then begin
  573. {$ENDIF RX_D4}
  574.     List := TParams.Create{$IFDEF RX_D4}(Self){$ENDIF};
  575.     try
  576.   {$IFDEF WIN32}
  577.       CreateMacros(List, PChar(FSQLPattern.Text));
  578.   {$ELSE}
  579.       P := FSQLPattern.GetText;
  580.       try
  581.         CreateMacros(List, P);
  582.       finally
  583.         StrDispose(P);
  584.       end;
  585.   {$ENDIF WIN32}
  586.       List.AssignValues(FMacros);
  587.   {$IFDEF RX_D4}
  588.       FMacros.Clear;
  589.       FMacros.Assign(List);
  590.     finally
  591.   {$ELSE}
  592.       FMacros.Free;
  593.       FMacros := List;
  594.     except
  595.   {$ENDIF RX_D4}
  596.       List.Free;
  597.     end;
  598. {$IFDEF RX_D4}
  599.   end
  600.   else begin
  601.     FMacros.Clear;
  602.     CreateMacros(FMacros, PChar(FSQLPattern.Text));
  603.   end;
  604. {$ENDIF RX_D4}
  605. end;
  606.  
  607. procedure TRxQuery.CreateMacros(List: TParams; const Value: PChar);
  608. begin
  609.   CreateQueryParams(List, Value, True, MacroChar, ['.']);
  610. end;
  611.  
  612. procedure TRxQuery.Expand(Query: TStrings);
  613.  
  614.   function ReplaceString(const S: string): string;
  615.   var
  616.     I, J, P, LiteralChars: Integer;
  617.     Param: TParam;
  618.     Found: Boolean;
  619.   begin
  620.     Result := S;
  621.     for I := Macros.Count - 1 downto 0 do begin
  622.       Param := Macros[I];
  623.       if Param.DataType = ftUnknown then Continue;
  624.       repeat
  625.         P := Pos(MacroChar + Param.Name, Result);
  626.         Found := (P > 0) and ((Length(Result) = P + Length(Param.Name)) or
  627.           NameDelimiter(Result[P + Length(Param.Name) + 1], ['.']));
  628.         if Found then begin
  629.           LiteralChars := 0;
  630.           for J := 1 to P - 1 do
  631.             if IsLiteral(Result[J]) then Inc(LiteralChars);
  632.           Found := LiteralChars mod 2 = 0;
  633.           if Found then begin
  634.             Result := Copy(Result, 1, P - 1) + Param.Text + Copy(Result,
  635.               P + Length(Param.Name) + 1, MaxInt);
  636.           end;
  637.         end;
  638.       until not Found;
  639.     end;
  640.   end;
  641.  
  642. var
  643.   I: Integer;
  644. begin
  645.   for I := 0 to FSQLPattern.Count - 1 do
  646.     Query.Add(ReplaceString(FSQLPattern[I]));
  647. end;
  648.  
  649. function TRxQuery.GetMacroCount: Word;
  650. begin
  651.   Result := FMacros.Count;
  652. end;
  653.  
  654. function TRxQuery.MacroByName(const Value: string): TParam;
  655. begin
  656.   Result := FMacros.ParamByName(Value);
  657. end;
  658.  
  659. {$IFNDEF RX_D3}
  660. function TRxQuery.IsEmpty: Boolean;
  661. begin
  662.   Result := IsDataSetEmpty(Self);
  663. end;
  664. {$ENDIF RX_D3}
  665.  
  666. function TRxQuery.GetRealSQL: TStrings;
  667. begin
  668.   try
  669.     ExpandMacros;
  670.   except
  671.   end;
  672.   Result := inherited SQL;
  673. end;
  674.  
  675. {$IFDEF RX_D5}
  676.  
  677. { TRxQuery.IProviderSupport }
  678.  
  679. function TRxQuery.PSGetDefaultOrder: TIndexDef;
  680. begin
  681.   ExpandMacros;
  682.   Result := inherited PSGetDefaultOrder;
  683. end;
  684.  
  685. function TRxQuery.PSGetTableName: string;
  686. begin
  687.   ExpandMacros;
  688.   Result := inherited PSGetTableName;
  689. end;
  690.  
  691. procedure TRxQuery.PSExecute;
  692. begin
  693.   ExecSQL;
  694. end;
  695.  
  696. {$ENDIF RX_D5}
  697.  
  698. {$IFDEF DEBUG}
  699. procedure TRxQuery.SetRealSQL(Value: TStrings);
  700. begin
  701. end;
  702. {$ENDIF DEBUG}
  703.  
  704. {$IFDEF WIN32}
  705.  
  706. { TRxQueryThread }
  707.  
  708. constructor TRxQueryThread.Create(Data: TBDEDataSet; RunMode: TRunQueryMode;
  709.   Prepare, CreateSuspended: Boolean);
  710. begin
  711.   inherited Create(True);
  712.   FData := Data;
  713.   FMode := RunMode;
  714.   FPrepare := Prepare;
  715.   FreeOnTerminate := True;
  716.   FData.DisableControls;
  717.   if not CreateSuspended then Resume;
  718. end;
  719.  
  720. procedure TRxQueryThread.DoTerminate;
  721. begin
  722.   Synchronize(FData.EnableControls);
  723.   inherited DoTerminate;
  724. end;
  725.  
  726. procedure TRxQueryThread.ModeError;
  727. begin
  728.   SysUtils.Abort;
  729. end;
  730.  
  731. procedure TRxQueryThread.DoHandleException;
  732. begin
  733.   if (FException is Exception) and not (FException is EAbort) then begin
  734.     if Assigned(Application.OnException) then
  735.       Application.OnException(FData, Exception(FException))
  736.     else
  737.       Application.ShowException(Exception(FException));
  738.   end;
  739. end;
  740.  
  741. procedure TRxQueryThread.HandleException;
  742. begin
  743.   FException := TObject(ExceptObject);
  744.   Synchronize(DoHandleException);
  745. end;
  746.  
  747. procedure TRxQueryThread.Execute;
  748. begin
  749.   try
  750.     if FPrepare and not (FMode in [rqExecDirect]) then begin
  751.       if FData is TRxQuery then TRxQuery(FData).Prepare
  752.       else if FData is TQuery then TQuery(FData).Prepare
  753.       else if FData is TStoredProc then TStoredProc(FData).Prepare;
  754.     end;
  755.     case FMode of
  756.       rqOpen:
  757.         FData.Open;
  758.       rqExecute:
  759.         begin
  760.           if FData is TRxQuery then TRxQuery(FData).ExecSQL
  761.           else if FData is TQuery then TQuery(FData).ExecSQL
  762.           else if FData is TStoredProc then TStoredProc(FData).ExecProc
  763.           else ModeError;
  764.         end;
  765.       rqExecDirect:
  766.         begin
  767.           if FData is TRxQuery then TRxQuery(FData).ExecDirect
  768.           else ModeError;
  769.         end;
  770.       rqOpenOrExec:
  771.         begin
  772.           if FData is TRxQuery then TRxQuery(FData).OpenOrExec(True)
  773.           else FData.Open;
  774.         end;
  775.     end;
  776.   except
  777.     HandleException;
  778.   end;
  779. end;
  780.  
  781. {$ENDIF WIN32}
  782.  
  783. { TSQLScript }
  784.  
  785. constructor TSQLScript.Create(AOwner: TComponent);
  786. begin
  787.   inherited Create(AOwner);
  788.   FSQL := TStringList.Create;
  789.   TStringList(SQL).OnChange := QueryChanged;
  790.   FParams := TParams.Create{$IFDEF RX_D4}(Self){$ENDIF};
  791.   FQuery := TRxQuery.Create(Self);
  792.   FSemicolonTerm := True;
  793.   FTerm := DefaultTermChar;
  794. end;
  795.  
  796. destructor TSQLScript.Destroy;
  797. begin
  798.   FQuery.Free;
  799.   FSQL.Free;
  800.   FParams.Free;
  801.   inherited Destroy;
  802. end;
  803.  
  804. function TSQLScript.GetDatabase: TDatabase;
  805. begin
  806.   Result := FQuery.Database;
  807. end;
  808.  
  809. function TSQLScript.GetDatabaseName: string;
  810. begin
  811.   Result := FQuery.DatabaseName;
  812. end;
  813.  
  814. procedure TSQLScript.SetDatabaseName(const Value: string);
  815. begin
  816.   FQuery.DatabaseName := Value;
  817. end;
  818.  
  819. {$IFDEF WIN32}
  820. function TSQLScript.GetSessionName: string;
  821. begin
  822.   Result := FQuery.SessionName;
  823. end;
  824.  
  825. procedure TSQLScript.SetSessionName(const Value: string);
  826. begin
  827.   FQuery.SessionName := Value;
  828. end;
  829.  
  830. function TSQLScript.GetDBSession: TSession;
  831. begin
  832.   Result := FQuery.DBSession;
  833. end;
  834. {$ENDIF WIN32}
  835.  
  836. procedure TSQLScript.CheckExecQuery(LineNo, StatementNo: Integer);
  837. var
  838.   Done: Boolean;
  839.   Action: TScriptAction;
  840.   I: Integer;
  841.   Param: TParam;
  842. {$IFNDEF WIN32}
  843.   Msg: array[0..255] of Char;
  844. {$ENDIF}
  845.   S: string;
  846. begin
  847.   Done := False;
  848.   repeat
  849.     try
  850.       if IgnoreParams then FQuery.ExecDirect
  851.       else begin
  852.         for I := 0 to FQuery.Params.Count - 1 do begin
  853.           Param := FQuery.Params[I];
  854.           Param.Assign(Params.ParamByName(Param.Name));
  855.         end;
  856.         FQuery.ExecSQL;
  857.       end;
  858.       Done := True;
  859.     except
  860.       on E: EDatabaseError do begin
  861.         Action := saFail;
  862.         S := Format(ResStr(SParseError), [ResStr(SMsgdlgError), LineNo]);
  863.         if E is EDBEngineError then
  864.           TDBError.Create(EDBEngineError(E), 0, LineNo,
  865.             {$IFDEF WIN32} PChar(S) {$ELSE} StrPCopy(Msg, S) {$ENDIF})
  866.         else begin
  867.           if E.Message <> '' then E.Message := E.Message + '. ';
  868.           E.Message := E.Message + S;
  869.         end;
  870.         if Assigned(FOnScriptError) then
  871.           FOnScriptError(Self, E, LineNo, StatementNo, Action);
  872.         if Action = saFail then raise;
  873.         if Action = saAbort then SysUtils.Abort;
  874.         if Action = saContinue then begin
  875.           Application.HandleException(Self);
  876.           Done := True;
  877.         end
  878.         else if Action = saIgnore then Done := True;
  879.       end;
  880.     end;
  881.   until Done;
  882. end;
  883.  
  884. procedure TSQLScript.ExecuteScript(StatementNo: Integer);
  885. var
  886.   S, LastStr: string;
  887.   IsTrans, SQLFilled, StmtFound: Boolean;
  888.   I, P, CurrStatement: Integer;
  889. begin
  890.   IsTrans := FTransaction {$IFNDEF WIN32} and Database.IsSQLBased {$ENDIF}
  891.     and not TransActive(Database) and (StatementNo < 0);
  892.   LastStr := '';
  893.   try
  894.     if IsTrans then begin
  895. {$IFDEF WIN32}
  896.       if not Database.IsSQLBased then
  897.         Database.TransIsolation := tiDirtyRead;
  898. {$ENDIF}
  899.       Database.StartTransaction;
  900.     end;
  901.   except
  902.     IsTrans := False;
  903.   end;
  904.   try
  905.     I := 0;
  906.     CurrStatement := 0;
  907.     StmtFound := False;
  908.     while I < SQL.Count do begin
  909.       FQuery.SQL.BeginUpdate;
  910.       try
  911.         FQuery.SQL.Clear;
  912.         SQLFilled := False;
  913.         repeat
  914.           if LastStr <> '' then begin
  915.             FQuery.SQL.Add(LastStr);
  916.             LastStr := '';
  917.           end;
  918.           if I < SQL.Count then begin
  919.             S := Trim(SQL[I]);
  920.             Inc(I);
  921.             P := Pos(';', S);
  922.             if (P > 0) and FSemicolonTerm then begin
  923.               LastStr := Trim(Copy(S, P + 1, MaxInt));
  924.               S := Copy(S, 1, P - 1);
  925.               if S <> '' then FQuery.SQL.Add(S);
  926.               SQLFilled := True;
  927.             end
  928.             else begin
  929.               if (S = Term) then SQLFilled := True
  930.               else if S <> '' then FQuery.SQL.Add(S);
  931.             end;
  932.           end
  933.           else SQLFilled := True;
  934.         until SQLFilled;
  935.       finally
  936.         FQuery.SQL.EndUpdate;
  937.       end;
  938.       if FQuery.SQL.Count > 0 then begin
  939.         if (StatementNo < 0) or (StatementNo = CurrStatement) then begin
  940.           StmtFound := True;
  941.           CheckExecQuery(I - 1, CurrStatement);
  942.           if StatementNo = CurrStatement then Break;
  943.         end;
  944.         Inc(CurrStatement);
  945.       end;
  946.     end;
  947.     if not StmtFound then begin
  948. {$IFDEF RX_D3}
  949.       DatabaseError(Format(SListIndexError, [StatementNo]));
  950. {$ELSE}
  951.       DatabaseError(Format('%s: %d', [LoadStr(SListIndexError), StatementNo]));
  952. {$ENDIF RX_D3}
  953.     end;
  954.     if IsTrans then Database.Commit;
  955.   except
  956.     if IsTrans then Database.Rollback;
  957.     raise;
  958.   end;
  959. end;
  960.  
  961. procedure TSQLScript.ExecStatement(StatementNo: Integer);
  962. begin
  963.   if FSQL.Count = 0 then _DBError(SEmptySQLStatement);
  964.   FQuery.SetDBFlag(dbfExecScript, True);
  965.   try
  966.     if not Database.Connected then _DBError(SDatabaseClosed);
  967.     if Assigned(FBeforeExec) then FBeforeExec(Self);
  968.     ExecuteScript(StatementNo);
  969.     if Assigned(FAfterExec) then FAfterExec(Self);
  970.   finally
  971.     FQuery.SetDBFlag(dbfExecScript, False);
  972.   end;
  973. end;
  974.  
  975. procedure TSQLScript.ExecSQL;
  976. begin
  977.   ExecStatement(-1);
  978. end;
  979.  
  980. procedure TSQLScript.CreateParams(List: TParams; const Value: PChar);
  981. begin
  982.   CreateQueryParams(List, Value, False, ':', []);
  983. end;
  984.  
  985. procedure TSQLScript.SetQuery(Value: TStrings);
  986. begin
  987.   TStringList(SQL).OnChange := nil;
  988.   FSQL.Assign(Value);
  989.   TStringList(SQL).OnChange := QueryChanged;
  990.   QueryChanged(nil);
  991. end;
  992.  
  993. function TSQLScript.GetText: {$IFDEF WIN32} string {$ELSE} PChar {$ENDIF};
  994. begin
  995. {$IFDEF WIN32}
  996.   Result := SQL.Text;
  997. {$ELSE}
  998.   Result := SQL.GetText;
  999. {$ENDIF}
  1000. end;
  1001.  
  1002. procedure TSQLScript.QueryChanged(Sender: TObject);
  1003. var
  1004.   List: TParams;
  1005. {$IFNDEF WIN32}
  1006.   P: PChar;
  1007. {$ENDIF}
  1008. begin
  1009. {$IFDEF RX_D4}
  1010.   if not (csReading in ComponentState) then begin
  1011. {$ENDIF RX_D4}
  1012.     List := TParams.Create{$IFDEF RX_D4}(Self){$ENDIF};
  1013.     try
  1014.   {$IFDEF WIN32}
  1015.       CreateParams(List, PChar(Text));
  1016.   {$ELSE}
  1017.       P := GetText;
  1018.       try
  1019.         CreateParams(List, P);
  1020.       finally
  1021.         StrDispose(P);
  1022.       end;
  1023.   {$ENDIF WIN32}
  1024.       List.AssignValues(FParams);
  1025.   {$IFDEF RX_D4}
  1026.       FParams.Clear;
  1027.       FParams.Assign(List);
  1028.     finally
  1029.   {$ELSE}
  1030.       FParams.Free;
  1031.       FParams := List;
  1032.     except
  1033.   {$ENDIF RX_D4}
  1034.       List.Free;
  1035.     end;
  1036. {$IFDEF RX_D4}
  1037.   end
  1038.   else begin
  1039.     FParams.Clear;
  1040.     CreateParams(FParams, PChar(Text));
  1041.   end;
  1042. {$ENDIF RX_D4}
  1043. end;
  1044.  
  1045. function TSQLScript.ParamByName(const Value: string): TParam;
  1046. begin
  1047.   Result := FParams.ParamByName(Value);
  1048. end;
  1049.  
  1050. procedure TSQLScript.SetParamsList(Value: TParams);
  1051. begin
  1052.   FParams.AssignValues(Value);
  1053. end;
  1054.  
  1055. function TSQLScript.GetParamsCount: Cardinal;
  1056. begin
  1057.   Result := FParams.Count;
  1058. end;
  1059.  
  1060. {$IFDEF RX_D4}
  1061. procedure TSQLScript.DefineProperties(Filer: TFiler);
  1062. begin
  1063.   inherited DefineProperties(Filer);
  1064.   Filer.DefineProperty('ParamData', ReadParamData, WriteParamData, True);
  1065. end;
  1066.  
  1067. procedure TSQLScript.ReadParamData(Reader: TReader);
  1068. begin
  1069.   Reader.ReadValue;
  1070.   Reader.ReadCollection(FParams);
  1071. end;
  1072.  
  1073. procedure TSQLScript.WriteParamData(Writer: TWriter);
  1074. begin
  1075.   Writer.WriteCollection(Params);
  1076. end;
  1077. {$ENDIF RX_D4}
  1078.  
  1079. end.