unit ABSTempEngine;

{$I ABSVer.inc}

interface

uses SysUtils, Classes,

// AbsoluteDatabase units

{$IFNDEF D5H}
     ABSD4Routines,
{$ENDIF}
{$IFDEF DEBUG_LOG}
     ABSDebug,
{$ENDIF}
     ABSExcept,
     ABSBase,
     ABSPage,
     ABSBaseEngine,
     ABSMemory,
     ABSCompression,
     ABSSecurity,
     ABSTypes,
     ABSConst;

type
   TABSTemporaryTableData = class;


////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryRecordManager
//
////////////////////////////////////////////////////////////////////////////////


  TABSTemporaryRecordManager = class (TABSBaseRecordManager)
   private
    FRecordsPerPage:        Integer;
    FPageSize:              Integer;
    FCachedRecordCount:     Integer;
    FCache:                 TAbsPByte;
    FAllocatedRecordCount:  Integer;
    FTempPage:              TAbsPByte;
    FTempPageRecordCount:   Integer;
    FTempPageFile:          TABSFileStream;
    FMaxCachedRecordCount:  Integer;
    FAllocRecordsBy:        Integer;
    FDisableTempFiles:      Boolean;

    procedure SaveTempPage;
    function ReadRecord(
                        var RecordBuffer: TABSRecordBuffer;
                        RecordID: TABSRecordNo
                       ): Boolean;
    // return result for attempt of getting record relatively to first position
    // and set RecordID to new record ID
    function GetRecordFromFirstPosition(
            GetRecordMode: TABSGetRecordMode;
            var RecordID:  TABSRecordNo
                                       ): 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:  TABSRecordNo
                                      ): TABSGetRecordResult;
    // return result for attempt of getting record relatively any position
    // and set RecordID to new record ID
    function GetRecordFromAnyPosition(
            GetRecordMode: TABSGetRecordMode;
            var RecordID:  TABSRecordNo
                                      ): TABSGetRecordResult;
   public
    constructor Create(
                        RecordBufferSize:     Integer;
                        RecordsPerPage:       Integer;
                        AllocRecordsBy:       Integer;
                        DisableTempFiles:     Boolean
                      );
    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;
  end; // TABSTemporaryRecordManager


////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryPageManager
//
////////////////////////////////////////////////////////////////////////////////


  TABSTemporaryPageManager = class (TABSPageManager)
   protected
     FAllocatedPageMap:    TABSBitsArray;
     FAllocatedPageCount:  TABSPageNo;
     FTempPageFile:        TABSFileStream;
     FMaxMemoryPageCount:  TABSPageNo;
     FMemoryPageManager:   TABSPageManager;
     FDisableTempFiles:    Boolean;

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

     constructor Create(DisableTempFiles: Boolean);
     destructor Destroy; override;
  end; // TABSMemoryPageManager



////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryDatabaseData
//
////////////////////////////////////////////////////////////////////////////////


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


////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryTableData
//
////////////////////////////////////////////////////////////////////////////////


  TABSTemporaryTableData = class (TABSTableData)
   private
    FBLOBFile:          TABSStream;
    FRecordsPerPage:    Integer;
    FAllocRecordsBy:    Integer;

   protected
    procedure CreateRecordManager; override;
    procedure CreateFieldManager(FieldDefs: TABSFieldDefs); override;
    procedure CreateIndexManager(IndexDefs: TABSIndexDefs); override;
    procedure CreateConstraintManager(ConstraintDefs: TABSConstraintDefs); override;
    procedure CreateBLOBFile;
    procedure DeleteBLOBFile;
    // 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;
                        RecordsPerPage: Integer = 10;
                        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;

    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;

    //---------------------------------------------------------------------------
    // BLOB methods
    //---------------------------------------------------------------------------
    procedure WriteBLOBFieldToRecordBuffer(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              BLOBStream: TABSStream
              ); 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;

  end; // TABSTemporaryTableData


implementation


uses

// AbsoluteDatabase units

  ABSLocalEngine,
  ABSMemEngine;


////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryRecordManager
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// save temporary page
//------------------------------------------------------------------------------
procedure TABSTemporaryRecordManager.SaveTempPage;
var WriteBytes,WriteSize:   Integer;
    OldPos,NewPos:          Int64;
begin
 if (FTempPageRecordCount > 0) then
  begin
   if (FTempPageRecordCount > FRecordsPerPage) then
    raise EABSException.Create(10126,ErrorLInvalidRecordCount,
      [FTempPageRecordCount,FRecordsPerPage]);
   OldPos := FTempPageFile.Position;
   NewPos := Int64(FRecordCount - FMaxCachedRecordCount - FTempPageRecordCount) *
             Int64(FRecordBufferSize);
   FTempPageFile.Position := NewPos;
   if (FTempPageFile.Position <> NewPos) then
    raise EABSException.Create(10128,ErrorLCannotSetPosition,
      [NewPos,OldPos,FTempPageFile.Size]);
   OldPos := FTempPageFile.Position;
   WriteSize := FTempPageRecordCount * FRecordBufferSize;
   WriteBytes := FTempPageFile.Write(FTempPage^,WriteSize);
   if (WriteBytes <> WriteSize) then
    raise EABSException.Create(10125,ErrorLWriteToStream,
      [OldPos,FTempPageFile.Size,WriteSize,WriteBytes]);
   FTempPageRecordCount := 0;
  end;
end; // SaveTempPage


//------------------------------------------------------------------------------
// read record
//------------------------------------------------------------------------------
function TABSTemporaryRecordManager.ReadRecord(
                        var RecordBuffer: TABSRecordBuffer;
                        RecordID: TABSRecordNo
                       ): Boolean;
var ReadBytes:          Integer;
    OldPos,NewPos:      Int64;
begin
 if (FDisableTempFiles or (RecordID < FMaxCachedRecordCount)) then
  begin
   try
     Move((FCache + RecordID * FRecordBufferSize)^,RecordBuffer^,
      FRecordBufferSize);
   except
     raise EABSException.Create(10127,ErrorLRetreivingRecord);
   end;
   Result := True;
  end
 else
  begin
   SaveTempPage;
   OldPos := FTempPageFile.Position;
   NewPos := Int64(RecordID - FMaxCachedRecordCount) *
             Int64(FRecordBufferSize);
   FTempPageFile.Position := NewPos;
   if (FTempPageFile.Position <> NewPos) then
    raise EABSException.Create(10129,ErrorLCannotSetPosition,
      [NewPos,OldPos,FTempPageFile.Size]);
   OldPos := FTempPageFile.Position;
   ReadBytes := FTempPageFile.Read(RecordBuffer^,FRecordBufferSize);
   if (ReadBytes <> FRecordBufferSize) then
    raise EABSException.Create(10130,ErrorLCannotReadFromStream,
      [OldPos,FTempPageFile.Size,FRecordBufferSize,ReadBytes]);
   Result := True;
  end;
end; // ReadRecord


//------------------------------------------------------------------------------
// return result for attempt of getting record relatively to first position
// and set RecordID to new record ID
//------------------------------------------------------------------------------
function TABSTemporaryRecordManager.GetRecordFromFirstPosition(
        GetRecordMode: TABSGetRecordMode;
        var RecordID:  TABSRecordNo
                                   ): TABSGetRecordResult;
begin
 Result := grrError;
 case GetRecordMode of
  grmPrior:
   begin
    Result := grrBOF;
   end;
  grmCurrent:
   begin
    Result := grrError;
   end;
  grmNext:
   begin
    RecordID := 0;
    Result := grrOK;
   end;
 end; // GetRecordMode
end; // GetRecordFromFirstPosition


//------------------------------------------------------------------------------
// return result for attempt of getting record relatively to last position
// and set RecordID to new record ID
//------------------------------------------------------------------------------
function TABSTemporaryRecordManager.GetRecordFromLastPosition(
        GetRecordMode: TABSGetRecordMode;
        var RecordID:  TABSRecordNo
                                  ): TABSGetRecordResult;
begin
 Result := grrError;
 case GetRecordMode of
  grmPrior:
   begin
    RecordID := FRecordCount-1;
    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 TABSTemporaryRecordManager.GetRecordFromAnyPosition(
        GetRecordMode: TABSGetRecordMode;
        var RecordID:  TABSRecordNo
                                  ): TABSGetRecordResult;
begin
 Result := grrError;
 case GetRecordMode of
  grmPrior:
   begin
    Result := grrBOF;
    if (RecordID > 0) then
     begin
      Dec(RecordID);
      Result := grrOK;
     end; // RecordID > 0
   end;
  grmCurrent:
   begin
    if ((RecordID >= FRecordCount) or (RecordID < 0)) then
     // current record does not exist
     Result := grrError
    else
     Result := grrOK;
   end;
  grmNext:
   begin
    Result := grrEOF;
    if (RecordID < FRecordCount-1) then
     begin
      Inc(RecordID);
      Result := grrOK;
     end;
   end;
 end; // GetRecordMode
end; // GetRecordFromAnyPosition


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
constructor TABSTemporaryRecordManager.Create(
                        RecordBufferSize:     Integer;
                        RecordsPerPage:       Integer;
                        AllocRecordsBy:       Integer;
                        DisableTempFiles:     Boolean
                      );
begin
 if (RecordBufferSize = 0) then
  raise EABSException.Create(10124,ErrorLInvalidRecordSize);
 FRecordCount := 0;
 FRecordBufferSize := RecordBufferSize;
 FRecordsPerPage := RecordsPerPage;
 if (FRecordsPerPage = 0) then
  FRecordsPerPage := 1;
 FMaxCachedRecordCount := ((MemoryManager.GetFreeMemorySize div 10) div FRecordBufferSize);
 FPageSize := FRecordsPerPage * FRecordBufferSize;
 FTempPage := MemoryManager.GetMem(FPageSize);
 FTempPageRecordCount := 0;
 FCachedRecordCount := 0;
 FCache := nil;
 FAllocRecordsBy := AllocRecordsBy;
 FTempPageFile := nil;
 FDisableTempFiles := DisableTempFiles;
end; // Create;


//------------------------------------------------------------------------------
// destroy
//------------------------------------------------------------------------------
destructor TABSTemporaryRecordManager.Destroy;
begin
 Empty(SYSTEM_SESSION_ID);
 MemoryManager.FreeAndNillMem(FTempPage);
 inherited Destroy;
end; // Destroy


//------------------------------------------------------------------------------
// Empty
//------------------------------------------------------------------------------
procedure TABSTemporaryRecordManager.Empty(SessionID: TABSSessionID);
begin
 FRecordCount := 0;
 FTempPageRecordCount := 0;
 if (FCache <> nil) then
  MemoryManager.FreeAndNillMem(FCache);
 FCachedRecordCount := 0;
 FAllocatedRecordCount := 0;
 if (FTempPageFile <> nil) then
   FreeAndNil(FTempPageFile);
end; // Empty


//------------------------------------------------------------------------------
// add record and return its number
//------------------------------------------------------------------------------
function TABSTemporaryRecordManager.AddRecord(
                                              SessionID: TABSSessionID;
                                              RecordBuffer: TABSRecordBuffer;
                                              var RecordID: TABSRecordID
                                              ): Boolean;
var SaveToTemp: Boolean;
begin
 try
   SaveToTemp := False;
   if (FCachedRecordCount >= FAllocatedRecordCount) then
    begin
     // need extending cache or writing to temporary file
     if (FDisableTempFiles or (FAllocatedRecordCount < FMaxCachedRecordCount)) then
      begin
       // create or extend cache
       if (FDisableTempFiles) then
        Inc(FAllocatedRecordCount,FAllocRecordsBy)
       else
         if (FAllocatedRecordCount + FAllocRecordsBy <= FMaxCachedRecordCount) then
          Inc(FAllocatedRecordCount,FAllocRecordsBy)
         else
          FAllocatedRecordCount := FMaxCachedRecordCount;
       if (FCache = nil) then
        FCache := MemoryManager.GetMem(FAllocatedRecordCount * FRecordBufferSize)
       else
        MemoryManager.ReallocMem(FCache,(FAllocatedRecordCount * FRecordBufferSize));
       Inc(FCachedRecordCount);
      end
     else
      begin
       SaveToTemp := True;
       // need writing to temporary file
       if (FTempPageFile = nil) then
        FTempPageFile := TABSFileStream.Create(GetTempFileName,fmCreate,True);
       SaveTempPage;
       Move(RecordBuffer^,(FTempPage + FTempPageRecordCount * FRecordBufferSize)^,
         FRecordBufferSize);
       Inc(FTempPageRecordCount);
      end;
    end
   else
    begin
     // no need in extending
     Inc(FCachedRecordCount);
    end;
   if (not SaveToTemp) then
    Move(RecordBuffer^,(FCache + FRecordCount * FRecordBufferSize)^,
         FRecordBufferSize);
   Move(FRecordCount,RecordID,sizeof(RecordID));
   Inc(FRecordCount);
   Result := True;
 except
   Result := False;
 end;
end; // AddRecord


//------------------------------------------------------------------------------
// update record, return true if record was updated, false if record was deleted
//------------------------------------------------------------------------------
function TABSTemporaryRecordManager.UpdateRecord(SessionID: TABSSessionID; RecordBuffer: TABSRecordBuffer; RecordID: TABSRecordID): Boolean;
begin
  Result := False;
end;// UpdateRecord


//------------------------------------------------------------------------------
// delete record, return true if record was deleted, false if record was deleted earlier
//------------------------------------------------------------------------------
function TABSTemporaryRecordManager.DeleteRecord(SessionID: TABSSessionID; var RecordID: TABSRecordID): Boolean;
begin
  Result := False;
end;// DeleteRecord


//------------------------------------------------------------------------------
// get record
//------------------------------------------------------------------------------
procedure TABSTemporaryRecordManager.GetRecordBuffer(SessionID: TABSSessionID;
                                        var NavigationInfo: TABSNavigationInfo);
var RecordNo: TABSRecordNo;
begin
 RecordNo := 0;
 Move(NavigationInfo.RecordID,RecordNo,sizeof(NavigationInfo.RecordID));
 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,
                                        RecordNo
                                                               );
   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,
                                        RecordNo
                                                               );
   if (NavigationInfo.GetRecordResult = grrOK) then
    NavigationInfo.LastPosition := False;
  end
 else
  NavigationInfo.GetRecordResult := GetRecordFromAnyPosition(
                                        NavigationInfo.GetRecordMode,
                                        RecordNo
                                                               );
 if (NavigationInfo.GetRecordResult = grrOK) then
  begin
   NavigationInfo.GetRecordResult := grrError;
   if (ReadRecord(NavigationInfo.RecordBuffer,RecordNo)) then
    NavigationInfo.GetRecordResult := grrOK;
  end; // record retrieved successfully
 Move(RecordNo,NavigationInfo.RecordID,sizeof(NavigationInfo.RecordID));
end; // GetRecordBuffer


//------------------------------------------------------------------------------
// return 0,1, or -1 if (1 = 2), (1 > 2) or (1 < 2)
//------------------------------------------------------------------------------
function TABSTemporaryRecordManager.CompareRecordID(RecordID1: TABSRecordID; RecordID2: TABSRecordID): Integer;
var RecordNo1, RecordNo2: TABSRecordNo;
begin
 RecordNo1 := 0;
 RecordNo2 := 0;
 Move(RecordID1,RecordNo1,Sizeof(RecordID1));
 Move(RecordID2,RecordNo2,Sizeof(RecordID2));
 if (RecordNo1 = RecordNo2) then
  Result := 0
 else
 if (RecordNo1 > RecordNo2) then
  Result := 1
 else
  Result := -1;
end; // CompareRecordID


//------------------------------------------------------------------------------
// return record no
//------------------------------------------------------------------------------
function TABSTemporaryRecordManager.GetApproximateRecNo(SessionID: TABSSessionID;
                                          RecordID: TABSRecordID): TABSRecordNo;
begin
 Result := 0;
 Move(RecordID,Result,Sizeof(RecordID));
 if (Result >= FRecordCount) then
  Result := -1
 else
  begin
   Inc(Result);
  end;
end; // GetApproximateRecNo


////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryPageManager
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// InternalAddPage
//------------------------------------------------------------------------------
procedure TABSTemporaryPageManager.InternalAddPage(aPage: TABSPage);
var
  PageNo: Integer;
begin
  PageNo := FAllocatedPageCount;
  if (FDisableTempFiles or (PageNo < FMaxMemoryPageCount)) then
   begin
    FMemoryPageManager.InternalAddPage(aPage);
    if (aPage.PageNo >= FAllocatedPageCount) then
     begin
      FAllocatedPageCount := aPage.PageNo +1;
      FAllocatedPageMap.Size := FAllocatedPageCount;
     end;
    FAllocatedPageMap.SetBit(aPage.PageNo, True);
    Inc(FPageCount);
   end
  else
   begin
    if (FTempPageFile = nil) then
      FTempPageFile := TABSFileStream.Create(GetTempFileName,fmCreate, True);
    Inc(FAllocatedPageCount);
    FAllocatedPageMap.Size := FAllocatedPageCount;
    Inc(FPageCount);
    FAllocatedPageMap.SetBit(PageNo, True);
    if (FTempPageFile.Size < FAllocatedPageCount * FPageSize) then
      FTempPageFile.Size := FAllocatedPageCount * FPageSize * 3 div 2;
    aPage.PageNo := PageNo;
    if ((not aPage.OwnBuffer) or (aPage.PageBuffer = nil)) then
     begin
       aPage.AllocPageBuffer;
       aPage.OwnBuffer := True;
     end;
   end;
end;// InternalAddPage


//------------------------------------------------------------------------------
// InternalRemovePage
//------------------------------------------------------------------------------
procedure TABSTemporaryPageManager.InternalRemovePage(PageNo: TABSPageNo);
begin
  Dec(FPageCount);
  FAllocatedPageMap.SetBit(PageNo, False);
  if (FDisableTempFiles or (PageNo < FMaxMemoryPageCount)) then
   FMemoryPageManager.InternalRemovePage(PageNo)
end;// InternalRemovePage


//------------------------------------------------------------------------------
// InternalReadPage
//------------------------------------------------------------------------------
function TABSTemporaryPageManager.InternalReadPage(aPage: TABSPage): Boolean;
begin
  if (not FAllocatedPageMap.GetBit(aPage.PageNo)) then
   raise EABSException.Create(10396, ErrorANotReleasedPageIsAllocated);
  if (FDisableTempFiles or (aPage.PageNo < FMaxMemoryPageCount)) then
   FMemoryPageManager.InternalReadPage(aPage)
  else
   begin
    if ((not aPage.OwnBuffer) or (aPage.PageBuffer = nil)) then
     begin
      aPage.AllocPageBuffer;
      aPage.OwnBuffer := True;
     end;
    if (FTempPageFile = nil) then
      raise EABSException.Create(30482, ErrorGInvalidPointer);
    FTempPageFile.Position := aPage.PageNo * FPageSize;
    FTempPageFile.ReadBuffer(aPage.PageBuffer^, FPageSize);
   end;
  Result := True;
end;// InternalReadPage


//------------------------------------------------------------------------------
// InternalWritePage
//------------------------------------------------------------------------------
procedure TABSTemporaryPageManager.InternalWritePage(aPage: TABSPage);
begin
  if (not FAllocatedPageMap.GetBit(aPage.PageNo)) then
   raise EABSException.Create(10397, ErrorANotReleasedPageIsAllocated);
  if (FDisableTempFiles or (aPage.PageNo < FMaxMemoryPageCount)) then
   FMemoryPageManager.InternalWritePage(aPage)
  else
   begin
    if (FTempPageFile = nil) then
      FTempPageFile := TABSFileStream.Create(GetTempFileName,fmCreate, True);
    FTempPageFile.Position := aPage.PageNo * FPageSize;
    FTempPageFile.WriteBuffer(aPage.PageBuffer^, FPageSize);
   end;
end;// InternalWritePage


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
constructor TABSTemporaryPageManager.Create(DisableTempFiles: Boolean);
begin
  inherited Create;
  FDisableTempFiles := DisableTempFiles;
  FAllocatedPageMap := TABSBitsArray.Create;
  FAllocatedPageCount := 0;
  FPageCount := 0;
  FPageHeaderSize := 0;
  FPageSize := 4096;
  FPageDataSize := FPageSize;
  FMaxMemoryPageCount := ((MemoryManager.GetFreeMemorySize div 10) div FPageSize);

  //FTempPageFile := TABSFileStream.Create(GetTempFileName,fmCreate, True);
  FTempPageFile := nil;
  FMemoryPageManager := TABSMemoryPageManager.Create;
end;// Create


//------------------------------------------------------------------------------
// destroy
//------------------------------------------------------------------------------
destructor TABSTemporaryPageManager.Destroy;
begin
// allow to free with not freed pages to speed up sql queries
//  if (FPageCount > 0) then
//   raise EABSException.Create(20036, ErrorAIndexPagesNotReleased);

  FAllocatedPageMap.Free;
  if (FTempPageFile <> nil) then
    FreeAndNil(FTempPageFile);
  FMemoryPageManager.Free;
  inherited Destroy;
end;// Destroy



////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryDatabaseData
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
constructor TABSTemporaryDatabaseData.Create;
begin
  inherited Create;
  DatabaseName := ABSTemporaryDatabaseName;
  FLastObjectID := 0;
end;// Create


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


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


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



////////////////////////////////////////////////////////////////////////////////
//
// TABSTemporaryTableData
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// create record manager
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.CreateRecordManager;
begin
 if (FRecordManager <> nil) then
  FRecordManager.Free;
 FRecordManager := TABSTemporaryRecordManager.Create(GetRecordBufferSize,
  FRecordsPerPage,FAllocRecordsBy, FDisableTempFiles);
end; // CreateRecordManager


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


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


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.CreateConstraintManager(ConstraintDefs: TABSConstraintDefs);
begin
 if (FConstraintManager <> nil) then
   FConstraintManager.Free;
 // Create ConstraintManager
 FConstraintManager := TABSBaseConstraintManager.Create(Self);
 inherited CreateConstraintManager(ConstraintDefs);
end; // CreateConstraintManager


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.CreateBLOBFile;
begin
 if (FFieldManager.BlobFieldsPresent) then
  begin
   DeleteBLOBFile;
   if (FDisableTempFiles) then
     begin
       FBLOBFile := TABSMemoryStream.Create(nil, DEFAULT_TABLE_MEMORY_BLOB_FILE_SIZE);
     end
   else
     FBLOBFile := TABSTemporaryStream.Create(FDisableTempFiles);
  end;
end;


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.DeleteBLOBFile;
begin
 if (FBLOBFile <> nil) then
   FreeAndNil(FBLOBFile);
end;


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


//------------------------------------------------------------------------------
// return filter bitmap rec no by record id
//------------------------------------------------------------------------------
function TABSTemporaryTableData.GetBitmapRecNoByRecordID(RecordID: TABSRecordID): TABSRecordNo;
begin
  Result := 0;
  Move(RecordID,Result,sizeof(RecordID));
end; // GetBitmapRecNoByRecordID


//------------------------------------------------------------------------------
// return filter bitmap rec no by record id
//------------------------------------------------------------------------------
function TABSTemporaryTableData.GetRecordIDByBitmapRecNo(RecordNo: TABSRecordNo): TABSRecordID;
begin
  Move(RecordNo,Result,sizeof(Result));
end; // GetRecordIDByBitmapRecNo


//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
constructor TABSTemporaryTableData.Create(
                    aDatabaseData:  TABSDatabaseData;
                    RecordsPerPage: Integer;
                    AllocRecordsBy: Integer
                  );
begin
 inherited Create(aDatabaseData);
 FPageManager := nil;
 FRecordsPerPage := RecordsPerPage;
 FAllocRecordsBy := AllocRecordsBy;
 FBLOBFile := nil;
end; // Create


//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
destructor TABSTemporaryTableData.Destroy;
begin
  EmptyTable(nil);
  DeleteAllIndexes(nil);
  if (FPageManager <> nil) then
    begin
      FPageManager.ApplyChanges(SYSTEM_SESSION_ID);
      FPageManager.Free;
    end;
  inherited Destroy;
end; // Destroy


//------------------------------------------------------------------------------
// create table
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.CreateTable(
                          Cursor: TABSCursor;
                          FieldDefs: TABSFieldDefs;
                          IndexDefs: TABSIndexDefs;
                          ConstraintDefs: TABSConstraintDefs
                                         );
begin
 if (FieldDefs.Count <= 0) then
  raise EABSException.Create(10132,ErrorLNoFields);
 TableName := Cursor.TableName;
 FDisableTempFiles := Cursor.FDisableTempFiles;

 if (FPageManager <> nil) then
  FPageManager.Free;
 FPageManager := TABSTemporaryPageManager.Create(FDisableTempFiles);
 CreateFieldManager(FieldDefs);
 CreateIndexManager(IndexDefs);
 CreateConstraintManager(ConstraintDefs);

 CreateRecordManager;
 InitCursor(Cursor);
 CreateBLOBFile;
 FIndexManager.CreateIndexesByIndexDefs(Cursor);
 if (Cursor <> nil) then
   if (not Cursor.Session.InTransaction) then
    PageManager.ApplyChanges(Cursor.Session.SessionID);
end;// CreateTable


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


//------------------------------------------------------------------------------
// empty table
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.EmptyTable(Cursor: TABSCursor);
begin
 DeleteBLOBFile;
 inherited EmptyTable(nil);
 if (FRecordManager <> nil) then
  begin
   FRecordManager.Empty(SYSTEM_SESSION_ID);
  end;
 if (FPageManager <> nil) then
   FPageManager.ApplyChanges(SYSTEM_SESSION_ID);
end; // EmptyTable


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


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


//------------------------------------------------------------------------------
// AddIndex
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.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 TABSTemporaryTableData.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


//------------------------------------------------------------------------------
// insert record
//------------------------------------------------------------------------------
function TABSTemporaryTableData.InsertRecord(var Cursor: TABSCursor): Boolean;
var RecordID: TABSRecordID;
begin
  if (FRecordManager = nil) then
   raise EABSException.Create(10134,ErrorLNilPointer);
  if (Cursor.CurrentRecordBuffer = nil) then
   raise EABSException.Create(10135,ErrorLNilPointer);
  // add record to first empty space
  try
    Result := FRecordManager.AddRecord(Cursor.Session.SessionID,
                                           Cursor.CurrentRecordBuffer,RecordID);
    Move(RecordID,Cursor.CurrentRecordID,sizeof(Cursor.CurrentRecordID));
  except
    Result := False;
  end;
  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;
end; // InsertRecord;


//------------------------------------------------------------------------------
// delete record
//------------------------------------------------------------------------------
function TABSTemporaryTableData.DeleteRecord(Cursor: TABSCursor): Boolean;
begin
 Result := False;
end; // DeleteRecord


//------------------------------------------------------------------------------
// update record
//------------------------------------------------------------------------------
function TABSTemporaryTableData.UpdateRecord(Cursor: TABSCursor): Boolean;
begin
 Result := False;
end; // UpdateRecord


//------------------------------------------------------------------------------
// return record count
//------------------------------------------------------------------------------
function TABSTemporaryTableData.InternalGetRecordCount(Cursor: TABSCursor): TABSRecordNo;
begin
  if (FRecordManager = nil) then
   raise EABSException.Create(10133,ErrorLNilPointer);
  Result := FRecordManager.RecordCount;
end; // GetRecordCount


//------------------------------------------------------------------------------
// move cursor to specified position and set current record id in cursor
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.InternalSetRecNo(Cursor: TABSCursor; RecNo: TABSRecordNo);
var RecordID: TABSRecordNo;
begin
  if (FRecordManager = nil) then
   raise EABSException.Create(10136,ErrorLNilPointer);
  if (RecNo > 1) then
   RecordID := RecNo-1
  else
   RecordID := 0;
  Move(RecordID,Cursor.CurrentRecordID,sizeof(Cursor.CurrentRecordID));
end; // SetRecNo


//------------------------------------------------------------------------------
// get current record position from cursor
//------------------------------------------------------------------------------
function TABSTemporaryTableData.InternalGetRecNo(Cursor: TABSCursor): TABSRecordNo;
begin
  if (FRecordManager = nil) then
   raise EABSException.Create(10137,ErrorLNilPointer);
  Result := 0;
  Move(Cursor.CurrentRecordID,Result,sizeof(Cursor.CurrentRecordID));
  Inc(Result);
end; // GetRecNo


//---------------------------------------------------------------------------
// write BLOB field to record buffer
//---------------------------------------------------------------------------
procedure TABSTemporaryTableData.WriteBLOBFieldToRecordBuffer(
          Cursor:     TABSCursor;
          FieldNo:    Integer;
          BLOBStream: TABSStream
          );
var
    WriteSize,WriteBytes:     Integer;
    PartialBLOBDescriptor:    TABSPartialTemporaryBLOBDescriptor;
    DiskOffset:               Int64;
begin
 if (FBLOBFile = nil) then
  raise EABSException.Create(10144,ErrorLNilPointer);
 if (BLOBStream.Modified) then
  begin
   if (BLOBStream.Size = 0) then
    begin
     // empty stream
     SetNullFlag(True,FieldNo,Cursor.CurrentRecordBuffer);
    end
   else
    begin
     PartialBLOBDescriptor.NumBlocks := TABSCompressedBLOBStream(
        TABSLocalBLOBStream(BLOBStream).TemporaryStream).BLOBDescriptor.NumBlocks;
     PartialBLOBDescriptor.UncompressedSize := TABSCompressedBLOBStream(
        TABSLocalBLOBStream(BLOBStream).TemporaryStream).BLOBDescriptor.UncompressedSize;
     PartialBLOBDescriptor.CompressedSize := TABSCompressedBLOBStream(
        TABSLocalBLOBStream(BLOBStream).TemporaryStream).CompressedSize;
     SetStreamPosition(FBLOBFile,FBLOBFile.Size,10422);
     DiskOffset := FBLOBFile.Position;
     // save blob descriptor
     WriteSize := sizeof(PartialBLOBDescriptor);
     WriteBytes := FBLOBFile.Write(PartialBLOBDescriptor,WriteSize);
     if (WriteBytes <> WriteSize) then
      raise EABSException.Create(10145,ErrorLCannotWriteToStream,
        [DiskOffset,FBLOBFile.Size,WriteSize,WriteBytes]);
     // save compressed data to blob file
     TABSStream(TABSCompressedBLOBStream(TABSLocalBLOBStream(BLOBStream).TemporaryStream).
        CompressedStream).SaveToStream(FBLOBFile);
     // store offset of blob descriptor in blob file to record buffer
     Move(DiskOffset,TAbsPByte(Cursor.CurrentRecordBuffer +
      FieldManager.FieldDefs[FieldNo].MemoryOffset)^,sizeof(DiskOffset));
     SetNullFlag(False,FieldNo,Cursor.CurrentRecordBuffer);
    end; // not empty stream
  end; // Modified
end; // WriteBLOBFieldToRecordBuffer


//---------------------------------------------------------------------------
// create BLOB stream
//---------------------------------------------------------------------------
function TABSTemporaryTableData.InternalCreateBlobStream(
          Cursor:   TABSCursor;
          ToInsert: Boolean;
          FieldNo:  Integer;
          OpenMode: TABSBLOBOpenMode
          ): TABSStream;
var TempStream:             TABSTemporaryStream;
    CompressedStream:       TABSCompressedBLOBStream;
    Offset:                 Integer;
    DiskOffset,OldPos:      Int64;
    ReadBytes,ReadSize:     Integer;
    BLOBDescriptor:         TABSBLOBDescriptor;
    PartialBLOBDescriptor:  TABSPartialTemporaryBLOBDescriptor;
