unit ABSMemEngine;

{$I ABSVer.inc}

interface

uses SysUtils, Classes, Math,

// AbsoluteDatabase units

     {$IFDEF DEBUG_LOG}
     ABSDebug,
     {$ENDIF}
     ABSExcept,
     ABSBase,
     ABSBaseEngine,
     ABSBTree,
     ABSPage,
     ABSMemory,
     ABSCompression,
     ABSSecurity,
     ABSTypes,
     ABSExpressions,
     ABSConverts,
     ABSConst;

type

  TABSMemoryPageManager = class;

////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryDatabaseData
//
////////////////////////////////////////////////////////////////////////////////


  TABSMemoryDatabaseData = class (TABSDatabaseData)
   private
    FLastObjectID: TABSLastObjectID;
   public
    constructor Create;
    destructor Destroy; override;
    // create table data
    function CreateTableData(Cursor: TABSCursor): TABSTableData; override;
    procedure ConnectSession(Session: TABSBaseSession); override;
    
    function GetNewObjectId: TABSObjectID; override;
  end; // TABSMemoryDatabaseData


////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryRecordManager
//
////////////////////////////////////////////////////////////////////////////////


  TABSMemoryRecordManager = class (TABSBaseRecordManager)
   private
    // record buffers
    FRecordBuffer:          PByte;
    // sequence of bits: 1 - for deleted record, 0 - for existing record
    FDeleteFlagBuffer:      PByte;
    FAllocatedRecordCount:  Integer;
    FAllocRecordsBy:        Integer;
    FFirstRecordID:         Integer;
    FLastRecordID:          Integer;
    FBitsTable:             array [0..255] of byte;
    FNoRecordsDeleted:      ByteBool;
   private
    // find record id by record position
    function FindRecord(RecordNo: TABSRecordNo): TABSRecordNo;
    // returns position of the record specified by record id,
    // or INVALID_ID8 if the record with this id does not exists
    function GetTablePositionByRecordID(
              RecordNo:           TABSRecordNo
                                        ): TABSRecordNo;
    // return result for attempt of getting record relatively to first position
    // and set RecordID to new record ID
    function GetRecordFromFirstPosition(
            GetRecordMode: TABSGetRecordMode;
            var RecordID: Integer
                                       ): TABSGetRecordResult;
    // return result for attempt of getting record relatively to last position
    // and set RecordID to new record ID
    function GetRecordFromLastPosition(
            GetRecordMode: TABSGetRecordMode;
            var RecordID: Integer
                                      ): TABSGetRecordResult;
    // return result for attempt of getting record relatively any position
    // and set RecordID to new record ID
    function GetRecordFromAnyPosition(
            GetRecordMode: TABSGetRecordMode;
            var RecordID: Integer
                                      ): TABSGetRecordResult;
   protected
    procedure SetRecordCount(NewRecordCount: Integer);
   public
    constructor Create(
                        RecordBufferSize:     Integer;
                        AllocRecordsBy:       Integer
                      );
    destructor Destroy; override;

    procedure Empty(SessionID: TABSSessionID); override;

    // add record and return its number
    function AddRecord(SessionID: TABSSessionID; RecordBuffer: TABSRecordBuffer; var RecordID: TABSRecordID): Boolean; override;
    // update record, return true if record was updated, false if record was deleted
    function UpdateRecord(SessionID: TABSSessionID; RecordBuffer: TABSRecordBuffer; RecordID: TABSRecordID): Boolean; override;
    // delete record, return true if record was deleted, false if record was deleted earlier
    function DeleteRecord(SessionID: TABSSessionID; var RecordID: TABSRecordID): Boolean; override;
    procedure GetRecordBuffer(SessionID: TABSSessionID; var NavigationInfo: TABSNavigationInfo); override;
    // return 0,1, or -1 if (1 = 2), (1 > 2) or (1 < 2)
    function CompareRecordID(RecordID1: TABSRecordID; RecordID2: TABSRecordID): Integer; override;
    // return record no
    function GetApproximateRecNo(SessionID: TABSSessionID; RecordID: TABSRecordID): TABSRecordNo; override;
    // add loaded record
    //procedure AddLoadedRecord(RecordBuffer: TABSRecordBuffer); override;
  end; // TABSMemoryRecordManager


////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryTableData
//
////////////////////////////////////////////////////////////////////////////////


  TABSMemoryTableData = class (TABSTableData)
   private
    FLoadedRecordCount: Integer;
    FAllocRecordsBy:    Integer;

   protected
    procedure CreateRecordManager; override;
    procedure CreateFieldManager(FieldDefs: TABSFieldDefs); override;
    procedure CreateIndexManager(IndexDefs: TABSIndexDefs); override;
    procedure CreateConstraintManager(ConstraintDefs: TABSConstraintDefs); override;
    //procedure CreateSequenceManager;
    //procedure BuildSequences;
    procedure LoadTableHeader(
              Stream:             TStream;
              var BLOBDescriptor: TABSBLOBDescriptor
                              );
    procedure SaveTableHeader(
              Stream:             TStream;
              var BLOBDescriptor: TABSBLOBDescriptor
                              );
    //procedure LoadSequencesFromStream(Stream: TStream);
    //procedure SaveSequencesToStream(Stream: TStream);
    procedure LoadBLOBDataFromStream(
            RecordBuffer: TABSRecordBuffer;
            FieldNo:      Integer;
            Stream:       TStream
                                 );
    procedure LoadRecordFromStream(
            RecordBuffer: TABSRecordBuffer;
            Stream:       TStream
                                 );
    procedure PrepareRecordBufferForSave(
            RecordBuffer:             TABSRecordBuffer;
            BLOBDescriptorList:       TList;
            BLOBDataList:             TList;
            BLOBPosition:             Int64
                                                );
    procedure SaveRecordToStream(
            RecordBuffer: TABSRecordBuffer;
            Stream:       TStream
                                 );
    //procedure LoadRecordManager(Stream: TStream);
    //procedure SaveRecordManager(Stream: TStream);
    // return filter bitmap rec count
    function GetBitmapRecordCount(SessionID: TABSSessionID): TABSRecordNo; override;
    // return filter bitmap rec no by record id
    function GetBitmapRecNoByRecordID(RecordID: TABSRecordID): TABSRecordNo; override;
    // return filter bitmap rec no by record id
    function GetRecordIDByBitmapRecNo(RecordNo: TABSRecordNo): TABSRecordID; override;
   public
    constructor Create(
                        aDatabaseData: TABSDatabaseData;
                        AllocRecordsBy: Integer = 1000
                      );
    destructor Destroy; override;

    procedure CreateTable(
                          Cursor: TABSCursor;
                          FieldDefs: TABSFieldDefs;
                          IndexDefs: TABSIndexDefs;
                          ConstraintDefs: TABSConstraintDefs
                         ); override;
    procedure DeleteTable(Cursor: TABSCursor; DesignMode: Boolean = False); override;
    procedure EmptyTable(Cursor: TABSCursor); override;
    procedure OpenTable(Cursor: TABSCursor); override;
    procedure CloseTable(Cursor: TABSCursor); override;

    procedure AddIndex(IndexDef: TABSIndexDef; Cursor: TABSCursor); override;
    procedure DeleteIndex(IndexID: TABSObjectID; Cursor: TABSCursor); override;

    //---------------------------------------------------------------------------
    // BLOB methods
    //---------------------------------------------------------------------------
    procedure WriteBLOBFieldToRecordBuffer(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              BLOBStream: TABSStream
              ); override;

    procedure ClearBLOBFieldInRecordBuffer(
              RecordBuffer: TABSRecordBuffer;
              FieldNo:    Integer
              ); override;

    function InternalCreateBlobStream(
              Cursor:   TABSCursor;
              ToInsert: Boolean;
              FieldNo:  Integer;
              OpenMode: TABSBLOBOpenMode
              ): TABSStream; override;

    procedure GetDirectBlobData(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              RecordBuffer: TABSRecordBuffer;
              var BLOBDescriptor: TABSPartialTemporaryBLOBDescriptor;
              var pBlobData: TAbsPByte); override;

    procedure SetDirectBlobData(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              RecordBuffer: TABSRecordBuffer;
              var BLOBDescriptor: TABSPartialTemporaryBLOBDescriptor;
              var pBlobData: TAbsPByte); override;

    procedure FreeDirectBlobData(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              RecordBuffer: TABSRecordBuffer;
              var BLOBDescriptor: TABSPartialTemporaryBLOBDescriptor;
              var pBlobData: TAbsPByte); override;

    function InsertRecord(var Cursor: TABSCursor): Boolean; override;
    function DeleteRecord(Cursor: TABSCursor): Boolean; override;
    function UpdateRecord(Cursor: TABSCursor): Boolean; override;

    function InternalGetRecordCount(Cursor: TABSCursor): TABSRecordNo; override;
    // move cursor to specified position and set current record id in cursor
    procedure InternalSetRecNo(Cursor: TABSCursor; RecNo: TABSRecordNo); override;
    // get current record position from cursor
    function InternalGetRecNo(Cursor: TABSCursor): TABSRecordNo; override;

    function LastAutoincValue(FieldNo: Integer; Session: TABSBaseSession): Int64; override;
  end; // TABSMemoryTableData




////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryPageManager
//
////////////////////////////////////////////////////////////////////////////////


  TABSMemoryPageManager = class (TABSPageManager)
   protected
     FAllocatedPageMap:    TABSBitsArray;
     FAllocatedPageCount:  TABSPageNo;
     FAllocateBy:          Integer;
     LastAllocatedPageNo:  TABSPageNo;
     FDataPtrs:            array of TAbsPByte;

   public
     procedure LoadFromStream(Stream: TStream); override;
     procedure SaveToStream(Stream: TStream); override;
     procedure InternalAddPage(aPage: TABSPage); override;
     procedure InternalRemovePage(PageNo: TABSPageNo); override;
     function InternalReadPage(aPage: TABSPage): Boolean; override;
     procedure InternalWritePage(aPage: TABSPage); override;

     constructor Create;
     destructor Destroy; override;
  end; // TABSMemoryPageManager


