unit ABSLocalEngine;

{$I ABSVer.inc}

interface

uses SysUtils, Classes, Windows,

// AbsoluteDatabase units

     ABSRelationalAlgebra,
     {$IFDEF DEBUG_LOG}
     ABSDebug,
     {$ENDIF}
     ABSExcept,
     ABSBase,
     ABSBaseEngine,
     {$IFDEF MEMORY_ENGINE}
     ABSMemEngine,
     {$ENDIF}
     {$IFDEF TEMPORARY_ENGINE}
     ABSTempEngine,
     {$ENDIF}
     {$IFDEF DISK_ENGINE}
     ABSDiskEngine,
     {$ENDIF}
     ABSConverts,
     ABSVariant,
     ABSCompression,
     ABSSecurity,
     ABSTypes,
     ABSSQLProcessor,
     ABSExpressions,
     ABSConst,
     ABSMemory;


type


////////////////////////////////////////////////////////////////////////////////
//
// TABSLocalBLOBStream
//
////////////////////////////////////////////////////////////////////////////////

  // local BLOB stream
  TABSLocalBLOBStream = class (TABSStream)
   private
    FOpenMode:         TABSBLOBOpenMode;
    FTemporaryStream:  TABSStream;
    FUserBLOBStream:   TABSStream;
    FFieldNo:          Integer;
    FCursor:           TABSCursor;
    FPosition:         Int64;
   protected
    // sets new size of the stream
    procedure InternalSetSize(const NewSize: Int64);
    // sets new size of the stream
    procedure SetSize(NewSize: Longint);
    {$IFDEF D6H}
      overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    procedure SetSize(const NewSize: Int64); overload; override;
    {$ENDIF}
   public
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint;
    {$IFDEF D6H}
            overload;
    {$ENDIF}
      override;
    {$IFDEF D6H}
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
    {$ENDIF}
    constructor Create(
                        TemporaryStream: TABSStream;
                        Cursor: TABSCursor;
                        OpenMode: TABSBLOBOpenMode;
                        FieldNo: Integer
                      );
    destructor Destroy; override;
   public
    // blob stream interface
   public
    property Cursor: TABSCursor read FCursor;
    property FieldNo: Integer read FFieldNo;
    property OpenMode: TABSBLOBOpenMode read FOpenMode write FOpenMode;
    property TemporaryStream: TABSStream read FTemporaryStream write FTemporaryStream;
    property UserBLOBStream: TABSStream read FUserBLOBStream write FUserBLOBStream;
  end; // TABSLocalBLOBtream


////////////////////////////////////////////////////////////////////////////////
//
// TABSLocalSQLProcessor
//
////////////////////////////////////////////////////////////////////////////////


  TABSLocalSQLProcessor = class (TABSSQLProcessor)
  end; // TABSLocalSQLProcessor


////////////////////////////////////////////////////////////////////////////////
//
// TABSLocalCursor
//
////////////////////////////////////////////////////////////////////////////////


  TABSLocalCursor = class (TABSCursor)
   private
    FTableData:                     TABSTableData;
    FDatabaseData:                  TABSDatabaseData;

   public
    constructor Create;
    destructor Destroy; override;
    // create table
    procedure CreateTable(
                          FieldDefs: TABSFieldDefs;
                          IndexDefs: TABSIndexDefs;
                          ConstraintDefs: TABSConstraintDefs
                         ); override;
    procedure DeleteTable; override;
    procedure EmptyTable; override;
    procedure RenameTable(NewTableName: string); override;
    procedure InternalInitFieldDefs; override;
    procedure OpenTableByFieldDefs(
                          FieldDefs: TABSFieldDefs;
                          IndexDefs: TABSIndexDefs;
                          ConstraintDefs: TABSConstraintDefs
                       ); override;
    procedure OpenTable(aTableData: TABSTableData);
    procedure CloseTable; override;
    procedure LockTableData; override;
    procedure UnlockTableData; override;
    function LockTable(LockType: TABSLockType;
                       TryCount, Delay: Integer;
                       AllowXIRWAfterSIRW: Boolean = True): Boolean; override;
    function UnlockTable(LockType: TABSLockType; IgnoreIfNoLock: Boolean=False): Boolean; override;

    // initialize record buffer
    procedure InternalInitRecord(RecordBuffer: TABSRecordBuffer;
                InsertMode: Boolean); override;

    // Rename Field by Field Index in FieldDefs
    procedure RenameField(FieldName, NewFieldName: string); override;

    procedure CopyFieldValue(SrcFieldNo: Integer;
                             DirectAccess: Boolean;
                             DestFieldNo: Integer;
                             DestCursor: TABSCursor); override;

    // index operations
    function GetIndexDefs: TABSIndexDefs; override;
    procedure AddIndex(IndexDef: TABSIndexDef); override;
    procedure DeleteIndex(Name: string); override;
    procedure DeleteAllIndexes; override;
    // return index name
    function FindOrCreateIndex(FieldNamesList, AscDescList,
              CaseSensitivityList: TStringList; var IsCreated: Boolean): string; override;
    function IndexExists(FieldNamesList, AscDescList, CaseSensitivityList: TStringList): Boolean; override;

    //---------------------------------------------------------------------------
    // navigation & bookmark methods
    //---------------------------------------------------------------------------
    procedure FreeRecordBuffer(var Buffer: TABSRecordBuffer); override;
    function GetRecordBuffer(
              GetRecordMode:  TABSGetRecordMode
              ): TABSGetRecordResult; override;
    function GetRecordCount: TABSRecordNo; override;
    // go to record
    procedure SetRecNo(Value: TABSRecordNo); override;
    function GetRecNo: TABSRecordNo; override;
    // -1,0,1 - compare records order with or without active index
    function CompareRecordsOrder(RecordID1, RecordID2: TABSRecordID): integer; override;

    // go to first record
    procedure InternalFirst; override;
    // go to last record
    procedure InternalLast; override;

    //---------------------------------------------------------------------------
    // insert, edit, post, delete methods
    //---------------------------------------------------------------------------
    // insert record
    procedure InternalInsert; override;
    // edit record
    procedure InternalEdit; override;
    // cancels updates
    procedure InternalCancel(ToInsert: Boolean); override;
    // insert or update record
    procedure InternalPost(ToInsert: Boolean); override;
    // delete record
    procedure InternalDelete; override;
    // check if record is locked by another user
    function IsRecordLocked: Boolean; override;

    //---------------------------------------------------------------------------
    // search & filter methods
    //---------------------------------------------------------------------------

    // set distinct
    procedure ApplyDistinct(FieldNamesList, AscDescList, CaseSensitivityList: TStringList); override;
    procedure ActivateFilters(
                              FilterText:      string;
                              CaseInsensitive: Boolean;
                              PartialKey:      Boolean
                            ); override;
    procedure DeactivateFilters; override;
    function Locate(
                    const KeyFields: string;
                    const KeyValues: Variant;
                    CaseInsensitive: Boolean;
                    PartialKey:      Boolean
                   ): Boolean; override;
    function FindKey(SearchCondition: TABSSearchCondition): Boolean; override;
    // used by SQL: "where field > all (select ...)"
    function IsAnyRecordMatchCondition(const FieldName: string;
          const Operator: TABSDataOperator; const Value: TABSVariant): Boolean; override;

    //---------------------------------------------------------------------------
    // BLOB methods
    //---------------------------------------------------------------------------

    function InternalCreateBlobStream(
              ToInsert: Boolean;
              ToEdit: Boolean;
              FieldNo:  Integer;
              OpenMode: TABSBLOBOpenMode;
              CanDoGetRecordBuffer: Boolean
              ):TABSStream; override;

    procedure InternalCloseBLOB(FieldNo: Integer); override;

    // clear blob streams
    procedure ClearBLOBStreams(WriteOnly: Boolean = False); override;

    function LastAutoincValue(FieldNo: Integer): Int64; override;

    // batch update methods
    procedure BeginBatchUpdate; override;
    procedure EndBatchUpdate; override;
    procedure CancelBatchUpdate; override;

    function CheckConstraints(ToInsert: Boolean): Boolean; override;

   public
    property TableData: TABSTableData read FTableData;
    property DatabaseData: TABSDatabaseData read FDatabaseData;
  end; // TABSLocalCursor


////////////////////////////////////////////////////////////////////////////////
//
// TABSLocalSession
//
////////////////////////////////////////////////////////////////////////////////


  TABSLocalSession = class (TABSBaseSession)
   private
    FDatabaseData:  TABSDatabaseData;
    FPassword:      TABSPassword;
    FPageSize:            Integer;
    FPageCountInExtent:   Integer;

    function GetPassword: PABSPassword;
    function IsAppropriateDatabaseData(DBData: TABSDatabaseData): Boolean;
    function FindDatabaseData: TABSDatabaseData;
    function CreateDatabaseData: TABSDatabaseData;
    function FindOrCreateDatabaseData: TABSDatabaseData;

   protected
    // db connected?
    function GetConnected: Boolean; override;
    // connect / disconnect
    procedure SetConnected(Value: boolean); override;
    function GetSuppressDBHeaderErrors: Boolean; override;
    procedure SetSuppressDBHeaderErrors(Value: boolean); override;

   public
    // create database
    procedure CreateDatabase; override;
    // Truncate for empty pages
    procedure TruncateDatabase;
    // return Count of connections to Database File or -1 if it's openned in Exclusive
    function GetDBFileConnectionsCount: Integer;
    // delete database
    procedure DeleteDatabase; override;
    // rename database
    procedure RenameDatabase(NewDatabaseFileName: string); override;
    // check if database exists
    procedure GetTablesList(List: TStrings); override;
    function TableExists(TableName: string): Boolean; override;

    procedure StartTransaction; override;
    procedure Commit(DoFlushBuffers: Boolean=True); override;
    procedure Rollback; override;
    procedure FlushBuffers; override;

    property DatabaseData: TABSDatabaseData read FDatabaseData;
    property Password: PABSPassword read GetPassword;
    property PageSize: Integer read FPageSize write FPageSize;
    property PageCountInExtent: Integer read FPageCountInExtent write FPageCountInExtent;
  end; // TABSLocalSession

