Componentes.Terceros.jvcl/official/3.32/run/JvBdeUtils.pas

2249 lines
61 KiB
ObjectPascal
Raw Blame History

{-----------------------------------------------------------------------------
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 11104 2006-12-29 17:55:15Z marquardt $
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: PChar; 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/tags/JVCL3_32/run/JvBdeUtils.pas $';
Revision: '$Revision: 11104 $';
Date: '$Date: 2006-12-29 18:55:15 +0100 (ven., 29 déc. 2006) $';
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, Value, PChar(Buffer), FldSize);
fldBYTES, fldVARBYTES:
Move(Value[1], Buffer^, Min(Length(Value), 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 := Value[1] in ['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[127];
TempStr, AppConName: string[127];
UserName: string[30];
ExeName: string[12];
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 DatabaseName[I] in LeadBytes then
Inc(I)
else
if DatabaseName[I] in [':', '\'] 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, 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 := 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 + 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 Char;
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, 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 Char;
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(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: PChar; 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: PChar): PChar;
var
Blank: Boolean;
Source, Dest: PChar;
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 := 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,
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 (S[1] in [' ', 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 (S1[1] in [' ', 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, PChar(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, PChar(Dir)));
with RInt^ do
begin
StrPCopy(szRintName, RefName);
StrPCopy(szTblName, 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, 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, Table.TableName);
{ Place the table type in descriptor }
StrCopy(szTblType, szPARADOX);
{ Master Password, Password }
StrPCopy(szPassword, 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, 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, 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.