implementation

uses

// AbsoluteDatabase units

  ABSLocalEngine;

////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryDatabaseData
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
constructor TABSMemoryDatabaseData.Create;
begin
 inherited Create;
 FPageManager := nil;
 DatabaseName := ABSMemoryDatabaseName;
 FLastObjectID := 0;
end;// Create


//------------------------------------------------------------------------------
// destroy
//------------------------------------------------------------------------------
destructor TABSMemoryDatabaseData.Destroy;
begin
 while (FTableDataList.Count > 0) do
  begin
    if (FTableDataList.Items[0] = nil) then
     raise EABSException.Create(10258,ErrorLNilPointer);
    TABSTableData(FTableDataList.Items[0]).DeleteTable(nil);
  end;
 inherited Destroy;
end;// Destroy


//------------------------------------------------------------------------------
// create table data
//------------------------------------------------------------------------------
function TABSMemoryDatabaseData.CreateTableData(Cursor: TABSCursor): TABSTableData;
begin
 Result := TABSMemoryTableData.Create(Self);
 Result.TableName := Cursor.TableName;
 FTableDataList.Add(Result);
end;


//------------------------------------------------------------------------------
// ConnectSession
//------------------------------------------------------------------------------
procedure TABSMemoryDatabaseData.ConnectSession(Session: TABSBaseSession);
begin
  Session.SessionID := 0;
  inherited ConnectSession(Session);
end;// ConnectSession


//------------------------------------------------------------------------------
// GetNewObjectId
//------------------------------------------------------------------------------
function TABSMemoryDatabaseData.GetNewObjectId: TABSObjectID;
begin
  Lock;
  try
    Inc(FLastObjectID);
    Result := FLastObjectID;
  finally
   Unlock;
  end;
end;//GetNewObjectId


////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryRecordManager
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// find record position in buffer by record number
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.FindRecord(RecordNo: TABSRecordNo): TABSRecordNo;
var i{,l,Pos}:  Integer;
    b,k:        Byte;
    n:          TABSRecordNo;
begin
 Result := INVALID_ID8;
 if (RecordNo < 0) or (RecordNo > FRecordCount) then
  Exit;

 if (FNoRecordsDeleted) then
  begin
   Result := RecordNo;
   Exit;
  end;
{
 i := 0; // byte number
 n := 0; // bits count
 Pos := RecordNo div 8;
 while (i < Pos) do
  begin
   b := PByte(FDeleteFlagBuffer + i)^;
   Inc(n,FBitsTable[b]);
   Inc(i);
  end;
 if (i > 0) then Dec(i);
 Result := i * 8;
 if (i > 0) then
  Inc(i);
 if (i > FAllocatedRecordCount) then
  raise EABSException.Create(10457,ErrorLInvalidBitNo,[RecordNo,FAllocatedRecordCount]);
 b := PByte(FDeleteFlagBuffer + i)^;
 l := 7;
 if (i = (FAllocatedRecordCount div 8)) then
  l := (FAllocatedRecordCount mod 8)-1;
 for k := 0 to l do
  begin
   if ((b and (1 shl k)) = 0) then Inc(n);
   if (n > RecordNo) then Break;
   Inc(Result);
  end;

Exit;
}
 i := 0; // byte number
 n := 0; // bits count
 repeat
   b := PByte(PAnsiChar(FDeleteFlagBuffer) + i)^;
   n := n + FBitsTable[b];
   Inc(i);
 until (n >= RecordNo);
 Result := i * 8;
 // scan last byte
 if (n <> RecordNo) then
  for k := 7 downto 0 do
   begin
    if ((b and (1 shl k)) = 0) then Dec(n);
    Dec(Result);
    if (n = RecordNo) then Break;
   end;
end;


//------------------------------------------------------------------------------
// returns position of the record specified by record id,
// or INVALID_ID8 if the record with this id does not exists
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.GetTablePositionByRecordID(
              RecordNo:            TABSRecordNo
                ): TABSRecordNo;
var n,i,j:    Integer;
    b,k:      Byte;
begin
 Result := INVALID_ID8;
 if (RecordNo >= FAllocatedRecordCount) then
  Exit;
 if (FNoRecordsDeleted) then
  begin
   Result := RecordNo+1;
   Exit;
  end;
 // number of byte with flags
 i := RecordNo div 8;
 Result := 0; // bits count
 if (i > 0) then
   for j := 0 to i-1 do
     for k := 7 downto 0 do
      begin
       b := PByte(PAnsiChar(FDeleteFlagBuffer) + j)^;
       if ((b and (1 shl k)) = 0) then Inc(Result);
      end;
 // scan last byte
 b := PByte(PAnsiChar(FDeleteFlagBuffer) + i)^;
 n := RecordNo mod 8;
 for k := 0 to n do
   if ((b and (1 shl k)) = 0) then Inc(Result);
Exit;
 // number of byte with flags
 i := RecordNo div 8;
 Result := 0; // bits count
 // scan last byte
 b := PByte(PAnsiChar(FDeleteFlagBuffer) + i)^;
 n := RecordNo mod 8;
 for k := n downto 0 do
   if ((b and (1 shl k)) = 0) then Inc(Result);
 if (i > 0) then
   for j := 0 to i-1 do
     for k := 7 downto 0 do
      begin
       b := PByte(PAnsiChar(FDeleteFlagBuffer) + j)^;
       if ((b and (1 shl k)) = 0) then Inc(Result);
      end;
 if (Result > 0) then
  Dec(Result);      
end;


//------------------------------------------------------------------------------
// return result for attempt of getting record relatively to first position
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.GetRecordFromFirstPosition(
            GetRecordMode: TABSGetRecordMode;
            var RecordID: Integer
                                                           ): TABSGetRecordResult;
begin
 Result := grrError;
 case GetRecordMode of
  grmPrior:
   begin
    Result := grrBOF;
   end;
  grmCurrent:
   begin
    Result := grrError;
   end;
  grmNext:
   begin
    RecordID := FFirstRecordID;
    Result := grrOK;
   end;
 end; // GetRecordMode
end; // GetRecordFromFirstPosition


//------------------------------------------------------------------------------
// return result for attempt of getting record relatively to last position
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.GetRecordFromLastPosition(
            GetRecordMode: TABSGetRecordMode;
            var RecordID: Integer
                                                          ): TABSGetRecordResult;
begin
 Result := grrError;
 case GetRecordMode of
  grmPrior:
   begin
    RecordID := FLastRecordID;
    Result := grrOK;
   end;
  grmCurrent:
   begin
    Result := grrError;
   end;
  grmNext:
   begin
    Result := grrEOF;
   end;
 end; // GetRecordMode
end; // GetRecordFromLastPosition


//------------------------------------------------------------------------------
// return result for attempt of getting record relatively any position
// and set RecordID to new record ID
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.GetRecordFromAnyPosition(
            GetRecordMode: TABSGetRecordMode;
            var RecordID: Integer
                                      ): TABSGetRecordResult;
begin
 Result := grrError;
 case GetRecordMode of
  grmPrior:
   begin
    Result := grrBOF;
    if (RecordID > 0) then
     begin
      Dec(RecordID);
      while (RecordID >= FFirstRecordID) do
       begin
        // record found
        if (not CheckNullFlag(RecordID,TABSPbyte(FDeleteFlagBuffer))) then
         begin
          Result := grrOK;
          break;
         end;
        Dec(RecordID);
       end; // scan prior record
     end; // RecordID > 0
   end;
  grmCurrent:
   begin
    if ((RecordID > FLastRecordID) or (RecordID < FFirstRecordID)) then
     // current record does not exist
     Result := grrError
    else
    if (CheckNullFlag(RecordID,TABSPbyte(FDeleteFlagBuffer))) then
     // current record is deleted
     Result := grrError
    else
     Result := grrOK;
   end;
  grmNext:
   begin
    Result := grrEOF;
    if (RecordID < FAllocatedRecordCount-1) then
     begin
      Inc(RecordID);
      while ((RecordID <= FLastRecordID) and (RecordID < FAllocatedRecordCount)) do
       begin
        // record found
        if (not CheckNullFlag(RecordID,TABSPByte(FDeleteFlagBuffer))) then
         begin
          Result := grrOK;
          break;
         end;
        Inc(RecordID);
       end; // scan prior record
     end;
   end;
 end; // GetRecordMode
end; // GetRecordFromAnyPosition


//------------------------------------------------------------------------------
// set record count
//------------------------------------------------------------------------------
procedure TABSMemoryRecordManager.SetRecordCount(NewRecordCount: Integer);
var i: Integer;
begin
 if (NewRecordCount = 0) then
  MemoryManager.FreeAndNillMem(FRecordBuffer)
 else
 if (FAllocatedRecordCount = 0) then
   FRecordBuffer := MemoryManager.GetMem(NewRecordCount * FRecordBufferSize)
 else
   MemoryManager.ReallocMem(FRecordBuffer,NewRecordCount * FRecordBufferSize,False);
 i := (NewRecordCount div 8) + Integer((NewRecordCount mod 8) > 0);
 if (i = 0) then
  MemoryManager.FreeAndNillMem(FDeleteFlagBuffer)
 else
 if (FAllocatedRecordCount = 0) then
  FDeleteFlagBuffer := MemoryManager.GetMem(i)
 else
  MemoryManager.ReallocMem(Pointer(FDeleteFlagBuffer),i);
 FAllocatedRecordCount := NewRecordCount;
end; // SetRecordCount


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
constructor TABSMemoryRecordManager.Create(
                        RecordBufferSize:     Integer;
                        AllocRecordsBy:       Integer
                      );
var
    i,c: Byte;
begin
 if ((RecordBufferSize = 0) and (not IsDesignMode)) then
  raise EABSException.Create(10011,ErrorLInvalidRecordSize);
 FRecordBuffer := nil;
 FDeleteFlagBuffer := nil;
 FRecordCount := 0;
 FAllocatedRecordCount := 0;
 FFirstRecordID := 0;
 FLastRecordID := 0;
 FRecordBufferSize := RecordBufferSize;
 FAllocRecordsBy := AllocRecordsBy;
 FNoRecordsDeleted := True;
 for i := 0 to 255 do
  begin
   c := 0;
   if ((i and 1) = 0) then Inc(c);
   if ((i and 2) = 0) then Inc(c);
   if ((i and 4) = 0) then Inc(c);
   if ((i and 8) = 0) then Inc(c);
   if ((i and 16) = 0) then Inc(c);
   if ((i and 32) = 0) then Inc(c);
   if ((i and 64) = 0) then Inc(c);
   if ((i and 128) = 0) then Inc(c);
   FBitsTable[i] := c;
  end;
end; // Create;


//------------------------------------------------------------------------------
// destroy
//------------------------------------------------------------------------------
destructor TABSMemoryRecordManager.Destroy;
begin
 Empty(SYSTEM_SESSION_ID);
 inherited Destroy;
end; // Destroy


//------------------------------------------------------------------------------
// Empty
//------------------------------------------------------------------------------
procedure TABSMemoryRecordManager.Empty(SessionID: TABSSessionID);
begin
 FAllocatedRecordCount := 0;
 FRecordCount := 0;
 if (FRecordBuffer <> nil) then
   MemoryManager.FreeAndNillMem(FRecordBuffer);
 if (FDeleteFlagBuffer <> nil) then
   MemoryManager.FreeAndNillMem(FDeleteFlagBuffer);
 FFirstRecordID := 0;
 FLastRecordID := 0;
end; // Empty


//------------------------------------------------------------------------------
// add record and return its number
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.AddRecord(SessionID: TABSSessionID; RecordBuffer: TABSRecordBuffer; var RecordID: TABSRecordID): Boolean;
var OldRecordCount, i: Integer;
begin
 try
   // add to the end of buffer
//   RecordID.PageNo := FRecordCount;
   if (FRecordCount = 0) then
    RecordID.PageNo := 0
   else
    RecordID.PageNo := FLastRecordID+1;
   RecordID.PageItemNo := 0;
   if (FAllocatedRecordCount <= RecordID.PageNo) then
    begin
     // increase record buffer size
     OldRecordCount := FAllocatedRecordCount;
     Inc(FAllocatedRecordCount,FAllocRecordsBy);
     i := FAllocatedRecordCount * FRecordBufferSize;
     if (OldRecordCount = 0) then
      FRecordBuffer := MemoryManager.GetMem(i)
     else
      MemoryManager.ReallocMem(Pointer(FRecordBuffer),i);
     i := FAllocatedRecordCount div 8;
     if (FAllocatedRecordCount mod 8 > 0) then
      Inc(i);
     if (OldRecordCount = 0) then
      FDeleteFlagBuffer := MemoryManager.GetMem(i)
     else
      MemoryManager.ReallocMem(Pointer(FDeleteFlagBuffer),i);
    end; // increase record buffer size
    Move(RecordBuffer^,TAbsPByte(PAnsiChar(FRecordBuffer) + RecordID.PageNo * FRecordBufferSize)^,
          FRecordBufferSize);
    SetNullFlag(False,RecordID.PageNo,TABSPByte(FDeleteFlagBuffer));
    Inc(FRecordCount);
    if (RecordID.PageNo < FFirstRecordID) then
     FFirstRecordID := RecordID.PageNo;
    if (RecordID.PageNo > FLastRecordID) then
     FLastRecordID := RecordID.PageNo;
    Result := True;
    Inc(FTableState);
   except
  Result := False;
 end; // try
end; // AddRecord


//------------------------------------------------------------------------------
// update record, return true if record was updated, false if record was deleted
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.UpdateRecord(
            SessionID: TABSSessionID;
            RecordBuffer: TABSRecordBuffer; RecordID: TABSRecordID): Boolean;
begin
 Result := False;
 if (not ((RecordID.PageNo < FFirstRecordID) or (RecordID.PageNo > FLastRecordID))) then
  try
   Move(RecordBuffer^,TAbsPByte(PAnsiChar(FRecordBuffer) + RecordID.PageNo * FRecordBufferSize)^,
          FRecordBufferSize);
   Result := True;
   Inc(FTableState);
  except
   Result := False;
  end;
end; // UpdateRecord


//------------------------------------------------------------------------------
// delete record, return true if record was deleted, false if record was deleted earlier
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.DeleteRecord(SessionID: TABSSessionID;
                                           var RecordID: TABSRecordID): Boolean;
begin
 Result := False;
 RecordID.PageItemNo := 0;
 if (not (FRecordCount <= 0)) and
    (not ((RecordID.PageNo < FFirstRecordID) or (RecordID.PageNo > FLastRecordID))) and
    (not (CheckNullFlag(RecordID.PageNo,TABSPByte(FDeleteFlagBuffer)))) then
  try
   // set delete flag
   SetNullFlag(True,RecordID.PageNo,TABSPByte(FDeleteFlagBuffer));
   Dec(FRecordCount);
   if (FRecordCount = 0) then
    begin
     // no records
     FFirstRecordID := 0;
     FLastRecordID := 0;
    end
   else
    begin
     // update first and last records ID
     if (RecordID.PageNo = FFirstRecordID) then
      GetRecordFromAnyPosition(grmNext,FFirstRecordID);
     if (RecordID.PageNo = FLastRecordID) then
      begin
       GetRecordFromAnyPosition(grmPrior,FLastRecordID);
       RecordID.PageNo := FLastRecordID;
      end;
    end;
   FNoRecordsDeleted := False;
   Result := True;
   Inc(FTableState);
 except
  Result := False;
 end;
end; // DeleteRecord


//------------------------------------------------------------------------------
// get record
//------------------------------------------------------------------------------
procedure TABSMemoryRecordManager.GetRecordBuffer(SessionID: TABSSessionID;
                                        var NavigationInfo: TABSNavigationInfo);
begin
 if (FRecordCount = 0) then
  begin
   NavigationInfo.GetRecordResult := grrEOF;
   Exit;
  end;
 // get record relatively to the first position
 if (NavigationInfo.FirstPosition) then
  begin
   NavigationInfo.GetRecordResult := GetRecordFromFirstPosition(
                                      NavigationInfo.GetRecordMode,
                                      NavigationInfo.RecordID.PageNo
                                                               );
   if (NavigationInfo.GetRecordResult = grrOK) then
    NavigationInfo.FirstPosition := False;
  end
 else
 // get record relatively to the last position
 if (NavigationInfo.LastPosition) then
  begin
   NavigationInfo.GetRecordResult := GetRecordFromLastPosition(
                                      NavigationInfo.GetRecordMode,
                                      NavigationInfo.RecordID.PageNo
                                                               );
   if (NavigationInfo.GetRecordResult = grrOK) then
    NavigationInfo.LastPosition := False;
  end
 else
  NavigationInfo.GetRecordResult := GetRecordFromAnyPosition(
                                      NavigationInfo.GetRecordMode,
                                      NavigationInfo.RecordID.PageNo
                                                               );
 if (NavigationInfo.GetRecordResult = grrOK) then
  begin
   try
     Move(
          TAbsPByte(PAnsiChar(FRecordBuffer) + NavigationInfo.RecordID.PageNo * FRecordBufferSize)^,
          NavigationInfo.RecordBuffer^,
          FRecordBufferSize
         );
     NavigationInfo.GetRecordResult := grrOK;
   except
    raise EABSException.Create(10019,ErrorLRetreivingRecord);
   end;
  end; // record retrieved successfully
end; // GetRecordBuffer


//------------------------------------------------------------------------------
// return 0,1, or -1 if (1 = 2), (1 > 2) or (1 < 2)
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.CompareRecordID(RecordID1: TABSRecordID; RecordID2: TABSRecordID): Integer;
begin
 if (RecordID1.PageNo = RecordID2.PageNo) then
  Result := 0
 else
 if (RecordID1.PageNo > RecordID2.PageNo) then
  Result := 1
 else
  Result := -1;
end; // CompareRecordID


//------------------------------------------------------------------------------
// return record no
//------------------------------------------------------------------------------
function TABSMemoryRecordManager.GetApproximateRecNo(SessionID: TABSSessionID;
                                          RecordID: TABSRecordID): TABSRecordNo;
begin
 if (RecordCount = 0) or (RecordID.PageNo < FFirstRecordID) or (RecordID.PageNo > FLastRecordID) then
  Result := -1
 else
 if (RecordCount = 1) then
  Result := 1
 else
  Result :=  Round((RecordID.PageNo - FFirstRecordID) / (FRecordCount) *
                   (FLastRecordID - FFirstRecordID)) + 1;
end; // GetApproximateRecNo

{
//------------------------------------------------------------------------------
// Load from stream
//------------------------------------------------------------------------------
procedure TABSMemoryRecordManager.LoadFromStream(Stream: TStream);
var i: Integer;
begin
 LoadDataFromStream(i,Sizeof(i),Stream,10449);
 SetRecordCount(i);
 i := (FAllocatedRecordCount div 8) + Integer((FAllocatedRecordCount mod 8) > 0);
 LoadDataFromStream(FNoRecordsDeleted,Sizeof(FNoRecordsDeleted),Stream,10450);
 LoadDataFromStream(FFirstRecordID,Sizeof(FFirstRecordID),Stream,10451);
 LoadDataFromStream(FLastRecordID,Sizeof(FLastRecordID),Stream,10452);
 if (FAllocatedRecordCount > 0) then
   LoadDataFromStream(FDeleteFlagBuffer^,i,Stream,10453);
end; // LoadFromStream


//------------------------------------------------------------------------------
// Save from stream
//------------------------------------------------------------------------------
procedure TABSMemoryRecordManager.SaveToStream(Stream: TStream);
var i: Integer;
begin
 i := (FAllocatedRecordCount div 8) + Integer((FAllocatedRecordCount mod 8) > 0);
 SaveDataToStream(FAllocatedRecordCount,Sizeof(FAllocatedRecordCount),Stream,10444);
 SaveDataToStream(FNoRecordsDeleted,Sizeof(FNoRecordsDeleted),Stream,10445);
 SaveDataToStream(FFirstRecordID,Sizeof(FFirstRecordID),Stream,10446);
 SaveDataToStream(FLastRecordID,Sizeof(FLastRecordID),Stream,10447);
 if (FAllocatedRecordCount > 0) then
  SaveDataToStream(FDeleteFlagBuffer^,i,Stream,10448);
end; // SaveToStream


//------------------------------------------------------------------------------
// add loaded record
//------------------------------------------------------------------------------
procedure TABSMemoryRecordManager.AddLoadedRecord(RecordBuffer: TABSRecordBuffer);
var RecPos: TABSRecordNo;
begin
 Inc(FRecordCount);
 RecPos := FindRecord(FRecordCount-1);
 if (RecPos = INVALID_ID8) then
  raise EABSException.Create(10454,ErrorLInvalidRecordNo,[FRecordCount]);
 Move(RecordBuffer^,TAbsPByte(FRecordBuffer + RecPos * FRecordBufferSize)^,FRecordBufferSize);
end; // AddLoadedRecord
}

////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryTableData
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// create RecordManager
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.CreateRecordManager;
begin
 if (FRecordManager <> nil) then
  FRecordManager.Free;
 FRecordManager := TABSMemoryRecordManager.Create(
                      GetRecordBufferSize,
                      FAllocRecordsBy
                                                 );
end;// CreateRecordManager


//------------------------------------------------------------------------------
// create field manager
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.CreateFieldManager(FieldDefs: TABSFieldDefs);
begin
 if (FFieldManager <> nil) then
  FFieldManager.Free;
 FFieldManager := TABSBaseFieldManager.Create(Self);
 inherited CreateFieldManager(FieldDefs);
end;// CreateFieldManager


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.CreateIndexManager(IndexDefs: TABSIndexDefs);
begin
 if (FIndexManager <> nil) then
  FIndexManager.Free;
 FIndexManager := TABSBaseIndexManager.Create(Self, FPageManager);
 inherited CreateIndexManager(IndexDefs);
end;// CreateIndexManager


//------------------------------------------------------------------------------
// Create ConstraintManager
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.CreateConstraintManager(ConstraintDefs: TABSConstraintDefs);
var i: Integer;
begin
 if (FConstraintManager <> nil) then
   FConstraintManager.Free;
  // Fill New ObjectIds
  FillDefsByObjectId(ConstraintDefs);
  // Set TableName to Constraits
  for i:=0 to ConstraintDefs.Count-1 do
   case ConstraintDefs[i].ConstraintType of
    ctNotNull:
      TABSConstraintDefNotNull(ConstraintDefs[i]).TableName := FTableName;
    ctCheck:
      TABSConstraintDefCheck(ConstraintDefs[i]).TableName := FTableName;
    ctPK:
      TABSConstraintDefPrimary(ConstraintDefs[i]).TableName := FTableName;
    ctUnique:
      TABSConstraintDefUnique(ConstraintDefs[i]).TableName := FTableName;
    else
      raise EABSException.Create(30040, ErrorGNotImplementedYet);
   end;
  // Create ConstraintManager
  FConstraintManager := TABSBaseConstraintManager.Create(Self);
  FConstraintManager.ConstraintDefs.Assign(ConstraintDefs);
  // Links ObjectIds
  FConstraintManager.LinkObjectIds;
  // Set enpty Names
  FConstraintManager.FillConstraintAutoNames;
end;//CreateConstraintManager


//------------------------------------------------------------------------------
// load table header
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.LoadTableHeader(
              Stream:             TStream;
              var BLOBDescriptor: TABSBLOBDescriptor
                                             );
var FileHeader: TABSMemoryTableFileHeader;
    DatabaseID: TABSSequenceValue;
begin
  LoadDataFromStream(FileHeader,sizeof(FileHeader),Stream,10160);
  if (FileHeader.Signature <> ABSSignature) then
    raise EABSException.Create(10161,ErrorLInvalidSignature,
      [FileHeader.Signature,ABSSignature]);
  if (FileHeader.Version > ABSVersion + 0.0001) then
    raise EABSException.Create(10162,ErrorLInvalidVersion,
      [FileHeader.Version,ABSVersion]);
  if (FileHeader.Version < 0.99) then
    raise EABSException.Create(10443,ErrorLInvalidVersion,
      [FileHeader.Version,ABSVersion]);
  SetLength(FTableName,FileHeader.NameLength);
  LoadDataFromStream(TAbsPByte(@FTableName[1])^,FileHeader.NameLength,Stream,10163);
  LoadDataFromStream(DatabaseID,sizeof(DatabaseID),Stream,10417);


  if (TABSMemoryDatabaseData(FDatabaseData).FLastObjectID < DatabaseID) then
    TABSMemoryDatabaseData(FDatabaseData).FLastObjectID := DatabaseID;

  BLOBDescriptor.StartPosition := Stream.Position;
  BLOBDescriptor.BlockSize := FileHeader.BlockSize;
  BLOBDescriptor.NumBlocks := FileHeader.NumBlocks;
  BLOBDescriptor.UncompressedSize := FileHeader.UncompressedSize;
  BLOBDescriptor.CompressionAlgorithm := FileHeader.CompressionAlgorithm;
  BLOBDescriptor.CompressionMode := FileHeader.CompressionMode;
  FLoadedRecordCount := FileHeader.RecordCount;
end; // LoadTableHeader


//------------------------------------------------------------------------------
// save table header
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.SaveTableHeader(
              Stream:             TStream;
              var BLOBDescriptor: TABSBLOBDescriptor
              );
var FileHeader: TABSMemoryTableFileHeader;
    DatabaseID: TABSSequenceValue;
begin
  FileHeader.Signature := ABSSignature;
  FileHeader.Version := ABSVersion;
  FileHeader.RecordCount := FRecordManager.RecordCount;
  FileHeader.UncompressedSize := BLOBDescriptor.UncompressedSize;
  FileHeader.NumBlocks := BLOBDescriptor.NumBlocks;
  FileHeader.BlockSize := BLOBDescriptor.BlockSize;
  FileHeader.CompressionAlgorithm := BLOBDescriptor.CompressionAlgorithm;
  FileHeader.CompressionMode := BLOBDescriptor.CompressionMode;
  FileHeader.NameLength := Length(FTableName);
  SaveDataToStream(FileHeader,sizeof(FileHeader),Stream,10156);
  SaveDataToStream(TAbsPByte(@FTableName[1])^,FileHeader.NameLength,Stream,10157);
  DatabaseID := TABSMemoryDatabaseData(FDatabaseData).FLastObjectID;
  SaveDataToStream(DatabaseID,sizeof(DatabaseID),Stream,10416);
  BLOBDescriptor.StartPosition := Stream.Position;
end; // SaveTableHeader


//------------------------------------------------------------------------------
// load blob data
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.LoadBLOBDataFromStream(
        RecordBuffer: TABSRecordBuffer;
        FieldNo:      Integer;
        Stream:       TStream
                             );
var Buffer:                 TAbsPByte;
    FBLOBPosition:          Int64;
    Offset,Offset2:         Integer;
    BLOBPartialDescriptor:  TABSPartialTemporaryBLOBDescriptor;
begin
  Offset := FFieldManager.FieldDefs[FieldNo].MemoryOffset;
  Move(TAbsPByte(RecordBuffer + Offset)^,FBLOBPosition,sizeof(FBLOBPosition));
  // load disk blob descriptor
  SetStreamPosition(Stream,FBLOBPosition,10202);
  LoadDataFromStream(BLOBPartialDescriptor,sizeof(BLOBPartialDescriptor),
    Stream,10203);
  Offset2 := sizeof(TABSPartialBLOBDescriptor);
  Buffer := MemoryManager.GetMem(BLOBPartialDescriptor.CompressedSize + Offset2);
  PABSPartialBLOBDescriptor(Buffer)^.NumBlocks :=
    BLOBPartialDescriptor.NumBlocks;
  PABSPartialBLOBDescriptor(Buffer)^.UncompressedSize :=
    BLOBPartialDescriptor.UncompressedSize;
  // load compresse blob data
  LoadDataFromStream(TAbsPByte(Buffer + Offset2)^,
    BLOBPartialDescriptor.CompressedSize,Stream,10204);
  Move(Buffer,TAbsPByte(RecordBuffer + Offset)^,sizeof(Buffer));
end; // LoadBLOBDataFromStream


//------------------------------------------------------------------------------
// load record
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.LoadRecordFromStream(
            RecordBuffer: TABSRecordBuffer;
            Stream:       TStream
                                                  );
var i:                        Integer;
begin
  LoadDataFromStream(RecordBuffer^,GetRecordBufferSize,Stream,10201);
  if (FFieldManager.BlobFieldsPresent) then
   begin
     for i := 0 to FFieldManager.FieldDefs.Count - 1 do
      if (IsBLOBFieldType(FFieldManager.FieldDefs[i].BaseFieldType)) then
        if (not CheckNullFlag(i,RecordBuffer)) then
         begin
          // load blob
          LoadBLOBDataFromStream(RecordBuffer,i,Stream);
         end; // blob field not null
   end;