var
  DBDataList: TThreadList;
  DoUniqueAndPrimaryKeysCheckOnUpdate: Boolean = True;

implementation


////////////////////////////////////////////////////////////////////////////////
//
// TABSLocalBLOBStream
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSLocalBLOBStream.InternalSetSize(const NewSize: Int64);
begin
 if (OpenMode = bomRead) then
  raise EABSException.Create(10116,ErrorLCannotWriteToReadOnlyStream);
 FTemporaryStream.Size := NewSize;
end; // InternalSetSize


//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSLocalBLOBStream.SetSize(NewSize: Longint);
begin
 InternalSetSize(NewSize);
end; // SetSize


{$IFDEF D6H}
//------------------------------------------------------------------------------
// sets new size of the stream
//------------------------------------------------------------------------------
procedure TABSLocalBLOBStream.SetSize(const NewSize: Int64);
begin
 InternalSetSize(NewSize);
end; // SetSize
{$ENDIF}


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
function TABSLocalBLOBStream.Read(var Buffer; Count: Longint): Longint;
begin
 Result := FTemporaryStream.Read(Buffer,Count);
end; // Read


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
function TABSLocalBLOBStream.Write(const Buffer; Count: Longint): Longint;
begin
 if (OpenMode = bomRead) then
  raise EABSException.Create(10115,ErrorLCannotWriteToReadOnlyStream);
 Result := FTemporaryStream.Write(Buffer,Count);
end; // Write


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
function TABSLocalBLOBStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
 Result := FTemporaryStream.Seek(Offset,Origin);
end; // Seek


{$IFDEF D6H}
//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
function TABSLocalBLOBStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
 Result := FTemporaryStream.Seek(Offset,Origin);
end; // Seek
{$ENDIF}


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
constructor TABSLocalBLOBStream.Create(
                        TemporaryStream: TABSStream;
                        Cursor: TABSCursor;
                        OpenMode: TABSBLOBOpenMode;
                        FieldNo: Integer
                                       );
begin
 UserBLOBStream := nil;
 FPosition := 0;
 FTemporaryStream := TemporaryStream;
 FCursor := Cursor;
 FFieldNo := FieldNo;
 FOpenMode := OpenMode;
 inherited Create;
end; // Create


//------------------------------------------------------------------------------
// set size of compressed stream
//------------------------------------------------------------------------------
destructor TABSLocalBLOBStream.Destroy;
begin
 TABSCompressedBLOBStream(FTemporaryStream).CompressedStream.Free;
 FTemporaryStream.Free;
 inherited;
end; // Destroy


////////////////////////////////////////////////////////////////////////////////
//
// TABSLocalCursor
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
constructor TABSLocalCursor.Create;
begin
 CurrentRecordBuffer := nil;
 FDatabaseData := nil;
 FTableData := nil;
 FFieldDefs := nil;
 FVisibleFieldDefs := nil;
 FIndexDefs := nil;
 IsOpen := False;
 FBLOBStreams := nil;
 FIndexName := '';
 FIndexID := INVALID_OBJECT_ID;
 FilterExpression := nil;
 SQLFilterExpression := nil;
 FilterRecord := nil;
 FSettingProjection := False;
 DistinctFieldCount := 0;
 RecordBitmap := nil;
 FIndexPositionCache := TABSIndexPositionCache.Create;
 FTableLockedByCursor := False;
 FBatchUpdate := False;
end; // Create


//------------------------------------------------------------------------------
// destroy
//------------------------------------------------------------------------------
destructor TABSLocalCursor.Destroy;
begin
 CloseTable;
 FIndexPositionCache.Free;
 inherited;
end; // Destroy


//------------------------------------------------------------------------------
// create table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.CreateTable
                         (
                          FieldDefs: TABSFieldDefs;
                          IndexDefs: TABSIndexDefs;
                          ConstraintDefs: TABSConstraintDefs
                          );
begin
  FDatabaseData := TABSLocalSession(Session).DatabaseData;
  FDatabaseData.Lock;
  CloseTable;
  try
   if (FDatabaseData.TableExists(FTableName)) then
     DeleteTable;
//    raise EABSException.Create(10297,ErrorLTableExists,[FTableName]);
   FTableData := FDatabaseData.FindOrCreateTableData(Self);
   if (FTableData = nil) then
     raise EABSException.Create(20151, ErrorANilPointer);
   try
     FTableData.CreateTable(Self,FieldDefs,IndexDefs,ConstraintDefs);
   finally
     CloseTable;
   end;
  finally
   FDatabaseData.Unlock;
  end;
end;// CreateTable


//------------------------------------------------------------------------------
// delete table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.DeleteTable;
var
  I: Integer;
begin
  FDatabaseData := TABSLocalSession(Session).DatabaseData;
  FDatabaseData.Lock;
  try
   CloseTable;
   if (not FDatabaseData.TableExists(FTableName)) then
     raise EABSException.Create(20132, ErrorATableNotFound, [TableName]);
   FTableData := FDatabaseData.FindOrCreateTableData(Self);
     if (FTableData = nil) then
      raise EABSException.Create(10027,ErrorLTableDataNotFound,[TableName]);
   try
     // 5.11 delete TABSSessionNamedObjectSequenceValue objects from SessionNamedObjectList if any
     if Assigned(FTableData.FieldManager) then
     begin
       for I := 0 to  FTableData.FieldManager.FieldDefs.Count - 1 do
         Session.DeleteObject(FTableData.FieldManager.FieldDefs[I].ObjectID);
     end;
     if (not (FTableData is TABSDiskTableData)) then
       begin
         FTableData.DeleteTable(Self);
         FTableData := nil;
       end
     else
       FTableData.DeleteTable(Self);
   finally
     CloseTable;
   end;
  finally
   FDatabaseData.Unlock;
  end;
end; // DeleteTable;


//------------------------------------------------------------------------------
// empty table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.EmptyTable;
begin
  FDatabaseData := TABSLocalSession(Session).DatabaseData;
  FDatabaseData.Lock;
  try
   CloseTable;
   if (not FDatabaseData.TableExists(FTableName)) then
     raise EABSException.Create(20133, ErrorATableNotFound, [TableName]);
   FTableData := FDatabaseData.FindOrCreateTableData(Self);
     if (FTableData = nil) then
      raise EABSException.Create(10073,ErrorLTableDataNotFound,[TableName]);
   try
     FTableData.EmptyTable(Self);
   finally
     CloseTable;
   end;
  finally
   FDatabaseData.Unlock;
  end;
end; // EmptyTable


//------------------------------------------------------------------------------
// rename table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.RenameTable(NewTableName: string);
begin
  FDatabaseData := TABSLocalSession(Session).DatabaseData;
  FDatabaseData.Lock;
  try
   CloseTable;
   if (not FDatabaseData.TableExists(FTableName)) then
     raise EABSException.Create(20134, ErrorATableNotFound, [TableName]);
   if (UpperCase(String(NewTableName)) <> UpperCase(string(TableName))) then
     if (FDatabaseData.TableExists(NewTableName)) then
      raise EABSException.Create(30451,ErrorLTableExists,[NewTableName]);
   if (NewTableName = TableName) then
    raise EABSException.Create(30452,ErrorGCannotRenameTableIntoItSelf,[NewTableName]);
   FTableData := FDatabaseData.FindOrCreateTableData(Self);
   if (FTableData = nil) then
    raise EABSException.Create(10151,ErrorLTableDataNotFound,[TableName]);
   try
     FTableData.RenameTable(NewTableName, Self);
   finally
     CloseTable;
   end;
   FTableName := NewTableName;
  finally
   FDatabaseData.Unlock;
  end;
end; // RenameTable


//------------------------------------------------------------------------------
// open table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalInitFieldDefs;
var
  i: Integer;
begin
 if (FFieldDefs <> nil) then
  FFieldDefs.Free;
 FFieldDefs := TABSFieldDefs.Create;
 if (not FSettingProjection) then
  begin
   if (FVisibleFieldDefs <> nil) then
    FVisibleFieldDefs.Free;
   FVisibleFieldDefs := TABSFieldDefs.Create;
  end;

{$IFDEF DEBUG_TRACE_DATASET}
aaWriteToLog('TABSLocalCursor.InternalInitFieldDefs, TableData.FieldDefs.Count = '+
IntToStr(FTableData.FieldManager.FieldDefs.Count));
{$ENDIF}


 FFieldDefs.Assign(FTableData.FieldManager.FieldDefs);
 FFieldDefs.RecalcFieldOffsets;

 if (FFieldDefs.Count > 0) then
   FieldValuesOffset := FFieldDefs[0].MemoryOffset
 else
   FieldValuesOffset := 0;
 if (FFieldDefs.Count > 0) then
   BookmarkOffset := FFieldDefs[FFieldDefs.Count-1].MemoryOffset +
                     FFieldDefs[FFieldDefs.Count-1].MemoryDataSize
 else
   BookmarkOffset := 0;
 KeyOffset := BookmarkOffset;
 KeyBufferSize := BookmarkOffset + sizeof(TABSKeyBuffer);
 RecordSize := BookmarkOffset + SizeOf(TABSBookmarkInfo);
 RecordBufferSize := RecordSize;
 CalculatedFieldsOffset := RecordSize;

 if (not FSettingProjection) then
  begin
 FVisibleFieldDefs.Assign(FTableData.FieldManager.FieldDefs);
 // create default fields order
 for i := 0 to VisibleFieldDefs.Count-1 do
  FVisibleFieldDefs[i].FieldNoReference := i;
  end;

 // Constraints...
 if (FConstraintDefs <> nil) then
   FConstraintDefs.Free;
 FConstraintDefs := TABSConstraintDefs.Create;
 FConstraintDefs.Assign(FTableData.ConstraintManager.ConstraintDefs);
end;


//------------------------------------------------------------------------------
// open table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.OpenTableByFieldDefs(
                                    FieldDefs: TABSFieldDefs;
                                    IndexDefs: TABSIndexDefs;
                                    ConstraintDefs: TABSConstraintDefs
                                   );
begin
 if (not IsOpen) then
  begin
    FDatabaseData := TABSLocalSession(Session).DatabaseData;
    FDatabaseData.Lock;
    try
     FTableData := FDatabaseData.FindTableData(Self);
     if (FTableData = nil) then
      if (FDatabaseData is TABSDiskDatabaseData) then
        FTableData := FDatabaseData.CreateTableData(Self)
      else
      if (IsDesignMode) then
       begin
        FTableData := FDatabaseData.CreateTableData(Self);
        FTableData.CreateTable(Self,FieldDefs,IndexDefs, ConstraintDefs);
       end
      else
       raise EABSException.Create(10028,ErrorLTableDataNotFound,[TableName]);
     FTableData.IsRepairing := FIsRepairing;
     FTableData.OpenTable(Self);
     FirstPosition := True;
     LastPosition := False;

     if (FIndexDefs <> nil) then
      FIndexDefs.Free;
     FIndexDefs := TABSIndexDefs.Create;
     FIndexDefs.Assign(FTableData.IndexManager.IndexDefs);
     if (FBLOBStreams <> nil) then
      FBLOBStreams.Free;
     FBLOBStreams := TList.Create;
     if (RecordBitmap <> nil) then
      TABSRecordBitmap(RecordBitmap).Free;
     RecordBitmap := TABSRecordBitmap.Create(FTableData);
     IsOpen := True;
    finally
     FDatabaseData.Unlock;
    end;
  end;
end;// OpenTable


//------------------------------------------------------------------------------
// open table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.OpenTable(aTableData: TABSTableData);
begin
{$IFDEF DEBUG_TRACE_DATASET}
  aaWriteToLog('TABSLocalCursor.OpenTable(aTableData) start');
{$ENDIF}
  FDatabaseData := TABSLocalSession(Session).DatabaseData;
  FDatabaseData.Lock;
  try
   FTableData := aTableData;
   FTableData.OpenTable(Self);
   FirstPosition := True;
   LastPosition := False;
   if (FIndexDefs <> nil) then
    FIndexDefs.Free;
   FIndexDefs := TABSIndexDefs.Create;
   FIndexDefs.Assign(FTableData.IndexManager.IndexDefs);
   if (FBLOBStreams <> nil) then
    FBLOBStreams.Free;
   FBLOBStreams := TList.Create;
   InternalInitFieldDefs;
   if (RecordBitmap <> nil) then
    TABSRecordBitmap(RecordBitmap).Free;
   RecordBitmap := TABSRecordBitmap.Create(FTableData);
   IsOpen := True;
  finally
    FDatabaseData.Unlock;
  end;
{$IFDEF DEBUG_TRACE_DATASET}
 aaWriteToLog('TABSLocalCursor.OpenTable(aTableData) finish');
{$ENDIF}
end; // OpenTable


//------------------------------------------------------------------------------
// close table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.CloseTable;
begin
{$IFDEF DEBUG_TRACE_DATASET}
 aaWriteToLog('TABSLocalCursor.CloseTable start');
{$ENDIF}
  FDatabaseData := TABSLocalSession(Session).DatabaseData;
  FDatabaseData.Lock;
  try
     if (FTableData <> nil) then
      begin
       FTableData.CloseTable(Self);
       if (IsDesignMode) and (FInMemory or FTemporary)  then
         FTableData.DeleteTable(Self, True)
       else
         begin
           if (FTableData is TABSDiskTableData) then
             TABSDiskTableData(FTableData).FreeIfNoCursorsConnected;
         end;
       FTableData := nil;
      end;
     if (RecordBitmap <> nil) then
      TABSRecordBitmap(RecordBitmap).Free;
     RecordBitmap := nil;
     if (FilterExpression <> nil) then
      TABSExpression(FilterExpression).Free;
     FilterExpression := nil;
     if (FFieldDefs <> nil) then
       FFieldDefs.Free;
     FFieldDefs := nil;
     if (not FSettingProjection) then
      begin
     if (FVisibleFieldDefs <> nil) then
       FVisibleFieldDefs.Free;
     FVisibleFieldDefs := nil;
      end;
     if (FIndexDefs <> nil) then
       FIndexDefs.Free;
     FIndexDefs := nil;
     ClearBLOBStreams(False);
     if (FBLOBStreams <> nil) then
       FBLOBStreams.Free;
     FBLOBStreams := nil;
     // Constraints
     if (FConstraintDefs <> nil) then
       FConstraintDefs.Free;
     FConstraintDefs := nil;
  finally
    FDatabaseData.Unlock;
  end;
{$IFDEF DEBUG_TRACE_DATASET}
 aaWriteToLog('TABSLocalCursor.CloseTable finish');
{$ENDIF}
end; // CloseTable


//------------------------------------------------------------------------------
// lock table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.LockTableData;
begin
  if (FTableData = nil) then
    raise EABSException.Create(10260,ErrorLNilPointer);
  FTableData.Lock;
end; // LockTableData


//------------------------------------------------------------------------------
// unlock table
//------------------------------------------------------------------------------
procedure TABSLocalCursor.UnlockTableData;
begin
  if (FTableData = nil) then
    raise EABSException.Create(10261,ErrorLNilPointer);
  FTableData.Unlock;
end; // UnlockTableData


//------------------------------------------------------------------------------
// LockTable
//------------------------------------------------------------------------------
function TABSLocalCursor.LockTable(LockType: TABSLockType;
                                   TryCount, Delay: Integer;
                                   AllowXIRWAfterSIRW: Boolean = True): Boolean;
begin
  if (FTableData = nil) then
    raise EABSException.Create(20221, ErrorANilPointer);
  if (FTableData is TABSDiskTableData) then
    Result := TABSDiskTableData(FTableData).LockTable(Session.SessionID, LockType, TryCount, Delay, AllowXIRWAfterSIRW)
  else
    Result := True;
end;// LockTable


//------------------------------------------------------------------------------
// UnlockTable
//------------------------------------------------------------------------------
function TABSLocalCursor.UnlockTable(LockType: TABSLockType; IgnoreIfNoLock: Boolean=False): Boolean;
begin
  if (FTableData = nil) then
    raise EABSException.Create(20222, ErrorANilPointer);
  if (FTableData is TABSDiskTableData) then
    Result := TABSDiskTableData(FTableData).UnlockTable(Session.SessionID, LockType, IgnoreIfNoLock)
  else
    Result := True;
end;// UnlockTable


//------------------------------------------------------------------------------
// initialize record buffer
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalInitRecord(RecordBuffer: TABSRecordBuffer; InsertMode: Boolean);
var x,y: Integer;
begin
 // set null flags for all fields
 y := FFieldDefs.Count;
 x := y div 8;
 if (y mod 8 > 0) then
  Inc(x);
 FillChar(RecordBuffer^,x,$FF);
 FillChar((RecordBuffer+x)^,FFieldDefs.GetMemoryRecordBufferSize-x,$00);
 // set Default Values...
 if ((not FTemporary) and (InsertMode)) then
   FTableData.FieldManager.ApplyDefaultValuesToRecordBuffer(Session, RecordBuffer);
end; // InternalInitRecord


//------------------------------------------------------------------------------
// Rename Field by Field Index in FieldDefs
//------------------------------------------------------------------------------
procedure TABSLocalCursor.RenameField(FieldName, NewFieldName: string);
begin
  FTableData.RenameField(FieldName, NewFieldName);
end;//RenameField


//------------------------------------------------------------------------------
// CopyFieldValue
//------------------------------------------------------------------------------
procedure TABSLocalCursor.CopyFieldValue(SrcFieldNo: Integer;
                             DirectAccess: Boolean;
                             DestFieldNo: Integer;
                             DestCursor: TABSCursor);
var
  SrcBuffer, DestBuffer: TAbsPByte;
  SrcOffset, DestOffset: Integer;
  SrcDataSize, DestDataSize: Integer;
  SrcFieldType, DestFieldType: TABSBaseFieldType;
  IsNull: Boolean;
  ActualFieldDefs: TABSFieldDefs;
  Value: TABSVariant;
  BlobDescriptor: TABSPartialTemporaryBLOBDescriptor;
  pBlobData: TAbsPByte;
begin
  if (DirectAccess) then
    ActualFieldDefs := FieldDefs
  else
    ActualFieldDefs := VisibleFieldDefs;
  if (SrcFieldNo >= ActualFieldDefs.Count) then
     raise EABSException.Create(10501, ErrorLInvalidFieldNumber,
            [SrcFieldNo,ActualFieldDefs.Count]);
  if (DestFieldNo >= DestCursor.VisibleFieldDefs.Count) then
     raise EABSException.Create(10502, ErrorLInvalidFieldNumber,
            [DestFieldNo,DestCursor.VisibleFieldDefs.Count]);

  // 5.01 bug-fix
  if (ActualFieldDefs = VisibleFieldDefs) then
    IsNull := CheckNullFlag(ActualFieldDefs[SrcFieldNo].FieldNoReference, CurrentRecordBuffer)
  else
    IsNull := CheckNullFlag(SrcFieldNo, CurrentRecordBuffer);

  if (not IsNull) then
    begin
     SrcOffset := ActualFieldDefs[SrcFieldNo].MemoryOffset;
     SrcBuffer := TAbsPByte(CurrentRecordBuffer + SrcOffset);
     SrcDataSize := ActualFieldDefs[SrcFieldNo].MemoryDataSize;
     SrcFieldType := ActualFieldDefs[SrcFieldNo].BaseFieldType;

     DestOffset := DestCursor.VisibleFieldDefs[DestFieldNo].MemoryOffset;
     DestBuffer := TAbsPByte(DestCursor.CurrentRecordBuffer + DestOffset);
     DestDataSize := DestCursor.VisibleFieldDefs[DestFieldNo].MemoryDataSize;
     DestFieldType := DestCursor.VisibleFieldDefs[DestFieldNo].BaseFieldType;

     if (SrcFieldType = DestFieldType) and (not IsBLOBFieldType(SrcFieldType)) and
        (SrcDataSize = DestDataSize) then
       begin
         SetNullFlag(False,DestCursor.VisibleFieldDefs[DestFieldNo].FieldNoReference,DestCursor.CurrentRecordBuffer);
         Move(SrcBuffer^, DestBuffer^, SrcDataSize);
       end
     else
       if (not IsBLOBFieldType(SrcFieldType)) then
         begin
           Value := TABSVariant.Create;
           try
             GetFieldValue(Value, SrcFieldNo, DirectAccess);
             DestCursor.SetFieldValue(Value, DestFieldNo, False);
           finally
             Value.Free;
           end;
         end
       else
         begin
           // 5.12 fix blobs union bug
           if not DirectAccess then
             SrcFieldNo := ActualFieldDefs[SrcFieldNo].FieldNoReference;
           TableData.GetDirectBlobData(Self,SrcFieldNo,CurrentRecordBuffer,
                                       BlobDescriptor, pBlobData);
           TABSLocalCursor(DestCursor).TableData.SetDirectBlobData(DestCursor,DestFieldNo,DestCursor.CurrentRecordBuffer,
                                       BlobDescriptor, pBlobData);
           TableData.FreeDirectBlobData(Self,SrcFieldNo,CurrentRecordBuffer,
                                       BlobDescriptor, pBlobData);
         end;
    end
  else
    SetNullFlag(True,DestCursor.VisibleFieldDefs[DestFieldNo].FieldNoReference,DestCursor.CurrentRecordBuffer);
end;// CopyFieldValue


//------------------------------------------------------------------------------
// GetIndexDefs
//------------------------------------------------------------------------------
function TABSLocalCursor.GetIndexDefs: TABSIndexDefs;
begin
  if (FIndexDefs <> nil) then
   FIndexDefs.Assign(FTableData.IndexManager.IndexDefs);
  Result := inherited GetIndexDefs;
end;// GetIndexDefs


//------------------------------------------------------------------------------
// add index
//------------------------------------------------------------------------------
procedure TABSLocalCursor.AddIndex(IndexDef: TABSIndexDef);
begin
  if (FIndexDefs.GetIndexDefByName(string(IndexDef.Name)) <> nil) then
   raise EABSException.Create(20022, ErrorAIndexAlreadyExists, [IndexDef.Name]);
  FTableData.AddIndex(IndexDef, Self);
  FIndexDefs.Assign(FTableData.IndexManager.IndexDefs);
  //if (not FTemporary) then
   FConstraintDefs.Assign(FTableData.ConstraintManager.ConstraintDefs);
end;// AddIndex


//------------------------------------------------------------------------------
// DeleteIndex
//------------------------------------------------------------------------------
procedure TABSLocalCursor.DeleteIndex(Name: string);
begin
  if (FIndexDefs.GetIndexDefByName(Name) = nil) then
   raise EABSException.Create(20021, ErrorACannotDeleteIndex, [Name]);
  // Delete Index
  FTableData.DeleteIndex(FIndexDefs.GetIndexDefByName(Name).ObjectID, Self);
  FIndexDefs.Assign(FTableData.IndexManager.IndexDefs);
  //if (not FTemporary) then
   FConstraintDefs.Assign(FTableData.ConstraintManager.ConstraintDefs);
end;// DeleteIndex


//------------------------------------------------------------------------------
// DeleteAllIndexes
//------------------------------------------------------------------------------
procedure TABSLocalCursor.DeleteAllIndexes;
begin
  FTableData.DeleteAllIndexes(Self);
  FIndexDefs.Assign(FTableData.IndexManager.IndexDefs);
  //if (not FTemporary) then
   FConstraintDefs.Assign(FTableData.ConstraintManager.ConstraintDefs);
end;// DeleteAllIndexes


//------------------------------------------------------------------------------
// return index name
//------------------------------------------------------------------------------
function TABSLocalCursor.FindOrCreateIndex(FieldNamesList, AscDescList, CaseSensitivityList: TStringList; var IsCreated: Boolean): string;
var
  i: Integer;
begin
  // replace pseudonames
  for i:=0 to FieldNamesList.Count-1 do
    if (FFieldDefs.GetFieldDefByName(string(FieldNamesList[i])) = nil) and
         (FVisibleFieldDefs.GetFieldDefByName(string(FieldNamesList[i])) <> nil) then
      FieldNamesList[i] := string(FFieldDefs.Items[FVisibleFieldDefs.GetFieldDefByName(string(FieldNamesList[i])).FieldNoReference].Name);
  Result := FTableData.FindOrCreateIndex(Self, FieldNamesList, AscDescList, CaseSensitivityList, IsCreated);
  FIndexDefs.Assign(FTableData.IndexManager.IndexDefs);
end; // FindOrCreateIndex


//------------------------------------------------------------------------------
// IndexExists
//------------------------------------------------------------------------------
function TABSLocalCursor.IndexExists(FieldNamesList, AscDescList, CaseSensitivityList: TStringList): Boolean;
var
  i: Integer;
begin
  // replace pseudonames
  for i:=0 to FieldNamesList.Count-1 do
    if (FFieldDefs.GetFieldDefByName(string(FieldNamesList[i])) = nil) and
         (FVisibleFieldDefs.GetFieldDefByName(string(FieldNamesList[i])) <> nil) then
      FieldNamesList[i] := string(FFieldDefs.Items[FVisibleFieldDefs.GetFieldDefByName(string(FieldNamesList[i])).FieldNoReference].Name);
  Result := FTableData.IndexExists(Self, FieldNamesList, AscDescList, CaseSensitivityList);
end;// IndexExists


//------------------------------------------------------------------------------
// FreeRecordBuffer
//------------------------------------------------------------------------------
procedure TABSLocalCursor.FreeRecordBuffer(var Buffer: TABSRecordBuffer);
begin
  if (FTableData <> nil) then
   FTableData.ClearBlobsCacheInRecordBuffer(Buffer);
  MemoryManager.FreeAndNillMem(Buffer);
end;// FreeRecordBuffer


//------------------------------------------------------------------------------
// get record
//------------------------------------------------------------------------------
function TABSLocalCursor.GetRecordBuffer(
              GetRecordMode:  TABSGetRecordMode
              ): TABSGetRecordResult;
begin
  FTableData.Lock;
  try
    Result := FTableData.GetRecordBuffer(Self,GetRecordMode);
  finally
    FTableData.Unlock;
  end;
end; // GetRecordBuffer


//------------------------------------------------------------------------------
// returns record count
//------------------------------------------------------------------------------
function TABSLocalCursor.GetRecordCount: TABSRecordNo;
begin
  Result := FTableData.GetRecordCount(Self);
end; // GetRecordCount


//------------------------------------------------------------------------------
// go to record
//------------------------------------------------------------------------------
procedure TABSLocalCursor.SetRecNo(Value: Int64);
begin
  FTableData.SetRecNo(Self,Value);
end; // SetRecNo


//------------------------------------------------------------------------------
// return current record number
//------------------------------------------------------------------------------
function TABSLocalCursor.GetRecNo: Int64;
begin
  Result := FTableData.GetRecNo(Self);
end; // GetRecNo


//------------------------------------------------------------------------------
// -1,0,1 - compare records order with or without active index
//------------------------------------------------------------------------------
function TABSLocalCursor.CompareRecordsOrder(RecordID1, RecordID2: TABSRecordID): integer;
var 
    OldPos:               Pointer;
    OldBuffer:            TAbsPByte;
    TempBuffer1, TempBuffer2: TAbsPByte;
begin
  if (IndexID <> INVALID_OBJECT_ID) then
    begin
      OldPos := SavePosition;
      OldBuffer := CurrentRecordBuffer;
      TempBuffer1 := AllocateRecordBuffer;
      TempBuffer2 := AllocateRecordBuffer;
      try
        CurrentRecordBuffer := TempBuffer1;
        FirstPosition := False;
        LastPosition := False;
        CurrentRecordID := RecordID1;
        if (GetRecordBuffer(grmCurrent) <> grrOK) then
         Result := -1
        else
          begin
            CurrentRecordID := RecordID2;
            FirstPosition := False;
            LastPosition := False;
            CurrentRecordBuffer := TempBuffer2;
            if (GetRecordBuffer(grmCurrent) <> grrOK) then
             Result := 1
            else
             Result := FTableData.IndexManager.OpenIndex(IndexID).CompareRecordBuffersByIndex(TempBuffer1, TempBuffer2,
                    FTableData.IndexManager.OpenIndex(IndexID).IndexDef.ColumnCount);
          end;
      finally
        RestorePosition(OldPos);
        FreePosition(OldPos);
        FreeRecordBuffer(TempBuffer1);
        FreeRecordBuffer(TempBuffer2);
        CurrentRecordBuffer := OldBuffer;
      end;
    end;

    if (IndexID = INVALID_OBJECT_ID) or (Result = 0) then
      begin
        if (RecordID1.PageNo < RecordID2.PageNo) then
          Result := -1
        else
        if (RecordID1.PageNo > RecordID2.PageNo) then
          Result := 1
        else
          if (RecordID1.PageItemNo < RecordID2.PageItemNo) then
            Result := -1
          else
          if (RecordID1.PageItemNo > RecordID2.PageItemNo) then
            Result := 1
          else
            Result := 0;
      end;

end;// CompareRecordsOrder

//------------------------------------------------------------------------------
// go to first record (before first record to BOF)
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalFirst;
begin
 FirstPosition := True;
 LastPosition := False;
end; // InternalFirst


//------------------------------------------------------------------------------
// go to last record (after last record to EOF)
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalLast;
begin
 FirstPosition := False;
 LastPosition := True;
end; // InternalLast


//------------------------------------------------------------------------------
// insert record
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalInsert;
begin
end; // InternalInsert


//------------------------------------------------------------------------------
// edit record
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalEdit;
{$IFDEF FILE_SERVER_VERSION}
var
    TempBuffer: TABSRecordBuffer;
    LockType: TABSLockType;

procedure CheckOtherUsersModifications;
begin
  if (Session.InTransaction) then
    LockType := ltXIRW
  else
    LockType := ltSIRW;
  if (TABSDiskTableData(FTableData).LockTable(Session.SessionID, LockType,
                           EditRecordTableLockRetries, EditRecordTableLockDelay)) then
    begin
      if (TABSDiskTableData(FTableData).LockRecord(Session.SessionID,
           CurrentRecordID, RecordLockRetries, RecordLockDelay)) then
        begin
          try
            TempBuffer := AllocateRecordBuffer;
            try
              Move(CurrentRecordBuffer^,TempBuffer^,RecordBufferSize);
              if (GetRecordBuffer(grmCurrent) = grrOk) then
                begin
                 if (not FTableData.FieldManager.CompareRecordBuffers(
                          TempBuffer,CurrentRecordBuffer)) then
                  begin
                   ErrorCode := ABS_ERR_UPDATE_RECORD_MODIFIED;
                   // restore previous record buffer with new value
                   Move(TempBuffer^,CurrentRecordBuffer^,RecordBufferSize);
                 end
                end
              else
                begin
                  ErrorCode := ABS_ERR_UPDATE_RECORD_DELETED;
                  // restore previous record buffer with new value
                  Move(TempBuffer^,CurrentRecordBuffer^,RecordBufferSize);
                end;
            finally
              FreeRecordBuffer(TempBuffer);
            end;
          finally
            if (ErrorCode <> ABS_ERR_OK) then
              begin
                TABSDiskTableData(FTableData).UnlockRecord(Session.SessionID, CurrentRecordID);
                TABSDiskTableData(FTableData).UnlockTable(Session.SessionID, LockType);
              end;
          end;
        end
      else
        begin
          TABSDiskTableData(FTableData).UnlockTable(Session.SessionID, LockType);
          ErrorCode := ABS_ERR_RECORD_LOCKED;
        end;
    end
  else
    ErrorCode := ABS_ERR_TABLE_LOCKED;
end;

{$ENDIF}
begin
 LockTableData;
 try
  ErrorCode := ABS_ERR_OK;
  if (FTableData is TABSTemporaryTableData) then
    ErrorCode := ABS_ERR_UPDATE_RECORD_PROHIBITED
  else
    if (FTableData is TABSDiskTableData) then
      begin
{$IFDEF FILE_SERVER_VERSION}
        if (FSession.MultiUser) then
          CheckOtherUsersModifications
{$ENDIF}
      end;
 finally
   UnlockTableData;
 end;
end; // InternalEdit


//------------------------------------------------------------------------------
// cancels updates
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalCancel(ToInsert: Boolean);
var Buffer: TABSRecordBuffer;
begin
 LockTableData;
 try
  ErrorCode := ABS_ERR_OK;
//  if (FTableData is TABSTemporaryTableData) then
//    ErrorCode := ABS_ERR_CANCEL_PROHIBITED
//  else
    begin
     ClearBLOBStreams(True);
     Buffer := CurrentRecordBuffer;
     if (FTableData is TABSDiskTableData) then
        TABSDiskTableData(FTableData).ClearModifiedBLOBFieldsInRecordBuffer(Buffer);
     if (ToInsert) then
       FTableData.ClearBLOBFieldsInRecordBuffer(Buffer);
{$IFDEF FILE_SERVER_VERSION}
     if (FSession.MultiUser) then
       if (not ToInsert) then
         if (FTableData is TABSDiskTableData) then
           begin
             TABSDiskTableData(FTableData).UnlockRecord(Session.SessionID, CurrentRecordID);
             if (not Session.InTransaction) then
               TABSDiskTableData(FTableData).UnlockTable(Session.SessionID, ltSIRW, True);
           end;
{$ENDIF}
    end;
 finally
   UnlockTableData;
 end;
end; // InternalCancel


//------------------------------------------------------------------------------
// update record
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalPost(ToInsert: Boolean);
var
    TempCursor: TABSCursor;
    Bookmark:   PABSBookmarkInfo;
    TempBuffer: TABSRecordBuffer;
{$IFDEF FILE_SERVER_VERSION}
    LockSucceeded: Boolean;
    LockType: TABSLockType;
    i: Integer;
{$ENDIF}

procedure DoInternalPost;
begin
 // Check Constraints
 try
   if (ToInsert) then
     begin
{$IFDEF FILE_SERVER_VERSION}
       if (FTableData is TABSDiskTableData) then
         TABSDiskTableData(FTableData).LoadMostUpdated(Session.SessionID);
{$ENDIF}
       FTableData.FieldManager.ApplyAutoincsToRecordBuffer(Session, CurrentRecordBuffer);
     end;
   if (not (FTableData is TABSTemporaryTableData)) then
     FTableData.ConstraintManager.CheckConstraints(
            Session.SessionID, CurrentRecordBuffer, EditRecordBuffer,
            // 6.07 to avoid update set primarykey=primarykey+1 AV
            ToInsert, CurrentRecordID, True, (ToInsert) or (not InBatchUpdate)
                                      or DoUniqueAndPrimaryKeysCheckOnUpdate);
 except
   on e: Exception do
     begin
       ErrorCode := ABS_ERR_CONSTRAINT_VIOLATED;
       ErrorMessage := e.Message;
       exit;
     end;
 end;
 TempCursor := Self;
 ClearBLOBStreams(True);
 if (ToInsert) then
  begin
   // insert
   if (FTableData.InsertRecord(TempCursor)) then
    ErrorCode := ABS_ERR_OK
   else
    ErrorCode := ABS_ERR_INSERT_RECORD;
   // write bookmark info to new record buffer
   Bookmark := PABSBookmarkInfo(CurrentRecordBuffer + BookmarkOffset);
   Bookmark^.BookmarkData := CurrentRecordID;
   // ???
   Bookmark^.BookmarkFlag := abfInserted;
  end // insert
 else
  begin
   // update
   Move(TAbsPByte(FCurrentRecordBuffer + BookmarkOffset)^,
    CurrentRecordID,sizeof(TABSRecordID));
   TempBuffer := AllocateRecordBuffer;
   try
   Move(CurrentRecordBuffer^,TempBuffer^,RecordBufferSize);
   if (GetRecordBuffer(grmCurrent) = grrOk) then
    begin
     if (not FTableData.FieldManager.CompareRecordBuffers(
              EditRecordBuffer,CurrentRecordBuffer)) then
      begin
       ErrorCode := ABS_ERR_UPDATE_RECORD_MODIFIED;
       // restore previous record buffer with new value
       Move(TempBuffer^,CurrentRecordBuffer^,RecordBufferSize);
      end
     else
      begin
       // update record
       // restore previous record buffer with new value
       Move(TempBuffer^,CurrentRecordBuffer^,RecordBufferSize);
       if (FTableData.UpdateRecord(TempCursor)) then
        ErrorCode := ABS_ERR_OK
       else
        ErrorCode := ABS_ERR_UPDATE_RECORD;
      end;
    end // get record ok
   else
    begin
     // get record failed
     ErrorCode := ABS_ERR_UPDATE_RECORD_DELETED;
     // restore previous record buffer with new value
     Move(TempBuffer^,CurrentRecordBuffer^,RecordBufferSize);
    end;
   finally
    FreeRecordBuffer(TempBuffer);
   end; // try
  end; // update;
