home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 June / Chip_2002-06_cd1.bin / zkuste / delphi / kolekce / d6 / rxlibsetup.exe / {app} / units / Rxquery.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2002-02-19  |  27.1 KB  |  1,080 lines

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