end; // LoadRecord


//------------------------------------------------------------------------------
// prepare record buffer for save
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.PrepareRecordBufferForSave(
            RecordBuffer:             TABSRecordBuffer;
            BLOBDescriptorList:       TList;
            BLOBDataList:             TList;
            BLOBPosition:             Int64
                                                );
var FBLOBPosition:            Int64;
    i:                        Integer;
    BLOBDataSize:             Integer;
    Offset:                   Integer;
    Buffer:                   TAbsPByte;
    PBLOBPartialDescriptor:   PABSPartialTemporaryBLOBDescriptor;
begin
 FBLOBPosition := BLOBPosition;
 for i := 0 to FFieldManager.FieldDefs.Count - 1 do
  if (IsBLOBFieldType(FFieldManager.FieldDefs[i].BaseFieldType)) then
    if (not CheckNullFlag(i,RecordBuffer)) then
     begin
      // save blob field data
      Offset := FFieldManager.FieldDefs[i].MemoryOffset;
      Move(TAbsPByte(RecordBuffer + Offset)^,Buffer,Sizeof(Buffer));
      if (Buffer = nil) then
        raise EABSException.Create(10196,ErrorLNilPointer);
      // calculate size of blob data
      BLOBDataSize := MemoryManager.GetMemoryBufferSize(Buffer) -
        sizeof(TABSPartialBLOBDescriptor) +
        sizeof(TABSPartialTemporaryBLOBDescriptor);
      PBLOBPartialDescriptor :=
        MemoryManager.GetMem(sizeof(TABSPartialTemporaryBLOBDescriptor));
      PBLOBPartialDescriptor^.NumBlocks :=
        PABSPartialBLOBDescriptor(Buffer)^.NumBlocks;
      PBLOBPartialDescriptor^.UncompressedSize :=
        PABSPartialBLOBDescriptor(Buffer)^.UncompressedSize;
      PBLOBPartialDescriptor^.CompressedSize := BLOBDataSize -
        sizeof(TABSPartialTemporaryBLOBDescriptor);
      BLOBDataList.Add(Buffer);
      BLOBDescriptorList.Add(PBLOBPartialDescriptor);
      // store offset to blob data instead of pointer to memory buffer
      Move(FBLOBPosition,TAbsPByte(RecordBuffer + Offset)^,Sizeof(FBLOBPosition));
      Inc(FBLOBPosition,BLOBDataSize);
     end; // not null blob field
end;


//------------------------------------------------------------------------------
// save record
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.SaveRecordToStream(
            RecordBuffer: TABSRecordBuffer;
            Stream:       TStream
                                                );
var i:                        Integer;
    BLOBDataSize:             Integer;
    Buffer:                   TAbsPByte;
    PBLOBPartialDescriptor:   PABSPartialTemporaryBLOBDescriptor;
    BLOBDescriptorList:       TList;
    BLOBDataList:             TList;
begin
 if (FFieldManager.BlobFieldsPresent) then
  begin
   BLOBDescriptorList := TList.Create;
   BLOBDataList := TList.Create;
   try
     PrepareRecordBufferForSave(RecordBuffer,BLOBDescriptorList,
        BLOBDataList,Int64(Stream.Position + Int64(GetRecordBufferSize)));
     SaveDataToStream(RecordBuffer^,GetRecordBufferSize,Stream,10197);
     // save blob fields
     for i := 0 to BLOBDataList.Count - 1 do
      begin
       PBLOBPartialDescriptor := BLOBDescriptorList.Items[i];
       SaveDataToStream(PBLOBPartialDescriptor^,
        sizeof(TABSPartialTemporaryBLOBDescriptor),Stream,10198);
       Buffer := BLOBDataList.Items[i];
       BLOBDataSize := PBLOBPartialDescriptor^.CompressedSize;
       SaveDataToStream(TAbsPByte(Buffer + sizeof(TABSPartialBLOBDescriptor))^,BLOBDataSize,Stream,10199);
       MemoryManager.FreeAndNillMem(PBLOBPartialDescriptor);
      end; // not null blob field
   finally
    BLOBDescriptorList.Free;
    BLOBDataList.Free;
   end;
  end // blob fields
 else
  // no blob fields
  SaveDataToStream(RecordBuffer^,GetRecordBufferSize,Stream,10200);
end; // SaveRecord

{
//------------------------------------------------------------------------------
// load all records
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.LoadRecordManager(Stream: TStream);
var RecordBuffer:     TABSRecordBuffer;
    i:                Integer;
begin
  if (FLoadedRecordCount > 0) then
   begin
    FRecordManager.LoadFromStream(Stream);
    RecordBuffer := MemoryManager.GetMem(FRecordManager.RecordBufferSize);
    try
      for i := 0 to FLoadedRecordCount - 1 do
       begin
        LoadRecordFromStream(RecordBuffer,Stream);
        FRecordManager.AddLoadedRecord(RecordBuffer);
       end;
    finally
      MemoryManager.FreeAndNillMem(RecordBuffer);
    end;
   end;
end; // LoadRecordManager


//------------------------------------------------------------------------------
// save all records
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.SaveRecordManager(Stream: TStream);
var RecordBuffer:     TABSRecordBuffer;
    NavigationInfo:   TABSNavigationInfo;
begin
  if (FRecordManager.RecordCount > 0) then
   begin
     FRecordManager.SaveToStream(Stream);
     RecordBuffer := MemoryManager.GetMem(FRecordManager.RecordBufferSize);
     NavigationInfo.LastPosition := False;
     NavigationInfo.FirstPosition := True;
     NavigationInfo.RecordBuffer := RecordBuffer;
     NavigationInfo.GetRecordMode := grmNext;
     try
       FRecordManager.GetRecordBuffer(SYSTEM_SESSION_ID, NavigationInfo);
       if (NavigationInfo.GetRecordResult = grrOK) then
        begin
         NavigationInfo.FirstPosition := False;
         repeat
          // save record buffer and blob fields
          SaveRecordToStream(RecordBuffer,Stream);
          FRecordManager.GetRecordBuffer(SYSTEM_SESSION_ID, NavigationInfo);
         until (NavigationInfo.GetRecordResult <> grrOK);
        end;
     finally
      MemoryManager.FreeAndNillMem(RecordBuffer);
     end;
   end;
end; // SaveRecordManager
}

//------------------------------------------------------------------------------
// return filter bitmap rec count
//------------------------------------------------------------------------------
function TABSMemoryTableData.GetBitmapRecordCount(SessionID: TABSSessionID): TABSRecordNo;
begin
  Result := FRecordManager.RecordCount;
end;// GetBitmapRecordCount


//------------------------------------------------------------------------------
// return filter bitmap rec no by record id
//------------------------------------------------------------------------------
function TABSMemoryTableData.GetBitmapRecNoByRecordID(RecordID: TABSRecordID): TABSRecordNo;
begin
  Result := RecordID.PageNo;
end; // GetBitmapRecNoByRecordID


//------------------------------------------------------------------------------
// return filter bitmap rec no by record id
//------------------------------------------------------------------------------
function TABSMemoryTableData.GetRecordIDByBitmapRecNo(RecordNo: TABSRecordNo): TABSRecordID;
begin
  Result.PageNo := RecordNo;
  Result.PageItemNo := 0;
end; // GetRecordIDByBitmapRecNo


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
constructor TABSMemoryTableData.Create(
                        aDatabaseData: TABSDatabaseData;
                        AllocRecordsBy: Integer
                      );
begin
 inherited Create(aDatabaseData);
 FPageManager := TABSMemoryPageManager.Create;
 FAllocRecordsBy := AllocRecordsBy;
 FDisableTempFiles := True;
end; // Create;


//------------------------------------------------------------------------------
// destroy
//------------------------------------------------------------------------------
destructor TABSMemoryTableData.Destroy;
begin
 EmptyTable(nil);
 DeleteAllIndexes(nil);
 FPageManager.ApplyChanges(SYSTEM_SESSION_ID);
 FPageManager.Free;
 inherited Destroy;
end; // Destroy


//------------------------------------------------------------------------------
// create table
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.CreateTable(
                          Cursor: TABSCursor;
                          FieldDefs: TABSFieldDefs;
                          IndexDefs: TABSIndexDefs;
                          ConstraintDefs: TABSConstraintDefs
                                         );
begin
{$IFDEF DEBUG_TRACE_DATASET}
 aaWriteToLog('TABSMemoryTableData.CreateTable start');
 aaWriteToLog('TABSMemoryTableData.CreateTable FieldDefs.Count = '+IntToStr(FieldDefs.Count));
{$ENDIF}
 if ((FieldDefs.Count <= 0) and (not IsDesignMode)) then
   raise EABSException.Create(10025, ErrorLNoFields);
 TableName := Cursor.TableName;

 //CreateSequenceManager;
 CreateFieldManager(FieldDefs);
 CreateIndexManager(IndexDefs);
 CreateConstraintManager(ConstraintDefs);
 //BuildSequences;
 CreateRecordManager;
 InitCursor(Cursor);
 FIndexManager.CreateIndexesByIndexDefs(Cursor);
 if (Cursor <> nil) then
  if (not Cursor.Session.InTransaction) then
   PageManager.ApplyChanges(Cursor.Session.SessionID);
{$IFDEF DEBUG_TRACE_DATASET}
 aaWriteToLog('TABSMemoryTableData.CreateTable finish');
{$ENDIF}
end;// CreateTable


//------------------------------------------------------------------------------
// delete table
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.DeleteTable(Cursor: TABSCursor; DesignMode: Boolean = False);
begin
 inherited DeleteTable(Cursor, DesignMode);
 if ((not DesignMode) or (FCursorList.Count = 0)) then
   Free;
end; // DeleteTable


//------------------------------------------------------------------------------
// empty table
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.EmptyTable(Cursor: TABSCursor);
var
    NavigationInfo:   TABSNavigationInfo;
