📄 jvbdequery.pas
字号:
{-----------------------------------------------------------------------------
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,v 1.16 2005/02/17 10:19:59 marquardt Exp $
unit JvBDEQuery;
{$I jvcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
SysUtils, Classes, DB, DBTables, Bde,
JvComponent, 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: '$RCSfile: JvBDEQuery.pas,v $';
Revision: '$Revision: 1.16 $';
Date: '$Date: 2005/02/17 10:19:59 $';
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -