You've already forked lazarus-ccr
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5438 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2206 lines
83 KiB
ObjectPascal
2206 lines
83 KiB
ObjectPascal
{NOTES:
|
|
1. Have verification as optional--IFDEF'd out}
|
|
|
|
{*********************************************************}
|
|
{* FlashFiler: Table data dictionary *}
|
|
{*********************************************************}
|
|
|
|
(* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1
|
|
*
|
|
* 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/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is TurboPower FlashFiler
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* TurboPower Software
|
|
*
|
|
* Portions created by the Initial Developer are Copyright (C) 1996-2002
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* ***** END LICENSE BLOCK ***** *)
|
|
|
|
{$I ffdefine.inc}
|
|
|
|
unit fflldict;
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows,
|
|
SysUtils,
|
|
Classes,
|
|
FFConst,
|
|
ffllbase,
|
|
ffsrixhl,
|
|
ffsrmgr,
|
|
ffllexcp;
|
|
|
|
|
|
{---Data dictionary class---}
|
|
type
|
|
|
|
PffFieldDescriptorArray = ^TffFieldDescriptorArray;
|
|
TffFieldDescriptorArray = array[Word] of PffFieldDescriptor;
|
|
|
|
PffIndexDescriptorArray = ^TffIndexDescriptorArray;
|
|
TffIndexDescriptorArray = array[0..Pred(ffcl_MaxIndexes)] of PffIndexDescriptor;
|
|
|
|
PffIndexHelperArray = ^TffIndexHelperArray;
|
|
TffIndexHelperArray = array[0..Pred(ffcl_MaxIndexes),
|
|
0..Pred(ffcl_MaxIndexFlds)] of TffSrIndexHelper;
|
|
|
|
TffTriBool = (fftbUnknown, fftbTrue, fftbFalse); {!!.03}
|
|
|
|
TffDataDictionary = class(TPersistent)
|
|
protected {private}
|
|
FBLOBFileNumber : Integer; {file number for BLOBs}
|
|
FFieldCapacity : Longint; {the number of fields the FieldDescriptor
|
|
array has been sized to hold }
|
|
FFldCount : Integer; {count of fields--duplicate for speed}
|
|
FHasBLOBs : TffTriBool; {True if table contains any BLOB fields} {!!.03}
|
|
FIndexCapacity : Longint; {the number of indices the IndexDescriptor
|
|
array has been sized to hold }
|
|
FInxCount : Integer; {count of indexes--duplicate for speed}
|
|
FFileCount : Integer; {count of files--duplicate for speed}
|
|
FBaseName : TffTableName;{the base name for the table}
|
|
FLogRecLen : Longint; {logical rec length--dupe for speed}
|
|
FIsEncrypted : Boolean; {true is files are encrypted}
|
|
|
|
ddFileList : TList; {list of files}
|
|
ddDefFldList : TList; {list of field numbers that have defaults}
|
|
|
|
ddReadOnly : Boolean; {true if the dictionary cannot be updated}
|
|
|
|
procedure AnsiStringWriter(const aString : string; {!!.05}
|
|
aWriter : TWriter); {!!.05}
|
|
{ This method is used to bypass D6's TWriter.WriteString's logic
|
|
for writing strings with extended charcters as UTF8 strings.
|
|
Since D3-D5 and C3-C5 don't recognize the UTF8 string type, it
|
|
causes an error when TReader.ReadString tries to read the
|
|
streams created by D6 using the UTF8 string type.}
|
|
procedure ddExpandFieldArray(const minCapacity : Longint);
|
|
procedure ddExpandIndexArray(const minCapacity : Longint);
|
|
function GetBaseRecordLength : Longint;
|
|
function GetBlockSize : Longint;
|
|
function GetBookmarkSize(aIndexID : Integer) : Integer;
|
|
function GetDefaultFldCount : Integer;
|
|
function GetFieldDecPl(aField : Integer) : Longint;
|
|
function GetFieldDesc(aField : Integer) : TffDictItemDesc;
|
|
function GetFieldLength(aField : Integer) : Longint;
|
|
function GetFieldName(aField : integer) : TffDictItemName;
|
|
function GetFieldOffset(aField : integer) : Longint;
|
|
function GetFieldRequired(aField : integer) : boolean;
|
|
function GetFieldType(aField : integer) : TffFieldType;
|
|
function GetFieldUnits(aField : integer) : Longint;
|
|
function GetFieldVCheck(aField : integer) : PffVCheckDescriptor;
|
|
function GetFileBlockSize(aFile : integer) : Longint;
|
|
function GetFileDesc(aFile : integer) : TffDictItemDesc;
|
|
function GetFileDescriptor(aFile : integer) : PffFileDescriptor;
|
|
function GetFileExt(aFile : integer) : TffExtension;
|
|
function GetFileNameExt(aFile : integer) : TffFileNameExt;
|
|
function GetFileType(aFile : integer) : TffFileType;
|
|
function GetHasBLOBs : Boolean; {!!.03}
|
|
function GetIndexAllowDups(aIndexID : integer) : boolean;
|
|
function GetIndexAscend(aIndexID : integer) : boolean;
|
|
function GetIndexDesc(aIndexID : integer) : TffDictItemDesc;
|
|
function GetIndexFileNumber(aIndexID : integer) : Longint;
|
|
function GetIndexKeyLength(aIndexID : integer) : Longint;
|
|
function GetIndexName(aIndexID : integer) : TffDictItemName;
|
|
function GetIndexNoCase(aIndexID : Integer) : Boolean;
|
|
function GetIndexType(aIndexID : Integer) : TffIndexType;
|
|
function GetRecordLength : Longint;
|
|
procedure CheckForDefault(aVCheckDesc : PffVCheckDescriptor;
|
|
aFieldDesc : PffFieldDescriptor);
|
|
procedure SetBlockSize(BS : Longint);
|
|
procedure SetIsEncrypted(IE : Boolean);
|
|
protected
|
|
procedure ClearPrim(InclFileZero : boolean);
|
|
function CreateFieldDesc(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aType : TffFieldType;
|
|
aUnits : Integer;
|
|
aDecPl : Integer;
|
|
aReqFld : Boolean;
|
|
const aValCheck : PffVCheckDescriptor)
|
|
: PffFieldDescriptor;
|
|
function CreateFileDesc(const aDesc : TffDictItemDesc;
|
|
const aExtension : TffExtension;
|
|
aBlockSize : Longint;
|
|
aType : TffFileType) : PffFileDescriptor;
|
|
function CreateIndexDesc(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aFile : Integer;
|
|
aFldCount : Integer;
|
|
const aFldList : TffFieldList;
|
|
const aFldIHList : TffFieldIHList;
|
|
aAllowDups : Boolean;
|
|
aAscend : Boolean;
|
|
aNoCase : Boolean) : PffIndexDescriptor;
|
|
function CreateUserIndexDesc(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aFile : Integer;
|
|
aKeyLength : Integer;
|
|
aAllowDups : Boolean;
|
|
aAscend : Boolean;
|
|
aNoCase : Boolean) : PffIndexDescriptor;
|
|
|
|
public
|
|
FieldDescriptor : PffFieldDescriptorArray;
|
|
{ Array of field information for the fields in this dictionary.
|
|
Declared as a public array for speed reasons. }
|
|
|
|
IndexDescriptor : PffIndexDescriptorArray;
|
|
{ Array of index information for the indexes in this dictionary.
|
|
Declared as a public array for speed reasons. }
|
|
|
|
IndexHelpers: PffIndexHelperArray;
|
|
{ Index helper objects for composite indices
|
|
declared public (instead of private + public propert)
|
|
for speed reasons}
|
|
|
|
class function NewInstance: TObject; override;
|
|
procedure FreeInstance; override;
|
|
|
|
public
|
|
constructor Create(aBlockSize : Longint);
|
|
{-Create the instance, aBlockSize is the eventual block size
|
|
of the data file component of the table}
|
|
destructor Destroy; override;
|
|
{-Destroy the instance}
|
|
|
|
function AddFile(const aDesc : TffDictItemDesc;
|
|
const aExtension : TffExtension;
|
|
aBlockSize : Longint;
|
|
aFileType : TffFileType) : integer;
|
|
{-Add a file to the data dictionary (the actual file name will
|
|
be the base table name plus aExtension); result is the index
|
|
of the newly-added file in the file list}
|
|
procedure AddIndex(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aFile : integer;
|
|
aFldCount : integer;
|
|
const aFldList : TffFieldList;
|
|
const aFldIHList : TffFieldIHList;
|
|
aAllowDups : boolean;
|
|
aAscend : boolean;
|
|
aCaseInsens : boolean);
|
|
{-Add an extended index to the data dictionary}
|
|
procedure AddUserIndex(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aFile : integer;
|
|
aKeyLength : integer;
|
|
aAllowDups : boolean;
|
|
aAscend : boolean;
|
|
aCaseInsens: boolean);
|
|
{-Add a user defined index to the dictionary}
|
|
procedure AddField(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aType : TffFieldType;
|
|
aUnits : Integer;
|
|
aDecPl : Integer;
|
|
aReqFld : Boolean;
|
|
const aValCheck : PffVCheckDescriptor);
|
|
{-Append a field to the end of the data dictionary's field list}
|
|
procedure Assign(Source: TPersistent); override;
|
|
{-Assign a data dictionary's data}
|
|
procedure BindIndexHelpers;
|
|
{-Binds the TffSrIndexHelper objects to the dictionary}
|
|
procedure CheckValid;
|
|
{-Raise an exception if the dictionary is invalid}
|
|
procedure Clear;
|
|
{-Delete all field/index data from the data dictionary}
|
|
procedure ExtractKey(aIndexID : integer;
|
|
aData : PffByteArray;
|
|
aKey : PffByteArray);
|
|
{-Given a record buffer and an index number, extract the key
|
|
for that index from the record}
|
|
function GetFieldFromName(const aFieldName : TffDictItemName) : integer;
|
|
{-Return the field number for a given field name, or -1 if not
|
|
found}
|
|
function GetIndexFromName(const aIndexName : TffDictItemName) : integer;
|
|
{-Return the index number for a given index name, or -1 if not
|
|
found}
|
|
function HasAutoIncField(var aField : integer) : boolean;
|
|
{-Return true and the index of the first autoinc field in the
|
|
dictionary}
|
|
procedure InsertField(AtIndex : Integer;
|
|
const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aType : TffFieldType;
|
|
aUnits : Integer;
|
|
aDecPl : Integer;
|
|
aReqFld : Boolean;
|
|
const aValCheck : PffVCheckDescriptor);
|
|
{-Insert a field into the data dictionary's field list}
|
|
function IsIndexDescValid(const aIndexDesc : TffIndexDescriptor) : boolean;
|
|
{-Return true if the given index descriptor defines a valid index}
|
|
procedure RemoveField(aField : Longint);
|
|
{-Remove a field from the data dictionary's field list}
|
|
procedure RemoveFile(aFile : Longint);
|
|
{-Remove a file from the data dictionary; if index file, the
|
|
relevant indexes are also removed}
|
|
procedure RemoveIndex(aIndex : Longint);
|
|
{-Remove an index from the data dictionary's index list}
|
|
|
|
{===Validity check routines===}
|
|
procedure SetValidityCheck(aField : integer;
|
|
var aExists : boolean;
|
|
const aVCheck : TffVCheckDescriptor);
|
|
{-Set a field's validity check record}
|
|
|
|
function HasSameFields(aSrcDict : TffDataDictionary;
|
|
var aBLOBFields : TffPointerList) : boolean;
|
|
{-Use this method to verify a dictionary has the same field types,
|
|
sizes, and ordering as a source dictionary. Returns True if the
|
|
field information matches otherwise returns False. Note that the
|
|
fields may have different names. If the record contains any
|
|
BLOB fields, the number of each BLOB field is stored in output
|
|
parameter aBLOBFields. }
|
|
|
|
function HasSameFieldsEx(aSrcDict : TffDataDictionary;
|
|
aFields : PffLongintArray;
|
|
aNumFields : integer;
|
|
var aBLOBFields : TffPointerList) : boolean;
|
|
{-Use this method to verify a dictionary has the same field types,
|
|
sizes, and ordering as the specified fields within a source
|
|
dictionary. Returns True if the field information matches otherwise
|
|
returns False. Note that the fields may have different names. If the
|
|
record contains any BLOB fields, the number of each BLOB field is
|
|
stored in output parameter aBLOBFields. }
|
|
|
|
{===record utility routines===}
|
|
function CheckRequiredRecordFields(aData : PffByteArray) : boolean;
|
|
{-Given a record buffer, checks that all required fields are
|
|
non-null}
|
|
procedure GetRecordField(aField : integer;
|
|
aData : PffByteArray;
|
|
var aIsNull: boolean;
|
|
aValue : pointer);
|
|
{-Given a record buffer, read the required field; aIsNull is
|
|
set to true if the field is null (no data is written to
|
|
aValue)}
|
|
procedure InitRecord(aData : PffByteArray);
|
|
{-Given a record buffer, initialize it so that all fields are
|
|
null}
|
|
function IsRecordFieldNull(aField : integer;
|
|
aData : PffByteArray) : boolean;
|
|
{-Given a record buffer, return true if the field is null}
|
|
procedure SetRecordField(aField : integer;
|
|
aData : PffByteArray;
|
|
aValue : pointer);
|
|
{-Given a record buffer, write the required field from the
|
|
buffer pointed to by aValue; if aValue is nil, the field is
|
|
set to null}
|
|
procedure SetRecordFieldNull(aField : integer;
|
|
aData : PffByteArray;
|
|
aIsNull : boolean);
|
|
{-Given a record buffer, set the required field to null or
|
|
non-null. Set the field in the record to binary zeros.}
|
|
|
|
procedure SetBaseName(const BN : TffTableName);
|
|
{-Set the internal table base name - used for error messages}
|
|
|
|
{Begin !!.11}
|
|
procedure SetDefaultFieldValue(aData : PffByteArray;
|
|
const aField : Integer);
|
|
{ If the field has a default value, this method sets the field to that
|
|
value. }
|
|
{End !!.11}
|
|
|
|
procedure SetDefaultFieldValues(aData : PffByteArray);
|
|
{-Set any null fields to their default field, if the field
|
|
has a default value}
|
|
|
|
property BLOBFileNumber : integer
|
|
read FBLOBFileNumber;
|
|
{-The file number of the file that holds the BLOBs}
|
|
property BlockSize : Longint
|
|
read GetBlockSize write SetBlockSize;
|
|
{-The block size of the table to which this dictionary refers;
|
|
equals FileBlockSize[0] the block size of the base file}
|
|
property BookmarkSize [aIndexID : integer] : integer
|
|
read GetBookmarkSize;
|
|
{-The length of a bookmark for the given index}
|
|
property DefaultFieldCount : Integer
|
|
read GetDefaultFldCount;
|
|
{-Number of fields with default values}
|
|
property IsEncrypted : boolean
|
|
read FIsEncrypted write SetIsEncrypted;
|
|
{-Whether the files comprising the table are encrypted}
|
|
|
|
property FieldCount : integer
|
|
read FFldCount;
|
|
{-The number of fields in the data dictionary}
|
|
property FieldDecPl [aField : integer] : Longint
|
|
read GetFieldDecPl;
|
|
{-The decimal places value for a given field in the data dictionary}
|
|
property FieldDesc [aField : integer] : TffDictItemDesc
|
|
read GetFieldDesc;
|
|
{-The description of a given field in the data dictionary}
|
|
property FieldLength [aField : integer] : Longint
|
|
read GetFieldLength;
|
|
{-The length in bytes of a given field in the data dictionary}
|
|
property FieldName [aField : integer] : TffDictItemName
|
|
read GetFieldName;
|
|
{-The name of a given field in the data dictionary}
|
|
property FieldOffset [aField : integer] : Longint
|
|
read GetFieldOffset;
|
|
{-The offset of a given field in the record in the data dictionary}
|
|
property FieldRequired [aField : integer] : boolean
|
|
read GetFieldRequired;
|
|
{-Whether the field is required or not}
|
|
property FieldType [aField : integer] : TffFieldType
|
|
read GetFieldType;
|
|
{-The type of a given field in the data dictionary}
|
|
property FieldUnits [aField : integer] : Longint
|
|
read GetFieldUnits;
|
|
{-The units value for a given field in the data dictionary}
|
|
property FieldVCheck [aField : integer] : PffVCheckDescriptor
|
|
read GetFieldVCheck;
|
|
{-The validity check info for a given field}
|
|
|
|
property FileBlockSize [aFile : integer] : Longint
|
|
read GetFileBlockSize;
|
|
{-The block size of a given file in the data dictionary}
|
|
property FileCount : integer
|
|
read FFileCount;
|
|
{-The number of files in the data dictionary}
|
|
property FileDesc [aFile : integer] : TffDictItemDesc
|
|
read GetFileDesc;
|
|
{-The description of a given file in the data dictionary}
|
|
property FileDescriptor [aFile : integer] : PffFileDescriptor
|
|
read GetFileDescriptor;
|
|
{-The descriptor of a given file in the data dictionary}
|
|
property FileExt [aFile : integer] : TffExtension
|
|
read GetFileExt;
|
|
{-The extension of a given file in the data dictionary}
|
|
property DiskFileName [aFile : integer] : TffFileNameExt
|
|
read GetFileNameExt;
|
|
{-The disk name of a given file in the data dictionary}
|
|
property FileType [aFile : integer] : TffFileType
|
|
read GetFileType;
|
|
{-The type of file: data, index or BLOB}
|
|
property HasBLOBFields : Boolean {!!.03}
|
|
read GetHasBLOBs; {!!.03}
|
|
{-Returns True if the table contains any BLOB fields. } {!!.03}
|
|
property IndexAllowDups [aIndexID : integer] : boolean
|
|
read GetIndexAllowDups;
|
|
{-Whether the given index allows duplicate keys}
|
|
property IndexIsAscending [aIndexID : integer] : boolean
|
|
read GetIndexAscend;
|
|
{-Whether the given index has keys in ascending order}
|
|
property IndexIsCaseInsensitive [aIndexID : integer] : boolean
|
|
read GetIndexNoCase;
|
|
{-Whether the given index has keys in ascending order}
|
|
property IndexCount : integer
|
|
read FInxCount;
|
|
{-The number of indexes in the data dictionary}
|
|
property IndexDesc [aIndexID : integer] : TffDictItemDesc
|
|
read GetIndexDesc;
|
|
{-The description of a given index in the data dictionary}
|
|
property IndexFileNumber [aIndexID : integer] : Longint
|
|
read GetIndexFileNumber;
|
|
{-The descriptor of a given index in the data dictionary}
|
|
property IndexKeyLength [aIndexID : integer] : Longint
|
|
read GetIndexKeyLength;
|
|
{-The key length for the given index}
|
|
property IndexName [aIndexID : integer] : TffDictItemName
|
|
read GetIndexName;
|
|
{-The name of a given field in the data dictionary}
|
|
property IndexType [aIndexID : integer] : TffIndexType
|
|
read GetIndexType;
|
|
{-The type of the given index}
|
|
|
|
property RecordLength : Longint
|
|
read GetRecordLength;
|
|
{-The length of the physical record for the data dictionary. Includes
|
|
trailing byte array to identify null fields. }
|
|
property LogicalRecordLength : Longint
|
|
read GetBaseRecordLength;
|
|
{-The length of the logical record for the data dictionary (ie
|
|
just the total size of the fields. }
|
|
|
|
procedure ReadFromStream(S : TStream);
|
|
procedure WriteToStream(S : TStream);
|
|
|
|
end;
|
|
|
|
{===Key manipulation routines===} {moved here from FFTBBASE}
|
|
procedure FFInitKey(aKey : PffByteArray;
|
|
aKeyLen : integer;
|
|
aKeyFldCount : integer);
|
|
function FFIsKeyFieldNull(aKey : PffByteArray;
|
|
aKeyLen : integer;
|
|
aKeyFldCount : integer;
|
|
aKeyFld : integer) : boolean;
|
|
procedure FFSetKeyFieldNonNull(aKey : PffByteArray;
|
|
aKeyLen : integer;
|
|
aKeyFldCount : integer;
|
|
aKeyFld : integer);
|
|
|
|
implementation
|
|
|
|
const
|
|
ffcl_InitialFieldCapacity = 10;
|
|
{ Number of fields dictionary can hold upon creation. The dictionary
|
|
will expand its capacity as necessary. }
|
|
ffcl_InitialIndexCapacity = 5;
|
|
{ Number of indices dictionary can hold upon creation. The dictionary
|
|
will expand its capacity as necessary. }
|
|
|
|
{===TffDataDictionary================================================}
|
|
constructor TffDataDictionary.Create(aBlockSize : Longint);
|
|
var
|
|
NewFileDesc : PffFileDescriptor;
|
|
NewInxDesc : PffIndexDescriptor;
|
|
SeqAccessName : TffShStr;
|
|
begin
|
|
inherited Create;
|
|
FHasBLOBs := fftbUnknown; {!!.03}
|
|
{verify the block size}
|
|
if not FFVerifyBlockSize(aBlockSize) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBadBlockSize,
|
|
[aBlockSize]);
|
|
{create the file list}
|
|
ddFileList := TList.Create;
|
|
{add the first file name (for the data/data dict file)}
|
|
NewFileDesc := CreateFileDesc(ffStrResGeneral[ffscMainTableFileDesc],
|
|
ffc_ExtForData, aBlockSize, ftBaseFile);
|
|
try
|
|
NewFileDesc^.fdNumber := 0;
|
|
ddFileList.Add(pointer(NewFileDesc));
|
|
FFileCount := 1;
|
|
except
|
|
FFFreeMem(NewFileDesc,sizeof(TffFileDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
|
|
ddDefFldList := TList.Create;
|
|
|
|
{create the field list}
|
|
FFieldCapacity := ffcl_InitialFieldCapacity;
|
|
FFGetMem(FieldDescriptor, SizeOf(PffFieldDescriptor) * FFieldCapacity);
|
|
{create the index list, add index 0: this is the sequential access
|
|
index}
|
|
|
|
FIndexCapacity := ffcl_InitialIndexCapacity;
|
|
FFGetMem(IndexDescriptor, SizeOf(PffIndexDescriptor) * FIndexCapacity);
|
|
SeqAccessName := ffStrResGeneral[ffscSeqAccessIndexName];
|
|
NewInxDesc := CreateUserIndexDesc(SeqAccessName, SeqAccessName, 0,
|
|
sizeof(TffInt64), false, true, true);
|
|
try
|
|
NewInxDesc^.idNumber := 0;
|
|
IndexDescriptor^[0] := NewInxDesc;
|
|
FInxCount := 1;
|
|
except
|
|
FFFreeMem(NewInxDesc,sizeof(TffIndexDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
|
|
FFGetMem(IndexHelpers,
|
|
SizeOf(TffSrIndexHelper) * ffcl_MaxIndexFlds * FIndexCapacity);
|
|
end;
|
|
{--------}
|
|
destructor TffDataDictionary.Destroy;
|
|
var
|
|
index : integer;
|
|
P : pointer;
|
|
Pfd : PffFieldDescriptor absolute P; {!!.01}
|
|
begin
|
|
|
|
if assigned(IndexHelpers) then
|
|
FFFreeMem(IndexHelpers,
|
|
FIndexCapacity * ffcl_MaxIndexFlds * SizeOf(TffSrIndexHelper));
|
|
|
|
ClearPrim(true);
|
|
|
|
for Index := pred(FInxCount) downto 0 do begin
|
|
P := IndexDescriptor^[index];
|
|
FFFreeMem(P, sizeof(TffIndexDescriptor));
|
|
end;
|
|
FFFreeMem(IndexDescriptor, SizeOf(PffIndexDescriptor) * FIndexCapacity);
|
|
|
|
for Index := pred(FFldCount) downto 0 do begin
|
|
P := FieldDescriptor^[index];
|
|
if Pfd^.fdVCheck <> nil then {!!.01}
|
|
FFFreeMem(Pfd^.fdVCheck, sizeof(TffVCheckDescriptor)); {!!.01}
|
|
FFFreeMem(P, SizeOf(PffFieldDescriptor) * FFieldCapacity);
|
|
end;
|
|
FFFreeMem(FieldDescriptor, SizeOf(PffFieldDescriptor) * FFieldCapacity);
|
|
|
|
for index := (ddFileList.count - 1) downto 0 do begin
|
|
P := PffFileDescriptor(ddFileList[index]);
|
|
FFFreeMem(P, sizeOf(TffFileDescriptor));
|
|
ddFileList.delete(index);
|
|
end;
|
|
|
|
ddFileList.Free;
|
|
ddDefFldList.Free;
|
|
inherited Destroy;
|
|
end;
|
|
{--------}
|
|
class function TffDataDictionary.NewInstance: TObject;
|
|
begin
|
|
FFGetMem(Result, InstanceSize);
|
|
InitInstance(Result);
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.FreeInstance;
|
|
var
|
|
Temp : pointer;
|
|
begin
|
|
Temp := Self;
|
|
FFFreeMem(Temp, InstanceSize);
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.AddFile(const aDesc : TffDictItemDesc;
|
|
const aExtension : TffExtension;
|
|
aBlockSize : Longint;
|
|
aFileType : TffFileType) : integer;
|
|
var
|
|
NewDesc : PffFileDescriptor;
|
|
i : integer;
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
{verify the extension}
|
|
if not FFVerifyExtension(aExtension) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBadExtension, [FBaseName, aExtension]);
|
|
{verify the block size}
|
|
if not FFVerifyBlockSize(aBlockSize) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBadBlockSize, [aBlockSize]);
|
|
{if a base file type, check to see whether file 0 has been added
|
|
already}
|
|
if (aFileType = ftBaseFile) then
|
|
if (FFileCount > 0) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDataFileDefd, [FBaseName]);
|
|
{check to see whether the extension has been used already}
|
|
for i := 0 to pred(FFileCount) do
|
|
if (PffFileDescriptor(ddFileList[i])^.fdExtension = aExtension) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDupExtension, [FBaseName, aExtension]);
|
|
{if a BLOB file type check to see whether we have one already; we
|
|
can ignore file 0: it's the base file (ie data & dictionary)}
|
|
if (aFileType = ftBLOBFile) then
|
|
if (BLOBFileNumber <> 0) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBLOBFileDefd, [FBaseName]);
|
|
{add a new file descriptor}
|
|
NewDesc := CreateFileDesc(aDesc, aExtension, aBlockSize, aFileType);
|
|
try
|
|
Result := FFileCount;
|
|
NewDesc^.fdNumber := FFileCount;
|
|
if (aFileType = ftBLOBFile) then
|
|
FBLOBFileNumber := FFileCount;
|
|
ddFileList.Add(pointer(NewDesc));
|
|
inc(FFileCount);
|
|
except
|
|
FFFreeMem(NewDesc,sizeof(TffFileDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.AddIndex(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aFile : integer;
|
|
aFldCount : integer;
|
|
const aFldList : TffFieldList;
|
|
const aFldIHList : TffFieldIHList;
|
|
aAllowDups : boolean;
|
|
aAscend : boolean;
|
|
aCaseInsens: boolean);
|
|
var
|
|
NewDesc : PffIndexDescriptor;
|
|
i : integer;
|
|
begin
|
|
{check for a duplicate index name}
|
|
if (GetIndexFromName(aIdent) <> -1) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDupIndexName,
|
|
[FBaseName, aIdent]);
|
|
{check the file number}
|
|
if (0 > aFile) or (aFile >= FFileCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBadFileNumber,
|
|
[FBaseName, aFile]);
|
|
{check all field numbers in field list}
|
|
for i := 0 to pred(aFldCount) do
|
|
if (aFldList[i] < 0) or (aFldList[i] >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBadFieldRef,
|
|
[FBaseName, aFldList[i]]);
|
|
{create the new index}
|
|
NewDesc := CreateIndexDesc(aIdent, aDesc, aFile, aFldCount, aFldList,
|
|
aFldIHList, aAllowDups, aAscend, aCaseInsens);
|
|
try
|
|
NewDesc^.idNumber := FInxCount;
|
|
IndexDescriptor^[FInxCount] := NewDesc;
|
|
inc(FInxCount);
|
|
{ Have we reached our index capacity? }
|
|
if FInxCount = FIndexCapacity then
|
|
ddExpandIndexArray(0);
|
|
except
|
|
FFFreeMem(NewDesc,sizeof(TffIndexDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.AddUserIndex(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aFile : integer;
|
|
aKeyLength : integer;
|
|
aAllowDups : boolean;
|
|
aAscend : boolean;
|
|
aCaseInsens: boolean);
|
|
var
|
|
NewDesc : PffIndexDescriptor;
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
{check the file number}
|
|
if (0 > aFile) or (aFile >= FFileCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBadFileNumber, [FBaseName, aFile]);
|
|
{check the key length}
|
|
if not FFVerifyKeyLength(aKeyLength) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrKeyTooLong, [aKeyLength]);
|
|
{create the new index}
|
|
NewDesc := CreateUserIndexDesc(aIdent, aDesc, aFile, aKeyLength, aAllowDups, aAscend, aCaseInsens);
|
|
try
|
|
NewDesc^.idNumber := FInxCount;
|
|
IndexDescriptor^[FInxCount] := NewDesc;
|
|
inc(FInxCount);
|
|
{ Have we reached our index capacity? }
|
|
if FInxCount = FIndexCapacity then
|
|
ddExpandIndexArray(0);
|
|
except
|
|
FFFreeMem(NewDesc,sizeof(TffIndexDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.AddField(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aType : TffFieldType;
|
|
aUnits : Integer;
|
|
aDecPl : Integer;
|
|
aReqFld : Boolean;
|
|
const aValCheck : PffVCheckDescriptor);
|
|
var
|
|
NewDesc : PffFieldDescriptor;
|
|
TempDesc : PffFieldDescriptor;
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
{check for a duplicate field name}
|
|
if (GetFieldFromName(aIdent) <> -1) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDupFieldName, [FBaseName, aIdent]);
|
|
{create it}
|
|
NewDesc := CreateFieldDesc(aIdent, aDesc, aType, aUnits, aDecPl, aReqFld, aValCheck);
|
|
try
|
|
NewDesc^.fdNumber := FFldCount;
|
|
if (FFldCount > 0) then begin
|
|
TempDesc := FieldDescriptor^[pred(FFldCount)];
|
|
with TempDesc^ do
|
|
NewDesc^.fdOffset := fdOffset + fdLength;
|
|
end;
|
|
FieldDescriptor^[FFldCount] := NewDesc;
|
|
inc(FFldCount);
|
|
{ Have we reached our field capacity? }
|
|
if FFldCount = FFieldCapacity then
|
|
{ Yes, expand our field array. }
|
|
ddExpandFieldArray(0);
|
|
with NewDesc^ do
|
|
FLogRecLen := fdOffset + fdLength;
|
|
FHasBLOBs := fftbUnknown; {!!.03}
|
|
except
|
|
FFFreeMem(NewDesc,sizeof(TffFieldDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.AnsiStringWriter(const aString : string; {!!.05 - Added}
|
|
aWriter : TWriter);
|
|
var
|
|
TempInt : Integer;
|
|
begin
|
|
TempInt := Integer(vaString);
|
|
aWriter.Write(TempInt, SizeOf(vaString));
|
|
|
|
TempInt := Length(aString);
|
|
aWriter.Write(TempInt, SizeOf(Byte));
|
|
|
|
if (TempInt > 0) then
|
|
aWriter.Write(aString[1], TempInt);
|
|
end;
|
|
{--------} {!!.05 - End Added}
|
|
procedure TffDataDictionary.Assign(Source: TPersistent);
|
|
var
|
|
// CheckVal : PffVCheckDescriptor; {!!.01}
|
|
item : integer;
|
|
SelfFldDesc : PffFieldDescriptor;
|
|
SrcDict : TffDataDictionary absolute Source;
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
{Source must be one of us}
|
|
if not (Source is TffDataDictionary) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrNotADict, [FBaseName]);
|
|
{firstly clear our own lists (remove the base file item as well)}
|
|
ClearPrim(true);
|
|
{copy over the encrypted mode}
|
|
Self.FIsEncrypted := TffDataDictionary(Source).IsEncrypted;
|
|
{ Now duplicate the items in the Source's lists. }
|
|
try
|
|
{ The file list first; do include index 0. }
|
|
for item := 0 to pred(SrcDict.FFileCount) do
|
|
with PffFileDescriptor(SrcDict.ddFileList[item])^ do
|
|
Self.AddFile(fdDesc, fdExtension, fdBlockSize, fdType);
|
|
|
|
{ The field list next. }
|
|
FHasBLOBs := fftbUnknown; {!!.03}
|
|
for item := 0 to pred(SrcDict.FFldCount) do
|
|
with SrcDict.FieldDescriptor^[Item]^ do begin
|
|
if Assigned(fdVCheck) then
|
|
Self.AddField(fdName, fdDesc, fdType, fdUnits, fdDecPl, fdRequired,
|
|
fdVCheck)
|
|
else begin
|
|
// FFGetZeroMem(CheckVal, sizeof(TffVCheckDescriptor)); {Deleted !!.01}
|
|
Self.AddField(fdName, fdDesc, fdType, fdUnits, fdDecPl, fdRequired,
|
|
nil) {!!.01}
|
|
end;
|
|
if assigned(fdVCheck) then begin
|
|
SelfFldDesc := Self.FieldDescriptor^[item];
|
|
if SelfFldDesc^.fdVCheck = nil then {!!.06}
|
|
FFGetMem(SelfFldDesc^.fdVCheck, sizeOf(TffVCheckDescriptor)); {!!.06}
|
|
Move(fdVCheck^, SelfFldDesc^.fdVCheck^, sizeof(fdVCheck^));
|
|
end;
|
|
end;
|
|
|
|
{ The index list next; skip index 0. }
|
|
for item := 1 to pred(SrcDict.FInxCount) do
|
|
with SrcDict.IndexDescriptor^[item]^ do
|
|
if (idCount <> -1) then
|
|
Self.AddIndex(idName, idDesc, idFile, idCount,
|
|
idFields, idFieldIHlprs, idDups, idAscend, idNoCase)
|
|
else
|
|
Self.AddUserIndex(idName, idDesc, idFile, idKeyLen, idDups, idAscend, idNoCase)
|
|
except
|
|
ClearPrim(true);
|
|
raise;
|
|
end;{try..except}
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.BindIndexHelpers;
|
|
var
|
|
i,j : Integer;
|
|
begin
|
|
for i:= 0 to pred(IndexCount) do
|
|
with IndexDescriptor^[i]^do
|
|
if idCount>=0 then begin
|
|
for j:= 0 to Pred(idCount) do
|
|
IndexHelpers[i,j] :=
|
|
TffSrIndexHelper.FindHelper(idFieldIHlprs[j],GetFieldType(idFields[j]));
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.CheckRequiredRecordFields(aData : PffByteArray) : Boolean;
|
|
var
|
|
FieldInx : integer;
|
|
BS : PffByteArray;
|
|
begin
|
|
{note: it's probably faster to find all the null fields and then
|
|
check their required status, rather than the other way round
|
|
(getting a field descriptor requires a whole lot more calls
|
|
than checking a bit) but it does depend on a lotta factors.}
|
|
Result := false;
|
|
if (aData = nil) then
|
|
Exit;
|
|
BS := PffByteArray(@aData^[FLogRecLen]);
|
|
for FieldInx := 0 to pred(FFldCount) do begin
|
|
if FFIsBitSet(BS, FieldInx) then
|
|
if FieldDescriptor^[FieldInx]^.fdRequired then
|
|
Exit;
|
|
end;
|
|
Result := true;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.CheckValid;
|
|
var
|
|
item : integer;
|
|
i : integer;
|
|
Fld : PffFieldDescriptor;
|
|
Indx : PffIndexDescriptor;
|
|
begin
|
|
if (FFldCount <= 0) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrNoFields, [FBaseName]);
|
|
if (RecordLength > (BlockSize - ffc_BlockHeaderSizeData - sizeof(Longint))) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrRecTooLong, [FBaseName]);
|
|
if (IndexCount > ffcl_MaxIndexes) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrMaxIndexes, [FBaseName]);
|
|
{check all field numbers in all indexes, recalc key lengths}
|
|
if (FInxCount > 1) then
|
|
for item := 1 to pred(FInxCount) do
|
|
with IndexDescriptor^[item]^ do
|
|
if (idCount <> -1) then begin
|
|
if (idCount = 0) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrNoFieldsInKey, [FBaseName]);
|
|
idKeyLen := 0;
|
|
for i := 0 to pred(idCount) do begin
|
|
if (idFields[i] < 0) or (idFields[i] >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBadFieldRef, [FBaseName, idFields[i]]);
|
|
inc(idKeyLen, FieldDescriptor^[idFields[i]]^.fdLength);
|
|
end;
|
|
inc(idKeyLen, (idCount + 7) div 8);
|
|
end;
|
|
{field names must be unique}
|
|
for item := 0 to pred(FFldCount) do begin
|
|
Fld := FieldDescriptor^[item];
|
|
if (GetFieldFromName(Fld^.fdName) <> item) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDupFieldName, [FBaseName, Fld^.fdName]);
|
|
end;
|
|
{index names must be unique}
|
|
if (FInxCount > 1) then
|
|
for item := 1 to pred(FInxCount) do begin
|
|
Indx := IndexDescriptor^[item];
|
|
if (GetIndexFromName(Indx^.idName) <> item) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDupIndexName, [FBaseName, Indx^.idName]);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.Clear;
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
ClearPrim(false);
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.ClearPrim(InclFileZero : boolean);
|
|
var
|
|
item : integer;
|
|
BaseFileDesc : PffFileDescriptor;
|
|
TmpIndexDesc : PffIndexDescriptor;
|
|
FldDesc : PffFieldDescriptor;
|
|
begin
|
|
{clear the entire file list EXCEPT item zero}
|
|
for item := 1 to pred(FFileCount) do begin
|
|
BaseFileDesc := PffFileDescriptor(ddFileList[item]);
|
|
FFFreeMem(BaseFileDesc, sizeof(TffFileDescriptor));
|
|
end;
|
|
{decide what to do about item zero: save it or dispose of it}
|
|
if InclFileZero and (FFileCount > 0) then begin
|
|
BaseFileDesc := PffFileDescriptor(ddFileList[0]);
|
|
FFFreeMem(BaseFileDesc, sizeof(TffFileDescriptor));
|
|
ddFileList.Clear;
|
|
FFileCount := 0;
|
|
end
|
|
else {don't dispose of file 0} begin
|
|
BaseFileDesc := PffFileDescriptor(ddFileList[0]);
|
|
ddFileList.Clear;
|
|
ddFileList.Add(pointer(BaseFileDesc));
|
|
FFileCount := 1;
|
|
end;
|
|
{clear the entire field list}
|
|
for item := 0 to pred(FFldCount) do begin
|
|
FldDesc := FieldDescriptor^[item];
|
|
if (FldDesc^.fdVCheck <> nil) then
|
|
FFFreeMem(FldDesc^.fdVCheck, sizeOf(TffVCheckDescriptor));
|
|
FFFreeMem(FldDesc, sizeOf(TffFieldDescriptor));
|
|
end;
|
|
FFldCount := 0;
|
|
FLogRecLen := 0;
|
|
{clear the entire index list EXCEPT for the first item}
|
|
for item := 1 to pred(FInxCount) do begin
|
|
TmpIndexDesc := IndexDescriptor^[item];
|
|
FFFreeMem(TmpIndexDesc, sizeOf(TffIndexDescriptor));
|
|
IndexDescriptor^[item] := nil;
|
|
end;
|
|
FInxCount := 1;
|
|
|
|
{clear out any old default field values} {!!.03}
|
|
ddDefFldList.Clear; {!!.03}
|
|
FHasBLOBs := fftbUnknown; {!!.03}
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.CreateFieldDesc(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aType : TffFieldType;
|
|
aUnits : Integer;
|
|
aDecPl : Integer;
|
|
aReqFld : Boolean;
|
|
const aValCheck : PffVCheckDescriptor)
|
|
: PffFieldDescriptor;
|
|
var
|
|
FT : Integer;
|
|
begin
|
|
if (aType = fftAutoInc) then
|
|
aReqFld := false;
|
|
FFGetZeroMem(Result, sizeof(TffFieldDescriptor));
|
|
with Result^ do begin
|
|
fdName := aIdent;
|
|
fdDesc := aDesc;
|
|
fdType := aType;
|
|
fdRequired := aReqFld;
|
|
case aType of
|
|
fftBoolean :
|
|
begin
|
|
fdUnits := 0;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(Boolean);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftChar :
|
|
begin
|
|
fdUnits := 1;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(AnsiChar);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftWideChar :
|
|
begin
|
|
fdUnits := 1;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(WideChar);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftByte :
|
|
begin
|
|
if (aUnits < 0) or (aUnits > 3) then
|
|
fdUnits := 3
|
|
else
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(byte);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftWord16 :
|
|
begin
|
|
if (aUnits < 0) or (aUnits > 5) then
|
|
fdUnits := 5
|
|
else
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(TffWord16);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftWord32 :
|
|
begin
|
|
if (aUnits < 0) or (aUnits > 10) then
|
|
fdUnits := 10
|
|
else
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(TffWord32);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftInt8 :
|
|
begin
|
|
if (aUnits < 0) or (aUnits > 3) then
|
|
fdUnits := 3
|
|
else
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(shortint);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftInt16 :
|
|
begin
|
|
if (aUnits < 0) or (aUnits > 5) then
|
|
fdUnits := 5
|
|
else
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(smallint);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftInt32 :
|
|
begin
|
|
if (aUnits < 0) or (aUnits > 10) then
|
|
fdUnits := 10
|
|
else
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(Longint);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftAutoInc :
|
|
begin
|
|
fdUnits := 10;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(Longint);
|
|
end;
|
|
fftSingle :
|
|
begin
|
|
fdUnits := aUnits;
|
|
fdDecPl := aDecPl;
|
|
fdLength := sizeof(single);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftDouble :
|
|
begin
|
|
fdUnits := aUnits;
|
|
fdDecPl := aDecPl;
|
|
fdLength := sizeof(double);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftExtended :
|
|
begin
|
|
fdUnits := aUnits;
|
|
fdDecPl := aDecPl;
|
|
fdLength := sizeof(extended);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftComp :
|
|
begin
|
|
fdUnits := aUnits;
|
|
fdDecPl := aDecPl;
|
|
fdLength := sizeof(comp);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftCurrency :
|
|
begin
|
|
fdUnits := aUnits;
|
|
fdDecPl := aDecPl;
|
|
fdLength := sizeof(comp);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftStDate :
|
|
begin
|
|
fdUnits := 0;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(Longint);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftStTime :
|
|
begin
|
|
fdUnits := 0;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(Longint);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftDateTime :
|
|
begin
|
|
fdUnits := 0;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(double);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftBLOB,
|
|
fftBLOBMemo,
|
|
fftBLOBFmtMemo,
|
|
fftBLOBOLEObj,
|
|
fftBLOBGraphic,
|
|
fftBLOBDBSOLEObj,
|
|
fftBLOBTypedBin,
|
|
fftBLOBFile :
|
|
begin
|
|
fdUnits := 0;
|
|
fdDecPl := 0;
|
|
fdLength := sizeof(TffInt64);
|
|
end;
|
|
fftByteArray :
|
|
begin
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := aUnits;
|
|
end;
|
|
fftShortString, fftShortAnsiStr, fftNullString, fftNullAnsiStr :
|
|
begin
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := (aUnits + 1) * sizeof(AnsiChar);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
fftWideString :
|
|
begin
|
|
fdUnits := aUnits;
|
|
fdDecPl := 0;
|
|
fdLength := (aUnits + 1) * sizeof(WideChar);
|
|
CheckForDefault(aValCheck, Result);
|
|
end;
|
|
else
|
|
FT := ord(aType);
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBadFieldType, [FT]);
|
|
end;{case}
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.CreateFileDesc(const aDesc : TffDictItemDesc;
|
|
const aExtension : TffExtension;
|
|
aBlockSize : Longint;
|
|
aType : TffFileType)
|
|
: PffFileDescriptor;
|
|
begin
|
|
FFGetZeroMem(Result, sizeof(TffFileDescriptor));
|
|
with Result^ do
|
|
begin
|
|
fdDesc := aDesc;
|
|
fdExtension := aExtension;
|
|
fdBlockSize := aBlockSize;
|
|
fdType := aType;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.CreateIndexDesc(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aFile : integer;
|
|
aFldCount : integer;
|
|
const aFldList : TffFieldList;
|
|
const aFldIHList : TffFieldIHList;
|
|
aAllowDups : boolean;
|
|
aAscend : boolean;
|
|
aNoCase : boolean)
|
|
: PffIndexDescriptor;
|
|
var
|
|
i : integer;
|
|
begin
|
|
FFGetZeroMem(Result, sizeof(TffIndexDescriptor));
|
|
with Result^ do begin
|
|
idName := aIdent;
|
|
idDesc := aDesc;
|
|
idFile := aFile;
|
|
idCount := aFldCount;
|
|
idDups := aAllowDups;
|
|
idKeyLen := 0;
|
|
for i := 0 to pred(aFldCount) do begin
|
|
idFields[i] := aFldList[i];
|
|
inc(idKeyLen, FieldDescriptor^[aFldList[i]]^.fdLength);
|
|
end;
|
|
for i := 0 to pred(aFldCount) do
|
|
idFieldIHlprs[i] := aFldIHList[i];
|
|
inc(idKeyLen, {the key length itself}
|
|
(aFldCount + 7) div 8); {the bit array for nulls}
|
|
idAscend := aAscend;
|
|
idNoCase := aNoCase;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.CreateUserIndexDesc(const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aFile : integer;
|
|
aKeyLength : integer;
|
|
aAllowDups : boolean;
|
|
aAscend : boolean;
|
|
aNoCase : boolean)
|
|
: PffIndexDescriptor;
|
|
begin
|
|
FFGetZeroMem(Result, sizeof(TffIndexDescriptor));
|
|
with Result^ do begin
|
|
idName := aIdent;
|
|
idFile := aFile;
|
|
idDups := aAllowDups;
|
|
idCount := -1;
|
|
idKeyLen := aKeyLength;
|
|
idAscend := aAscend;
|
|
idNoCase := aNoCase;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.ddExpandFieldArray(const minCapacity : Longint);
|
|
var
|
|
OldCapacity : Longint;
|
|
begin
|
|
OldCapacity := FFieldCapacity;
|
|
{Begin !!.02}
|
|
if minCapacity = 0 then
|
|
inc(FFieldCapacity, ffcl_InitialFieldCapacity * 2)
|
|
else if FFieldCapacity = minCapacity then
|
|
Exit
|
|
else
|
|
FFieldCapacity := minCapacity;
|
|
{End !!.02}
|
|
FFReallocMem(FieldDescriptor, SizeOf(PffFieldDescriptor) * OldCapacity,
|
|
SizeOf(PffFieldDescriptor) * FFieldCapacity);
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.ddExpandIndexArray(const minCapacity : Longint);
|
|
var
|
|
OldCapacity : Longint;
|
|
begin
|
|
OldCapacity := FIndexCapacity;
|
|
{Begin !!.02}
|
|
if minCapacity = 0 then
|
|
inc(FIndexCapacity, ffcl_InitialIndexCapacity * 2)
|
|
else if FIndexCapacity = minCapacity then
|
|
Exit
|
|
else
|
|
FIndexCapacity := minCapacity;
|
|
{End !!.02}
|
|
FFReallocMem(IndexDescriptor, SizeOf(PffIndexDescriptor) * OldCapacity,
|
|
SizeOf(PffIndexDescriptor) * FIndexCapacity);
|
|
FFReallocMem(IndexHelpers,
|
|
SizeOf(TffSrIndexHelper) * ffcl_MaxIndexFlds * OldCapacity,
|
|
SizeOf(TffSrIndexHelper) * ffcl_MaxIndexFlds * FIndexCapacity);
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.ExtractKey(aIndexID : integer;
|
|
aData : PffByteArray;
|
|
aKey : PffByteArray);
|
|
var
|
|
KeyOffset : integer;
|
|
FieldNumber : integer;
|
|
begin
|
|
KeyOffset := 0;
|
|
with IndexDescriptor^[aIndexID]^ do begin
|
|
{clear the entire key - sets all fields to null as well}
|
|
FFInitKey(aKey, idKeyLen, idCount);
|
|
{now build it}
|
|
for FieldNumber := 0 to pred(idCount) do begin
|
|
with FieldDescriptor^[idFields[FieldNumber]]^ do begin
|
|
if not IsRecordFieldNull(idFields[FieldNumber], aData) then begin
|
|
Move(aData^[fdOffset], aKey^[KeyOffset], fdLength);
|
|
FFSetKeyFieldNonNull(aKey, idKeyLen, idCount, FieldNumber);
|
|
end;
|
|
inc(KeyOffset, fdLength);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetBaseRecordLength : Longint;
|
|
begin
|
|
{ A record must be at last ffcl_MinRecordLength bytes in length. This
|
|
is because we need that many bytes in order to store the next deleted
|
|
record when the record becomes part of the deleted record chain. }
|
|
Result := FFMaxL(FLogRecLen, ffcl_MinRecordLength);
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetBlockSize : Longint;
|
|
begin
|
|
if (FFileCount > 0) then
|
|
Result := PffFileDescriptor(ddFileList.Items[0])^.fdBlockSize
|
|
else
|
|
Result := 4096;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetBookmarkSize(aIndexID : integer) : integer;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := ffcl_FixedBookmarkSize + IndexDescriptor^[aIndexID]^.idKeyLen;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldDecPl(aField : integer) : Longint;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdDecPl;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldDesc(aField : integer) : TffDictItemDesc;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdDesc;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldFromName(const aFieldName : TffDictItemName) : integer;
|
|
begin
|
|
for Result := 0 to pred(FFldCount) do
|
|
if (FFCmpShStrUC(aFieldName,
|
|
FieldDescriptor^[Result]^.fdName,
|
|
255) = 0) then
|
|
Exit;
|
|
Result := -1;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldLength(aField : integer) : Longint;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdLength;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldName(aField : integer) : TffDictItemName;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdName;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldOffset(aField : integer) : Longint;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdOffset;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldRequired(aField : integer) : boolean;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdRequired;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldType(aField : integer) : TffFieldType;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdType;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldUnits(aField : integer) : Longint;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdUnits;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFieldVCheck(aField : integer) : PffVCheckDescriptor;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
Result := FieldDescriptor^[aField]^.fdVCheck;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFileBlockSize(aFile : integer) : Longint;
|
|
begin
|
|
if (aFile < 0) or (aFile >= FFileCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aFile]);
|
|
Result := PffFileDescriptor(ddFileList.Items[aFile])^.fdBlockSize;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFileDesc(aFile : integer) : TffDictItemDesc;
|
|
begin
|
|
if (aFile < 0) or (aFile >= FFileCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aFile]);
|
|
Result := PffFileDescriptor(ddFileList.Items[aFile])^.fdDesc;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFileDescriptor(aFile : integer) : PffFileDescriptor;
|
|
begin
|
|
if (aFile < 0) or (aFile >= FFileCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aFile]);
|
|
Result := PffFileDescriptor(ddFileList.Items[aFile]);
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFileExt(aFile : integer) : TffExtension;
|
|
begin
|
|
if (aFile < 0) or (aFile >= FFileCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aFile]);
|
|
Result := PffFileDescriptor(ddFileList.Items[aFile])^.fdExtension;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFileNameExt(aFile : integer) : TffFileNameExt;
|
|
var
|
|
Temp : PffFileDescriptor;
|
|
begin
|
|
if (aFile < 0) or (aFile >= FFileCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aFile]);
|
|
Temp := PffFileDescriptor(ddFileList.Items[aFile]);
|
|
Result := FFMakeFileNameExt(FBaseName, Temp^.fdExtension);
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetFileType(aFile : integer) : TffFileType;
|
|
begin
|
|
if (aFile < 0) or (aFile >= FFileCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aFile]);
|
|
Result := PffFileDescriptor(ddFileList.Items[aFile])^.fdType;
|
|
end;
|
|
{Begin !!.03}
|
|
{--------}
|
|
function TffDataDictionary.GetHasBLOBs : Boolean;
|
|
var
|
|
Index : Integer;
|
|
P : PffFieldDescriptor;
|
|
begin
|
|
if FHasBLOBs = fftbUnknown then begin
|
|
FHasBLOBs := fftbFalse;
|
|
for Index := 0 to Pred(FFldCount) do begin
|
|
P := FieldDescriptor^[index];
|
|
if P^.fdType in [fftBLOB..fftBLOBFile] then begin
|
|
FHasBLOBs := fftbTrue;
|
|
Break;
|
|
end; { if }
|
|
end; { for }
|
|
end; { if }
|
|
Result := (FHasBLOBs = fftbTrue);
|
|
end;
|
|
{End !!.03}
|
|
{--------}
|
|
function TffDataDictionary.GetIndexAllowDups(aIndexID : integer) : boolean;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := IndexDescriptor^[aIndexID]^.idDups;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetIndexAscend(aIndexID : integer) : boolean;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := IndexDescriptor^[aIndexID]^.idAscend;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetIndexDesc(aIndexID : integer) : TffDictItemDesc;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := IndexDescriptor^[aIndexID]^.idDesc;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetIndexFileNumber(aIndexID : integer) : Longint;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := IndexDescriptor^[aIndexId]^.idFile;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetIndexFromName(const aIndexName : TffDictItemName) : integer;
|
|
begin
|
|
for Result := 0 to pred(FInxCount) do
|
|
if (FFCmpShStrUC(aIndexName,
|
|
indexDescriptor^[Result]^.idName,
|
|
255) = 0) then
|
|
Exit;
|
|
Result := -1;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetIndexKeyLength(aIndexID : integer) : Longint;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := IndexDescriptor^[aIndexID]^.idKeyLen;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetIndexName(aIndexID : integer) : TffDictItemName;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := IndexDescriptor^[aIndexID]^.idName;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetIndexNoCase(aIndexID : integer) : boolean;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := IndexDescriptor^[aIndexID]^.idNoCase;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetIndexType(aIndexID : integer) : TffIndexType;
|
|
begin
|
|
if not ((0 <= aIndexID) and (aIndexID < FInxCount)) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aIndexID]);
|
|
Result := TffIndexType(IndexDescriptor^[aIndexID]^.idCount = -1);
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.GetRecordField(aField : integer;
|
|
aData : PffByteArray;
|
|
var aIsNull: boolean;
|
|
aValue : pointer);
|
|
begin
|
|
aIsNull := IsRecordFieldNull(aField, aData);
|
|
if (not aIsNull) and (aValue <> nil) then
|
|
with FieldDescriptor^[aField]^ do
|
|
Move(aData^[fdOffset], aValue^, fdLength);
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetRecordLength : Longint;
|
|
begin
|
|
Result := GetBaseRecordLength + {the fields themselves}
|
|
((FFldCount + 7) div 8); {the bit array for nulls}
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.HasAutoIncField(var aField : integer) : boolean;
|
|
begin
|
|
Result := true;
|
|
aField := 0;
|
|
while (aField < FFldCount) do begin
|
|
if FieldDescriptor^[aField]^.fdType = fftAutoInc then
|
|
Exit;
|
|
inc(aField);
|
|
end;
|
|
Result := false;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.HasSameFields(aSrcDict : TffDataDictionary;
|
|
var aBLOBFields : TffPointerList) : boolean;
|
|
var
|
|
anIndex : integer;
|
|
begin
|
|
Result := False;
|
|
if FieldCount <> aSrcDict.FieldCount then
|
|
Exit;
|
|
aBLOBFields.Empty;
|
|
|
|
for anIndex := 0 to pred(FieldCount) do begin
|
|
{ Must have same field type, length, decimal places, & units. }
|
|
Result := (FieldLength[anIndex] = aSrcDict.FieldLength[anIndex]) and
|
|
(FieldType[anIndex] = aSrcDict.FieldType[anIndex]) and
|
|
(FieldDecPl[anIndex] = aSrcDict.FieldDecPl[anIndex]) and
|
|
(FieldUnits[anIndex] = aSrcDict.FieldUnits[anIndex]);
|
|
if (not Result) then
|
|
Exit;
|
|
if FieldType[anIndex] in [fftBLOB..fftBLOBFile] then
|
|
aBLOBFields.Append(Pointer(anIndex));
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.HasSameFieldsEx(aSrcDict : TffDataDictionary;
|
|
aFields : PffLongintArray;
|
|
aNumFields : integer;
|
|
var aBLOBFields : TffPointerList) : boolean;
|
|
var
|
|
anIndex, aSrcIndex : integer;
|
|
begin
|
|
Result := False;
|
|
if FieldCount <> aNumFields then
|
|
Exit;
|
|
aBLOBFields.Empty;
|
|
|
|
for anIndex := 0 to pred(aNumFields) do begin
|
|
aSrcIndex := aFields^[anIndex];
|
|
{ Must have same field type, length, decimal places, & units. }
|
|
Result := (FieldLength[anIndex] = aSrcDict.FieldLength[aSrcIndex]) and
|
|
(FieldType[anIndex] = aSrcDict.FieldType[aSrcIndex]) and
|
|
(FieldDecPl[anIndex] = aSrcDict.FieldDecPl[aSrcIndex]) and
|
|
(FieldUnits[anIndex] = aSrcDict.FieldUnits[aSrcIndex]);
|
|
if (not Result) then
|
|
Exit;
|
|
if FieldType[anIndex] in [fftBLOB..fftBLOBFile] then
|
|
aBLOBFields.Append(Pointer(anIndex));
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.CheckForDefault(aVCheckDesc : PffVCheckDescriptor;
|
|
aFieldDesc : PffFieldDescriptor);
|
|
var
|
|
CheckVal : PffVCheckDescriptor;
|
|
begin
|
|
if Assigned(aVCheckDesc) and aVCheckDesc^.vdHasDefVal then begin
|
|
if (not Assigned(aFieldDesc^.fdVCheck)) then begin
|
|
FFGetZeroMem(CheckVal, sizeof(TffVCheckDescriptor));
|
|
aFieldDesc^.fdVCheck := CheckVal;
|
|
end;
|
|
aFieldDesc^.fdVCheck^.vdHasDefVal := True;
|
|
aFieldDesc^.fdVCheck^.vdDefVal := aVCheckDesc.vdDefVal;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.GetDefaultFldCount: Integer;
|
|
begin
|
|
ddDefFldList.Pack;
|
|
Result := ddDefFldList.Count;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.InitRecord(aData : PffByteArray);
|
|
begin
|
|
if (aData <> nil) and (FFldCount > 0) then begin
|
|
FillChar(aData^, FLogRecLen + ((FFldCount + 7) div 8), 0);
|
|
FFSetAllBits(PffByteArray(@aData^[LogicalRecordLength]), FFldCount); {!!.02}
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.InsertField(AtIndex : Integer;
|
|
const aIdent : TffDictItemName;
|
|
const aDesc : TffDictItemDesc;
|
|
aType : TffFieldType;
|
|
aUnits : Integer;
|
|
aDecPl : Integer;
|
|
aReqFld : Boolean;
|
|
const aValCheck : PffVCheckDescriptor);
|
|
var
|
|
NewDesc : PffFieldDescriptor;
|
|
TempDesc : PffFieldDescriptor;
|
|
NewOffset: integer;
|
|
Inx : integer;
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
{check for a duplicate field name}
|
|
if (GetFieldFromName(aIdent) <> -1) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDupFieldName, [FBaseName, aIdent]);
|
|
{create it}
|
|
if (0 <= AtIndex) and (AtIndex < FFldCount) then begin
|
|
FHasBLOBs := fftbUnknown; {!!03}
|
|
NewDesc := CreateFieldDesc(aIdent, aDesc, aType, aUnits, aDecPl, aReqFld, aValCheck);
|
|
try
|
|
NewDesc^.fdNumber := AtIndex;
|
|
if (AtIndex > 0) then begin
|
|
TempDesc := FieldDescriptor^[pred(AtIndex)];
|
|
with TempDesc^ do
|
|
NewDesc^.fdOffset := fdOffset + fdLength;
|
|
end;
|
|
{ Shift existing fields up. }
|
|
for Inx := pred(FFldCount) downto AtIndex do
|
|
FieldDescriptor^[succ(Inx)] := FieldDescriptor^[Inx];
|
|
FieldDescriptor^[AtIndex] := NewDesc;
|
|
inc(FFldCount);
|
|
{ Have we reached our field capacity? }
|
|
if FFldCount = FFieldCapacity then
|
|
{ Yes, expand our field array. }
|
|
ddExpandFieldArray(0);
|
|
{patch up all successive descriptors}
|
|
with NewDesc^ do
|
|
NewOffset := fdOffset + fdLength;
|
|
for Inx := succ(AtIndex) to pred(FFldCount) do begin
|
|
TempDesc := FieldDescriptor^[Inx];
|
|
with TempDesc^ do
|
|
begin
|
|
fdNumber := Inx;
|
|
fdOffset := NewOffset;
|
|
inc(NewOffset, fdLength);
|
|
end;
|
|
end;
|
|
FLogRecLen := NewOffset;
|
|
except
|
|
FFFreeMem(NewDesc,sizeof(TffFieldDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.IsIndexDescValid(const aIndexDesc : TffIndexDescriptor) : boolean;
|
|
var
|
|
i : integer;
|
|
KeyLen : integer;
|
|
begin
|
|
Result := false;
|
|
with aIndexDesc do begin
|
|
if (idName = '') then
|
|
Exit;
|
|
if (0 > idFile) or (idFile >= FFileCount) then
|
|
Exit;
|
|
if (idCount = -1) then begin {user-defined index}
|
|
if (idKeyLen <= 0) then
|
|
Exit;
|
|
end
|
|
else begin {composite index}
|
|
if (idCount = 0) then
|
|
Exit;
|
|
KeyLen := 0;
|
|
for i := 0 to pred(idCount) do begin
|
|
if (idFields[i] < 0) or (idFields[i] >= FFldCount) then
|
|
Exit;
|
|
inc(KeyLen, FieldDescriptor^[idfields[i]]^.fdLength);
|
|
end;
|
|
inc(KeyLen, (idCount + 7) div 8);
|
|
if (KeyLen > ffcl_MaxKeyLength) then
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := true;
|
|
end;
|
|
{--------}
|
|
function TffDataDictionary.IsRecordFieldNull(aField : integer;
|
|
aData : PffByteArray) : boolean;
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds,
|
|
[FBaseName, aField]);
|
|
Result := (aData = nil) or
|
|
FFIsBitSet(PffByteArray(@aData^[FLogRecLen]), aField);
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.ReadFromStream(S : TStream);
|
|
var
|
|
Reader : TReader;
|
|
i, j : Integer;
|
|
FileDesc : PffFileDescriptor;
|
|
FldDesc : PffFieldDescriptor;
|
|
InxDesc : PffIndexDescriptor;
|
|
HasVCheck : Boolean;
|
|
begin
|
|
ClearPrim(true);
|
|
Reader := TReader.Create(S, 4096);
|
|
try
|
|
with Reader do begin
|
|
FBLOBFileNumber := 0;
|
|
FIsEncrypted := ReadBoolean;
|
|
FFileCount := ReadInteger;
|
|
try
|
|
for i := 0 to pred(FFileCount) do begin
|
|
FFGetZeroMem(FileDesc, sizeof(TffFileDescriptor));
|
|
with FileDesc^ do begin
|
|
fdNumber := i;
|
|
fdDesc := ReadString;
|
|
fdExtension := ReadString; //<-- Soner fpc raises exception "Invalid Value for property"
|
|
// for embeddedserver in function classes.pas TReader.ReadString
|
|
fdBlockSize := ReadInteger;
|
|
fdType := TffFileType(ReadInteger);
|
|
if (fdType = ftBLOBFile) then
|
|
FBLOBFileNumber := i;
|
|
end;
|
|
ddFileList.Add(pointer(FileDesc));
|
|
FileDesc := nil;
|
|
end;
|
|
except
|
|
if Assigned(FileDesc) then
|
|
FFFreeMem(FileDesc, sizeOf(TffFileDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
FFldCount := ReadInteger;
|
|
ddExpandFieldArray(FFldCount + 1);
|
|
try
|
|
for i := 0 to pred(FFldCount) do begin
|
|
FFGetZeroMem(FldDesc, sizeof(TffFieldDescriptor));
|
|
with FldDesc^ do begin
|
|
fdNumber := i;
|
|
fdName := ReadString;
|
|
fdDesc := ReadString;
|
|
fdUnits := ReadInteger;
|
|
fdDecPl := ReadInteger;
|
|
fdOffset := ReadInteger;
|
|
fdLength := ReadInteger;
|
|
fdType := TffFieldType(ReadInteger);
|
|
fdRequired := ReadBoolean;
|
|
HasVCheck := ReadBoolean;
|
|
if HasVCheck then begin
|
|
FFGetZeroMem(fdVCheck, sizeof(TffVCheckDescriptor));
|
|
with fdVCheck^ do begin
|
|
vdPicture := ReadString;
|
|
vdHasMinVal := ReadBoolean;
|
|
vdHasMaxVal := ReadBoolean;
|
|
vdHasDefVal := ReadBoolean;
|
|
{if the field has a default value, we add the field
|
|
number to ddDefFldList}
|
|
if vdHasDefVal then begin
|
|
ddDefFldList.Add(Pointer(i));
|
|
end;
|
|
if vdHasMinVal then
|
|
Read(vdMinVal, fdLength);
|
|
if vdHasMaxVal then
|
|
Read(vdMaxVal, fdLength);
|
|
if vdHasDefVal then
|
|
Read(vdDefVal, fdLength);
|
|
end;
|
|
end;
|
|
end;
|
|
FieldDescriptor^[i] := FldDesc;
|
|
FldDesc := nil;
|
|
end;
|
|
except
|
|
if Assigned(FldDesc) then
|
|
FFFreeMem(FldDesc, sizeOf(TffFieldDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
FLogRecLen := ReadInteger;
|
|
FInxCount := ReadInteger;
|
|
ddExpandIndexArray(FInxCount + 1);
|
|
try
|
|
{note that index 0 is never stored on a stream}
|
|
for i := 1 to pred(FInxCount) do begin
|
|
FFGetZeroMem(InxDesc, sizeof(TffIndexDescriptor));
|
|
with InxDesc^ do begin
|
|
idNumber := i;
|
|
idName := ReadString;
|
|
idDesc := ReadString;
|
|
idFile := ReadInteger;
|
|
idKeyLen := ReadInteger;
|
|
idCount := ReadInteger;
|
|
if (idCount <> -1) then
|
|
for j := 0 to pred(idCount) do begin
|
|
idFields[j] := ReadInteger;
|
|
if NextValue=vaString then
|
|
idFieldIHlprs[j] := ReadString
|
|
else
|
|
idFieldIHlprs[j] := '';
|
|
end;
|
|
idDups := ReadBoolean;
|
|
idAscend := ReadBoolean;
|
|
idNoCase := ReadBoolean;
|
|
end;
|
|
IndexDescriptor^[i] := InxDesc;
|
|
InxDesc := nil;
|
|
end;
|
|
except
|
|
if Assigned(InxDesc) then
|
|
FFFreeMem(InxDesc, sizeOf(TffIndexDescriptor));
|
|
raise;
|
|
end;{try..except}
|
|
end;
|
|
finally
|
|
Reader.Free;
|
|
end;{try..finally}
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.RemoveField(aField : Longint);
|
|
var
|
|
TempDesc : PffFieldDescriptor;
|
|
NewOffset : Integer;
|
|
Inx, {!!.13}
|
|
FldInx : Integer; {!!.13}
|
|
InxDesc : PffIndexDescriptor; {!!.13}
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
if (0 <= aField) and (aField < FFldCount) then begin
|
|
{Begin !!.13}
|
|
{ Verify the field is not being used by an index. }
|
|
for Inx := Pred(IndexCount) downto 0 do begin
|
|
InxDesc := IndexDescriptor[Inx];
|
|
for FldInx := 0 to Pred(InxDesc^.idCount) do
|
|
if InxDesc^.idFields[FldInx] = aField then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrFileInUse,
|
|
[aField]);
|
|
end;
|
|
{End !!.13}
|
|
FHasBLOBs := fftbUnknown; {!!.03}
|
|
TempDesc := FieldDescriptor^[aField];
|
|
NewOffset := TempDesc^.fdOffset;
|
|
FFFreeMem(TempDesc, sizeOf(TffFieldDescriptor));
|
|
{ Shift fields down to cover the empty space. }
|
|
for Inx := aField to (FFldCount - 2) do
|
|
FieldDescriptor^[Inx] := FieldDescriptor^[succ(Inx)]; {!!.01}
|
|
dec(FFldCount);
|
|
{patch up all successive descriptors}
|
|
for Inx := aField to pred(FFldCount) do begin
|
|
TempDesc := FieldDescriptor^[Inx];
|
|
with TempDesc^ do begin
|
|
fdNumber := Inx;
|
|
fdOffset := NewOffset;
|
|
inc(NewOffset, fdLength);
|
|
end;
|
|
end;
|
|
FLogRecLen := NewOffset;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.RemoveFile(aFile : Longint);
|
|
var
|
|
TempDesc : PffFileDescriptor;
|
|
Inx : integer;
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
{can't remove entry 0: it's the base file}
|
|
if (aFile = 0) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrBaseFile, [FBaseName]);
|
|
{remove the entry}
|
|
if (0 < aFile) and (aFile < FFileCount) then begin
|
|
TempDesc := PffFileDescriptor(ddFileList.Items[aFile]);
|
|
{if the BLOB file is being removed from the dictionary then reset
|
|
the BLOB file number field}
|
|
if (TempDesc^.fdType = ftBLOBFile) then
|
|
FBLOBFileNumber := 0;
|
|
{Begin!!.13}
|
|
{ If an index file is being removed from the dictionary then make sure
|
|
it is not referenced by an index. }
|
|
if (TempDesc^.fdType = ftIndexFile) then begin
|
|
for Inx := pred(FInxCount) downto 0 do
|
|
if (IndexDescriptor^[Inx]^.idFile = aFile) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrFileInUse,
|
|
[aFile]);
|
|
{ Fixup index descriptors referencing files with higher file numbers. }
|
|
for Inx := Pred(IndexCount) downto 0 do
|
|
if (IndexDescriptor^[Inx]^.idFile > aFile) then
|
|
Dec(IndexDescriptor^[Inx]^.idFile);
|
|
end; { if }
|
|
{End !!.13}
|
|
|
|
FFFreeMem(TempDesc, sizeOf(TffFileDescriptor));
|
|
ddFileList.Delete(aFile);
|
|
dec(FFileCount);
|
|
{patch up all successive descriptors}
|
|
for Inx := aFile to pred(FFileCount) do begin
|
|
TempDesc := PffFileDescriptor(ddFileList[Inx]);
|
|
TempDesc^.fdNumber := Inx;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.RemoveIndex(aIndex : Longint);
|
|
var
|
|
TempDesc : PffIndexDescriptor;
|
|
Inx : integer;
|
|
begin
|
|
(*
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
*)
|
|
{remove the entry}
|
|
if (0 <= aIndex) and (aIndex < FInxCount) then begin
|
|
TempDesc := IndexDescriptor^[aIndex];
|
|
FFFreeMem(TempDesc, sizeOf(TffIndexDescriptor));
|
|
{Begin !!.02}
|
|
{ Shift the descriptors above the deleted index down to fill in
|
|
the gap. }
|
|
for Inx := aIndex to (FInxCount - 2) do begin
|
|
IndexDescriptor^[Inx] := IndexDescriptor^[succ(Inx)];
|
|
IndexDescriptor^[Inx]^.idNumber := Inx;
|
|
end;
|
|
dec(FInxCount);
|
|
end;
|
|
{End !!.02}
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.SetBaseName(const BN : TffTableName);
|
|
begin
|
|
FBaseName := BN;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.SetBlockSize(BS : Longint);
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
if (BS <> BlockSize) and FFVerifyBlockSize(BS) then
|
|
if (BS > BlockSize) or
|
|
(RecordLength <= (BS - ffc_BlockHeaderSizeData - sizeof(Longint))) then begin
|
|
if (FFileCount > 0) then
|
|
PffFileDescriptor(ddFileList.Items[0])^.fdBlockSize := BS;
|
|
end;
|
|
end;
|
|
{Begin !!.11}
|
|
{--------}
|
|
procedure TffDataDictionary.SetDefaultFieldValue(aData : PffByteArray;
|
|
const aField : Integer);
|
|
var
|
|
i : Integer;
|
|
BS : PffByteArray;
|
|
CurrField : PffByteArray;
|
|
HasDefault : Boolean;
|
|
begin
|
|
if (aData = nil) then
|
|
Exit;
|
|
BS := PffByteArray(@aData^[LogicalRecordLength]);
|
|
HasDefault := False;
|
|
for i := 0 to Pred(ddDefFldList.Count) do begin
|
|
HasDefault := (Integer(ddDefFldList[i]) = aField);
|
|
if HasDefault then begin
|
|
{ If the field is nil and it has a default value, we're going to
|
|
add the default value for the field. }
|
|
if FieldDescriptor^[aField]^.fdVCheck <> nil then
|
|
if FFIsBitSet(BS, aField) and
|
|
FieldDescriptor^[aField]^.fdVCheck^.vdHasDefVal then begin
|
|
CurrField := PffByteArray(@aData^[FieldDescriptor^[aField]^.fdOffset]);
|
|
Move(FieldDescriptor^[aField]^.fdVCheck^.vdDefVal,
|
|
CurrField^,
|
|
FieldDescriptor^[afield]^.fdLength);
|
|
FFClearBit(BS, aField);
|
|
end; { if }
|
|
break;
|
|
end; { if }
|
|
end; { for }
|
|
if not HasDefault then
|
|
SetRecordFieldNull(aField, aData, True);
|
|
end;
|
|
{End !!.11}
|
|
{--------}
|
|
procedure TffDataDictionary.SetDefaultFieldValues(aData : PffByteArray);
|
|
var
|
|
DefFldNo : Integer;
|
|
i : Integer;
|
|
BS : PffByteArray;
|
|
CurrField : PffByteArray;
|
|
begin
|
|
if (aData = nil) then
|
|
Exit;
|
|
BS := PffByteArray(@aData^[LogicalRecordLength]); {!!.06}
|
|
for i := 0 to pred(ddDefFldList.Count) do begin
|
|
{if the field is nil and it has a default value, we're going to
|
|
add the default value for the field}
|
|
DefFldNo := Integer(ddDefFldList[i]);
|
|
if FieldDescriptor^[DefFldNo]^.fdVCheck <> nil then
|
|
if FFIsBitSet(BS, DefFldNo) and
|
|
FieldDescriptor^[DefFldNo]^.fdVCheck^.vdHasDefVal then begin
|
|
CurrField := PffByteArray(@aData^[FieldDescriptor^[DefFldNo]^.fdOffset]);
|
|
Move(FieldDescriptor^[DefFldNo]^.fdVCheck^.vdDefVal,
|
|
CurrField^,
|
|
FieldDescriptor^[DefFldNo]^.fdLength);
|
|
FFClearBit(BS, DefFldNo);
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.SetIsEncrypted(IE : Boolean);
|
|
begin
|
|
{can't be done in readonly mode}
|
|
if ddReadOnly then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrDictReadOnly, [FBaseName]);
|
|
FIsEncrypted := IE;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.SetRecordField(aField : integer;
|
|
aData : PffByteArray;
|
|
aValue : pointer);
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
with FieldDescriptor^[aField]^ do begin
|
|
if (aValue = nil) then begin
|
|
FFSetBit(PffByteArray(@aData^[FLogRecLen]), aField);
|
|
FillChar(aData^[fdOffset], fdLength, 0);
|
|
end
|
|
else begin
|
|
FFClearBit(PffByteArray(@aData^[FLogRecLen]), aField);
|
|
Move(aValue^, aData^[fdOffset], fdLength);
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.SetRecordFieldNull(aField : integer;
|
|
aData : PffByteArray;
|
|
aIsNull : boolean);
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
with FieldDescriptor^[aField]^ do begin
|
|
if aIsNull then
|
|
FFSetBit(PffByteArray(@aData^[FLogRecLen]), aField)
|
|
else
|
|
FFClearBit(PffByteArray(@aData^[FLogRecLen]), aField);
|
|
FillChar(aData^[fdOffset], fdLength, 0);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.SetValidityCheck(aField : integer;
|
|
var aExists : boolean;
|
|
const aVCheck : TffVCheckDescriptor);
|
|
begin
|
|
if (aField < 0) or (aField >= FFldCount) then
|
|
FFRaiseException(EffException, ffStrResGeneral, fferrOutOfBounds, [FBaseName, aField]);
|
|
with FieldDescriptor^[aField]^ do begin
|
|
if aExists then begin
|
|
if (fdVCheck = nil) then
|
|
FFGetZeroMem(fdVCheck, sizeOf(TffVCheckDescriptor));
|
|
if (@aVCheck <> fdVCheck) then
|
|
Move(aVCheck, fdVCheck^, sizeof(fdVCheck))
|
|
end
|
|
else {aExists is false} begin
|
|
if (fdVCheck <> nil) then
|
|
FFFreeMem(fdVCheck, sizeOf(TffVCheckDescriptor));
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffDataDictionary.WriteToStream(S : TStream);
|
|
var
|
|
Writer : TWriter;
|
|
i, j : Integer;
|
|
FileDesc : PffFileDescriptor;
|
|
FldDesc : PffFieldDescriptor;
|
|
InxDesc : PffIndexDescriptor;
|
|
begin
|
|
CheckValid;
|
|
Writer := TWriter.Create(S, 4096);
|
|
try
|
|
with Writer do begin
|
|
WriteBoolean(FIsEncrypted);
|
|
WriteInteger(FFileCount);
|
|
for i := 0 to pred(FFileCount) do begin
|
|
FileDesc := PffFileDescriptor(ddFileList[i]);
|
|
with FileDesc^ do begin
|
|
AnsiStringWriter(fdDesc, Writer); {!!.05}
|
|
AnsiStringWriter(fdExtension, Writer);
|
|
WriteInteger(fdBlockSize);
|
|
WriteInteger(ord(fdType));
|
|
end;
|
|
end;
|
|
WriteInteger(FFldCount);
|
|
for i := 0 to pred(FFldCount) do begin
|
|
FldDesc := FieldDescriptor^[i];
|
|
with FldDesc^ do begin
|
|
AnsiStringWriter(fdName, Writer); {!!.05}
|
|
AnsiStringWriter(fdDesc, Writer); {!!.05}
|
|
WriteInteger(fdUnits);
|
|
WriteInteger(fdDecPl);
|
|
WriteInteger(fdOffset);
|
|
WriteInteger(fdLength);
|
|
WriteInteger(ord(fdType));
|
|
WriteBoolean(fdRequired);
|
|
WriteBoolean(fdVCheck <> nil);
|
|
if (fdVCheck <> nil) then begin
|
|
with fdVCheck^ do begin
|
|
AnsiStringWriter(vdPicture, Writer); {!!.05}
|
|
WriteBoolean(vdHasMinVal);
|
|
WriteBoolean(vdHasMaxVal);
|
|
WriteBoolean(vdHasDefVal);
|
|
if vdHasMinVal then
|
|
Write(vdMinVal, fdLength);
|
|
if vdHasMaxVal then
|
|
Write(vdMaxVal, fdLength);
|
|
if vdHasDefVal then
|
|
Write(vdDefVal, fdLength);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
WriteInteger(FLogRecLen);
|
|
WriteInteger(FInxCount);
|
|
{note we don't write index 0 to the stream}
|
|
for i := 1 to pred(FInxCount) do begin
|
|
InxDesc := IndexDescriptor^[i];
|
|
with InxDesc^ do begin
|
|
AnsiStringWriter(idName, Writer); {!!.05}
|
|
AnsiStringWriter(idDesc, Writer); {!!.05}
|
|
WriteInteger(idFile);
|
|
WriteInteger(idKeyLen);
|
|
WriteInteger(idCount);
|
|
if (idCount <> -1) then
|
|
for j := 0 to pred(idCount) do begin
|
|
WriteInteger(idFields[j]);
|
|
if Length(idFieldIHlprs[j]) > 0 then
|
|
AnsiStringWriter(idFieldIHlprs[j], Writer); {!!.05}
|
|
end;
|
|
WriteBoolean(idDups);
|
|
WriteBoolean(idAscend);
|
|
WriteBoolean(idNoCase);
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
Writer.Free;
|
|
end;{try..finally}
|
|
end;
|
|
{====================================================================}
|
|
|
|
{moved from FFTBBASE}
|
|
{===Composite Key manipulation routines==============================}
|
|
procedure FFInitKey(aKey : PffByteArray;
|
|
aKeyLen : integer;
|
|
aKeyFldCount : integer);
|
|
begin
|
|
if (aKey <> nil) then begin
|
|
FillChar(aKey^, aKeyLen, 0);
|
|
if (aKeyFldCount <= 8) then
|
|
FFSetAllBits(PffByteArray(@aKey^[aKeyLen-1]), aKeyFldCount)
|
|
else
|
|
FFSetAllBits(PffByteArray(@aKey^[aKeyLen-2]), aKeyFldCount);
|
|
end;
|
|
end;
|
|
{--------}
|
|
function FFIsKeyFieldNull(aKey : PffByteArray;
|
|
aKeyLen : integer;
|
|
aKeyFldCount : integer;
|
|
aKeyFld : integer) : boolean;
|
|
begin
|
|
if (aKey = nil) then
|
|
Result := true
|
|
else begin
|
|
if (aKeyFldCount <= 8) then
|
|
Result := FFIsBitSet(PffByteArray(@aKey^[aKeyLen-1]), aKeyFld)
|
|
else
|
|
Result := FFIsBitSet(PffByteArray(@aKey^[aKeyLen-2]), aKeyFld);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure FFSetKeyFieldNonNull(aKey : PffByteArray;
|
|
aKeyLen : integer;
|
|
aKeyFldCount : integer;
|
|
aKeyFld : integer);
|
|
begin
|
|
if (aKey <> nil) then begin
|
|
if (aKeyFldCount <= 8) then
|
|
FFClearBit(PffByteArray(@aKey^[aKeyLen-1]), aKeyFld)
|
|
else
|
|
FFClearBit(PffByteArray(@aKey^[aKeyLen-2]), aKeyFld);
|
|
end;
|
|
end;
|
|
{====================================================================}
|
|
end.
|