begin
 inherited EmptyTable(nil);

 if (FRecordManager <> nil) then
   if (FFieldManager.BlobFieldsPresent and (FRecordManager.RecordCount > 0)) then
    begin
     NavigationInfo.LastPosition := False;
     NavigationInfo.FirstPosition := True;
     NavigationInfo.RecordBuffer := MemoryManager.GetMem(FRecordManager.RecordBufferSize);
     NavigationInfo.GetRecordMode := grmNext;
     try
       FRecordManager.GetRecordBuffer(SYSTEM_SESSION_ID, NavigationInfo);
       if (NavigationInfo.GetRecordResult = grrOk) then
        begin
         NavigationInfo.FirstPosition := False;
         repeat
          ClearBLOBFieldsInRecordBuffer(NavigationInfo.RecordBuffer);
          FRecordManager.GetRecordBuffer(SYSTEM_SESSION_ID, NavigationInfo);
         until (NavigationInfo.GetRecordResult <> grrOK);
        end;
     finally
      MemoryManager.FreeAndNillMem(NavigationInfo.RecordBuffer);
     end;
    end;
 if (FRecordManager <> nil) then
  FRecordManager.Empty(SYSTEM_SESSION_ID);

 FPageManager.ApplyChanges(SYSTEM_SESSION_ID);
end; // EmptyTable


//------------------------------------------------------------------------------
// open table
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.OpenTable(Cursor: TABSCursor);
begin
 TableName := Cursor.TableName;
 inherited OpenTable(Cursor);
end;// OpenTable


//------------------------------------------------------------------------------
// close table
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.CloseTable(Cursor: TABSCursor);
begin
 inherited CloseTable(Cursor);
end;// CloseTable


//------------------------------------------------------------------------------
// AddIndex
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.AddIndex(IndexDef: TABSIndexDef; Cursor: TABSCursor);
begin
  Lock;
  try
    inherited AddIndex(IndexDef, Cursor);
    if (not Cursor.Session.InTransaction) then
      PageManager.ApplyChanges(Cursor.Session.SessionID);
  finally
    Unlock;
  end;
end;// AddIndex


//------------------------------------------------------------------------------
// DeleteIndex
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.DeleteIndex(IndexID: TABSObjectID; Cursor: TABSCursor);
begin
  Lock;
  try
    inherited DeleteIndex(IndexID, Cursor);
    if (Cursor <> nil) then
      begin
        if (not Cursor.Session.InTransaction) then
          PageManager.ApplyChanges(Cursor.Session.SessionID);
      end
    else
      PageManager.ApplyChanges(SYSTEM_SESSION_ID);
  finally
    Unlock;
  end;
end;// DeleteIndex


//------------------------------------------------------------------------------
// Write BLOB Field To Record Buffer
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.WriteBLOBFieldToRecordBuffer(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              BLOBStream: TABSStream
              );
var Buffer:                   TAbsPByte;
    BufferSize,Offset:        Integer;
    ReadBytes:                Integer;
begin
 Lock;
 try
   Buffer := Cursor.CurrentRecordBuffer;
   if (BLOBStream.Modified) then
    begin
     if (BLOBStream.Size = 0) then
      begin
       // empty stream
       ClearBLOBFieldInRecordBuffer(Buffer,FieldNo);
       Buffer := nil;
       Move(Buffer,TAbsPByte(Cursor.CurrentRecordBuffer +
        FieldManager.FieldDefs[FieldNo].MemoryOffset)^,sizeof(Buffer));
       SetNullFlag(True,FieldNo,Cursor.CurrentRecordBuffer);
      end
     else
      begin
       ClearBLOBFieldInRecordBuffer(Buffer,FieldNo);
       Offset := sizeof(TABSPartialBLOBDescriptor);
       BufferSize := Integer(TABSCompressedBLOBStream(
          TABSLocalBLOBStream(BLOBStream).TemporaryStream).CompressedStream.Size) +
          Offset;
       Buffer := MemoryManager.GetMem(BufferSize);
       PABSPartialBLOBDescriptor(Buffer)^.NumBlocks := TABSCompressedBLOBStream(
          TABSLocalBLOBStream(BLOBStream).TemporaryStream).BLOBDescriptor.NumBlocks;
       PABSPartialBLOBDescriptor(Buffer)^.UncompressedSize := TABSCompressedBLOBStream(
          TABSLocalBLOBStream(BLOBStream).TemporaryStream).BLOBDescriptor.UncompressedSize;
       TABSCompressedBLOBStream(
          TABSLocalBLOBStream(BLOBStream).TemporaryStream).CompressedStream.
          Position := 0;

       ReadBytes := TABSCompressedBLOBStream(
          TABSLocalBLOBStream(BLOBStream).TemporaryStream).CompressedStream.
          Read(TAbsPByte(Buffer + Offset)^,(BufferSize - Offset));

       if (ReadBytes <> (BufferSize - Offset)) then
        begin
          MemoryManager.FreeAndNillMem(Buffer);
          raise EABSException.Create(10117,ErrorLCannotReadFromStream,
          [0,(BufferSize - Offset),(BufferSize - Offset),ReadBytes]);
        end;

       //Move(Buffer,TAbsPByte(Cursor.CurrentRecordBuffer +
       //  FieldManager.FieldDefs[FieldNo].MemoryOffset)^, sizeof(Buffer));
{$IFDEF WIN64}
       PNativeUInt(Cursor.CurrentRecordBuffer + FieldManager.FieldDefs[FieldNo].MemoryOffset)^ := NativeUInt(Buffer);
{$ELSE}
       PCardinal(Cursor.CurrentRecordBuffer + FieldManager.FieldDefs[FieldNo].MemoryOffset)^ := Cardinal(Buffer);
{$ENDIF}
       SetNullFlag(False,FieldNo,Cursor.CurrentRecordBuffer);
      end; // not empty stream
    end; // Modified
 finally
  Unlock;
 end;
end; // WriteBLOBFieldToRecordBuffer


//------------------------------------------------------------------------------
// create blob stream
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.ClearBLOBFieldInRecordBuffer(
              RecordBuffer: TABSRecordBuffer;
              FieldNo:      Integer);
var Buffer: TAbsPByte;
begin
 Lock;
 try
   if (FFieldManager.BlobFieldsPresent) then
    begin
     // check null
     if (not ((PByte(RecordBuffer + (FieldNo div 8))^) and
            (1 shl (FieldNo mod 8)) <> 0)) then
      begin
       Move(TAbsPByte(RecordBuffer +
         FieldManager.FieldDefs[FieldNo].MemoryOffset)^,Buffer,sizeof(Buffer));
       if (Buffer = nil) then
        raise EABSException.Create(10118,ErrorLNilPointer);
       MemoryManager.FreeAndNillMem(Buffer);

       // set null
       PByte(RecordBuffer + (FieldNo div 8))^ := PByte(RecordBuffer + (FieldNo div 8))^ or
        (1 shl (FieldNo mod 8));
      end;
    end;
 finally
  Unlock;
 end;
end; // ClearBLOBFieldInRecordBuffer


//------------------------------------------------------------------------------
// create blob stream
//------------------------------------------------------------------------------
function TABSMemoryTableData.InternalCreateBlobStream(
              Cursor:   TABSCursor;
              ToInsert: Boolean;
              FieldNo:  Integer;
              OpenMode: TABSBLOBOpenMode
              ): TABSStream;
var
    TempStream:             TABSTemporaryStream;
    CompressedStream:       TABSCompressedBLOBStream;
    Buffer:                 TAbsPByte;
    Offset,BufferSize:      Integer;
    BLOBDescriptor:         TABSBLOBDescriptor;
begin
 Lock;
 try
   Result := nil;
   BLOBDescriptor.CompressionAlgorithm :=
       Byte(FieldManager.FieldDefs[FieldNo].BLOBCompressionAlgorithm);
   BLOBDescriptor.CompressionMode := FieldManager.FieldDefs[FieldNo].BLOBCompressionMode;
   BLOBDescriptor.BlockSize := FieldManager.FieldDefs[FieldNo].BLOBBlockSize;
   if (BLOBDescriptor.BlockSize = 0) then
    raise EABSException.Create(10420,ErrorLZeroBlockSizeIsNotAllowed);
   BLOBDescriptor.StartPosition := 0;
   TempStream := TABSTemporaryStream.Create(FDisableTempFiles);
   // create new compressed stream
   if ((ToInsert and (OpenMode = bomWrite)) or (OpenMode = bomWrite) or
       (Cursor.CurrentRecordBuffer = nil) or
       (CheckNullFlag(FieldNo,Cursor.CurrentRecordBuffer))) then
    begin
     // empty stream
     BLOBDescriptor.NumBlocks := 0;
     BLOBDescriptor.UncompressedSize := 0;
     CompressedStream := TABSCompressedBLOBStream.Create(TempStream,
      BLOBDescriptor,True);
     Result := TABSLocalBLOBStream.Create(CompressedStream,Cursor,OpenMode,FieldNo);
    end // empty stream
   else
    begin
     // copy value from TableData
     Offset := FieldManager.FieldDefs[FieldNo].MemoryOffset;
     // get pointer to memory buffer with blob stream content

     //Move(TAbsPByte(Cursor.CurrentRecordBuffer + Offset)^,Buffer,Sizeof(Buffer));
  	 {$IFDEF WIN64}
     Buffer := TAbsPByte(PPointer(Cursor.CurrentRecordBuffer + Offset)^);
	 {$ELSE}
     Buffer := TAbsPByte(PCardinal(Cursor.CurrentRecordBuffer + Offset)^);
	 {$ENDIF}

     if (Buffer = nil) then
      raise EABSException.Create(10112,ErrorLNilPointer);
     // creating source memory stream
     Offset := sizeof(TABSPartialBLOBDescriptor);
     BufferSize := MemoryManager.GetMemoryBufferSize(Buffer) - Offset;
     // copy partial blob descriptor
     BLOBDescriptor.NumBlocks := PABSPartialBLOBDescriptor(Buffer)^.NumBlocks;
     BLOBDescriptor.UncompressedSize := PABSPartialBLOBDescriptor(Buffer)^.UncompressedSize;
     TempStream.Write(TAbsPByte(Buffer + Offset)^,BufferSize);
     TempStream.Position := 0;
     CompressedStream := TABSCompressedBLOBStream.Create(TempStream,
      BLOBDescriptor,False);
     Result := TABSLocalBLOBStream.Create(CompressedStream,Cursor,OpenMode,FieldNo);
    end; // copy value from TableData
 finally
  Unlock;
 end;