begin
 if (FBLOBFile = nil) then
  raise EABSException.Create(10140,ErrorLNilPointer);
 BLOBDescriptor.CompressionAlgorithm :=
     Byte(FieldManager.FieldDefs[FieldNo].BLOBCompressionAlgorithm);
 BLOBDescriptor.CompressionMode := FieldManager.FieldDefs[FieldNo].BLOBCompressionMode;
 BLOBDescriptor.BlockSize := FieldManager.FieldDefs[FieldNo].BLOBBlockSize;
 BLOBDescriptor.StartPosition := 0;
 TempStream := TABSTemporaryStream.Create(FDisableTempFiles);
 // create new compressed stream
 if ((ToInsert) or (OpenMode = bomWrite) 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;
   // offset to blob field data
   Move(TAbsPByte(Cursor.CurrentRecordBuffer + Offset)^,DiskOffset,Sizeof(DiskOffset));
   OldPos := FBLOBFile.Position;

   // set position in BLOB file
   FBLOBFile.Position := DiskOffset;
   if (FBLOBFile.Position <> DiskOffset) then
     raise EABSException.Create(10141,ErrorLCannotSetPosition,
       [DiskOffset,OldPos,FBLOBFile.Size]);

   // read partial BLOB descriptor
   OldPos := FBLOBFile.Position;
   ReadSize := sizeof(TABSPartialTemporaryBLOBDescriptor);
   ReadBytes := FBLOBFile.Read(PartialBLOBDescriptor,ReadSize);
   if (ReadBytes <> ReadSize) then
    raise EABSException.Create(10142,ErrorLCannotReadFromStream,
      [OldPos,FBLOBFile.Size,ReadSize,ReadBytes]);

   // read compressed blob data
   TempStream.LoadFromStreamWithPosition(FBLOBFile,FBLOBFile.Position,
      PartialBLOBDescriptor.CompressedSize);

   // copy partial blob descriptor
   BLOBDescriptor.NumBlocks := PartialBLOBDescriptor.NumBlocks;
   BLOBDescriptor.UncompressedSize := PartialBLOBDescriptor.UncompressedSize;
   CompressedStream := TABSCompressedBLOBStream.Create(TempStream,
    BLOBDescriptor,False);
   Result := TABSLocalBLOBStream.Create(CompressedStream,Cursor,OpenMode,FieldNo);
  end; // copy value from TableData
end; // InternalCreateBlobStream


//------------------------------------------------------------------------------
// GetDirectBlobData
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.GetDirectBlobData(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              RecordBuffer: TABSRecordBuffer;
              var BLOBDescriptor: TABSPartialTemporaryBLOBDescriptor;
              var pBlobData: TAbsPByte);
var
    Offset:                 Integer;
    DiskOffset,OldPos:      Int64;
    ReadBytes,ReadSize:     Integer;
begin
 if (CheckNullFlag(FieldNo,RecordBuffer)) then
  begin
   // empty stream
   BLOBDescriptor.NumBlocks := 0;
   BLOBDescriptor.UncompressedSize := 0;
   BLOBDescriptor.CompressedSize := 0;
   pBlobData := nil;
  end // empty stream
 else
  begin
   // copy value from TableData
   Offset := FieldManager.FieldDefs[FieldNo].MemoryOffset;
   // offset to blob field data
   Move(TAbsPByte(RecordBuffer + Offset)^,DiskOffset,Sizeof(DiskOffset));
   OldPos := FBLOBFile.Position;

   // set position in BLOB file
   FBLOBFile.Position := DiskOffset;
   if (FBLOBFile.Position <> DiskOffset) then
     raise EABSException.Create(10509,ErrorLCannotSetPosition,
       [DiskOffset,OldPos,FBLOBFile.Size]);

   // read partial BLOB descriptor
   OldPos := FBLOBFile.Position;
   ReadSize := sizeof(TABSPartialTemporaryBLOBDescriptor);
   ReadBytes := FBLOBFile.Read(BLOBDescriptor,ReadSize);
   if (ReadBytes <> ReadSize) then
    raise EABSException.Create(10510,ErrorLCannotReadFromStream,
      [OldPos,FBLOBFile.Size,ReadSize,ReadBytes]);

   // read compressed blob data
   pBlobData := MemoryManager.GetMem(BLOBDescriptor.CompressedSize);
   FBLOBFile.ReadBuffer(pBlobData^, BLOBDescriptor.CompressedSize);
   FBLOBFile.Position := OldPos;
  end; // copy value from TableData
end;// GetDirectBlobData


//------------------------------------------------------------------------------
// SetDirectBlobData
//------------------------------------------------------------------------------
procedure TABSTemporaryTableData.SetDirectBlobData(
              Cursor:     TABSCursor;
              FieldNo:    Integer;
              RecordBuffer: TABSRecordBuffer;
              var BLOBDescriptor: TABSPartialTemporaryBLOBDescriptor;
              var pBlobData: TAbsPByte);
var
    WriteSize,WriteBytes:     Integer;
    DiskOffset:               Int64;
begin
 if (FBLOBFile = nil) then
  raise EABSException.Create(10506,ErrorLNilPointer);
 if (BLOBDescriptor.UncompressedSize = 0) then
   SetNullFlag(True,FieldNo,Cursor.CurrentRecordBuffer)
 else
    begin
     SetStreamPosition(FBLOBFile,FBLOBFile.Size,10507);
     DiskOffset := FBLOBFile.Position;
     // save blob descriptor
     WriteSize := sizeof(BLOBDescriptor);
     WriteBytes := FBLOBFile.Write(BLOBDescriptor,WriteSize);
     if (WriteBytes <> WriteSize) then
      raise EABSException.Create(10508,ErrorLCannotWriteToStream,
        [DiskOffset,FBLOBFile.Size,WriteSize,WriteBytes]);
     // save compressed data to blob file
     FBLOBFile.WriteBuffer(pBlobData^,BLOBDescriptor.CompressedSize);
     // store offset of blob descriptor in blob file to record buffer
     Move(DiskOffset,TAbsPByte(Cursor.CurrentRecordBuffer +
      FieldManager.FieldDefs[FieldNo].MemoryOffset)^,sizeof(DiskOffset));
     SetNullFlag(False,FieldNo,Cursor.CurrentRecordBuffer);
    end;
end;// SetDirectBlobData


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

end.
