1057 lines
26 KiB
ObjectPascal
1057 lines
26 KiB
ObjectPascal
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvQuery.PAS, released on 2002-07-04.
|
|
|
|
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
|
|
Copyright (c) 2001,2002 SGB Software
|
|
All Rights Reserved.
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.sourceforge.net
|
|
|
|
Known Issues:
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvBDEQuery.pas 10612 2006-05-19 19:04:09Z jfudickar $
|
|
|
|
unit JvBDEQuery;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
SysUtils, Classes, DB, DBTables, Bde,
|
|
JvComponentBase, JVCLVer;
|
|
|
|
const
|
|
DefaultMacroChar = '%';
|
|
DefaultTermChar = '/';
|
|
|
|
type
|
|
TQueryOpenStatus = (qsOpened, qsExecuted, qsFailed);
|
|
|
|
TJvQuery = class(TQuery)
|
|
private
|
|
FAboutJVCL: TJVCLAboutInfo;
|
|
FDisconnectExpected: Boolean;
|
|
FSaveQueryChanged: TNotifyEvent;
|
|
FMacroChar: Char;
|
|
FMacros: TParams;
|
|
FSQL: TStringList;
|
|
FStreamPatternChanged: Boolean;
|
|
FPatternChanged: Boolean;
|
|
FOpenStatus: TQueryOpenStatus;
|
|
function GetMacros: TParams;
|
|
procedure SetMacros(Value: TParams);
|
|
function GetSQL: TStrings;
|
|
procedure SetSQL(Value: TStrings);
|
|
procedure PatternChanged(Sender: TObject);
|
|
procedure QueryChanged(Sender: TObject);
|
|
procedure RecreateMacros;
|
|
procedure CreateMacros(List: TParams; const Value: PChar);
|
|
procedure Expand(Query: TStrings);
|
|
function GetMacroCount: Word;
|
|
procedure SetMacroChar(Value: Char);
|
|
function GetRealSQL: TStrings;
|
|
protected
|
|
procedure InternalFirst; override;
|
|
function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
|
|
procedure Loaded; override;
|
|
function CreateHandle: HDBICur; override;
|
|
procedure OpenCursor(InfoQuery: Boolean); override;
|
|
procedure Disconnect; override;
|
|
{ IProviderSupport }
|
|
procedure PSExecute; override;
|
|
function PSGetDefaultOrder: TIndexDef; override;
|
|
function PSGetTableName: string; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure ExpandMacros;
|
|
procedure ExecSQL;
|
|
procedure Prepare;
|
|
procedure OpenOrExec(ChangeLive: Boolean);
|
|
procedure ExecDirect;
|
|
function MacroByName(const Value: string): TParam;
|
|
property MacroCount: Word read GetMacroCount;
|
|
property OpenStatus: TQueryOpenStatus read FOpenStatus;
|
|
property RealSQL: TStrings read GetRealSQL;
|
|
published
|
|
property AboutJVCL: TJVCLAboutInfo read FAboutJVCL write FAboutJVCL stored False;
|
|
property MacroChar: Char read FMacroChar write SetMacroChar default DefaultMacroChar;
|
|
property SQL: TStrings read GetSQL write SetSQL;
|
|
property Macros: TParams read GetMacros write SetMacros;
|
|
end;
|
|
|
|
TRunQueryMode = (rqOpen, rqExecute, rqExecDirect, rqOpenOrExec);
|
|
|
|
TJvQueryThread = class(TThread)
|
|
private
|
|
FData: TBDEDataSet;
|
|
FMode: TRunQueryMode;
|
|
FPrepare: Boolean;
|
|
FException: TObject;
|
|
procedure DoHandleException;
|
|
protected
|
|
procedure ModeError; virtual;
|
|
procedure DoTerminate; override;
|
|
procedure Execute; override;
|
|
procedure HandleException; virtual;
|
|
public
|
|
constructor Create(Data: TBDEDataSet; RunMode: TRunQueryMode;
|
|
Prepare, CreateSuspended: Boolean);
|
|
end;
|
|
|
|
TScriptAction = (saFail, saAbort, saRetry, saIgnore, saContinue);
|
|
|
|
TScriptErrorEvent = procedure(Sender: TObject; E: EDatabaseError;
|
|
LineNo, StatementNo: Integer; var Action: TScriptAction) of object;
|
|
|
|
TJvSQLScript = class(TJvComponent)
|
|
private
|
|
FSQL: TStringList;
|
|
FParams: TParams;
|
|
FQuery: TJvQuery;
|
|
FTransaction: Boolean;
|
|
FSemicolonTerm: Boolean;
|
|
FIgnoreParams: Boolean;
|
|
FTerm: Char;
|
|
FBeforeExec: TNotifyEvent;
|
|
FAfterExec: TNotifyEvent;
|
|
FOnScriptError: TScriptErrorEvent;
|
|
function GetSessionName: string;
|
|
procedure SetSessionName(const Value: string);
|
|
function GetDBSession: TSession;
|
|
function GetText: string;
|
|
procedure ReadParamData(Reader: TReader);
|
|
procedure WriteParamData(Writer: TWriter);
|
|
function GetDatabase: TDatabase;
|
|
function GetDatabaseName: string;
|
|
procedure SetDatabaseName(const Value: string);
|
|
procedure CreateParams(List: TParams; const Value: PChar);
|
|
procedure QueryChanged(Sender: TObject);
|
|
function GetSQL: TStrings;
|
|
procedure SetSQL(Value: TStrings);
|
|
procedure SetParamsList(Value: TParams);
|
|
function GetParamsCount: Cardinal;
|
|
protected
|
|
procedure DefineProperties(Filer: TFiler); override;
|
|
procedure CheckExecQuery(LineNo, StatementNo: Integer);
|
|
procedure ExecuteScript(StatementNo: Integer); virtual;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure ExecSQL;
|
|
procedure ExecStatement(StatementNo: Integer);
|
|
function ParamByName(const Value: string): TParam;
|
|
property DBSession: TSession read GetDBSession;
|
|
property Text: string read GetText;
|
|
property Database: TDatabase read GetDatabase;
|
|
property ParamCount: Cardinal read GetParamsCount;
|
|
published
|
|
property DatabaseName: string read GetDatabaseName write SetDatabaseName;
|
|
property IgnoreParams: Boolean read FIgnoreParams write FIgnoreParams default False;
|
|
property SemicolonTerm: Boolean read FSemicolonTerm write FSemicolonTerm default True;
|
|
property SessionName: string read GetSessionName write SetSessionName;
|
|
property Term: Char read FTerm write FTerm default DefaultTermChar;
|
|
property SQL: TStrings read GetSQL write SetSQL;
|
|
property Params: TParams read FParams write SetParamsList stored False;
|
|
property Transaction: Boolean read FTransaction write FTransaction;
|
|
property BeforeExec: TNotifyEvent read FBeforeExec write FBeforeExec;
|
|
property AfterExec: TNotifyEvent read FAfterExec write FAfterExec;
|
|
property OnScriptError: TScriptErrorEvent read FOnScriptError write FOnScriptError;
|
|
end;
|
|
|
|
const
|
|
dbfExecScript = dbfTable;
|
|
|
|
procedure CreateQueryParams(List: TParams; const Value: PChar; Macro: Boolean;
|
|
SpecialChar: Char; Delims: TSysCharSet);
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvBDEQuery.pas $';
|
|
Revision: '$Revision: 10612 $';
|
|
Date: '$Date: 2006-05-19 21:04:09 +0200 (ven., 19 mai 2006) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IFDEF HAS_UNIT_RTLCONSTS}
|
|
RTLConsts,
|
|
{$ENDIF HAS_UNIT_RTLCONSTS}
|
|
Forms, Consts, BDEConst,
|
|
JvDBUtils, JvBdeUtils;
|
|
|
|
{ Parse SQL utility routines }
|
|
|
|
function NameDelimiters(C: Char; Delims: TSysCharSet): Boolean;
|
|
begin
|
|
Result := NameDelimiter(C) or (C in Delims);
|
|
end;
|
|
|
|
procedure CreateQueryParams(List: TParams; const Value: PChar; Macro: Boolean;
|
|
SpecialChar: Char; Delims: TSysCharSet);
|
|
var
|
|
CurPos, StartPos: PChar;
|
|
CurChar: Char;
|
|
Literal: Boolean;
|
|
EmbeddedLiteral: Boolean;
|
|
Name: string;
|
|
|
|
function StripLiterals(Buffer: PChar): string;
|
|
var
|
|
Len: Word;
|
|
TempBuf: PChar;
|
|
|
|
procedure StripChar(Value: Char);
|
|
begin
|
|
if TempBuf^ = Value then
|
|
StrMove(TempBuf, TempBuf + 1, Len - 1);
|
|
if TempBuf[StrLen(TempBuf) - 1] = Value then
|
|
TempBuf[StrLen(TempBuf) - 1] := #0;
|
|
end;
|
|
|
|
begin
|
|
Len := StrLen(Buffer) + 1;
|
|
TempBuf := AllocMem(Len);
|
|
Result := '';
|
|
try
|
|
StrCopy(TempBuf, Buffer);
|
|
StripChar('''');
|
|
StripChar('"');
|
|
Result := StrPas(TempBuf);
|
|
finally
|
|
FreeMem(TempBuf, Len);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if SpecialChar = #0 then
|
|
Exit;
|
|
CurPos := Value;
|
|
Literal := False;
|
|
EmbeddedLiteral := False;
|
|
repeat
|
|
CurChar := CurPos^;
|
|
if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ <> SpecialChar) then
|
|
begin
|
|
StartPos := CurPos;
|
|
while (CurChar <> #0) and (Literal or not NameDelimiters(CurChar, Delims)) do
|
|
begin
|
|
Inc(CurPos);
|
|
CurChar := CurPos^;
|
|
if IsLiteral(CurChar) then
|
|
begin
|
|
Literal := Literal xor True;
|
|
if CurPos = StartPos + 1 then
|
|
EmbeddedLiteral := True;
|
|
end;
|
|
end;
|
|
CurPos^ := #0;
|
|
if EmbeddedLiteral then
|
|
begin
|
|
Name := StripLiterals(StartPos + 1);
|
|
EmbeddedLiteral := False;
|
|
end
|
|
else
|
|
Name := StrPas(StartPos + 1);
|
|
if Assigned(List) then
|
|
begin
|
|
if List.FindParam(Name) = nil then
|
|
begin
|
|
if Macro then
|
|
List.CreateParam(ftString, Name, ptInput).AsString := TrueExpr
|
|
else
|
|
List.CreateParam(ftUnknown, Name, ptUnknown);
|
|
end;
|
|
end;
|
|
CurPos^ := CurChar;
|
|
StartPos^ := '?';
|
|
Inc(StartPos);
|
|
StrMove(StartPos, CurPos, StrLen(CurPos) + 1);
|
|
CurPos := StartPos;
|
|
end
|
|
else
|
|
if (CurChar = SpecialChar) and not Literal and ((CurPos + 1)^ = SpecialChar) then
|
|
StrMove(CurPos, CurPos + 1, StrLen(CurPos) + 1)
|
|
else
|
|
if IsLiteral(CurChar) then
|
|
Literal := Literal xor True;
|
|
Inc(CurPos);
|
|
until CurChar = #0;
|
|
end;
|
|
|
|
//=== { TJvQuery } ===========================================================
|
|
|
|
constructor TJvQuery.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FOpenStatus := qsFailed;
|
|
FSaveQueryChanged := TStringList(inherited SQL).OnChange;
|
|
TStringList(inherited SQL).OnChange := QueryChanged;
|
|
FMacroChar := DefaultMacroChar;
|
|
FSQL := TStringList.Create;
|
|
FSQL.OnChange := PatternChanged;
|
|
FMacros := TParams.Create(Self);
|
|
end;
|
|
|
|
destructor TJvQuery.Destroy;
|
|
begin
|
|
Destroying;
|
|
Disconnect;
|
|
FMacros.Free;
|
|
FSQL.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvQuery.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
GetMacros; {!! trying this way}
|
|
end;
|
|
|
|
procedure TJvQuery.InternalFirst;
|
|
begin
|
|
if not (UniDirectional and BOF) then
|
|
inherited InternalFirst;
|
|
end;
|
|
|
|
function TJvQuery.GetRecord(Buffer: PChar; GetMode: TGetMode;
|
|
DoCheck: Boolean): TGetResult;
|
|
begin
|
|
//!!!!!!
|
|
if UniDirectional and (GetMode in [gmPrior, gmNext]) then
|
|
DoCheck := False;
|
|
Result := inherited GetRecord(Buffer, GetMode, DoCheck);
|
|
end;
|
|
|
|
function TJvQuery.CreateHandle: HDBICur;
|
|
begin
|
|
FOpenStatus := qsFailed;
|
|
Result := inherited CreateHandle;
|
|
if Result = nil then
|
|
FOpenStatus := qsExecuted
|
|
else
|
|
FOpenStatus := qsOpened;
|
|
end;
|
|
|
|
procedure TJvQuery.OpenCursor;
|
|
begin
|
|
ExpandMacros;
|
|
inherited OpenCursor(InfoQuery);
|
|
end;
|
|
|
|
procedure TJvQuery.ExecSQL;
|
|
begin
|
|
ExpandMacros;
|
|
inherited ExecSQL;
|
|
end;
|
|
|
|
procedure TJvQuery.Prepare;
|
|
begin
|
|
ExpandMacros;
|
|
inherited Prepare;
|
|
end;
|
|
|
|
procedure TJvQuery.OpenOrExec(ChangeLive: Boolean);
|
|
|
|
procedure TryOpen;
|
|
begin
|
|
try
|
|
Open;
|
|
except
|
|
if OpenStatus <> qsExecuted then
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
try
|
|
TryOpen;
|
|
except
|
|
on E: EDatabaseError do
|
|
if RequestLive and ChangeLive then
|
|
begin
|
|
RequestLive := False;
|
|
try
|
|
TryOpen;
|
|
except
|
|
on E: EDatabaseError do
|
|
if OpenStatus <> qsOpened then
|
|
ExecDirect
|
|
else
|
|
begin
|
|
FOpenStatus := qsFailed;
|
|
raise;
|
|
end;
|
|
else
|
|
raise;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if OpenStatus <> qsOpened then
|
|
ExecDirect
|
|
else
|
|
begin
|
|
FOpenStatus := qsFailed;
|
|
raise;
|
|
end;
|
|
end;
|
|
else
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvQuery.ExecDirect;
|
|
begin
|
|
CheckInactive;
|
|
SetDBFlag(dbfExecSQL, True);
|
|
try
|
|
if SQL.Count > 0 then
|
|
begin
|
|
FOpenStatus := qsFailed;
|
|
Check(DbiQExecDirect(DBHandle, qryLangSQL, PChar(inherited SQL.Text),
|
|
nil));
|
|
FOpenStatus := qsExecuted;
|
|
end
|
|
else
|
|
_DBError(SEmptySQLStatement);
|
|
finally
|
|
SetDBFlag(dbfExecSQL, False);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvQuery.Disconnect;
|
|
var
|
|
Strings: TStrings;
|
|
Event1, Event2: TNotifyEvent;
|
|
begin
|
|
inherited Disconnect;
|
|
if csDestroying in ComponentState then
|
|
Exit;
|
|
Strings := inherited SQL;
|
|
Event1 := TStringList(Strings).OnChange;
|
|
Event2 := QueryChanged;
|
|
if @Event1 <> @Event2 then
|
|
begin
|
|
if not FDisconnectExpected then
|
|
SQL := inherited SQL;
|
|
TStringList(inherited SQL).OnChange := QueryChanged;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvQuery.SetMacroChar(Value: Char);
|
|
begin
|
|
if Value <> FMacroChar then
|
|
begin
|
|
FMacroChar := Value;
|
|
RecreateMacros;
|
|
end;
|
|
end;
|
|
|
|
function TJvQuery.GetMacros: TParams;
|
|
begin
|
|
if FStreamPatternChanged then
|
|
begin
|
|
FStreamPatternChanged := False;
|
|
PatternChanged(nil);
|
|
end;
|
|
Result := FMacros;
|
|
end;
|
|
|
|
procedure TJvQuery.SetMacros(Value: TParams);
|
|
begin
|
|
FMacros.AssignValues(Value);
|
|
end;
|
|
|
|
function TJvQuery.GetSQL: TStrings;
|
|
begin
|
|
Result := FSQL;
|
|
end;
|
|
|
|
procedure TJvQuery.SetSQL(Value: TStrings);
|
|
begin
|
|
inherited Disconnect;
|
|
FSQL.OnChange := nil;
|
|
FSQL.Assign(Value);
|
|
FSQL.OnChange := PatternChanged;
|
|
PatternChanged(nil);
|
|
end;
|
|
|
|
procedure TJvQuery.PatternChanged(Sender: TObject);
|
|
begin
|
|
if csLoading in ComponentState then
|
|
begin
|
|
FStreamPatternChanged := True;
|
|
Exit;
|
|
end;
|
|
inherited Disconnect;
|
|
RecreateMacros;
|
|
FPatternChanged := True;
|
|
try
|
|
ExpandMacros;
|
|
finally
|
|
FPatternChanged := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvQuery.QueryChanged(Sender: TObject);
|
|
begin
|
|
FSaveQueryChanged(Sender);
|
|
if not FDisconnectExpected then
|
|
begin
|
|
SQL := inherited SQL;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvQuery.ExpandMacros;
|
|
var
|
|
ExpandedSQL: TStringList;
|
|
begin
|
|
if not FPatternChanged and not FStreamPatternChanged and
|
|
(MacroCount = 0) then
|
|
Exit;
|
|
ExpandedSQL := TStringList.Create;
|
|
try
|
|
Expand(ExpandedSQL);
|
|
FDisconnectExpected := True;
|
|
try
|
|
inherited SQL := ExpandedSQL;
|
|
finally
|
|
FDisconnectExpected := False;
|
|
end;
|
|
finally
|
|
ExpandedSQL.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvQuery.RecreateMacros;
|
|
var
|
|
List: TParams;
|
|
begin
|
|
if not (csReading in ComponentState) then
|
|
begin
|
|
List := TParams.Create(Self);
|
|
try
|
|
CreateMacros(List, PChar(FSQL.Text));
|
|
List.AssignValues(FMacros);
|
|
FMacros.Clear;
|
|
FMacros.Assign(List);
|
|
finally
|
|
List.Free;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
FMacros.Clear;
|
|
CreateMacros(FMacros, PChar(FSQL.Text));
|
|
end;
|
|
end;
|
|
|
|
procedure TJvQuery.CreateMacros(List: TParams; const Value: PChar);
|
|
begin
|
|
CreateQueryParams(List, Value, True, MacroChar, ['.']);
|
|
end;
|
|
|
|
procedure TJvQuery.Expand(Query: TStrings);
|
|
var
|
|
I: Integer;
|
|
|
|
function ReplaceString(const S: string): string;
|
|
var
|
|
I, J, P, LiteralChars: Integer;
|
|
Param: TParam;
|
|
Found: Boolean;
|
|
begin
|
|
Result := S;
|
|
for I := Macros.Count - 1 downto 0 do
|
|
begin
|
|
Param := Macros[I];
|
|
if Param.DataType = ftUnknown then
|
|
Continue;
|
|
repeat
|
|
P := Pos(MacroChar + Param.Name, Result);
|
|
Found := (P > 0) and ((Length(Result) = P + Length(Param.Name)) or
|
|
NameDelimiters(Result[P + Length(Param.Name) + 1], ['.']));
|
|
if Found then
|
|
begin
|
|
LiteralChars := 0;
|
|
for J := 1 to P - 1 do
|
|
if IsLiteral(Result[J]) then
|
|
Inc(LiteralChars);
|
|
Found := LiteralChars mod 2 = 0;
|
|
if Found then
|
|
begin
|
|
Result := Copy(Result, 1, P - 1) + Param.Text + Copy(Result,
|
|
P + Length(Param.Name) + 1, MaxInt);
|
|
end;
|
|
end;
|
|
until not Found;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Query.BeginUpdate;
|
|
try
|
|
for I := 0 to SQL.Count - 1 do
|
|
Query.Add(ReplaceString(SQL[I]));
|
|
finally
|
|
Query.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TJvQuery.GetMacroCount: Word;
|
|
begin
|
|
Result := FMacros.Count;
|
|
end;
|
|
|
|
function TJvQuery.MacroByName(const Value: string): TParam;
|
|
begin
|
|
Result := FMacros.ParamByName(Value);
|
|
end;
|
|
|
|
function TJvQuery.GetRealSQL: TStrings;
|
|
begin
|
|
try
|
|
ExpandMacros;
|
|
except
|
|
end;
|
|
Result := inherited SQL;
|
|
end;
|
|
|
|
|
|
function TJvQuery.PSGetDefaultOrder: TIndexDef;
|
|
begin
|
|
ExpandMacros;
|
|
Result := inherited PSGetDefaultOrder;
|
|
end;
|
|
|
|
function TJvQuery.PSGetTableName: string;
|
|
begin
|
|
ExpandMacros;
|
|
Result := inherited PSGetTableName;
|
|
end;
|
|
|
|
procedure TJvQuery.PSExecute;
|
|
begin
|
|
ExecSQL;
|
|
end;
|
|
|
|
//=== { TJvQueryThread } =====================================================
|
|
|
|
constructor TJvQueryThread.Create(Data: TBDEDataSet; RunMode: TRunQueryMode;
|
|
Prepare, CreateSuspended: Boolean);
|
|
begin
|
|
inherited Create(True);
|
|
FData := Data;
|
|
FMode := RunMode;
|
|
FPrepare := Prepare;
|
|
FreeOnTerminate := True;
|
|
FData.DisableControls;
|
|
if not CreateSuspended then
|
|
Resume;
|
|
end;
|
|
|
|
procedure TJvQueryThread.DoTerminate;
|
|
begin
|
|
Synchronize(FData.EnableControls);
|
|
inherited DoTerminate;
|
|
end;
|
|
|
|
procedure TJvQueryThread.ModeError;
|
|
begin
|
|
SysUtils.Abort;
|
|
end;
|
|
|
|
procedure TJvQueryThread.DoHandleException;
|
|
begin
|
|
if (FException is Exception) and not (FException is EAbort) then
|
|
begin
|
|
if Assigned(Application.OnException) then
|
|
Application.OnException(FData, Exception(FException))
|
|
else
|
|
Application.ShowException(Exception(FException));
|
|
end;
|
|
end;
|
|
|
|
procedure TJvQueryThread.HandleException;
|
|
begin
|
|
FException := TObject(ExceptObject);
|
|
Synchronize(DoHandleException);
|
|
end;
|
|
|
|
procedure TJvQueryThread.Execute;
|
|
begin
|
|
try
|
|
if FPrepare and not (FMode in [rqExecDirect]) then
|
|
begin
|
|
if FData is TJvQuery then
|
|
TJvQuery(FData).Prepare
|
|
else
|
|
if FData is TQuery then
|
|
TQuery(FData).Prepare
|
|
else
|
|
if FData is TStoredProc then
|
|
TStoredProc(FData).Prepare;
|
|
end;
|
|
case FMode of
|
|
rqOpen:
|
|
FData.Open;
|
|
rqExecute:
|
|
begin
|
|
if FData is TJvQuery then
|
|
TJvQuery(FData).ExecSQL
|
|
else
|
|
if FData is TQuery then
|
|
TQuery(FData).ExecSQL
|
|
else
|
|
if FData is TStoredProc then
|
|
TStoredProc(FData).ExecProc
|
|
else
|
|
ModeError;
|
|
end;
|
|
rqExecDirect:
|
|
begin
|
|
if FData is TJvQuery then
|
|
TJvQuery(FData).ExecDirect
|
|
else
|
|
ModeError;
|
|
end;
|
|
rqOpenOrExec:
|
|
begin
|
|
if FData is TJvQuery then
|
|
TJvQuery(FData).OpenOrExec(True)
|
|
else
|
|
FData.Open;
|
|
end;
|
|
end;
|
|
except
|
|
HandleException;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvSQLScript } =======================================================
|
|
|
|
constructor TJvSQLScript.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FSQL := TStringList.Create;
|
|
FSQL.OnChange := QueryChanged;
|
|
FParams := TParams.Create(Self);
|
|
FQuery := TJvQuery.Create(Self);
|
|
FSemicolonTerm := True;
|
|
FTerm := DefaultTermChar;
|
|
end;
|
|
|
|
destructor TJvSQLScript.Destroy;
|
|
begin
|
|
FQuery.Free;
|
|
FSQL.Free;
|
|
FParams.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJvSQLScript.GetDatabase: TDatabase;
|
|
begin
|
|
Result := FQuery.Database;
|
|
end;
|
|
|
|
function TJvSQLScript.GetDatabaseName: string;
|
|
begin
|
|
Result := FQuery.DatabaseName;
|
|
end;
|
|
|
|
procedure TJvSQLScript.SetDatabaseName(const Value: string);
|
|
begin
|
|
FQuery.DatabaseName := Value;
|
|
end;
|
|
|
|
function TJvSQLScript.GetSessionName: string;
|
|
begin
|
|
Result := FQuery.SessionName;
|
|
end;
|
|
|
|
procedure TJvSQLScript.SetSessionName(const Value: string);
|
|
begin
|
|
FQuery.SessionName := Value;
|
|
end;
|
|
|
|
function TJvSQLScript.GetDBSession: TSession;
|
|
begin
|
|
Result := FQuery.DBSession;
|
|
end;
|
|
|
|
procedure TJvSQLScript.CheckExecQuery(LineNo, StatementNo: Integer);
|
|
var
|
|
Done: Boolean;
|
|
Action: TScriptAction;
|
|
I: Integer;
|
|
Param: TParam;
|
|
S: string;
|
|
begin
|
|
Done := False;
|
|
repeat
|
|
try
|
|
if IgnoreParams then
|
|
FQuery.ExecDirect
|
|
else
|
|
begin
|
|
for I := 0 to FQuery.Params.Count - 1 do
|
|
begin
|
|
Param := FQuery.Params[I];
|
|
Param.Assign(Params.ParamByName(Param.Name));
|
|
end;
|
|
FQuery.ExecSQL;
|
|
end;
|
|
Done := True;
|
|
except
|
|
on E: EDatabaseError do
|
|
begin
|
|
Action := saFail;
|
|
S := Format(SParseError, [SMsgdlgError, LineNo]);
|
|
if E is EDBEngineError then
|
|
TDBError.Create(EDBEngineError(E), 0, LineNo,
|
|
PChar(S))
|
|
else
|
|
begin
|
|
if E.Message <> '' then
|
|
E.Message := E.Message + '. ';
|
|
E.Message := E.Message + S;
|
|
end;
|
|
if Assigned(FOnScriptError) then
|
|
FOnScriptError(Self, E, LineNo, StatementNo, Action);
|
|
if Action = saFail then
|
|
raise;
|
|
if Action = saAbort then
|
|
SysUtils.Abort;
|
|
if Action = saContinue then
|
|
begin
|
|
Application.HandleException(Self);
|
|
Done := True;
|
|
end
|
|
else
|
|
if Action = saIgnore then
|
|
Done := True;
|
|
end;
|
|
end;
|
|
until Done;
|
|
end;
|
|
|
|
procedure TJvSQLScript.ExecuteScript(StatementNo: Integer);
|
|
var
|
|
S, LastStr: string;
|
|
IsTrans, SQLFilled, StmtFound: Boolean;
|
|
I, P, CurrStatement: Integer;
|
|
begin
|
|
IsTrans := FTransaction and not TransActive(Database) and (StatementNo < 0);
|
|
LastStr := '';
|
|
try
|
|
if IsTrans then
|
|
begin
|
|
if not Database.IsSQLBased then
|
|
Database.TransIsolation := tiDirtyRead;
|
|
Database.StartTransaction;
|
|
end;
|
|
except
|
|
IsTrans := False;
|
|
end;
|
|
try
|
|
I := 0;
|
|
CurrStatement := 0;
|
|
StmtFound := False;
|
|
while I < SQL.Count do
|
|
begin
|
|
FQuery.SQL.BeginUpdate;
|
|
try
|
|
FQuery.SQL.Clear;
|
|
SQLFilled := False;
|
|
repeat
|
|
if LastStr <> '' then
|
|
begin
|
|
FQuery.SQL.Add(LastStr);
|
|
LastStr := '';
|
|
end;
|
|
if I < SQL.Count then
|
|
begin
|
|
S := Trim(SQL[I]);
|
|
Inc(I);
|
|
P := Pos(';', S);
|
|
if (P > 0) and FSemicolonTerm then
|
|
begin
|
|
LastStr := Trim(Copy(S, P + 1, MaxInt));
|
|
S := Copy(S, 1, P - 1);
|
|
if S <> '' then
|
|
FQuery.SQL.Add(S);
|
|
SQLFilled := True;
|
|
end
|
|
else
|
|
begin
|
|
if S = Term then
|
|
SQLFilled := True
|
|
else
|
|
if S <> '' then
|
|
FQuery.SQL.Add(S);
|
|
end;
|
|
end
|
|
else
|
|
SQLFilled := True;
|
|
until SQLFilled;
|
|
finally
|
|
FQuery.SQL.EndUpdate;
|
|
end;
|
|
if FQuery.SQL.Count > 0 then
|
|
begin
|
|
if (StatementNo < 0) or (StatementNo = CurrStatement) then
|
|
begin
|
|
StmtFound := True;
|
|
CheckExecQuery(I - 1, CurrStatement);
|
|
if StatementNo = CurrStatement then
|
|
Break;
|
|
end;
|
|
Inc(CurrStatement);
|
|
end;
|
|
end;
|
|
if not StmtFound then
|
|
begin
|
|
DatabaseError(Format(SListIndexError, [StatementNo]));
|
|
end;
|
|
if IsTrans then
|
|
Database.Commit;
|
|
except
|
|
if IsTrans then
|
|
Database.Rollback;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvSQLScript.ExecStatement(StatementNo: Integer);
|
|
begin
|
|
if SQL.Count = 0 then
|
|
_DBError(SEmptySQLStatement);
|
|
FQuery.SetDBFlag(dbfExecScript, True);
|
|
try
|
|
if not Database.Connected then
|
|
_DBError(SDatabaseClosed);
|
|
if Assigned(FBeforeExec) then
|
|
FBeforeExec(Self);
|
|
ExecuteScript(StatementNo);
|
|
if Assigned(FAfterExec) then
|
|
FAfterExec(Self);
|
|
finally
|
|
FQuery.SetDBFlag(dbfExecScript, False);
|
|
end;
|
|
end;
|
|
|
|
procedure TJvSQLScript.ExecSQL;
|
|
begin
|
|
ExecStatement(-1);
|
|
end;
|
|
|
|
procedure TJvSQLScript.CreateParams(List: TParams; const Value: PChar);
|
|
begin
|
|
CreateQueryParams(List, Value, False, ':', []);
|
|
end;
|
|
|
|
function TJvSQLScript.GetSQL: TStrings;
|
|
begin
|
|
Result := FSQL;
|
|
end;
|
|
|
|
procedure TJvSQLScript.SetSQL(Value: TStrings);
|
|
begin
|
|
FSQL.OnChange := nil;
|
|
FSQL.Assign(Value);
|
|
FSQL.OnChange := QueryChanged;
|
|
QueryChanged(nil);
|
|
end;
|
|
|
|
function TJvSQLScript.GetText: string;
|
|
begin
|
|
Result := SQL.Text;
|
|
end;
|
|
|
|
procedure TJvSQLScript.QueryChanged(Sender: TObject);
|
|
var
|
|
List: TParams;
|
|
begin
|
|
if not (csReading in ComponentState) then
|
|
begin
|
|
List := TParams.Create(Self);
|
|
try
|
|
CreateParams(List, PChar(Text));
|
|
List.AssignValues(FParams);
|
|
FParams.Clear;
|
|
FParams.Assign(List);
|
|
finally
|
|
List.Free;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
FParams.Clear;
|
|
CreateParams(FParams, PChar(Text));
|
|
end;
|
|
end;
|
|
|
|
function TJvSQLScript.ParamByName(const Value: string): TParam;
|
|
begin
|
|
Result := FParams.ParamByName(Value);
|
|
end;
|
|
|
|
procedure TJvSQLScript.SetParamsList(Value: TParams);
|
|
begin
|
|
FParams.AssignValues(Value);
|
|
end;
|
|
|
|
function TJvSQLScript.GetParamsCount: Cardinal;
|
|
begin
|
|
Result := FParams.Count;
|
|
end;
|
|
|
|
|
|
procedure TJvSQLScript.DefineProperties(Filer: TFiler);
|
|
begin
|
|
inherited DefineProperties(Filer);
|
|
Filer.DefineProperty('ParamData', ReadParamData, WriteParamData, True);
|
|
end;
|
|
|
|
procedure TJvSQLScript.ReadParamData(Reader: TReader);
|
|
begin
|
|
Reader.ReadValue;
|
|
Reader.ReadCollection(FParams);
|
|
end;
|
|
|
|
procedure TJvSQLScript.WriteParamData(Writer: TWriter);
|
|
begin
|
|
Writer.WriteCollection(Params);
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|
|
|