end;

begin
 LockTableData;
 try
{$IFDEF FILE_SERVER_VERSION}
   if (FSession.MultiUser) then
     begin
       if (Session.InTransaction) then
         LockType := ltXIRW
       else
         LockType := ltRW;
       if (FTableData is TABSDiskTableData) then
         for i:=1 to PostRecordTableLockRetries do
           begin
             LockSucceeded := TABSDiskTableData(FTableData).LockTable(Session.SessionID,
                                         LockType, 1, 0);
             if (LockSucceeded) then break;
             UnlockTableData;
             Sleep(PostRecordTableLockDelay);
             LockTableData;
           end
       else
         LockSucceeded := True;
       if (LockSucceeded) then
         begin
             try
               DoInternalPost;
             finally
               if (not ToInsert) then
                 if ((FTableData is TABSDiskTableData) and
                     (ErrorCode <> ABS_ERR_CONSTRAINT_VIOLATED)) then
                   begin
                     TABSDiskTableData(FTableData).UnlockRecord(Session.SessionID, CurrentRecordID);
                     TABSDiskTableData(FTableData).UnlockTable(Session.SessionID, ltSIRW, Session.InTransaction);
                   end;
               if (FTableData is TABSDiskTableData) then
                 if (not Session.InTransaction) then
                   TABSDiskTableData(FTableData).UnlockTable(Session.SessionID, ltRW);
             end;
         end;
       if (not LockSucceeded) then
         ErrorCode := ABS_ERR_TABLE_LOCKED;
     end
   else
{$ENDIF}
     DoInternalPost;
 finally
   UnlockTableData;
 end;
end; // InternalPost


//------------------------------------------------------------------------------
// delete record
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalDelete;
var
  TempBuffer:     TABSRecordBuffer;
{$IFDEF FILE_SERVER_VERSION}
  TableLockSucceeded: Boolean;
  RecordLockSucceeded: Boolean;
  LockType: TABSLockType;
  i: Integer;
{$ENDIF}

procedure DoInternalDelete;
begin
 if (FTableData is TABSTemporaryTableData) then
  ErrorCode := ABS_ERR_DELETE_RECORD_PROHIBITED
 else
  begin
   // delete
   Move(TAbsPByte(FCurrentRecordBuffer + BookmarkOffset)^,
    CurrentRecordID,sizeof(TABSRecordID));
   // record exist
   TempBuffer := AllocateRecordBuffer;
   try
     Move(CurrentRecordBuffer^,TempBuffer^,RecordBufferSize);
     if (GetRecordBuffer(grmCurrent) = grrOk) then
      begin
       if (not CompareRecordBuffers(
                TempBuffer,CurrentRecordBuffer,RecordSize)) then
        begin
         ErrorCode := ABS_ERR_DELETE_RECORD_MODIFIED;
         // restore previous record buffer with new value
         Move(TempBuffer^,CurrentRecordBuffer^,RecordBufferSize);
        end
       else
        begin
         // update record
         // restore previous record buffer with new value
         Move(TempBuffer^,CurrentRecordBuffer^,RecordBufferSize);
         if (FTableData.DeleteRecord(Self)) then
          ErrorCode := ABS_ERR_OK
         else
          ErrorCode := ABS_ERR_DELETE_RECORD;
        end;
      end // get record ok
     else
      begin
       // get record failed
       ErrorCode := ABS_ERR_DELETE_RECORD_DELETED;
       // restore previous record buffer with new value
       Move(TempBuffer^,CurrentRecordBuffer^,RecordBufferSize);
      end;
   finally
     FreeRecordBuffer(TempBuffer);
   end; // try
  end;
end;

begin
 LockTableData;
 try
{$IFDEF FILE_SERVER_VERSION}
   if (FSession.MultiUser) then
     begin
       if (Session.InTransaction) then
         LockType := ltXIRW
       else
         LockType := ltRW;
       if (FTableData is TABSDiskTableData) then
         begin
           for i:=1 to DeleteRecordTableLockRetries do
             begin
               TableLockSucceeded := TABSDiskTableData(FTableData).LockTable(
                              Session.SessionID, LockType, 1, 0);
               if (TableLockSucceeded) then break;
               UnlockTableData;
               Sleep(DeleteRecordTableLockDelay);
               LockTableData;
             end;
           if (TableLockSucceeded) then
             RecordLockSucceeded := TABSDiskTableData(FTableData).LockRecord(
             Session.SessionID, CurrentRecordID, RecordLockRetries, RecordLockDelay)
           else
             RecordLockSucceeded := False;
         end
       else
         begin
           TableLockSucceeded := True;
           RecordLockSucceeded := True;
         end;
       if ((TableLockSucceeded) and (RecordLockSucceeded)) then
         try
           DoInternalDelete;
         finally
           if (FTableData is TABSDiskTableData) then
             begin
               TABSDiskTableData(FTableData).UnlockRecord(Session.SessionID, CurrentRecordID);
               if (not Session.InTransaction) then
                 TABSDiskTableData(FTableData).UnlockTable(Session.SessionID, ltRW);
             end;
         end;
       if (not TableLockSucceeded) then
         ErrorCode := ABS_ERR_TABLE_LOCKED
       else
         if (not RecordLockSucceeded) then
           begin
             ErrorCode := ABS_ERR_RECORD_LOCKED;
             TABSDiskTableData(FTableData).UnlockTable(Session.SessionID, LockType);
           end;
     end
   else
{$ENDIF}
     DoInternalDelete;
 finally
   UnlockTableData;
 end;
end; // InternalDelete

//------------------------------------------------------------------------------
// check if record is locked by another user
//------------------------------------------------------------------------------
function TABSLocalCursor.IsRecordLocked: Boolean;
begin
 LockTableData;
 try
   Move(TAbsPByte(FCurrentRecordBuffer + BookmarkOffset)^,
    CurrentRecordID,sizeof(TABSRecordID));
   if (FTableData is TABSDiskTableData) then
     Result := TABSDiskTableData(FTableData).IsRecordLocked(Session.SessionID, CurrentRecordID)
   else
     Result := False;
 finally
   UnlockTableData;
 end;
end;// IsRecordLocked


//------------------------------------------------------------------------------
// activate filters
//------------------------------------------------------------------------------
procedure TABSLocalCursor.ApplyDistinct(FieldNamesList, AscDescList, CaseSensitivityList: TStringList);
var IsCreated: Boolean;
begin
 LockTableData;
 try
  FIndexName := FindOrCreateIndex(FieldNamesList, AscDescList, CaseSensitivityList, IsCreated);
  SetIndexName(FIndexName);
  DistinctFieldCount := FieldNamesList.Count;
  DisableRecordBitmap;
 finally
   UnlockTableData;
 end;
end;


//------------------------------------------------------------------------------
// activate filters
//------------------------------------------------------------------------------
procedure TABSLocalCursor.ActivateFilters(
                          FilterText:      string;
                          CaseInsensitive: Boolean;
                          PartialKey:      Boolean
                        );
begin
  if (FilterExpression <> nil) then
    TABSExpression(FilterExpression).Free;
  FilterExpression := TABSExpression.Create;
  TABSExpression(FilterExpression).ParseForFilter(Self,FilterText,CaseInsensitive,PartialKey);
  DisableRecordBitmap;
end; // ActivateFilters


//------------------------------------------------------------------------------
// deactivate filters
//------------------------------------------------------------------------------
procedure TABSLocalCursor.DeactivateFilters;
begin
  if (FilterExpression <> nil) then
    TABSExpression(FilterExpression).Free;
  FilterExpression := nil;
  DisableRecordBitmap;
end; // DeactivateFilters


//------------------------------------------------------------------------------
// locate
//------------------------------------------------------------------------------
function TABSLocalCursor.Locate(
                const KeyFields: string;
                const KeyValues: Variant;
                CaseInsensitive: Boolean;
                PartialKey:      Boolean
               ): Boolean;
var SearchExpression: TABSExpression;
begin
  SearchExpression := TABSExpression.Create;
  try
    SearchExpression.ParseForLocate(Self,KeyFields,KeyValues,CaseInsensitive,PartialKey);
    Result := FTableData.Locate(Self,SearchExpression);
  finally
    SearchExpression.Free;
  end;
end; // Locate


//------------------------------------------------------------------------------
// find key
//------------------------------------------------------------------------------
function TABSLocalCursor.FindKey(SearchCondition: TABSSearchCondition): Boolean;
begin
  Result := FTableData.FindKey(Self,SearchCondition);
end; // FindKey


//------------------------------------------------------------------------------
// used by SQL: "where field > all (select ...)"
//------------------------------------------------------------------------------
function TABSLocalCursor.IsAnyRecordMatchCondition(const FieldName: string;
    const Operator: TABSDataOperator; const Value: TABSVariant): Boolean;
