git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.jvcl@12 7f62d464-2af8-f54e-996c-e91b33f51cbe
2249 lines
62 KiB
ObjectPascal
2249 lines
62 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: JvBdeUtils.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.
|
||
|
||
Contributor(s):
|
||
Burov Dmitry, translation of russian text.
|
||
|
||
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: JvBdeUtils.pas 11963 2008-10-16 09:12:52Z obones $
|
||
|
||
unit JvBdeUtils;
|
||
|
||
{$I jvcl.inc}
|
||
|
||
interface
|
||
|
||
uses
|
||
{$IFDEF UNITVERSIONING}
|
||
JclUnitVersioning,
|
||
{$ENDIF UNITVERSIONING}
|
||
Windows, Bde, Classes, DB, DBTables,
|
||
JvDBUtils;
|
||
|
||
type
|
||
TJvDBLocate = class(TJvLocateObject)
|
||
private
|
||
function LocateCallback: Boolean;
|
||
procedure RecordFilter(DataSet: TDataSet; var Accept: Boolean);
|
||
protected
|
||
function LocateFilter: Boolean; override;
|
||
procedure CheckFieldType(Field: TField); override;
|
||
function LocateKey: Boolean; override;
|
||
function UseKey: Boolean; override;
|
||
function FilterApplicable: Boolean; override;
|
||
public
|
||
destructor Destroy; override;
|
||
end;
|
||
|
||
TJvCloneDataset = class(TBDEDataSet)
|
||
private
|
||
FSourceHandle: HDBICur;
|
||
FReadOnly: Boolean;
|
||
procedure SetReadOnly(Value: Boolean);
|
||
procedure SetSourceHandle(ASourceHandle: HDBICur);
|
||
protected
|
||
function CreateHandle: HDBICur; override;
|
||
public
|
||
property SourceHandle: HDBICur read FSourceHandle write SetSourceHandle;
|
||
published
|
||
property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
|
||
end;
|
||
|
||
TJvCloneDbDataset = class(TDBDataSet)
|
||
private
|
||
FSourceHandle: HDBICur;
|
||
FReadOnly: Boolean;
|
||
procedure SetReadOnly(Value: Boolean);
|
||
procedure SetSourceHandle(ASourceHandle: HDBICur);
|
||
protected
|
||
function CreateHandle: HDBICur; override;
|
||
public
|
||
procedure InitFromDataSet(Source: TDBDataSet; Reset: Boolean);
|
||
property SourceHandle: HDBICur read FSourceHandle write SetSourceHandle;
|
||
published
|
||
property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
|
||
end;
|
||
|
||
TJvCloneTable = class(TTable)
|
||
private
|
||
FSourceHandle: HDBICur;
|
||
FReadOnly: Boolean;
|
||
procedure SetReadOnly(Value: Boolean);
|
||
procedure SetSourceHandle(ASourceHandle: HDBICur);
|
||
protected
|
||
function CreateHandle: HDBICur; override;
|
||
public
|
||
procedure InitFromTable(SourceTable: TTable; Reset: Boolean);
|
||
published
|
||
property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
|
||
end;
|
||
|
||
{ Utility routines }
|
||
|
||
function CreateDbLocate: TJvLocateObject;
|
||
procedure FetchAllRecords(DataSet: TBDEDataSet);
|
||
function TransActive(Database: TDatabase): Boolean;
|
||
function AsyncQrySupported(Database: TDatabase): Boolean;
|
||
function GetQuoteChar(Database: TDatabase): string;
|
||
procedure ExecuteQuery(const DbName, QueryText: string);
|
||
procedure ExecuteQueryEx(const SessName, DbName, QueryText: string);
|
||
procedure BdeTranslate(Locale: TLocale; Source, Dest: PAnsiChar; ToOem: Boolean);
|
||
function FieldLogicMap(FldType: TFieldType): Integer;
|
||
function FieldSubtypeMap(FldType: TFieldType): Integer;
|
||
procedure ConvertStringToLogicType(Locale: TLocale; FldLogicType: Integer;
|
||
FldSize: Word; const FldName, Value: string; Buffer: Pointer);
|
||
function GetAliasPath(const AliasName: string): string;
|
||
function IsDirectory(const DatabaseName: string): Boolean;
|
||
function GetBdeDirectory: string;
|
||
function BdeErrorMsg(ErrorCode: DBIResult): string;
|
||
function LoginToDatabase(Database: TDatabase; OnLogin: TDatabaseLoginEvent): Boolean;
|
||
function DataSetFindValue(ADataSet: TBDEDataSet; const Value, FieldName: string): Boolean;
|
||
function DataSetFindLike(ADataSet: TBDEDataSet; const Value, FieldName: string): Boolean;
|
||
function DataSetRecNo(DataSet: TDataSet): Longint;
|
||
function DataSetRecordCount(DataSet: TDataSet): Longint;
|
||
function DataSetPositionStr(DataSet: TDataSet): string;
|
||
procedure DataSetShowDeleted(DataSet: TBDEDataSet; Show: Boolean);
|
||
function CurrentRecordDeleted(DataSet: TBDEDataSet): Boolean;
|
||
function IsFilterApplicable(DataSet: TDataSet): Boolean;
|
||
function IsBookmarkStable(DataSet: TBDEDataSet): Boolean;
|
||
function BookmarksCompare(DataSet: TBDEDataSet; Bookmark1,
|
||
Bookmark2: TBookmark): Integer;
|
||
function SetToBookmark(ADataSet: TDataSet; ABookmark: TBookmark): Boolean;
|
||
procedure SetIndex(Table: TTable; const IndexFieldNames: string);
|
||
procedure RestoreIndex(Table: TTable);
|
||
procedure DeleteRange(Table: TTable; IndexFields: array of const;
|
||
FieldValues: array of const);
|
||
procedure PackTable(Table: TTable);
|
||
procedure ReindexTable(Table: TTable);
|
||
procedure BdeFlushBuffers;
|
||
function GetNativeHandle(Database: TDatabase; Buffer: Pointer;
|
||
BufSize: Integer): Pointer;
|
||
procedure ToggleDebugLayer(Active: Boolean; const DebugFile: string);
|
||
procedure DbNotSupported;
|
||
|
||
{ Export/import DataSet routines }
|
||
|
||
procedure ExportDataSet(Source: TBDEDataSet; DestTable: TTable;
|
||
TableType: TTableType; const AsciiCharSet: string;
|
||
AsciiDelimited: Boolean; MaxRecordCount: Longint);
|
||
procedure ExportDataSetEx(Source: TBDEDataSet; DestTable: TTable;
|
||
TableType: TTableType; const AsciiCharSet: string;
|
||
AsciiDelimited: Boolean; AsciiDelimiter, AsciiSeparator: Char;
|
||
MaxRecordCount: Longint);
|
||
procedure ImportDataSet(Source: TBDEDataSet; DestTable: TTable;
|
||
MaxRecordCount: Longint; Mappings: TStrings; Mode: TBatchMode);
|
||
|
||
{ ReportSmith initialization }
|
||
|
||
procedure InitRSRUN(Database: TDatabase; const ConName: string;
|
||
ConType: Integer; const ConServer: string);
|
||
|
||
{ begin JvDBUtil }
|
||
{ ExecuteSQLScript executes SQL script }
|
||
|
||
procedure ExecuteSQLScript(Base: TDatabase; const Script: string; const Commit: TCommit; OnProgress: TJvDBProgressEvent; const UserData: Integer);
|
||
|
||
{ GetQueryResult executes SQL Query and returns Result as Variant }
|
||
|
||
function GetQueryResult(const DatabaseName, SQL: string): Variant;
|
||
|
||
{ GetStoredProcResult executes SQL stored procedure and returns
|
||
value of ResultName parameters as Variant }
|
||
|
||
function GetStoredProcResult(const ADatabaseName, AStoredProcName: string; AParams: array of Variant;
|
||
const AResultName: string): Variant;
|
||
|
||
{ StrFieldDesc returns field description of given FLDDesc record }
|
||
|
||
function StrFieldDesc(Field: FLDDesc): string;
|
||
|
||
function Var2Type(V: Variant; const VarType: Integer): Variant;
|
||
|
||
procedure CopyRecord(DataSet: TDataSet);
|
||
|
||
{ AddReference create reference for paradox table,
|
||
RefField and MasterField are field numbers (first field has number 1)
|
||
Tables allready must have indices for this fields }
|
||
|
||
procedure AddReference(Tbl: TTable; RefName: string; RefField: Word;
|
||
MasterTable: string; MasterField: Word; ModOp, DelOp: RINTQual);
|
||
|
||
{ AddMasterPassword extracted from "bde.hlp" file }
|
||
procedure AddMasterPassword(Table: TTable; pswd: string);
|
||
|
||
procedure PackEncryptedTable(Table: TTable; pswd: string);
|
||
|
||
function EncodeQuotes(const S: string): string;
|
||
|
||
{*********************** from JvStrUtil unit ***********************}
|
||
|
||
function Cmp(const S1, S2: string): Boolean;
|
||
|
||
{ SubStr returns substring from string, S,
|
||
separated with Separator string}
|
||
|
||
function SubStr(const S: string; const Index: Integer; const Separator: string): string;
|
||
|
||
{ SubStrEnd same to previous function but Index numerated
|
||
from the end of string }
|
||
|
||
function SubStrEnd(const S: string; const Index: Integer; const Separator: string): string;
|
||
|
||
{ ReplaceString searches for all substrings, OldPattern,
|
||
in a string, S, and replaces them with NewPattern }
|
||
|
||
function ReplaceString(S: string; const OldPattern, NewPattern: string): string;
|
||
|
||
{ GetXYByPos is same to previous function, but
|
||
returns X position in line too}
|
||
|
||
procedure GetXYByPos(const S: string; const Pos: Integer; var X, Y: Integer);
|
||
|
||
{####################### from JvStrUtil unit #######################}
|
||
|
||
{ end JvDBUtil }
|
||
|
||
{$IFDEF UNITVERSIONING}
|
||
const
|
||
UnitVersioning: TUnitVersionInfo = (
|
||
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/branches/JVCL3_36_PREPARATION/run/JvBdeUtils.pas $';
|
||
Revision: '$Revision: 11963 $';
|
||
Date: '$Date: 2008-10-16 11:12:52 +0200 (jeu., 16 oct. 2008) $';
|
||
LogPath: 'JVCL\run'
|
||
);
|
||
{$ENDIF UNITVERSIONING}
|
||
|
||
implementation
|
||
|
||
uses
|
||
SysUtils, Registry, Forms, Controls, Dialogs, Consts, Math,
|
||
IniFiles, DBConsts, BDEConst, DBCommon,
|
||
{$IFDEF HAS_UNIT_VARIANTS}
|
||
Variants,
|
||
{$ENDIF HAS_UNIT_VARIANTS}
|
||
{$IFDEF HAS_UNIT_RTLCONSTS}
|
||
RTLConsts,
|
||
{$ENDIF HAS_UNIT_RTLCONSTS}
|
||
JvConsts, JvJVCLUtils, JvJCLUtils, JvTypes, JvResources;
|
||
|
||
{ Utility routines }
|
||
|
||
procedure DBError(const Ident: string);
|
||
begin
|
||
DatabaseError(Ident);
|
||
end;
|
||
|
||
function IsBookmarkStable(DataSet: TBDEDataSet): Boolean;
|
||
var
|
||
Props: CURProps;
|
||
begin
|
||
with DataSet do
|
||
Result := Active and (DbiGetCursorProps(Handle, Props) = DBIERR_NONE) and
|
||
Props.bBookMarkStable;
|
||
end;
|
||
|
||
function SetToBookmark(ADataSet: TDataSet; ABookmark: TBookmark): Boolean;
|
||
begin
|
||
Result := False;
|
||
with ADataSet do
|
||
if Active and (ABookmark <> nil) and not (Bof and Eof) and
|
||
BookmarkValid(ABookmark) then
|
||
try
|
||
ADataSet.GotoBookmark(ABookmark);
|
||
Result := True;
|
||
except
|
||
end;
|
||
end;
|
||
|
||
function BookmarksCompare(DataSet: TBDEDataSet; Bookmark1, Bookmark2: TBookmark): Integer;
|
||
const
|
||
RetCodes: array[Boolean, Boolean] of ShortInt =
|
||
((2, CMPLess), (CMPGtr, CMPEql));
|
||
begin
|
||
Result := RetCodes[Bookmark1 = nil, Bookmark2 = nil];
|
||
if Result = 2 then
|
||
begin
|
||
Check(DbiCompareBookmarks(DataSet.Handle, Bookmark1, Bookmark2,
|
||
Result));
|
||
if Result = CMPKeyEql then
|
||
Result := CMPEql;
|
||
end;
|
||
end;
|
||
|
||
function DBGetIntProp(const Handle: Pointer; PropName: Longint): Longint;
|
||
var
|
||
Length: Word;
|
||
Value: Longint;
|
||
begin
|
||
Value := 0;
|
||
Check(DbiGetProp(hDBIObj(Handle), PropName, @Value, SizeOf(Value), Length));
|
||
Result := Value;
|
||
end;
|
||
|
||
function GetQuoteChar(Database: TDatabase): string;
|
||
var
|
||
Q: Char;
|
||
Len: Word;
|
||
begin
|
||
Result := '';
|
||
if Database.IsSQLBased then
|
||
begin
|
||
Q := #0;
|
||
DbiGetProp(hDBIObj(Database.Handle), dbQUOTECHAR, @Q, SizeOf(Q), Len);
|
||
if Q <> #0 then
|
||
Result := Q;
|
||
end
|
||
else
|
||
Result := '"';
|
||
end;
|
||
|
||
function AsyncQrySupported(Database: TDatabase): Boolean;
|
||
begin
|
||
Result := False;
|
||
if Database.Connected then
|
||
if Database.IsSQLBased then
|
||
try
|
||
Result := BOOL(DBGetIntProp(Database.Handle, dbASYNCSUPPORT));
|
||
except
|
||
end
|
||
else
|
||
Result := True;
|
||
end;
|
||
|
||
function FieldLogicMap(FldType: TFieldType): Integer;
|
||
begin
|
||
Result := FldTypeMap[FldType];
|
||
end;
|
||
|
||
function FieldSubtypeMap(FldType: TFieldType): Integer;
|
||
begin
|
||
Result := FldSubtypeMap[FldType];
|
||
end;
|
||
|
||
{ Routine for convert string to IDAPI logical field type }
|
||
|
||
procedure ConvertStringToLogicType(Locale: TLocale; FldLogicType: Integer;
|
||
FldSize: Word; const FldName, Value: string; Buffer: Pointer);
|
||
var
|
||
Allocate: Boolean;
|
||
BCD: FMTBcd;
|
||
E: Integer;
|
||
L: Longint;
|
||
B: WordBool;
|
||
DateTime: TDateTime;
|
||
D: Double;
|
||
Data: Longint;
|
||
TimeStamp: TTimeStamp;
|
||
begin
|
||
if Buffer = nil then
|
||
begin
|
||
Buffer := AllocMem(FldSize);
|
||
Allocate := Buffer <> nil;
|
||
end
|
||
else
|
||
Allocate := False;
|
||
try
|
||
case FldLogicType of
|
||
fldZSTRING:
|
||
AnsiToNative(Locale, AnsiString(Value), PAnsiChar(Buffer), FldSize); // potential data loss under D2009 because of AnsiString cast
|
||
fldBYTES, fldVARBYTES:
|
||
Move(Value[1], Buffer^, Min(Length(Value) * SizeOf(Char), FldSize));
|
||
fldINT16, fldINT32, fldUINT16, fldINT64:
|
||
begin
|
||
if Value = '' then
|
||
FillChar(Buffer^, FldSize, 0)
|
||
else
|
||
begin
|
||
Val(Value, L, E);
|
||
if E <> 0 then
|
||
DatabaseErrorFmt(SInvalidIntegerValue, [Value, FldName]);
|
||
Move(L, Buffer^, FldSize);
|
||
end;
|
||
end;
|
||
fldBOOL:
|
||
begin
|
||
L := Length(Value);
|
||
if L = 0 then
|
||
B := False
|
||
else
|
||
B := CharInSet(Value[1], ['Y', 'y', 'T', 't', '1']);
|
||
Move(B, Buffer^, SizeOf(WordBool));
|
||
end;
|
||
fldFLOAT, fldBCD:
|
||
begin
|
||
if Value = '' then
|
||
FillChar(Buffer^, FldSize, 0)
|
||
else
|
||
begin
|
||
D := StrToFloat(Value);
|
||
if FldLogicType <> fldBCD then
|
||
Move(D, Buffer^, SizeOf(Double))
|
||
else
|
||
begin
|
||
DbiBcdFromFloat(D, 32, FldSize, BCD);
|
||
Move(BCD, Buffer^, SizeOf(BCD));
|
||
end;
|
||
end;
|
||
end;
|
||
fldDATE:
|
||
begin
|
||
if Value = '' then
|
||
FillChar(Buffer^, FldSize, 0)
|
||
else
|
||
begin
|
||
DateTime := StrToDate(Value);
|
||
TimeStamp := DateTimeToTimeStamp(DateTime);
|
||
Data := TimeStamp.Date;
|
||
Move(Data, Buffer^, Min(FldSize, SizeOf(Data)));
|
||
end;
|
||
end;
|
||
fldTIME:
|
||
begin
|
||
if Value = '' then
|
||
FillChar(Buffer^, FldSize, 0)
|
||
else
|
||
begin
|
||
DateTime := StrToTime(Value);
|
||
TimeStamp := DateTimeToTimeStamp(DateTime);
|
||
Data := TimeStamp.Time;
|
||
Move(Data, Buffer^, Min(FldSize, SizeOf(Data)));
|
||
end;
|
||
end;
|
||
fldTIMESTAMP:
|
||
begin
|
||
if Value = '' then
|
||
FillChar(Buffer^, FldSize, 0)
|
||
else
|
||
begin
|
||
DateTime := StrToDateTime(Value);
|
||
TimeStamp := DateTimeToTimeStamp(DateTime);
|
||
D := TimeStampToMSecs(DateTimeToTimeStamp(DateTime));
|
||
Move(D, Buffer^, Min(FldSize, SizeOf(D)));
|
||
end;
|
||
end;
|
||
else
|
||
DbiError(DBIERR_INVALIDFLDTYPE);
|
||
end;
|
||
finally
|
||
if Allocate then
|
||
FreeMem(Buffer, FldSize);
|
||
end;
|
||
end;
|
||
|
||
{ Execute Query routine }
|
||
|
||
procedure ExecuteQueryEx(const SessName, DbName, QueryText: string);
|
||
begin
|
||
with TQuery.Create(Application) do
|
||
try
|
||
DatabaseName := DbName;
|
||
SessionName := SessName;
|
||
SQL.Add(QueryText);
|
||
ExecSQL;
|
||
finally
|
||
Free;
|
||
end;
|
||
end;
|
||
|
||
procedure ExecuteQuery(const DbName, QueryText: string);
|
||
begin
|
||
ExecuteQueryEx('', DbName, QueryText);
|
||
end;
|
||
|
||
{ Database Login routine }
|
||
|
||
function LoginToDatabase(Database: TDatabase; OnLogin: TDatabaseLoginEvent): Boolean;
|
||
var
|
||
EndLogin: Boolean;
|
||
begin
|
||
Result := Database.Connected;
|
||
if Result then
|
||
Exit;
|
||
Database.OnLogin := OnLogin;
|
||
EndLogin := True;
|
||
repeat
|
||
try
|
||
Database.Connected := True;
|
||
EndLogin := True;
|
||
except
|
||
on E: EDbEngineError do
|
||
begin
|
||
EndLogin := (MessageDlg(E.Message + '. ' + RsRetryLogin,
|
||
mtConfirmation, [mbYes, mbNo], 0) <> mrYes);
|
||
end;
|
||
on E: EDatabaseError do
|
||
begin
|
||
{ User select "Cancel" in login dialog }
|
||
MessageDlg(E.Message, mtError, [mbOk], 0);
|
||
end;
|
||
else
|
||
raise;
|
||
end;
|
||
until EndLogin;
|
||
Result := Database.Connected;
|
||
end;
|
||
|
||
{ ReportSmith runtime initialization routine }
|
||
|
||
procedure InitRSRUN(Database: TDatabase; const ConName: string;
|
||
ConType: Integer; const ConServer: string);
|
||
const
|
||
IniFileName = 'RPTSMITH.CON';
|
||
scConNames = 'ConnectNamesSection';
|
||
idConNames = 'ConnectNames';
|
||
idType = 'Type';
|
||
idServer = 'Server';
|
||
idSQLDataFilePath = 'Database';
|
||
idDataFilePath = 'DataFilePath';
|
||
idSQLUserID = 'USERID';
|
||
var
|
||
ParamList: TStringList;
|
||
DBPath: string;
|
||
TempStr, AppConName: string;
|
||
UserName: string;
|
||
ExeName: string;
|
||
IniFile: TIniFile;
|
||
begin
|
||
ParamList := TStringList.Create;
|
||
try
|
||
Database.Session.GetAliasParams(Database.AliasName, ParamList);
|
||
if Database.IsSQLBased then
|
||
DBPath := ParamList.Values['SERVER NAME']
|
||
else
|
||
DBPath := ParamList.Values['PATH'];
|
||
UserName := ParamList.Values['USER NAME'];
|
||
finally
|
||
ParamList.Free;
|
||
end;
|
||
AppConName := ConName;
|
||
if AppConName = '' then
|
||
begin
|
||
ExeName := ExtractFileName(Application.ExeName);
|
||
AppConName := Copy(ExeName, 1, Pos('.', ExeName) - 1);
|
||
end;
|
||
IniFile := TIniFile.Create(IniFileName);
|
||
try
|
||
TempStr := IniFile.ReadString(scConNames, idConNames, '');
|
||
if Pos(AppConName, TempStr) = 0 then
|
||
begin
|
||
if TempStr <> '' then
|
||
TempStr := TempStr + ',';
|
||
IniFile.WriteString(scConNames, idConNames, TempStr + AppConName);
|
||
end;
|
||
IniFile.WriteInteger(AppConName, idType, ConType);
|
||
IniFile.WriteString(AppConName, idServer, ConServer);
|
||
if Database.IsSQLBased then
|
||
begin
|
||
IniFile.WriteString(AppConName, idSQLDataFilePath, DBPath);
|
||
IniFile.WriteString(AppConName, idSQLUserID, UserName);
|
||
end
|
||
else
|
||
IniFile.WriteString(AppConName, idDataFilePath, DBPath);
|
||
finally
|
||
IniFile.Free;
|
||
end;
|
||
end;
|
||
|
||
{ BDE aliases routines }
|
||
|
||
function IsDirectory(const DatabaseName: string): Boolean;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
Result := True;
|
||
if (DatabaseName = '') then
|
||
Exit;
|
||
I := 1;
|
||
while I <= Length(DatabaseName) do
|
||
begin
|
||
if CharInSet(DatabaseName[I], LeadBytes) then
|
||
Inc(I)
|
||
else
|
||
if CharInSet(DatabaseName[I], [':', '\']) then
|
||
Exit;
|
||
Inc(I);
|
||
end;
|
||
Result := False;
|
||
end;
|
||
|
||
function GetAliasPath(const AliasName: string): string;
|
||
var
|
||
SAlias: DBINAME;
|
||
Desc: DBDesc;
|
||
Params: TStrings;
|
||
begin
|
||
Result := '';
|
||
StrPLCopy(SAlias, AnsiString(AliasName), SizeOf(SAlias) - 1);
|
||
AnsiToOem(SAlias, SAlias);
|
||
Check(DbiGetDatabaseDesc(SAlias, @Desc));
|
||
if StrIComp(Desc.szDbType, szCFGDBSTANDARD) = 0 then
|
||
begin
|
||
OemToAnsi(Desc.szPhyName, Desc.szPhyName);
|
||
Result := string(StrPas(Desc.szPhyName));
|
||
end
|
||
else
|
||
begin
|
||
Params := TStringList.Create;
|
||
try
|
||
Session.Active := True;
|
||
Session.GetAliasParams(AliasName, Params);
|
||
Result := Params.Values['SERVER NAME'];
|
||
finally
|
||
Params.Free;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
//=== { TJvCloneDataset } ====================================================
|
||
|
||
procedure TJvCloneDataset.SetSourceHandle(ASourceHandle: HDBICur);
|
||
begin
|
||
if ASourceHandle <> FSourceHandle then
|
||
begin
|
||
Close;
|
||
FSourceHandle := ASourceHandle;
|
||
if FSourceHandle <> nil then
|
||
Open;
|
||
end;
|
||
end;
|
||
|
||
function TJvCloneDataset.CreateHandle: HDBICur;
|
||
begin
|
||
Check(DbiCloneCursor(FSourceHandle, FReadOnly, False, Result));
|
||
end;
|
||
|
||
procedure TJvCloneDataset.SetReadOnly(Value: Boolean);
|
||
begin
|
||
CheckInactive;
|
||
FReadOnly := Value;
|
||
end;
|
||
|
||
//=== { TJvCloneDbDataset } ==================================================
|
||
|
||
procedure TJvCloneDbDataset.InitFromDataSet(Source: TDBDataSet; Reset: Boolean);
|
||
begin
|
||
with Source do
|
||
begin
|
||
Self.SessionName := SessionName;
|
||
Self.DatabaseName := DatabaseName;
|
||
SetSourceHandle(Handle);
|
||
Self.Filter := Filter;
|
||
Self.OnFilterRecord := OnFilterRecord;
|
||
if not Reset then
|
||
Self.Filtered := Filtered;
|
||
end;
|
||
if Reset then
|
||
begin
|
||
Filtered := False;
|
||
First;
|
||
end;
|
||
end;
|
||
|
||
procedure TJvCloneDbDataset.SetSourceHandle(ASourceHandle: HDBICur);
|
||
begin
|
||
if ASourceHandle <> FSourceHandle then
|
||
begin
|
||
Close;
|
||
FSourceHandle := ASourceHandle;
|
||
if FSourceHandle <> nil then
|
||
Open;
|
||
end;
|
||
end;
|
||
|
||
function TJvCloneDbDataset.CreateHandle: HDBICur;
|
||
begin
|
||
Check(DbiCloneCursor(FSourceHandle, FReadOnly, False, Result));
|
||
end;
|
||
|
||
procedure TJvCloneDbDataset.SetReadOnly(Value: Boolean);
|
||
begin
|
||
CheckInactive;
|
||
FReadOnly := Value;
|
||
end;
|
||
|
||
//=== { TJvCloneTable } ======================================================
|
||
|
||
procedure TJvCloneTable.InitFromTable(SourceTable: TTable; Reset: Boolean);
|
||
begin
|
||
with SourceTable do
|
||
begin
|
||
Self.TableType := TableType;
|
||
Self.TableName := TableName;
|
||
Self.SessionName := SessionName;
|
||
Self.DatabaseName := DatabaseName;
|
||
if not Reset then
|
||
begin
|
||
if IndexName <> '' then
|
||
Self.IndexName := IndexName
|
||
else
|
||
if IndexFieldNames <> '' then
|
||
Self.IndexFieldNames := IndexFieldNames;
|
||
end;
|
||
SetSourceHandle(Handle);
|
||
Self.Filter := Filter;
|
||
Self.OnFilterRecord := OnFilterRecord;
|
||
if not Reset then
|
||
Self.Filtered := Filtered;
|
||
end;
|
||
if Reset then
|
||
begin
|
||
Filtered := False;
|
||
DbiResetRange(Handle);
|
||
IndexName := '';
|
||
IndexFieldNames := '';
|
||
First;
|
||
end;
|
||
end;
|
||
|
||
procedure TJvCloneTable.SetSourceHandle(ASourceHandle: HDBICur);
|
||
begin
|
||
if ASourceHandle <> FSourceHandle then
|
||
begin
|
||
Close;
|
||
FSourceHandle := ASourceHandle;
|
||
if FSourceHandle <> nil then
|
||
Open;
|
||
end;
|
||
end;
|
||
|
||
procedure TJvCloneTable.SetReadOnly(Value: Boolean);
|
||
begin
|
||
CheckInactive;
|
||
FReadOnly := Value;
|
||
end;
|
||
|
||
function TJvCloneTable.CreateHandle: HDBICur;
|
||
begin
|
||
Check(DbiCloneCursor(FSourceHandle, FReadOnly, False, Result));
|
||
end;
|
||
|
||
//=== { TJvDBLocate } ========================================================
|
||
|
||
function CreateDbLocate: TJvLocateObject;
|
||
begin
|
||
Result := TJvDBLocate.Create;
|
||
end;
|
||
|
||
destructor TJvDBLocate.Destroy;
|
||
begin
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TJvDBLocate.CheckFieldType(Field: TField);
|
||
var
|
||
Locale: TLocale;
|
||
begin
|
||
if not (Field.DataType in [ftDate, ftTime, ftDateTime]) then
|
||
begin
|
||
if DataSet is TBDEDataSet then
|
||
Locale := TBDEDataSet(DataSet).Locale
|
||
else
|
||
Locale := Session.Locale;
|
||
ConvertStringToLogicType(Locale, FieldLogicMap(Field.DataType),
|
||
Field.DataSize, Field.FieldName, LookupValue, nil);
|
||
end;
|
||
end;
|
||
|
||
function TJvDBLocate.UseKey: Boolean;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
Result := False;
|
||
if DataSet is TTable then
|
||
with DataSet as TTable do
|
||
begin
|
||
if (not Self.LookupField.IsIndexField) and (not IndexSwitch or
|
||
(not CaseSensitive and Database.IsSQLBased)) then
|
||
Exit;
|
||
if (not LookupExact) and (Self.LookupField.DataType <> ftString) then
|
||
Exit;
|
||
IndexDefs.Update;
|
||
for I := 0 to IndexDefs.Count - 1 do
|
||
with IndexDefs[I] do
|
||
if not (ixExpression in Options) and
|
||
((ixCaseInsensitive in Options) or CaseSensitive) then
|
||
if SameText(Fields, Self.LookupField.FieldName) then
|
||
begin
|
||
Result := True;
|
||
Exit;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function TJvDBLocate.LocateKey: Boolean;
|
||
var
|
||
Clone: TJvCloneTable;
|
||
|
||
function LocateIndex(Table: TTable): Boolean;
|
||
begin
|
||
with Table do
|
||
begin
|
||
SetKey;
|
||
FieldByName(Self.LookupField.FieldName).AsString := LookupValue;
|
||
if LookupExact then
|
||
Result := GotoKey
|
||
else
|
||
begin
|
||
GotoNearest;
|
||
Result := MatchesLookup(FieldByName(Self.LookupField.FieldName));
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
begin
|
||
try
|
||
TTable(DataSet).CheckBrowseMode;
|
||
if TTable(DataSet).IndexFieldNames = LookupField.FieldName then
|
||
Result := LocateIndex(TTable(DataSet))
|
||
else
|
||
begin
|
||
Clone := TJvCloneTable.Create(DataSet);
|
||
with Clone do
|
||
try
|
||
ReadOnly := True;
|
||
InitFromTable(TTable(DataSet), True);
|
||
IndexFieldNames := Self.LookupField.FieldName;
|
||
Result := LocateIndex(Clone);
|
||
if Result then
|
||
begin
|
||
Check(DbiSetToCursor(TTable(DataSet).Handle, Handle));
|
||
DataSet.Resync([rmExact, rmCenter]);
|
||
end;
|
||
finally
|
||
Free;
|
||
end;
|
||
end;
|
||
except
|
||
Result := False;
|
||
end;
|
||
end;
|
||
|
||
function TJvDBLocate.FilterApplicable: Boolean;
|
||
begin
|
||
Result := IsFilterApplicable(DataSet);
|
||
end;
|
||
|
||
|
||
function TJvDBLocate.LocateCallback: Boolean;
|
||
var
|
||
Clone: TJvCloneDbDataset;
|
||
begin
|
||
Result := False;
|
||
try
|
||
TBDEDataSet(DataSet).CheckBrowseMode;
|
||
Clone := TJvCloneDbDataset.Create(DataSet);
|
||
with Clone do
|
||
try
|
||
ReadOnly := True;
|
||
InitFromDataSet(TDBDataSet(DataSet), True);
|
||
OnFilterRecord := RecordFilter;
|
||
Filtered := True;
|
||
if not (Bof and Eof) then
|
||
begin
|
||
First;
|
||
Result := True;
|
||
end;
|
||
if Result then
|
||
begin
|
||
Check(DbiSetToCursor(TBDEDataSet(DataSet).Handle, Handle));
|
||
DataSet.Resync([rmExact, rmCenter]);
|
||
end;
|
||
finally
|
||
Free;
|
||
end;
|
||
except
|
||
Result := False;
|
||
end;
|
||
end;
|
||
|
||
procedure TJvDBLocate.RecordFilter(DataSet: TDataSet; var Accept: Boolean);
|
||
begin
|
||
Accept := MatchesLookup(DataSet.FieldByName(LookupField.FieldName));
|
||
end;
|
||
|
||
function TJvDBLocate.LocateFilter: Boolean;
|
||
var
|
||
SaveCursor: TCursor;
|
||
begin
|
||
if LookupExact or (LookupField.DataType = ftString) or
|
||
not (DataSet is TDBDataSet) then
|
||
Result := inherited LocateFilter
|
||
else
|
||
begin
|
||
SaveCursor := Screen.Cursor;
|
||
Screen.Cursor := crHourGlass;
|
||
try
|
||
Result := LocateCallback;
|
||
finally
|
||
Screen.Cursor := SaveCursor;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
{ DataSet locate routines }
|
||
|
||
function IsFilterApplicable(DataSet: TDataSet): Boolean;
|
||
var
|
||
Status: DBIResult;
|
||
Filter: hDBIFilter;
|
||
begin
|
||
if DataSet is TBDEDataSet then
|
||
begin
|
||
Status := DbiAddFilter(TBDEDataSet(DataSet).Handle, 0, 0, False, nil,
|
||
nil, Filter);
|
||
Result := (Status = DBIERR_NONE) or (Status = DBIERR_INVALIDFILTER);
|
||
if Result then
|
||
DbiDropFilter(TBDEDataSet(DataSet).Handle, Filter);
|
||
end
|
||
else
|
||
Result := True;
|
||
end;
|
||
|
||
function DataSetFindValue(ADataSet: TBDEDataSet; const Value,
|
||
FieldName: string): Boolean;
|
||
begin
|
||
with TJvDBLocate.Create do
|
||
try
|
||
DataSet := ADataSet;
|
||
if ADataSet is TDBDataSet then
|
||
IndexSwitch := not TDBDataSet(DataSet).Database.IsSQLBased;
|
||
Result := Locate(FieldName, Value, True, False);
|
||
finally
|
||
Free;
|
||
end;
|
||
end;
|
||
|
||
function DataSetFindLike(ADataSet: TBDEDataSet; const Value,
|
||
FieldName: string): Boolean;
|
||
begin
|
||
with TJvDBLocate.Create do
|
||
try
|
||
DataSet := ADataSet;
|
||
if ADataSet is TDBDataSet then
|
||
IndexSwitch := not TDBDataSet(DataSet).Database.IsSQLBased;
|
||
Result := Locate(FieldName, Value, False, False);
|
||
finally
|
||
Free;
|
||
end;
|
||
end;
|
||
|
||
var
|
||
SaveIndexFieldNames: TStringList = nil;
|
||
|
||
procedure UsesSaveIndexies;
|
||
begin
|
||
if SaveIndexFieldNames = nil then
|
||
SaveIndexFieldNames := TStringList.Create;
|
||
end;
|
||
|
||
procedure ReleaseSaveIndices;
|
||
begin
|
||
FreeAndNil(SaveIndexFieldNames);
|
||
end;
|
||
|
||
procedure SetIndex(Table: TTable; const IndexFieldNames: string);
|
||
var
|
||
IndexToSave: string;
|
||
begin
|
||
IndexToSave := Table.IndexFieldNames;
|
||
Table.IndexFieldNames := IndexFieldNames;
|
||
UsesSaveIndexies;
|
||
SaveIndexFieldNames.AddObject(IndexToSave, Table.MasterSource);
|
||
end;
|
||
|
||
procedure RestoreIndex(Table: TTable);
|
||
begin
|
||
if (SaveIndexFieldNames <> nil) and (SaveIndexFieldNames.Count > 0) then
|
||
begin
|
||
try
|
||
Table.IndexFieldNames :=
|
||
SaveIndexFieldNames[SaveIndexFieldNames.Count - 1];
|
||
Table.MasterSource :=
|
||
TDataSource(SaveIndexFieldNames.Objects[SaveIndexFieldNames.Count - 1]);
|
||
finally
|
||
SaveIndexFieldNames.Delete(SaveIndexFieldNames.Count - 1);
|
||
if SaveIndexFieldNames.Count = 0 then
|
||
ReleaseSaveIndices;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure DeleteRange(Table: TTable; IndexFields: array of const;
|
||
FieldValues: array of const);
|
||
var
|
||
I: Integer;
|
||
NewIndex: string;
|
||
begin
|
||
NewIndex := '';
|
||
for I := Low(IndexFields) to High(IndexFields) do
|
||
begin
|
||
NewIndex := NewIndex + string(TVarRec(IndexFields[I]).VString^);
|
||
if I <> High(IndexFields) then
|
||
NewIndex := NewIndex + ';';
|
||
end;
|
||
SetIndex(Table, NewIndex);
|
||
try
|
||
Table.SetRange(FieldValues, FieldValues);
|
||
try
|
||
while not Table.Eof do
|
||
Table.Delete;
|
||
finally
|
||
Table.CancelRange;
|
||
end;
|
||
finally
|
||
RestoreIndex(Table);
|
||
end;
|
||
end;
|
||
|
||
procedure ReindexTable(Table: TTable);
|
||
var
|
||
WasActive: Boolean;
|
||
WasExclusive: Boolean;
|
||
begin
|
||
with Table do
|
||
begin
|
||
WasActive := Active;
|
||
WasExclusive := Exclusive;
|
||
DisableControls;
|
||
try
|
||
if not (WasActive and WasExclusive) then
|
||
Close;
|
||
try
|
||
Exclusive := True;
|
||
Open;
|
||
Check(dbiRegenIndexes(Handle));
|
||
finally
|
||
if not (WasActive and WasExclusive) then
|
||
begin
|
||
Close;
|
||
Exclusive := WasExclusive;
|
||
Active := WasActive;
|
||
end;
|
||
end;
|
||
finally
|
||
EnableControls;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure PackTable(Table: TTable);
|
||
{ This routine copied and modified from demo unit TableEnh.pas
|
||
from Borland Int. }
|
||
var
|
||
{ CurProp holds information about the structure of the table }
|
||
CurProp: CURProps;
|
||
{ Specific information about the table structure, indexes, etc. }
|
||
TblDesc: CRTblDesc;
|
||
{ Uses as a handle to the database }
|
||
hDb: hDBIDb;
|
||
{ Path to the currently opened table }
|
||
TablePath: array [0..dbiMaxPathLen] of AnsiChar;
|
||
Exclusive: Boolean;
|
||
begin
|
||
if not Table.Active then
|
||
_DBError(SDataSetClosed);
|
||
Check(DbiGetCursorProps(Table.Handle, CurProp));
|
||
if StrComp(CurProp.szTableType, szPARADOX) = 0 then
|
||
begin
|
||
{ Call DbiDoRestructure procedure if PARADOX table }
|
||
hDb := nil;
|
||
{ Initialize the table descriptor }
|
||
FillChar(TblDesc, SizeOf(CRTblDesc), 0);
|
||
with TblDesc do
|
||
begin
|
||
{ Place the table name in descriptor }
|
||
StrPCopy(szTblName, AnsiString(Table.TableName));
|
||
{ Place the table type in descriptor }
|
||
StrCopy(szTblType, CurProp.szTableType);
|
||
bPack := True;
|
||
bProtected := CurProp.bProtected;
|
||
end;
|
||
{ Get the current table's directory. This is why the table MUST be
|
||
opened until now }
|
||
Check(DbiGetDirectory(Table.DBHandle, False, TablePath));
|
||
{ Close the table }
|
||
Table.Close;
|
||
try
|
||
{ NOW: since the DbiDoRestructure call needs a valid DB handle BUT the
|
||
table cannot be opened, call DbiOpenDatabase to get a valid handle.
|
||
Setting TTable.Active = False does not give you a valid handle }
|
||
Check(DbiOpenDatabase(nil, szCFGDBSTANDARD, dbiReadWrite, dbiOpenExcl, nil,
|
||
0, nil, nil, hDb));
|
||
{ Set the table's directory to the old directory }
|
||
Check(DbiSetDirectory(hDb, TablePath));
|
||
{ Pack the PARADOX table }
|
||
Check(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False));
|
||
{ Close the temporary database handle }
|
||
Check(DbiCloseDatabase(hDb));
|
||
finally
|
||
{ Re-Open the table }
|
||
Table.Open;
|
||
end;
|
||
end
|
||
else
|
||
if StrComp(CurProp.szTableType, szDBASE) = 0 then
|
||
begin
|
||
{ Call DbiPackTable procedure if dBase table }
|
||
Exclusive := Table.Exclusive;
|
||
Table.Close;
|
||
try
|
||
Table.Exclusive := True;
|
||
Table.Open;
|
||
try
|
||
Check(DbiPackTable(Table.DBHandle, Table.Handle, nil, nil, True));
|
||
finally
|
||
Table.Close;
|
||
end;
|
||
finally
|
||
Table.Exclusive := Exclusive;
|
||
Table.Open;
|
||
end;
|
||
end
|
||
else
|
||
DbiError(DBIERR_WRONGDRVTYPE);
|
||
end;
|
||
|
||
procedure FetchAllRecords(DataSet: TBDEDataSet);
|
||
begin
|
||
with DataSet do
|
||
if not Eof then
|
||
begin
|
||
CheckBrowseMode;
|
||
Check(DbiSetToEnd(Handle));
|
||
Check(DbiGetPriorRecord(Handle, dbiNOLOCK, nil, nil));
|
||
CursorPosChanged;
|
||
UpdateCursorPos;
|
||
end;
|
||
end;
|
||
|
||
procedure BdeFlushBuffers;
|
||
var
|
||
I, L: Integer;
|
||
Session: TSession;
|
||
J: Integer;
|
||
begin
|
||
for J := 0 to Sessions.Count - 1 do
|
||
begin
|
||
Session := Sessions[J];
|
||
if not Session.Active then
|
||
Continue;
|
||
for I := 0 to Session.DatabaseCount - 1 do
|
||
begin
|
||
with Session.Databases[I] do
|
||
if Connected and not IsSQLBased then
|
||
begin
|
||
for L := 0 to DataSetCount - 1 do
|
||
begin
|
||
if DataSets[L].Active then
|
||
DbiSaveChanges(DataSets[L].Handle);
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function DataSetRecordCount(DataSet: TDataSet): Longint;
|
||
var
|
||
IsCount: Boolean;
|
||
begin
|
||
if DataSet is TBDEDataSet then
|
||
begin
|
||
IsCount := (DbiGetExactRecordCount(TBDEDataSet(DataSet).Handle,
|
||
Result) = DBIERR_NONE) or (DbiGetRecordCount(TBDEDataSet(DataSet).Handle,
|
||
Result) = DBIERR_NONE);
|
||
end
|
||
else
|
||
try
|
||
Result := DataSet.RecordCount;
|
||
IsCount := True;
|
||
except
|
||
IsCount := False;
|
||
end;
|
||
if not IsCount then
|
||
Result := -1;
|
||
end;
|
||
|
||
function DataSetRecNo(DataSet: TDataSet): Longint;
|
||
var
|
||
CurProp: CURProps;
|
||
FRecProp: RECProps;
|
||
begin
|
||
Result := -1;
|
||
if (DataSet <> nil) and DataSet.Active and (DataSet.State in [dsBrowse,
|
||
dsEdit]) then
|
||
begin
|
||
if not (DataSet is TBDEDataSet) then
|
||
begin
|
||
Result := DataSet.RecNo;
|
||
Exit;
|
||
end;
|
||
if DbiGetCursorProps(TBDEDataSet(DataSet).Handle, CurProp) <> DBIERR_NONE then
|
||
Exit;
|
||
if (StrComp(CurProp.szTableType, szPARADOX) = 0) or
|
||
(CurProp.iSeqNums = 1) then
|
||
begin
|
||
DataSet.GetCurrentRecord(nil);
|
||
if DbiGetSeqNo(TBDEDataSet(DataSet).Handle, Result) <> DBIERR_NONE then
|
||
Result := -1;
|
||
end
|
||
else
|
||
if StrComp(CurProp.szTableType, szDBASE) = 0 then
|
||
begin
|
||
DataSet.GetCurrentRecord(nil);
|
||
if DbiGetRecord(TBDEDataSet(DataSet).Handle, dbiNOLOCK, nil, @FRecProp) = DBIERR_NONE
|
||
then
|
||
Result := FRecProp.iPhyRecNum;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function DataSetPositionStr(DataSet: TDataSet): string;
|
||
var
|
||
RecNo, RecCount: Longint;
|
||
begin
|
||
try
|
||
RecNo := DataSetRecNo(DataSet);
|
||
except
|
||
RecNo := -1;
|
||
end;
|
||
if RecNo >= 0 then
|
||
begin
|
||
RecCount := DataSetRecordCount(DataSet);
|
||
if RecCount >= 0 then
|
||
Result := Format('%d:%d', [RecNo, RecCount])
|
||
else
|
||
Result := IntToStr(RecNo);
|
||
end
|
||
else
|
||
Result := '';
|
||
end;
|
||
|
||
function TransActive(Database: TDatabase): Boolean;
|
||
var
|
||
Info: XInfo;
|
||
S: hDBISes;
|
||
begin
|
||
Result := False;
|
||
if DbiGetCurrSession(S) <> DBIERR_NONE then
|
||
Exit;
|
||
Result := (Database.Handle <> nil) and
|
||
(DbiGetTranInfo(Database.Handle, nil, @Info) = DBIERR_NONE) and
|
||
(Info.exState = xsActive);
|
||
DbiSetCurrSession(S);
|
||
end;
|
||
|
||
function GetBdeDirectory: string;
|
||
const
|
||
Ident = 'DLLPATH';
|
||
var
|
||
Ini: TRegistry;
|
||
const
|
||
BdeKey = 'SOFTWARE\Borland\Database Engine';
|
||
begin
|
||
Result := '';
|
||
Ini := TRegistry.Create;
|
||
try
|
||
Ini.RootKey := HKEY_LOCAL_MACHINE;
|
||
if Ini.OpenKey(BdeKey, False) then
|
||
if Ini.ValueExists(Ident) then
|
||
Result := Ini.ReadString(Ident);
|
||
{ Check for multiple directories, use only the first one }
|
||
if Pos(';', Result) > 0 then
|
||
Delete(Result, Pos(';', Result), MaxInt);
|
||
if (Length(Result) > 2) and (Result[Length(Result)] <> '\') then
|
||
Result := Result + '\';
|
||
finally
|
||
Ini.Free;
|
||
end;
|
||
end;
|
||
|
||
procedure ExportDataSetEx(Source: TBDEDataSet; DestTable: TTable;
|
||
TableType: TTableType; const AsciiCharSet: string;
|
||
AsciiDelimited: Boolean; AsciiDelimiter, AsciiSeparator: Char;
|
||
MaxRecordCount: Longint);
|
||
|
||
function ExportAsciiField(Field: TField): Boolean;
|
||
begin
|
||
Result := Field.Visible and not (Field.Calculated or Field.Lookup) and
|
||
not (Field.DataType in ftNonTextTypes + [ftUnknown]);
|
||
end;
|
||
|
||
const
|
||
TextExt = '.TXT';
|
||
SchemaExt = '.SCH';
|
||
var
|
||
I: Integer;
|
||
S, Path: string;
|
||
BatchMove: TBatchMove;
|
||
TablePath: array[0..dbiMaxPathLen] of AnsiChar;
|
||
begin
|
||
if Source = nil then
|
||
_DBError(SDataSetEmpty);
|
||
if DestTable.Active then
|
||
DestTable.Close;
|
||
if Source is TDBDataSet then
|
||
DestTable.SessionName := TDBDataSet(Source).SessionName;
|
||
if (TableType = ttDefault) then
|
||
begin
|
||
if DestTable.TableType <> ttDefault then
|
||
TableType := DestTable.TableType
|
||
else
|
||
if AnsiSameText(ExtractFileExt(DestTable.TableName), TextExt) then
|
||
TableType := ttASCII;
|
||
end;
|
||
BatchMove := TBatchMove.Create(Application);
|
||
try
|
||
StartWait;
|
||
try
|
||
BatchMove.Mode := batCopy;
|
||
BatchMove.Source := Source;
|
||
BatchMove.Destination := DestTable;
|
||
DestTable.TableType := TableType;
|
||
BatchMove.Mappings.Clear;
|
||
if (DestTable.TableType = ttASCII) then
|
||
begin
|
||
if AnsiSameText(ExtractFileExt(DestTable.TableName), SchemaExt) then
|
||
DestTable.TableName := ChangeFileExt(DestTable.TableName, TextExt);
|
||
with Source do
|
||
for I := 0 to FieldCount - 1 do
|
||
begin
|
||
if ExportAsciiField(Fields[I]) then
|
||
BatchMove.Mappings.Add(Format('%s=%0:s',
|
||
[Fields[I].FieldName]));
|
||
end;
|
||
BatchMove.RecordCount := 1;
|
||
end
|
||
else
|
||
BatchMove.RecordCount := MaxRecordCount;
|
||
BatchMove.Execute;
|
||
if DestTable.TableType = ttASCII then
|
||
begin
|
||
{ ASCII table always created in "fixed" format with "ascii"
|
||
character set }
|
||
with BatchMove do
|
||
begin
|
||
Mode := batAppend;
|
||
RecordCount := MaxRecordCount;
|
||
end;
|
||
S := ChangeFileExt(ExtractFileName(DestTable.TableName), '');
|
||
Path := NormalDir(ExtractFilePath(DestTable.TableName));
|
||
if Path = '' then
|
||
begin
|
||
DestTable.Open;
|
||
try
|
||
Check(DbiGetDirectory(DestTable.DBHandle, False, TablePath));
|
||
Path := NormalDir(string(OemToAnsiStr(StrPas(TablePath))));
|
||
finally
|
||
DestTable.Close;
|
||
end;
|
||
end;
|
||
with TIniFile.Create(ChangeFileExt(Path + S, SchemaExt)) do
|
||
try
|
||
if AsciiCharSet <> '' then
|
||
WriteString(S, 'CharSet', AsciiCharSet)
|
||
else
|
||
WriteString(S, 'CharSet', 'ascii');
|
||
if AsciiDelimited then
|
||
begin { change ASCII-file format to CSV }
|
||
WriteString(S, 'Filetype', 'VARYING');
|
||
WriteString(S, 'Delimiter', AsciiDelimiter);
|
||
WriteString(S, 'Separator', AsciiSeparator);
|
||
end;
|
||
finally
|
||
Free;
|
||
end;
|
||
{ clear previous output - overwrite existing file }
|
||
S := Path + ExtractFileName(DestTable.TableName);
|
||
if Length(ExtractFileExt(S)) < 2 then
|
||
S := ChangeFileExt(S, TextExt);
|
||
I := FileCreate(S);
|
||
if I < 0 then
|
||
raise EFCreateError.CreateResFmt(@SFCreateError, [S]);
|
||
FileClose(I);
|
||
BatchMove.Execute;
|
||
end;
|
||
finally
|
||
StopWait;
|
||
end;
|
||
finally
|
||
BatchMove.Free;
|
||
end;
|
||
end;
|
||
|
||
procedure ExportDataSet(Source: TBDEDataSet; DestTable: TTable;
|
||
TableType: TTableType; const AsciiCharSet: string;
|
||
AsciiDelimited: Boolean; MaxRecordCount: Longint);
|
||
begin
|
||
ExportDataSetEx(Source, DestTable, TableType, AsciiCharSet,
|
||
AsciiDelimited, '"', ',', MaxRecordCount);
|
||
end;
|
||
|
||
procedure ImportDataSet(Source: TBDEDataSet; DestTable: TTable;
|
||
MaxRecordCount: Longint; Mappings: TStrings; Mode: TBatchMode);
|
||
var
|
||
BatchMove: TBatchMove;
|
||
begin
|
||
if Source = nil then
|
||
_DBError(SDataSetEmpty);
|
||
if (Source is TDBDataSet) and not Source.Active then
|
||
TDBDataSet(Source).SessionName := DestTable.SessionName;
|
||
BatchMove := TBatchMove.Create(Application);
|
||
try
|
||
StartWait;
|
||
try
|
||
BatchMove.Mode := Mode;
|
||
BatchMove.Source := Source;
|
||
BatchMove.Destination := DestTable;
|
||
if Mappings.Count > 0 then
|
||
BatchMove.Mappings.AddStrings(Mappings);
|
||
BatchMove.RecordCount := MaxRecordCount;
|
||
BatchMove.Execute;
|
||
finally
|
||
StopWait;
|
||
end;
|
||
finally
|
||
BatchMove.Free;
|
||
end;
|
||
end;
|
||
|
||
function GetNativeHandle(Database: TDatabase; Buffer: Pointer;
|
||
BufSize: Integer): Pointer;
|
||
var
|
||
Len: Word;
|
||
begin
|
||
Result := nil;
|
||
if Assigned(Database) and Database.Connected then
|
||
begin
|
||
if Database.IsSQLBased then
|
||
begin
|
||
Check(DbiGetProp(hDBIObj(Database.Handle), dbNATIVEHNDL,
|
||
Buffer, BufSize, Len));
|
||
Result := Buffer;
|
||
end
|
||
else
|
||
DBError(RsELocalDatabase);
|
||
end
|
||
else
|
||
_DBError(SDatabaseClosed);
|
||
end;
|
||
|
||
procedure BdeTranslate(Locale: TLocale; Source, Dest: PAnsiChar; ToOem: Boolean);
|
||
var
|
||
Len: Cardinal;
|
||
begin
|
||
Len := StrLen(Source);
|
||
if ToOem then
|
||
AnsiToNativeBuf(Locale, Source, Dest, Len)
|
||
else
|
||
NativeToAnsiBuf(Locale, Source, Dest, Len);
|
||
if Source <> Dest then
|
||
Dest[Len] := #0;
|
||
end;
|
||
|
||
function TrimMessage(Msg: PAnsiChar): PAnsiChar;
|
||
var
|
||
Blank: Boolean;
|
||
Source, Dest: PAnsiChar;
|
||
begin
|
||
Source := Msg;
|
||
Dest := Msg;
|
||
Blank := False;
|
||
while Source^ <> #0 do
|
||
begin
|
||
if Source^ <= ' ' then
|
||
Blank := True
|
||
else
|
||
begin
|
||
if Blank then
|
||
begin
|
||
Dest^ := ' ';
|
||
Inc(Dest);
|
||
Blank := False;
|
||
end;
|
||
Dest^ := Source^;
|
||
Inc(Dest);
|
||
end;
|
||
Inc(Source);
|
||
end;
|
||
if (Dest > Msg) and ((Dest - 1)^ = '.') then
|
||
Dec(Dest);
|
||
Dest^ := #0;
|
||
Result := Msg;
|
||
end;
|
||
|
||
function BdeErrorMsg(ErrorCode: DBIResult): string;
|
||
var
|
||
I: Integer;
|
||
NativeError: Longint;
|
||
Msg, LastMsg: DBIMSG;
|
||
begin
|
||
I := 1;
|
||
DbiGetErrorString(ErrorCode, Msg);
|
||
TrimMessage(Msg);
|
||
if Msg[0] = #0 then
|
||
Result := Format(SBDEError, [ErrorCode])
|
||
else
|
||
Result := string(StrPas(Msg));
|
||
while True do
|
||
begin
|
||
StrCopy(LastMsg, Msg);
|
||
ErrorCode := DbiGetErrorEntry(I, NativeError, Msg);
|
||
if (ErrorCode = DBIERR_NONE) or
|
||
(ErrorCode = DBIERR_NOTINITIALIZED) then
|
||
Break;
|
||
TrimMessage(Msg);
|
||
if (Msg[0] <> #0) and (StrComp(Msg, LastMsg) <> 0) then
|
||
Result := Format('%s. %s', [Result, Msg]);
|
||
Inc(I);
|
||
end;
|
||
for I := 1 to Length(Result) do
|
||
if Result[I] < ' ' then
|
||
Result[I] := ' ';
|
||
end;
|
||
|
||
procedure DataSetShowDeleted(DataSet: TBDEDataSet; Show: Boolean);
|
||
begin
|
||
with DataSet do
|
||
begin
|
||
CheckBrowseMode;
|
||
Check(DbiValidateProp(hDBIObj(Handle), curSOFTDELETEON, True));
|
||
DisableControls;
|
||
try
|
||
Check(DbiSetProp(hDBIObj(Handle), curSOFTDELETEON, Ord(Show)));
|
||
finally
|
||
EnableControls;
|
||
end;
|
||
if DataSet is TTable then
|
||
TTable(DataSet).Refresh
|
||
else
|
||
begin
|
||
CursorPosChanged;
|
||
First;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function CurrentRecordDeleted(DataSet: TBDEDataSet): Boolean;
|
||
var
|
||
FRecProp: RECProps;
|
||
begin
|
||
Result := False;
|
||
if (DataSet <> nil) and DataSet.Active then
|
||
begin
|
||
DataSet.GetCurrentRecord(nil);
|
||
if DbiGetRecord(DataSet.Handle, dbiNOLOCK, nil, @FRecProp) = DBIERR_NONE
|
||
then
|
||
Result := FRecProp.bDeleteFlag;
|
||
end;
|
||
end;
|
||
|
||
procedure DbNotSupported;
|
||
begin
|
||
DbiError(DBIERR_NOTSUPPORTED);
|
||
end;
|
||
|
||
procedure ToggleDebugLayer(Active: Boolean; const DebugFile: string);
|
||
const
|
||
Options: array[Boolean] of Longint = (0, DEBUGON or OUTPUTTOFILE or
|
||
APPENDTOLOG);
|
||
var
|
||
FileName: DBIPATH;
|
||
begin
|
||
Check(DbiDebugLayerOptions(Options[Active], StrPLCopy(FileName,
|
||
AnsiString(DebugFile), SizeOf(DBIPATH) - 1)));
|
||
end;
|
||
{ begin JvDBUtil }
|
||
|
||
procedure ExecuteSQLScript(Base: TDatabase; const Script: string; const Commit: TCommit; OnProgress: TJvDBProgressEvent; const UserData: Integer);
|
||
var
|
||
N: Integer;
|
||
Term: Char;
|
||
|
||
function NextQuery: string;
|
||
var
|
||
C: Char;
|
||
Rem: Boolean;
|
||
begin
|
||
Result := '';
|
||
Rem := False;
|
||
while Length(Script) >= N do
|
||
begin
|
||
C := Script[N];
|
||
Inc(N);
|
||
if (C = Term) and not Rem then
|
||
Exit;
|
||
Result := Result + C;
|
||
if (C = '/') and (Length(Script) >= N) and (Script[N] = '*') then
|
||
Rem := True;
|
||
if (C = '*') and (Length(Script) >= N) and (Script[N] = '/') and Rem then
|
||
Rem := False;
|
||
end;
|
||
Result := '';
|
||
end;
|
||
|
||
function SetTerm(S: string): Boolean;
|
||
var
|
||
Rem: Boolean;
|
||
begin
|
||
Rem := False;
|
||
while (Length(S) > 0) do
|
||
begin
|
||
if CharInSet(S[1], [' ', Cr, Lf]) then
|
||
Delete(S, 1, 1)
|
||
else
|
||
if Rem then
|
||
if (S[1] = '*') and (Length(S) > 1) and (S[2] = '/') then
|
||
begin
|
||
Delete(S, 1, 2);
|
||
Rem := False;
|
||
end
|
||
else
|
||
Delete(S, 1, 1)
|
||
else
|
||
if (S[1] = '/') and (Length(S) > 1) and (S[2] = '*') then
|
||
begin
|
||
Delete(S, 1, 2);
|
||
Rem := True;
|
||
end
|
||
else
|
||
Break;
|
||
end;
|
||
Result := AnsiStrLIComp(PChar(S), 'set term', 8) = 0;
|
||
if Result then
|
||
begin
|
||
S := Trim(Copy(S, 9, 1024));
|
||
if Length(S) = 1 then
|
||
Term := S[1]
|
||
else
|
||
EDatabaseError.Create('Bad term');
|
||
Exit;
|
||
end;
|
||
Result := AnsiStrLIComp(PChar(S), 'commit work', 11) = 0;
|
||
if Result then
|
||
begin
|
||
Base.Commit;
|
||
Base.StartTransaction;
|
||
Exit;
|
||
end;
|
||
end;
|
||
|
||
var
|
||
Q: string;
|
||
ErrPos: Integer;
|
||
NBeg: Integer;
|
||
X, Y, N2: Integer;
|
||
S1: string;
|
||
Query: TQuery;
|
||
Stop: Boolean;
|
||
begin
|
||
if Commit in [ctStep, ctAll] then
|
||
Base.StartTransaction;
|
||
Query := TQuery.Create(Application);
|
||
try
|
||
Query.DatabaseName := Base.DatabaseName;
|
||
Query.ParamCheck := False;
|
||
N := 1;
|
||
Term := ';';
|
||
Stop := False;
|
||
NBeg := 1;
|
||
try
|
||
Q := NextQuery;
|
||
while Q <> '' do
|
||
begin
|
||
if not SetTerm(Q) then
|
||
begin
|
||
if Assigned(OnProgress) then
|
||
begin
|
||
S1 := Q;
|
||
N2 := 0;
|
||
while (Length(S1) > 0) and CharInSet(S1[1], [' ', Cr, Lf]) do
|
||
begin
|
||
Delete(S1, 1, 1);
|
||
Inc(N2);
|
||
end;
|
||
GetXYByPos(Script, NBeg + N2, X, Y);
|
||
if Assigned(OnProgress) then
|
||
OnProgress(UserData, Stop, Y)
|
||
else
|
||
// (rom) i do not like this
|
||
Application.ProcessMessages;
|
||
if Stop then
|
||
SysUtils.Abort;
|
||
end;
|
||
Query.SQL.Text := Q;
|
||
Query.ExecSQL;
|
||
if Commit = ctStep then
|
||
begin
|
||
Base.Commit;
|
||
Base.StartTransaction;
|
||
end;
|
||
Query.Close;
|
||
end;
|
||
NBeg := N + 1;
|
||
Q := NextQuery;
|
||
end;
|
||
if Commit in [ctStep, ctAll] then
|
||
Base.Commit;
|
||
except
|
||
on E: Exception do
|
||
begin
|
||
if Commit in [ctStep, ctAll] then
|
||
Base.Rollback;
|
||
if E is EDatabaseError then
|
||
begin
|
||
ErrPos := NBeg;
|
||
//..
|
||
raise EJvScriptError.Create(E.Message, ErrPos);
|
||
end
|
||
else
|
||
raise;
|
||
end;
|
||
end;
|
||
finally
|
||
Query.Free;
|
||
end;
|
||
end;
|
||
|
||
function GetQueryResult(const DatabaseName, SQL: string): Variant;
|
||
var
|
||
Query: TQuery;
|
||
begin
|
||
Query := TQuery.Create(Application);
|
||
try
|
||
Query.DatabaseName := DatabaseName;
|
||
Query.ParamCheck := False;
|
||
Query.SQL.Text := SQL;
|
||
Query.Open;
|
||
Result := Query.Fields[0].AsVariant;
|
||
finally
|
||
Query.Free;
|
||
end;
|
||
end;
|
||
|
||
function GetStoredProcResult(const ADatabaseName, AStoredProcName: string; AParams: array of Variant;
|
||
const AResultName: string): Variant;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
with TStoredProc.Create(Application) do
|
||
try
|
||
DatabaseName := ADatabaseName;
|
||
ParamBindMode := pbByNumber;
|
||
StoredProcName := AStoredProcName;
|
||
Prepare;
|
||
for I := Low(AParams) to High(AParams) do
|
||
Params[I].Value := AParams[I];
|
||
ExecProc;
|
||
Result := ParamByName(AResultName).Value;
|
||
finally
|
||
Free;
|
||
end;
|
||
end;
|
||
|
||
function StrFieldDesc(Field: FLDDesc): string;
|
||
|
||
function SUnits1: string;
|
||
begin
|
||
Result := IntToStr(Field.iUnits1);
|
||
end;
|
||
|
||
function SUnits2: string;
|
||
begin
|
||
if Field.iUnits2 < 0 then
|
||
Result := IntToStr(-Field.iUnits2)
|
||
else
|
||
Result := IntToStr(Field.iUnits2);
|
||
end;
|
||
|
||
begin
|
||
with Field do
|
||
case iFldType of
|
||
fldUNKNOWN:
|
||
Result := 'unknown';
|
||
fldZSTRING:
|
||
Result := 'string'; { Null terminated string }
|
||
fldDATE:
|
||
Result := 'date'; { Date (32 bit) }
|
||
fldBLOB:
|
||
Result := 'blob'; { Blob }
|
||
fldBOOL:
|
||
Result := 'boolean'; { Boolean (16 bit) }
|
||
fldINT16:
|
||
Result := 'integer'; { 16 bit signed number }
|
||
fldINT32:
|
||
Result := 'long integer'; { 32 bit signed number }
|
||
|
||
fldFLOAT:
|
||
Result := 'float'; { 64 bit floating point }
|
||
fldBCD:
|
||
Result := 'BCD'; { BCD }
|
||
fldBYTES:
|
||
Result := 'bytes'; { Fixed number of bytes }
|
||
fldTIME:
|
||
Result := 'time'; { Time (32 bit) }
|
||
fldTIMESTAMP:
|
||
Result := 'timestamp'; { Time-stamp (64 bit) }
|
||
fldUINT16:
|
||
Result := 'unsigned int'; { Unsigned 16 bit Integer }
|
||
fldUINT32:
|
||
Result := 'unsigned long int'; { Unsigned 32 bit Integer }
|
||
|
||
fldFLOATIEEE:
|
||
Result := 'float IEEE'; { 80-bit IEEE float }
|
||
fldVARBYTES:
|
||
Result := 'varbytes'; { Length prefixed var bytes }
|
||
fldLOCKINFO:
|
||
Result := 'lockinfo'; { Look for LOCKINFO typedef }
|
||
fldCURSOR:
|
||
Result := 'Oracle cursor'; { For Oracle Cursor type }
|
||
|
||
{ Paradox types (Physical) }
|
||
fldPDXCHAR:
|
||
Result := 'alpha(' + SUnits1 + ')'; { Alpha (string) }
|
||
fldPDXNUM:
|
||
Result := 'numeric(' + SUnits1 + ', ' + SUnits2 + ')'; { Numeric }
|
||
|
||
fldPDXMONEY:
|
||
Result := 'money'; { Money }
|
||
fldPDXDATE:
|
||
Result := 'date'; { Date }
|
||
fldPDXSHORT:
|
||
Result := 'smallint'; { Short }
|
||
fldPDXMEMO:
|
||
Result := 'memo blob'; { Text Memo (blob) }
|
||
fldPDXBINARYBLOB:
|
||
Result := 'binary blob'; { Binary data (blob) }
|
||
fldPDXFMTMEMO:
|
||
Result := 'formatted blob'; { Formatted text (blob) }
|
||
fldPDXOLEBLOB:
|
||
Result := 'OLE blob'; { OLE object (blob) }
|
||
|
||
fldPDXGRAPHIC:
|
||
Result := 'graphic blob'; { Graphics object (blob) }
|
||
fldPDXLONG:
|
||
Result := 'long integer'; { Long }
|
||
fldPDXTIME:
|
||
Result := 'time'; { Time }
|
||
fldPDXDATETIME:
|
||
Result := 'date time'; { Time Stamp }
|
||
fldPDXBOOL:
|
||
Result := 'boolean'; { Logical }
|
||
fldPDXAUTOINC:
|
||
Result := 'auto increment'; { Auto increment (long) }
|
||
fldPDXBYTES:
|
||
Result := 'bytes'; { Fixed number of bytes }
|
||
|
||
fldPDXBCD:
|
||
Result := 'BCD'; { BCD (32 digits) }
|
||
|
||
{ xBASE types (Physical) }
|
||
fldDBCHAR:
|
||
Result := 'character'; { Char string }
|
||
fldDBNUM:
|
||
Result := 'number'; { Number }
|
||
fldDBMEMO:
|
||
Result := 'memo blob'; { Memo (blob) }
|
||
fldDBBOOL:
|
||
Result := 'logical'; { Logical }
|
||
fldDBDATE:
|
||
Result := 'date'; { Date }
|
||
fldDBFLOAT:
|
||
Result := 'float'; { Float }
|
||
|
||
fldDBLOCK:
|
||
Result := 'LOCKINFO'; { Logical type is LOCKINFO }
|
||
fldDBOLEBLOB:
|
||
Result := 'OLE blob'; { OLE object (blob) }
|
||
fldDBBINARY:
|
||
Result := 'binary blob'; { Binary data (blob) }
|
||
fldDBBYTES:
|
||
Result := 'bytes'; { Only for TEMPORARY tables }
|
||
fldDBLONG:
|
||
Result := 'long integer'; { Long (Integer) }
|
||
fldDBDATETIME:
|
||
Result := 'date time'; { Time Stamp }
|
||
fldDBDOUBLE:
|
||
Result := 'double'; { Double }
|
||
|
||
fldDBAUTOINC:
|
||
Result := 'auto increment'; { Auto increment (long) }
|
||
|
||
{ InterBase types (Physical) }
|
||
1026:
|
||
Result := 'integer';
|
||
1028:
|
||
Result := 'numeric(' + SUnits1 + ', ' + SUnits2 + ')'; { Numeric }
|
||
1029:
|
||
Result := 'char(' + SUnits1 + ')';
|
||
1031:
|
||
Result := 'date'; { Date }
|
||
else
|
||
Result := 'unknown type';
|
||
end;
|
||
end;
|
||
|
||
{************************ Variant conversion routines ************************}
|
||
|
||
function Var2Type(V: Variant; const VarType: Integer): Variant;
|
||
begin
|
||
if V = Null then
|
||
begin
|
||
case VarType of
|
||
varString, varOleStr:
|
||
Result := '';
|
||
varInteger, varSmallint, varByte:
|
||
Result := 0;
|
||
varBoolean:
|
||
Result := False;
|
||
varSingle, varDouble, varCurrency, varDate:
|
||
Result := 0.0;
|
||
else
|
||
Result := VarAsType(V, VarType);
|
||
end;
|
||
end
|
||
else
|
||
Result := VarAsType(V, VarType);
|
||
end;
|
||
|
||
procedure CopyRecord(DataSet: TDataSet);
|
||
var
|
||
I: Integer;
|
||
begin
|
||
with DataSet, TStringList.Create do
|
||
try
|
||
for I := 0 to FieldCount - 1 do
|
||
Add(Fields[I].AsString);
|
||
DataSet.Append;
|
||
for I := 0 to FieldCount - 1 do
|
||
if Fields[I].IsNull then
|
||
Fields[I].AsString := Strings[I];
|
||
finally
|
||
Free;
|
||
end
|
||
end;
|
||
|
||
procedure AddReference(Tbl: TTable; RefName: string; RefField: Word;
|
||
MasterTable: string; MasterField: Word; ModOp, DelOp: RINTQual);
|
||
var
|
||
hDb: hDBIDb;
|
||
TblDesc: CRTblDesc;
|
||
RInt: pRINTDesc;
|
||
Dir: string;
|
||
OpType: CROpType;
|
||
begin
|
||
SetLength(Dir, dbiMaxNameLen + 1);
|
||
Check(DbiGetDirectory(Tbl.DBHandle, False, PAnsiChar(AnsiString(Dir))));
|
||
SetLength(Dir, StrLen(PChar(Dir)));
|
||
RInt := AllocMem(SizeOf(RINTDesc));
|
||
try
|
||
FillChar(TblDesc, SizeOf(CRTblDesc), #0);
|
||
Tbl.DisableControls;
|
||
Tbl.Close;
|
||
Check(DbiOpenDatabase(nil, nil, dbiReadWrite, dbiOpenExcl, nil, 0, nil, nil, hDb));
|
||
Check(DbiSetDirectory(hDb, PAnsiChar(AnsiString(Dir))));
|
||
with RInt^ do
|
||
begin
|
||
StrPCopy(szRintName, AnsiString(RefName));
|
||
StrPCopy(szTblName, AnsiString(MasterTable));
|
||
eType := rintDEPENDENT;
|
||
eModOp := ModOp;
|
||
eDelOp := DelOp;
|
||
iFldCount := 1;
|
||
aiThisTabFld[0] := RefField;
|
||
aiOthTabFld[0] := MasterField;
|
||
end;
|
||
TblDesc.iRintCount := 1;
|
||
TblDesc.pRINTDesc := RInt;
|
||
OpType := crADD;
|
||
TblDesc.pecrRintOp := @OpType;
|
||
StrPCopy(TblDesc.szTblName, AnsiString(Tbl.TableName));
|
||
StrCopy(TblDesc.szTblType, szPARADOX);
|
||
Check(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False));
|
||
finally
|
||
Check(DbiCloseDatabase(hDb));
|
||
FreeMem(RInt, SizeOf(RINTDesc));
|
||
Tbl.EnableControls;
|
||
Tbl.Open;
|
||
end;
|
||
end;
|
||
|
||
{
|
||
procedure PackTable(Table: TTable);
|
||
var
|
||
Props: CURProps;
|
||
hDb: hDBIDb;
|
||
TableDesc: CRTblDesc;
|
||
begin
|
||
// Make sure the table is open exclusively so we can get the db handle...
|
||
if not Table.Active then
|
||
raise EDatabaseError.CreateRes(@STableNotOpen);
|
||
if not Table.Exclusive then
|
||
raise EDatabaseError.CreateRes(@STableNotOpenExclusively);
|
||
|
||
// Get the table properties to determine table type...
|
||
Check(DbiGetCursorProps(Table.Handle, Props));
|
||
|
||
// If the table is a Paradox table, you must call DbiDoRestructure...
|
||
if Props.szTableType = szPARADOX then
|
||
begin
|
||
// Blank out the structure...
|
||
FillChar(TableDesc, SizeOf(TableDesc), 0);
|
||
// Get the database handle from the table's cursor handle...
|
||
|
||
Check(DbiGetObjFromObj(hDBIObj(Table.Handle), objDATABASE, hDBIObj(hDb)));
|
||
// Put the table name in the table descriptor...
|
||
StrPCopy(TableDesc.szTblName, Table.TableName);
|
||
// Put the table type in the table descriptor...
|
||
StrPCopy(TableDesc.szTblType, Props.szTableType);
|
||
// Set the Pack option in the table descriptor to True...
|
||
TableDesc.bPack := True;
|
||
// Close the table so the restructure can complete...
|
||
Table.Close;
|
||
// Call DbiDoRestructure...
|
||
Check(DbiDoRestructure(hDb, 1, @TableDesc, nil, nil, nil, False));
|
||
end
|
||
else
|
||
// If the table is a dBASE table, simply call DbiPackTable...
|
||
if (Props.szTableType = szDBASE) then
|
||
Check(DbiPackTable(Table.DBHandle, Table.Handle, nil, szDBASE, True))
|
||
else
|
||
// Pack only works on Paradox or dBASE; nothing else...
|
||
raise EDatabaseError.CreateRes(@SNoParadoxDBaseTable);
|
||
Table.Open;
|
||
end;
|
||
}
|
||
//Add a master password to a Paradox table.
|
||
//This procedure uses the following input:
|
||
//AddMasterPassword(Table1, 'MyNewPassword')
|
||
|
||
procedure AddMasterPassword(Table: TTable; pswd: string);
|
||
const
|
||
RESTRUCTURE_TRUE = WordBool(1);
|
||
var
|
||
TblDesc: CRTblDesc;
|
||
hDb: hDBIDb;
|
||
begin
|
||
{ Make sure that the table is opened and is exclusive }
|
||
if not Table.Active or not Table.Exclusive then
|
||
raise EDatabaseError.CreateRes(@RsETableNotInExclusiveMode);
|
||
{ Initialize the table descriptor }
|
||
FillChar(TblDesc, SizeOf(CRTblDesc), #0);
|
||
with TblDesc do
|
||
begin
|
||
{ Place the table name in descriptor }
|
||
StrPCopy(szTblName, AnsiString(Table.TableName));
|
||
{ Place the table type in descriptor }
|
||
StrCopy(szTblType, szPARADOX);
|
||
{ Master Password, Password }
|
||
StrPCopy(szPassword, AnsiString(pswd));
|
||
{ Set bProtected to True }
|
||
bProtected := RESTRUCTURE_TRUE;
|
||
end;
|
||
{ Get the database handle from the cursor handle }
|
||
Check(DbiGetObjFromObj(hDBIObj(Table.Handle), objDATABASE, hDBIObj(hDb)));
|
||
{ Close the table }
|
||
Table.Close;
|
||
|
||
{ Add the master password to the Paradox table }
|
||
Check(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False));
|
||
{ Add the new password to the session }
|
||
Session.AddPassword(pswd);
|
||
{ Re-Open the table }
|
||
Table.Open;
|
||
end;
|
||
|
||
// Pack a Paradox table with Password
|
||
// The table must be opened execlusively before calling this function...
|
||
|
||
procedure PackEncryptedTable(Table: TTable; pswd: string);
|
||
const
|
||
RESTRUCTURE_TRUE = WordBool(1);
|
||
var
|
||
Props: CURProps;
|
||
hDb: hDBIDb;
|
||
TableDesc: CRTblDesc;
|
||
begin
|
||
// Make sure the table is open exclusively so we can get the db handle...
|
||
if not Table.Active then
|
||
raise EDatabaseError.CreateRes(@RsETableNotOpen);
|
||
if not Table.Exclusive then
|
||
raise EDatabaseError.CreateRes(@RsETableNotOpenExclusively);
|
||
|
||
// Get the table properties to determine table type...
|
||
Check(DbiGetCursorProps(Table.Handle, Props));
|
||
|
||
// If the table is a Paradox table, you must call DbiDoRestructure...
|
||
if Props.szTableType = szPARADOX then
|
||
begin
|
||
// Blank out the structure...
|
||
FillChar(TableDesc, SizeOf(TableDesc), 0);
|
||
// Get the database handle from the table's cursor handle...
|
||
Check(DbiGetObjFromObj(hDBIObj(Table.Handle), objDATABASE, hDBIObj(hDb)));
|
||
// Put the table name in the table descriptor...
|
||
StrPCopy(TableDesc.szTblName, AnsiString(Table.TableName));
|
||
// Put the table type in the table descriptor...
|
||
StrPCopy(TableDesc.szTblType, Props.szTableType);
|
||
// Set the Pack option in the table descriptor to True...
|
||
TableDesc.bPack := True;
|
||
{ Master Password, Password }
|
||
StrPCopy(TableDesc.szPassword, AnsiString(pswd));
|
||
{ Set bProtected to True }
|
||
TableDesc.bProtected := RESTRUCTURE_TRUE;
|
||
// Close the table so the restructure can complete...
|
||
Table.Close;
|
||
// Call DbiDoRestructure...
|
||
Check(DbiDoRestructure(hDb, 1, @TableDesc, nil, nil, nil, False));
|
||
end
|
||
else
|
||
// If the table is a dBASE table, simply call DbiPackTable...
|
||
if Props.szTableType = szDBASE then
|
||
Check(DbiPackTable(Table.DBHandle, Table.Handle, nil, szDBASE, True))
|
||
else
|
||
// Pack only works on Paradox or dBASE; nothing else...
|
||
raise EDatabaseError.CreateRes(@RsENoParadoxDBaseTable);
|
||
Table.Open;
|
||
end;
|
||
|
||
function EncodeQuotes(const S: string): string;
|
||
begin
|
||
Result := S;
|
||
Result := ReplaceString(Result, CrLf, Cr);
|
||
Result := ReplaceString(Result, Cr, '\#13');
|
||
Result := ReplaceString(Result, '"', '\#34');
|
||
Result := ReplaceString(Result, ',', '\#44');
|
||
end;
|
||
|
||
{*********************** from JvStrUtil unit ***********************}
|
||
|
||
function SubStr(const S: string; const Index: Integer; const Separator: string): string;
|
||
// {<7B><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Sep}
|
||
{ SubStr returns substring from string, S, separated with Separator string [translated]}
|
||
var
|
||
I: Integer;
|
||
pB, pE: PChar;
|
||
begin
|
||
Result := '';
|
||
if ((Index < 0) or ((Index = 0) and (Length(S) > 0) and (S[1] = Separator))) or
|
||
(Length(S) = 0) then
|
||
Exit;
|
||
pB := PChar(S);
|
||
for I := 1 to Index do
|
||
begin
|
||
pB := StrPos(pB, PChar(Separator));
|
||
if pB = nil then
|
||
Exit;
|
||
pB := pB + Length(Separator);
|
||
if pB[0] = #0 then
|
||
Exit;
|
||
end;
|
||
pE := StrPos(pB + 1, PChar(Separator));
|
||
if pE = nil then
|
||
pE := PChar(S) + Length(S);
|
||
if AnsiStrLIComp(pB, PChar(Separator), Length(Separator)) <> 0 then
|
||
SetString(Result, pB, pE - pB);
|
||
end;
|
||
|
||
function SubStrEnd(const S: string; const Index: Integer; const Separator: string): string;
|
||
var
|
||
MaxIndex: Integer;
|
||
pB: PChar;
|
||
begin
|
||
// Not optimal implementation [translated]
|
||
MaxIndex := 0;
|
||
pB := StrPos(PChar(S), PChar(Separator));
|
||
while pB <> nil do
|
||
begin
|
||
Inc(MaxIndex);
|
||
pB := StrPos(pB + Length(Separator), PChar(Separator));
|
||
end;
|
||
Result := SubStr(S, MaxIndex - Index, Separator);
|
||
end;
|
||
|
||
function Cmp(const S1, S2: string): Boolean;
|
||
begin
|
||
Result := AnsiStrIComp(PChar(S1), PChar(S2)) = 0;
|
||
end;
|
||
|
||
{ ReplaceString searches for all substrings, OldPattern,
|
||
in a string, S, and replaces them with NewPattern }
|
||
|
||
function ReplaceString(S: string; const OldPattern, NewPattern: string): string;
|
||
var
|
||
LW: Integer;
|
||
P: PChar;
|
||
Sm: Integer;
|
||
begin
|
||
LW := Length(OldPattern);
|
||
P := StrPos(PChar(S), PChar(OldPattern));
|
||
while P <> nil do
|
||
begin
|
||
Sm := P - PChar(S);
|
||
S := Copy(S, 1, Sm) + NewPattern + Copy(S, Sm + LW + 1, Length(S));
|
||
P := StrPos(PChar(S) + Sm + Length(NewPattern), PChar(OldPattern));
|
||
end;
|
||
Result := S;
|
||
end;
|
||
|
||
{ GetXYByPos is same to previous function, but
|
||
returns X position in line too}
|
||
|
||
procedure GetXYByPos(const S: string; const Pos: Integer; var X, Y: Integer);
|
||
var
|
||
I, iB: Integer;
|
||
begin
|
||
X := -1;
|
||
Y := -1;
|
||
iB := 0;
|
||
if (Length(S) >= Pos) and (Pos >= 0) then
|
||
begin
|
||
I := 1;
|
||
Y := 0;
|
||
while I <= Pos do
|
||
begin
|
||
if S[I] = Cr then
|
||
begin
|
||
Inc(Y);
|
||
iB := I + 1
|
||
end;
|
||
Inc(I);
|
||
end;
|
||
X := Pos - iB;
|
||
end;
|
||
end;
|
||
{####################### from JvStrUtil unit #######################}
|
||
|
||
initialization
|
||
{$IFDEF UNITVERSIONING}
|
||
RegisterUnitVersion(HInstance, UnitVersioning);
|
||
{$ENDIF UNITVERSIONING}
|
||
JvDBUtils.CreateLocateObject := CreateDbLocate;
|
||
|
||
finalization
|
||
ReleaseSaveIndices;
|
||
// (rom) i tried deleting the elements created by CreateDbLocate
|
||
// (rom) but that causes crashes
|
||
{$IFDEF UNITVERSIONING}
|
||
UnregisterUnitVersion(HInstance);
|
||
{$ENDIF UNITVERSIONING}
|
||
|
||
end.
|
||
|