end; // InternalCreateBlobStream


//------------------------------------------------------------------------------
// GetDirectBlobData
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.GetDirectBlobData(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              RecordBuffer: TABSRecordBuffer;
              var BLOBDescriptor: TABSPartialTemporaryBLOBDescriptor;
              var pBlobData: TAbsPByte);
var
    Buffer:                 TAbsPByte;
    Offset,BufferSize:      Integer;
begin
 Lock;
 try
   if (CheckNullFlag(FieldNo,RecordBuffer)) then
    begin
     BLOBDescriptor.NumBlocks := 0;
     BLOBDescriptor.UncompressedSize := 0;
     BLOBDescriptor.CompressedSize := 0;
     pBlobData := nil;
    end
   else
    begin
     // copy value from TableData
     Offset := FieldManager.FieldDefs[FieldNo].MemoryOffset;
     // get pointer to memory buffer with blob stream content
	 {$IFDEF WIN64}
	 Buffer := TAbsPByte(PPointer(RecordBuffer + Offset)^);
	 {$ELSE}
	 Buffer := TAbsPByte(PCardinal(RecordBuffer + Offset)^);
	 {$ENDIF}
     if (Buffer = nil) then
      raise EABSException.Create(10511,ErrorLNilPointer);
     // creating source memory stream
     Offset := sizeof(TABSPartialBLOBDescriptor);
     BufferSize := MemoryManager.GetMemoryBufferSize(Buffer) - Offset;
     // copy partial blob descriptor
     BLOBDescriptor.NumBlocks := PABSPartialBLOBDescriptor(Buffer)^.NumBlocks;
     BLOBDescriptor.UncompressedSize := PABSPartialBLOBDescriptor(Buffer)^.UncompressedSize;
     BLOBDescriptor.CompressedSize := BufferSize;
     // get blob data
     pBlobData := MemoryManager.GetMem(BufferSize);
     Move(TAbsPByte(Buffer + Offset)^,pBlobData^,BufferSize);
    end; // copy value from TableData
 finally
  Unlock;
 end;
end;// GetDirectBlobData


//------------------------------------------------------------------------------
// SetDirectBlobData
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.SetDirectBlobData(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              RecordBuffer: TABSRecordBuffer;
              var BLOBDescriptor: TABSPartialTemporaryBLOBDescriptor;
              var pBlobData: TAbsPByte);
var
    Buffer: TAbsPByte;
    BufferSize,Offset: Integer;
begin
 Lock;
 try
   if (BLOBDescriptor.CompressedSize = 0) then
    begin
     // empty stream
     ClearBLOBFieldInRecordBuffer(RecordBuffer,FieldNo);
     Buffer := nil;
     Move(Buffer,TAbsPByte(RecordBuffer +
      FieldManager.FieldDefs[FieldNo].MemoryOffset)^,sizeof(Buffer));
     SetNullFlag(True,FieldNo,RecordBuffer);
    end
   else
    begin
     ClearBLOBFieldInRecordBuffer(RecordBuffer,FieldNo);
     Offset := sizeof(TABSPartialBLOBDescriptor);
     BufferSize := BLOBDescriptor.CompressedSize + Offset;
     Buffer := MemoryManager.GetMem(BufferSize);
     PABSPartialBLOBDescriptor(Buffer)^.NumBlocks := BLOBDescriptor.NumBlocks;
     PABSPartialBLOBDescriptor(Buffer)^.UncompressedSize := BLOBDescriptor.UncompressedSize;
     Move(pBlobData^,TAbsPByte(Buffer + Offset)^,(BufferSize - Offset));

{$IFDEF WIN64}
     PNativeUInt(RecordBuffer + FieldManager.FieldDefs[FieldNo].MemoryOffset)^ := NativeUInt(Buffer);
{$ELSE}
     PCardinal(RecordBuffer + FieldManager.FieldDefs[FieldNo].MemoryOffset)^ := Cardinal(Buffer);
{$ENDIF}
     SetNullFlag(False,FieldNo,RecordBuffer);
    end; // not empty stream
 finally
  Unlock;
 end;
end;// SetDirectBlobData


//------------------------------------------------------------------------------
// FreeDirectBlobData
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.FreeDirectBlobData(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              RecordBuffer: TABSRecordBuffer;
              var BLOBDescriptor: TABSPartialTemporaryBLOBDescriptor;
              var pBlobData: TAbsPByte);
begin
 Lock;
 try
  if (pBlobData <> nil) then
    MemoryManager.FreeAndNillMem(pBlobData);
 finally
  Unlock;
 end;
end;// FreeDirectBlobData


//------------------------------------------------------------------------------
// insert record
//------------------------------------------------------------------------------
function TABSMemoryTableData.InsertRecord(var Cursor: TABSCursor): Boolean;
begin
 Result := False;
 Lock;
 try
  if (FRecordManager = nil) then
   raise EABSException.Create(10010,ErrorLNilPointer);
  if (Cursor.CurrentRecordBuffer = nil) then
   raise EABSException.Create(10018,ErrorLNilPointer);
  // add record to first empty space
  Result := FRecordManager.AddRecord(Cursor.Session.SessionID,
                             Cursor.CurrentRecordBuffer,Cursor.CurrentRecordID);
  // current record id will be the same as position as we insert in first empty record
  Cursor.CurrentRecordID.PageItemNo := 0;
  if (Result) then
   begin
     if (TABSRecordBitmap(Cursor.RecordBitmap).Active) then
      begin
        if (IsRecordVisible(Cursor)) then
         TABSRecordBitmap(Cursor.RecordBitmap).ShowRecord(Cursor.CurrentRecordID)
        else
         TABSRecordBitmap(Cursor.RecordBitmap).HideRecord(Cursor.CurrentRecordID);
      end;
     FIndexManager.InsertRecord(Cursor);
     Cursor.FirstPosition := False;
     Cursor.LastPosition := False;
     if (not Cursor.Session.InTransaction) then
       FPageManager.ApplyChanges(Cursor.Session.SessionID);
   end;
 finally
  Unlock;
 end;
end; // InsertRecord;


//------------------------------------------------------------------------------
// delete record
//------------------------------------------------------------------------------
function TABSMemoryTableData.DeleteRecord(Cursor: TABSCursor): Boolean;
var
  RecordID:    TABSRecordID;
  NewRecordID: TABSRecordID;
  Buffer:      TABSRecordBuffer;
  TempBuffer:  TABSRecordBuffer;
  OldPos:      Pointer;
  IsLastRecordToDelete: Boolean;
  PrevTableState: Integer;
begin
 Result := False;
 Lock;
 try
  if (FRecordManager = nil) then
   raise EABSException.Create(10013,ErrorLNilPointer);
  RecordID := Cursor.CurrentRecordID;
  Buffer := Cursor.CurrentRecordBuffer;
  // clear blob fields
  ClearBLOBFieldsInRecordBuffer(Buffer);
  TempBuffer := Cursor.AllocateRecordBuffer;
  OldPos := Cursor.SavePosition;
  try
   Cursor.CurrentRecordBuffer := TempBuffer;
   IsLastRecordToDelete := False;
   if (GetRecordBuffer(Cursor, grmNext) = grrOK) then
     NewRecordID := Cursor.CurrentRecordID
   else
    begin
      Cursor.RestorePosition(OldPos);
      if (GetRecordBuffer(Cursor, grmPrior) = grrOK) then
        NewRecordID := Cursor.CurrentRecordID
      else
        IsLastRecordToDelete := True;
    end;
   Cursor.RestorePosition(OldPos);
   Cursor.CurrentRecordBuffer := Buffer;
   Cursor.CurrentRecordID := RecordID;
  finally
   Cursor.FreePosition(OldPos);
   Cursor.FreeRecordBuffer(TempBuffer);
  end;
  FIndexManager.DeleteRecord(Cursor);
  PrevTableState := FRecordManager.TableState;
  Result := FRecordManager.DeleteRecord(Cursor.Session.SessionID, RecordID);
  if (Result) then
   begin
    if (TABSRecordBitmap(Cursor.RecordBitmap).Active) then
      begin
        TABSRecordBitmap(Cursor.RecordBitmap).HideRecord(Cursor.CurrentRecordID);
        if (PrevTableState = TABSRecordBitmap(Cursor.RecordBitmap).TableState) and (not Cursor.IsDistinctApplied) then
          TABSRecordBitmap(Cursor.RecordBitmap).TableState := FRecordManager.TableState;
      end;
    if (IsLastRecordToDelete) then
     begin
      Cursor.FirstPosition := True;
      Cursor.LastPosition := False;
     end
    else
     begin
      Cursor.FirstPosition := False;
      Cursor.LastPosition := False;
      Cursor.CurrentRecordID := NewRecordID;
     end;
    if (not Cursor.Session.InTransaction) then
       FPageManager.ApplyChanges(Cursor.Session.SessionID);
   end;
 finally
  Unlock;
 end;
end; // DeleteRecord