var SearchExpression: TABSExpression;
begin
  SearchExpression := TABSExpression.Create;
  try
    SearchExpression.ParseForIsAnyRecordMatchCondition(Self, FieldName, Operator, Value);
    Result := FTableData.Locate(Self,SearchExpression);
  finally
    SearchExpression.Free;
  end;
end;// IsAnyRecordMatchCondition


//------------------------------------------------------------------------------
// create blob stream
//------------------------------------------------------------------------------
function TABSLocalCursor.InternalCreateBlobStream(
              ToInsert: Boolean;
              ToEdit: Boolean;
              FieldNo:  Integer;
              OpenMode: TABSBLOBOpenMode;
              CanDoGetRecordBuffer: Boolean
              ):TABSStream;
var
  grResult: TABSGetRecordResult;
{$IFDEF FILE_SERVER_VERSION}
  Pos:              Pointer;
{$ENDIF}

function DoInternalCreateBlobStream: TABSStream;
var
  FieldNumber:      Integer;
  i:                Integer;
  LocalBLOBStream:  TABSLocalBLOBStream;
begin
   if (FieldNo >= FVisibleFieldDefs.Count) then
    raise EABSException.Create(10110,ErrorLInvalidFieldNumber,
      [FieldNo,FVisibleFieldDefs.Count]);
   if (FBLOBStreams = nil) then
    raise EABSException.Create(10113,ErrorLNilPointer);
   FieldNumber := FVisibleFieldDefs.Items[FieldNo].FieldNoReference;

   // find existing blob stream
   if ((OpenMode = bomReadWrite) or (OpenMode = bomWrite)) then
    for i := 0 to FBLOBStreams.Count -1 do
      begin
        LocalBLOBStream := TABSLocalBLOBStream(FBLOBStreams.Items[i]);
        if (LocalBLOBStream = nil) then
          raise EABSException.Create(10114,ErrorLNilPointer);
        if (LocalBLOBStream.FFieldNo = FieldNumber) then
         if ((LocalBLOBStream.OpenMode = bomReadWrite) or
             (LocalBLOBStream.OpenMode = bomWrite)) then
          raise EABSException.Create(10115,ErrorLBLOBFieldAlreadyOpened,[FieldNumber,
            FFieldDefs.Items[FieldNumber].Name]);
      end;

   Result := FTableData.InternalCreateBlobStream(Self,ToInsert,FieldNumber,OpenMode);
   if (Result <> nil) then
    FBLOBStreams.Add(Result);
end;

begin
 LockTableData;
 Result := nil;
 try
{$IFDEF FILE_SERVER_VERSION}
  if (FDatabaseData.MultiUser and
      (FTableData is TABSDiskTableData)) then
    begin
      if (LockTable(ltS, CreateBlobStreamTableLockRetries, CreateBlobStreamTableLockDelay)) then
        try
          if ((not ToInsert) and (not ToEdit) and (CanDoGetRecordBuffer) and (FBlobStreams.Count = 0)) then
            begin
              Pos := SavePosition;
              try
                FirstPosition := False;
                LastPosition := False;
                grResult := GetRecordBuffer(grmCurrent);
              finally
                RestorePosition(Pos);
                FreePosition(Pos);
              end;
            end
          else
            grResult := grrOK;

          if (grResult = grrOK) then
            Result := DoInternalCreateBlobStream
          else
            Result := nil;
        finally
          UnlockTable(ltS);
        end
      else
    end
  else
{$ENDIF}
    Result := DoInternalCreateBlobStream;
 finally
   UnlockTableData;
 end;
end; // InternalCreateBlobStream


//------------------------------------------------------------------------------
// close blob
//------------------------------------------------------------------------------
procedure TABSLocalCursor.InternalCloseBLOB(FieldNo: Integer);
var FieldNumber:       Integer;
    i:                 Integer;
    LocalBLOBStream:   TABSLocalBLOBStream;
begin
 LockTableData;
 try
   if (FieldNo >= FVisibleFieldDefs.Count) then
    raise EABSException.Create(10118,ErrorLInvalidFieldNumber,
      [FieldNo,FVisibleFieldDefs.Count]);
   if (FBLOBStreams = nil) then
    raise EABSException.Create(10119,ErrorLNilPointer);
   FieldNumber := FVisibleFieldDefs.Items[FieldNo].FieldNoReference;
   i := 0;
   while (i < FBLOBStreams.Count) do
    begin
     LocalBLOBStream := TABSLocalBLOBStream(FBLOBStreams.Items[i]);
     if (LocalBLOBStream = nil) then
        raise EABSException.Create(10120,ErrorLNilPointer);
     if (LocalBLOBStream.FieldNo = FieldNumber) then
      begin
        if ((LocalBLOBStream.OpenMode = bomWrite) or
            (LocalBLOBStream.OpenMode = bomReadWrite)) then
          FTableData.WriteBLOBFieldToRecordBuffer(Self,FieldNumber,LocalBLOBStream);
        if (not LocalBLOBStream.Modified) then
         if (FTableData is TABSDiskTableData) then
           FTableData.ClearBLOBFieldInRecordBuffer(CurrentRecordBuffer, FieldNumber);
        if (LocalBLOBStream.UserBLOBStream <> nil) then
            LocalBLOBStream.UserBLOBStream.Free;
        LocalBLOBStream.Free;
        FBLOBStreams.Delete(i);
        Dec(i);
      end;
     Inc(i);
    end;
 finally
   UnlockTableData;
 end;
end; // InternalCloseBLOB


//------------------------------------------------------------------------------
// clear blob streams
//------------------------------------------------------------------------------
procedure TABSLocalCursor.ClearBLOBStreams(WriteOnly: Boolean = False);
var i:                  Integer;
    LocalBLOBStream:    TABSLocalBLOBStream;
begin
   i := 0;
   if (FBLOBStreams <> nil) then
     while (i < FBLOBStreams.Count) and (FBLOBStreams.Count > 0) do
      begin
       LocalBLOBStream := TABSLocalBLOBStream(FBLOBStreams.Items[i]);
       if (LocalBLOBStream  = nil) then
        raise EABSException.Create(10121,ErrorLNilPointer);
       if ((Not WriteOnly) or
           ((WriteOnly) and
            ((LocalBLOBStream.OpenMode = bomWrite) or
             (LocalBLOBStream.OpenMode = bomReadWrite)))) then
         begin
          if (LocalBLOBStream.UserBLOBStream <> nil) then
            LocalBLOBStream.UserBLOBStream.Free;
  //        LocalBLOBStream.Free;
  //        FBLOBStreams.Delete(i);
          Dec(i);
      end;
       Inc(i);
   end;
end; // ClearBLOBStreams


//------------------------------------------------------------------------------
//LastAutoincValue
//------------------------------------------------------------------------------
function TABSLocalCursor.LastAutoincValue(FieldNo: Integer): Int64;
begin
  Result := FTableData.LastAutoincValue(FieldNo, FSession);
end;//LastAutoincValue


//------------------------------------------------------------------------------
// BeginBatchUpdate
//------------------------------------------------------------------------------
procedure TABSLocalCursor.BeginBatchUpdate;
begin
  if (not Session.InTransaction) then
    begin
      InBatchUpdate := True;
    end;
end;// BeginBatchUpdate


//------------------------------------------------------------------------------
// EndBatchUpdate
//------------------------------------------------------------------------------
procedure TABSLocalCursor.EndBatchUpdate;
begin
  if ((not Session.InTransaction) and (InBatchUpdate)) then
    begin
      if (FTableData is TABSDiskTableData) then
        FTableData.ApplyChanges(Session.SessionID, Session.InTransaction);
      InBatchUpdate := False;
    end;
end;// EndBatchUpdate


//------------------------------------------------------------------------------
// CancelBatchUpdate
//------------------------------------------------------------------------------
procedure TABSLocalCursor.CancelBatchUpdate;
begin
  if ((not Session.InTransaction) and (InBatchUpdate)) then
    begin
      if (FTableData is TABSDiskTableData) then
        FTableData.CancelChanges(Session.SessionID, Session.InTransaction);
      InBatchUpdate := False;
    end;
end;// CancelBatchUpdate


//------------------------------------------------------------------------------
// CheckConstraints
//------------------------------------------------------------------------------
function TABSLocalCursor.CheckConstraints(ToInsert: Boolean): Boolean;
begin
   Result := FTableData.ConstraintManager.CheckConstraints(
          Session.SessionID, CurrentRecordBuffer, EditRecordBuffer,
          ToInsert, CurrentRecordID, False);
end;// CheckConstraints



////////////////////////////////////////////////////////////////////////////////
//
// TABSLocalSession
//
////////////////////////////////////////////////////////////////////////////////


//------------------------------------------------------------------------------
// GetPassword
//------------------------------------------------------------------------------
function TABSLocalSession.GetPassword: PABSPassword;
begin
  Result := @FPassword;
end;//GetPassword


//------------------------------------------------------------------------------
// does DBData correspond to this local sesion?
//------------------------------------------------------------------------------
function TABSLocalSession.IsAppropriateDatabaseData(DBData: TABSDatabaseData): Boolean;
begin
  Result := False;
  if ((DatabaseName <> ABSMemoryDatabaseName) and
      (DatabaseName <> ABSTemporaryDatabaseName)) then
   Result := ((AnsiUpperCase(DBData.DatabaseName) = AnsiUpperCase(DatabaseFileName))) and
              ((AnsiUpperCase(DBData.SessionName) = AnsiUpperCase(SessionName))) // 5.12 bugfix}

  else
  if (DatabaseName = ABSTemporaryDatabaseName) then
   Result := (AnsiUpperCase(DBData.DatabaseName) =
              AnsiUpperCase(ABSTemporaryDatabaseName))
  else
  if (AnsiUpperCase(DatabaseName) = AnsiUpperCase(ABSMemoryDatabaseName)) then
   Result := (AnsiUpperCase(DBData.DatabaseName) =
              AnsiUpperCase(ABSMemoryDatabaseName));
end;// IsAppropriateDatabaseData


//------------------------------------------------------------------------------
// FindDatabaseData
//------------------------------------------------------------------------------
function TABSLocalSession.FindDatabaseData: TABSDatabaseData;
var
  DBDatas: TList;
  i: integer;
begin
  DBDatas := DBDataList.LockList;
  Result := nil;
  try
    for i:=0 to DBDatas.Count-1 do
     if (IsAppropriateDatabaseData(DBDatas.Items[i])) then
      begin
       Result := DBDatas.Items[i];
       break;
      end;
  finally
    DBDataList.UnlockList;
  end;
end;// FindDatabaseData


//------------------------------------------------------------------------------
// create new database data and add it to global list
//------------------------------------------------------------------------------
function TABSLocalSession.CreateDatabaseData: TABSDatabaseData;
{$IFDEF DISK_ENGINE}
var
  DBDatas: TList;
{$ENDIF}
begin
{$IFDEF DISK_ENGINE}
  Result := TABSDiskDatabaseData.Create;
  Result.DatabaseName := DatabaseFileName;
  Result.ReadOnly := ReadOnly;
  TABSDiskDatabaseData(Result).MaxSessionCount := MaxSessionCount;
  TABSDiskDatabaseData(Result).Password^ := FPassword;
  TABSDiskDatabaseData(Result).PageSize := FPageSize;
  TABSDiskDatabaseData(Result).PageCountInExtent := FPageCountInExtent;
  TABSDiskDatabaseData(Result).SessionName := SessionName;  // 5.12 readonly bugfix
  DBDatas := DBDataList.LockList;
  DBDatas.Add(Result);
  DBDataList.UnlockList;
{$ENDIF}
end;// CreateDatabaseData


//------------------------------------------------------------------------------
// find or create database data
//------------------------------------------------------------------------------
function TABSLocalSession.FindOrCreateDatabaseData: TABSDatabaseData;
begin
  DBDataList.LockList;
  try
    Result := FindDatabaseData;
    if (Result = nil) then
      Result := CreateDatabaseData;
  finally
    DBDataList.UnlockList;
  end;
end;// FindOrCreateDatabaseData


//------------------------------------------------------------------------------
// db connected?
//------------------------------------------------------------------------------
function TABSLocalSession.GetConnected: Boolean;
begin
  Result := (FDatabaseData <> nil);
end;// GetConnected


//------------------------------------------------------------------------------
// connect / disconnect
//------------------------------------------------------------------------------
procedure TABSLocalSession.SetConnected(Value: boolean);
var
  DBDatas: TList;
begin
 if Value <> GetConnected then
  begin
    DBDatas := DBDataList.LockList;
    try
     if Value then
      begin
        FDatabaseData := FindOrCreateDatabaseData;
        FDatabaseData.ConnectSession(Self);
        ReadOnly := FDatabaseData.ReadOnly;
{$IFDEF DISK_ENGINE}
        if FDatabaseData is TABSDiskDatabaseData then
          begin
            if (TABSDiskDatabaseData(FDatabaseData).Encrypted) and
               (TABSDiskDatabaseData(FDatabaseData).Password^.Password <> '') and
               (FPassword.Password <> TABSDiskDatabaseData(FDatabaseData).Password^.Password) then
              raise EABSException.Create(20298, ErrorAInvalidDBPassword);
            FPassword := TABSDiskDatabaseData(FDatabaseData).Password^;
            FPageSize := TABSDiskDatabaseData(FDatabaseData).PageSize;
            FPageCountInExtent := TABSDiskDatabaseData(FDatabaseData).PageCountInExtent;
            MaxSessionCount := TABSDiskDatabaseData(FDatabaseData).MaxSessionCount;
          end;
{$ENDIF}
      end
     else
      begin
        FDatabaseData.DisconnectSession(Self);
        if (FDatabaseData.SessionList.Count = 0) then
          if (FDatabaseData is TABSDiskDatabaseData) then
            begin
              DBDatas.Remove(FDatabaseData);
              FDatabaseData.FreeIfNoSessionsConnected;
              FDatabaseData := nil;
            end;
      end;
    finally
     DBDataList.UnlockList;
    end;
  end;
end;// SetConnected


//------------------------------------------------------------------------------
// GetSuppressDBHeaderErrors
//------------------------------------------------------------------------------
function TABSLocalSession.GetSuppressDBHeaderErrors: Boolean;
begin
  Result := False;
  if (Connected) then
    if (FDatabaseData is TABSDiskDatabaseData) then
      Result := TABSDiskDatabaseData(FDatabaseData).GetSuppressDBHeaderErrors;
end;// GetSuppressDBHeaderErrors


//------------------------------------------------------------------------------
// SetSuppressDBHeaderErrors
//------------------------------------------------------------------------------
procedure TABSLocalSession.SetSuppressDBHeaderErrors(Value: boolean);
begin
  if (Connected) then
    if (FDatabaseData is TABSDiskDatabaseData) then
      TABSDiskDatabaseData(FDatabaseData).SetSuppressDBHeaderErrors(Value);
end;// SetSuppressDBHeaderErrors


//------------------------------------------------------------------------------
// create database
//------------------------------------------------------------------------------
procedure TABSLocalSession.CreateDatabase;
begin
  DBDataList.LockList;
  try
    if (FindDatabaseData <> nil) then
      raise EABSException.Create(20092, ErrorADatabaseAlreadyOpen);
    FDatabaseData := CreateDatabaseData;
    try
      FDatabaseData.CreateDatabase;
    finally
      FDatabaseData.FreeIfNoSessionsConnected;
      FDatabaseData := nil;
    end;
  finally
    DBDataList.UnlockList;
  end;
end;// CreateDatabase


//------------------------------------------------------------------------------
// Truncate for empty pages
//------------------------------------------------------------------------------
procedure TABSLocalSession.TruncateDatabase;
begin
{$IFDEF DISK_ENGINE}
  if (FDatabaseData is TABSDiskDatabaseData) then
    TABSDiskDatabaseData(FDatabaseData).TruncateDatabase;
{$ENDIF}
end;//TruncateDatabase


//------------------------------------------------------------------------------
// return Count of connections to Database File
//------------------------------------------------------------------------------
function TABSLocalSession.GetDBFileConnectionsCount: Integer;
begin
{$IFDEF DISK_ENGINE}
  if (FDatabaseData is TABSDiskDatabaseData) then
    Result := TABSDiskDatabaseData(FDatabaseData).GetDBFileConnectionsCount;
{$ELSE}
  Result := -1;
{$ENDIF}
end;//GetDBFileConnectionsCount


//------------------------------------------------------------------------------
// delete database
//------------------------------------------------------------------------------
procedure TABSLocalSession.DeleteDatabase;
begin

end;// DeleteDatabase


//------------------------------------------------------------------------------
// rename database
//------------------------------------------------------------------------------
procedure TABSLocalSession.RenameDatabase(NewDatabaseFileName: string);
begin

end;// RenameDatabase


//------------------------------------------------------------------------------
// get list of database tables
//------------------------------------------------------------------------------
procedure TABSLocalSession.GetTablesList(List: TStrings);
begin
  FDatabaseData := FindOrCreateDatabaseData;
  FDatabaseData.GetTablesList(List);
end;// GetTablesList


//------------------------------------------------------------------------------
// TableExists
//------------------------------------------------------------------------------
function TABSLocalSession.TableExists(TableName: string): Boolean;
begin
  FDatabaseData := FindOrCreateDatabaseData;
  Result := FDatabaseData.TableExists(TableName);
end;// TableExists


//------------------------------------------------------------------------------
// StartTransaction
//------------------------------------------------------------------------------
procedure TABSLocalSession.StartTransaction;
begin
  InTransaction := True;
end;// StartTransaction


//------------------------------------------------------------------------------
// Commit
//------------------------------------------------------------------------------
procedure TABSLocalSession.Commit(DoFlushBuffers: Boolean=True);
begin
  if (InTransaction) then
    begin
      FDatabaseData.Commit(SessionID, DoFlushBuffers);
      InTransaction := False;
    end
  else
    raise EABSException.Create(20229, ErrorADatabaseIsNotInTransaction);
end;// Commit


//------------------------------------------------------------------------------
// Rollback
//------------------------------------------------------------------------------
procedure TABSLocalSession.Rollback;
begin
  if (InTransaction) then
    begin
      FDatabaseData.Rollback(SessionID);
      InTransaction := False;
    end
  else
    raise EABSException.Create(20229, ErrorADatabaseIsNotInTransaction);
end;// Rollback


//------------------------------------------------------------------------------
// FlushBuffers
//------------------------------------------------------------------------------
procedure TABSLocalSession.FlushBuffers;
begin
  FDatabaseData.FlushBuffers;
end;// FlushBuffers


end.