//------------------------------------------------------------------------------
// update record
//------------------------------------------------------------------------------
function TABSMemoryTableData.UpdateRecord(Cursor: TABSCursor): Boolean;
begin
 Result := False;
 Lock;
 try
  if (FRecordManager = nil) then
   raise EABSException.Create(10014,ErrorLNilPointer);
  if (Cursor.CurrentRecordBuffer = nil) then
   raise EABSException.Create(10017,ErrorLNilPointer);
  FIndexManager.UpdateRecord(Cursor, True);
  Result := FRecordManager.UpdateRecord(Cursor.Session.SessionID,
                            Cursor.CurrentRecordBuffer, Cursor.CurrentRecordID);
  FIndexManager.UpdateRecord(Cursor, False);
  if (Result) then
   begin
     if (TABSRecordBitmap(Cursor.RecordBitmap).Active) then
      begin
        if (IsRecordVisible(Cursor)) then
         TABSRecordBitmap(Cursor.RecordBitmap).ShowRecord(Cursor.CurrentRecordID)
        else
         TABSRecordBitmap(Cursor.RecordBitmap).HideRecord(Cursor.CurrentRecordID);
      end;
     if (not Cursor.Session.InTransaction) then
       FPageManager.ApplyChanges(Cursor.Session.SessionID);
   end;
 finally
  Unlock;
 end;
end; // UpdateRecord


//------------------------------------------------------------------------------
// return record count
//------------------------------------------------------------------------------
function TABSMemoryTableData.InternalGetRecordCount(Cursor: TABSCursor): TABSRecordNo;
begin
 Result := 0;
 Lock;
 try
  if (FRecordManager = nil) then
   raise EABSException.Create(10009,ErrorLNilPointer);
  Result := FRecordManager.RecordCount;
 finally
  Unlock;
 end;
end; // GetRecordCount


//------------------------------------------------------------------------------
// move cursor to specified position and set current record id in cursor
//------------------------------------------------------------------------------
procedure TABSMemoryTableData.InternalSetRecNo(Cursor: TABSCursor; RecNo: TABSRecordNo);
var RecordID: TABSRecordNo;
begin
 Lock;
 try
  if (FRecordManager = nil) then
   raise EABSException.Create(10074,ErrorLNilPointer);
  RecordID := TABSMemoryRecordManager(FRecordManager).FindRecord(RecNo-1);
  if (RecordID <> INVALID_ID8) then
   begin
    Cursor.CurrentRecordID.PageNo := RecordID;
    Cursor.CurrentRecordID.PageItemNo := 0;
   end;
 finally
  Unlock;
 end;
end; // SetRecNo


//------------------------------------------------------------------------------
// get current record position from cursor
//------------------------------------------------------------------------------
function TABSMemoryTableData.InternalGetRecNo(Cursor: TABSCursor): TABSRecordNo;
begin
 Result := -1;
 Lock;
 try
  if (FRecordManager = nil) then
   raise EABSException.Create(10033,ErrorLNilPointer);
  Result := TABSMemoryRecordManager(FRecordManager).
                GetTablePositionByRecordID(Cursor.CurrentRecordID.PageNo);
  if (Result = INVALID_ID8) then
   Result := -1;
//  else
//   Inc(Result);
 finally
  Unlock;
 end;
end; // GetRecNo


//------------------------------------------------------------------------------
// LastAutoincValue
//------------------------------------------------------------------------------
function TABSMemoryTableData.LastAutoincValue(FieldNo: Integer; Session: TABSBaseSession): Int64;
begin
  Result := FFieldManager.GetLastAutoinc(Session, FieldNo);
end;//LastAutoincValue



////////////////////////////////////////////////////////////////////////////////
//
// TABSMemoryPageManager
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// load from stream
//------------------------------------------------------------------------------
procedure TABSMemoryPageManager.LoadFromStream(Stream: TStream);
var i: Integer;
begin
  LoadDataFromStream(FAllocatedPageCount,Sizeof(FAllocatedPageCount),Stream,10369);
  LoadDataFromStream(LastAllocatedPageNo,Sizeof(LastAllocatedPageNo),Stream,10370);
  LoadDataFromStream(FAllocateBy,Sizeof(FAllocateBy),Stream,10371);
  LoadDataFromStream(FPageSize,Sizeof(FPageSize),Stream,10372);
  LoadDataFromStream(FPageHeaderSize,Sizeof(FPageHeaderSize),Stream,10373);
  LoadDataFromStream(FPageCount,Sizeof(FPageCount),Stream,10374);
  FAllocatedPageMap.LoadFromStream(Stream);
  SetLength(FDataPtrs,FAllocatedPageCount);
  for i := 0 to Integer(FAllocatedPageCount) - 1 do
   begin
     FDataPtrs[i] := MemoryManager.AllocMem(FPageSize);
     LoadDataFromStream(FDataPtrs[i]^,FPageSize,Stream,10375);
   end;
end; // LoadFromStream


//------------------------------------------------------------------------------
// save to stream
//------------------------------------------------------------------------------
procedure TABSMemoryPageManager.SaveToStream(Stream: TStream);
var i: Integer;
begin
  SaveDataToStream(FAllocatedPageCount,Sizeof(FAllocatedPageCount),Stream,10362);
  SaveDataToStream(LastAllocatedPageNo,Sizeof(LastAllocatedPageNo),Stream,10363);
  SaveDataToStream(FAllocateBy,Sizeof(FAllocateBy),Stream,10364);
  SaveDataToStream(FPageSize,Sizeof(FPageSize),Stream,10365);
  SaveDataToStream(FPageHeaderSize,Sizeof(FPageHeaderSize),Stream,10366);
  SaveDataToStream(FPageCount,Sizeof(FPageCount),Stream,10367);
  FAllocatedPageMap.SaveToStream(Stream);
  for i := 0 to Integer(FAllocatedPageCount) - 1 do
    SaveDataToStream(FDataPtrs[i]^,FPageSize,Stream,10368);
end; // SaveToStream


//------------------------------------------------------------------------------
// InternalAddPage
//------------------------------------------------------------------------------
procedure TABSMemoryPageManager.InternalAddPage(aPage: TABSPage);
var
  PageNo:    Integer;
  OldLength: Integer;
  i:         Integer;
begin
  if (not FAllocatedPageMap.FindBit(False, PageNo)) then
   begin
    if (FAllocatedPageCount <> 0) then
     PageNo := LastAllocatedPageNo+1
    else
     PageNo := 0;
    if (PageNo >= FAllocatedPageCount) then
      begin
        Inc(FAllocatedPageCount, FAllocateBy);
        FAllocatedPageMap.Size := FAllocatedPageCount;
      end;
   end;
  Inc(FPageCount);
  if (PageNo < FPageCount -1) then
   if (FAllocatedPageMap.GetBit(PageNo)) then
    raise EABSException.Create(20037, ErrorANotReleasedPageIsAllocated);
  FAllocatedPageMap.SetBit(PageNo, True);

  if (Length(FDataPtrs) <= PageNo) then
   begin
    OldLength := Length(FDataPtrs);
    SetLength(FDataPtrs, max(PageNo+1, OldLength+FAllocateBy));
    for i := OldLength to Length(FDataPtrs)-1 do
     FDataPtrs[i] := MemoryManager.AllocMem(FPageSize);
   end;
  if (PageNo > LastAllocatedPageNo) then
    LastAllocatedPageNo := PageNo;

  aPage.PageNo := PageNo;
  aPage.OwnBuffer := False;
  aPage.PageBuffer := FDataPtrs[PageNo];
end;// InternalAddPage


//------------------------------------------------------------------------------
// InternalRemovePage
//------------------------------------------------------------------------------
procedure TABSMemoryPageManager.InternalRemovePage(PageNo: TABSPageNo);
begin
  Dec(FPageCount);
  FAllocatedPageMap.SetBit(PageNo, False);
end;// InternalRemovePage


//------------------------------------------------------------------------------
// InternalReadPage
//------------------------------------------------------------------------------
function TABSMemoryPageManager.InternalReadPage(aPage: TABSPage): Boolean;
begin
  if (not FAllocatedPageMap.GetBit(aPage.PageNo)) then
   raise EABSException.Create(10392, ErrorANotReleasedPageIsAllocated);
  if (aPage.OwnBuffer) then
   aPage.FreeAndNilPageBuffer;
  aPage.OwnBuffer := False;
  aPage.PageBuffer := FDataPtrs[aPage.PageNo];
  Result := True;
end;// InternalReadPage


//------------------------------------------------------------------------------
// InternalWritePage
//------------------------------------------------------------------------------
procedure TABSMemoryPageManager.InternalWritePage(aPage: TABSPage);
begin
  if (not FAllocatedPageMap.GetBit(aPage.PageNo)) then
   raise EABSException.Create(10393, ErrorANotReleasedPageIsAllocated);
  if (aPage.OwnBuffer) then
   Move(aPage.PageBuffer^, FDataPtrs[aPage.PageNo]^, FPageSize);
end;// InternalWritePage


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
constructor TABSMemoryPageManager.Create;
begin
  inherited Create;
  FAllocatedPageMap := TABSBitsArray.Create;
  SetLength(FDataPtrs, 0);
  FAllocatedPageCount := 0;
  FPageCount := 0;
  FPageHeaderSize := 0;
  FPageSize := 4096;
  FPageDataSize := FPageSize;
  FAllocateBy := 50;
end;// Create


//------------------------------------------------------------------------------
// destroy
//------------------------------------------------------------------------------
destructor TABSMemoryPageManager.Destroy;
var
  i: Integer;
begin
// allow to free with not freed pages to speed up sql queries
//  if (FPageCount > 0) then
//   raise EABSException.Create(20023, ErrorAIndexPagesNotReleased);
  for i := 0 to Length(FDataPtrs)-1 do
   MemoryManager.FreeAndNillMem(FDataPtrs[i]);
  FAllocatedPageMap.Free;
  inherited Destroy;
end;// Destroy



end.
