понедельник, 20 апреля 2026 г.

m3DBTools

 {$IfDef FPC}{$CodePage cp1251}{$EndIf FPC}


unit m3DBTools;


// --------------------------------------------------------------------------

// Родители: "m3DB" <<Unit>> MUID: (4742C9DB033A) :: "m3" <<Library>> MUID: (548712F60101) :: "Shared Delphi Low Level" <<Project>> MUID: (4ABCC25A0322)

// --------------------------------------------------------------------------

// Модуль: "w:\common\components\rtl\Garant\m3\m3DBTools.pas" GeneratorVersion: 1.0.0.883901

// Стереотип: "<<UtilityPack>>"

// Элемент модели: "m3DBTools" MUID: (5AEC481D0310)

// --------------------------------------------------------------------------


//#UC START# *5AEC481D0310beforeDefines*

//#UC END# *5AEC481D0310beforeDefines*

{$Include m3Define.inc}


interface


uses

 l3IntfUses

 , l3Ranges

 , m3DBInterfaces

 , Classes

 , l3CoreInterfaces

 , l3Types

 , l3IntegerToIntegerMap

 , SysUtils

 , m3SearcherInterfaces

 , l3Interfaces

 , m3StorageInterfaces

 , l3IntegerList

 , l3CProtoObject

 //#UC START# *5AEC481D0310intf_uses*

 //#UC END# *5AEC481D0310intf_uses*

;


type

 Tm3FilteredRange = class(Tl3CProtoObject, Im3DBRange)

  private

   f_Versions: Tl3IntegerToIntegerMap;

   f_Other: Im3DBRange;

  protected

   function DB: Im3DB;

   function Mul(const aRange: Im3DBRange): Im3DBRange;

    {* Пересекает выборки. }

   {$If NOT Defined(m3NoEVD)}

   function Iterate(const aFilter: Im3TagGenerator;

    aNeedWriteToBase: Boolean;

    const aParts: Tm3DocPartSet = m3_AllDocParts): Integer;

   {$IfEnd} // NOT Defined(m3NoEVD)

   function IterateF(anAction: Tm3FilerAction;

    const aParts: Tm3DocPartSet = m3_AllDocParts): Integer; overload;

   function IterateF(anAction: Tm3DocumentAction;

    const aParts: Tm3DocPartSet = m3_AllDocParts): Integer; overload;

   function CopyTo(const aDB: Im3DB;

    aMode: Tm3DBCopyMode = m3_cmRewrite;

    const aParts: Tm3DocPartSet = m3_AllDocParts): Boolean;

    {* переливает выборку в другую базу. }

   procedure Cleanup; override;

    {* Функция очистки полей объекта. }

   procedure ClearFields; override;

  public

   constructor Create(aVersions: Tl3IntegerToIntegerMap;

    const anOther: Im3DBRange); reintroduce;

   class function MakePrim(aVersions: Tl3IntegerToIntegerMap;

    const anOther: Im3DBRange): Im3DBRange; reintroduce;

   class function Make(aVersions: Tl3IntegerToIntegerMap;

    const anOther: Im3DBRange): Im3DBRange;

   {$If NOT Defined(l3NoSRT)}

   function SetRefTo(var thePlace: Tm3FilteredRange): Boolean; overload; {$If Defined(l3HasInl)}inline;{$IfEnd}

   {$IfEnd} // NOT Defined(l3NoSRT)

 end;//Tm3FilteredRange


 //#UC START# *5DA721C700ECci*

 //#UC END# *5DA721C700ECci*

 Tm3SubRange = class(Tl3CProtoObject, Im3DBRange)

  private

   f_MaxLen: Integer;

   f_Other: Im3DBRange;

   f_From: Integer;

   f_Last: Integer;

  protected

   function DB: Im3DB;

   function Mul(const aRange: Im3DBRange): Im3DBRange;

    {* Пересекает выборки. }

   {$If NOT Defined(m3NoEVD)}

   function Iterate(const aFilter: Im3TagGenerator;

    aNeedWriteToBase: Boolean;

    const aParts: Tm3DocPartSet = m3_AllDocParts): Integer;

   {$IfEnd} // NOT Defined(m3NoEVD)

   function IterateF(anAction: Tm3FilerAction;

    const aParts: Tm3DocPartSet = m3_AllDocParts): Integer; overload;

   function IterateF(anAction: Tm3DocumentAction;

    const aParts: Tm3DocPartSet = m3_AllDocParts): Integer; overload;

   function CopyTo(const aDB: Im3DB;

    aMode: Tm3DBCopyMode = m3_cmRewrite;

    const aParts: Tm3DocPartSet = m3_AllDocParts): Boolean;

    {* переливает выборку в другую базу. }

   procedure Cleanup; override;

    {* Функция очистки полей объекта. }

   procedure ClearFields; override;

  public

   constructor Create(aFrom: Integer;

    aMaxLen: Integer;

    const anOther: Im3DBRange); reintroduce;

   class function Make(aFrom: Integer;

    aMaxLen: Integer;

    const anOther: Im3DBRange): Im3DBRange; reintroduce;

   {$If NOT Defined(l3NoSRT)}

   function SetRefTo(var thePlace: Tm3SubRange): Boolean; overload; {$If Defined(l3HasInl)}inline;{$IfEnd}

   {$IfEnd} // NOT Defined(l3NoSRT)

  public

   property Last: Integer

    read f_Last;

 //#UC START# *5DA721C700ECpubl*

 //#UC END# *5DA721C700ECpubl*

 end;//Tm3SubRange


function m3ImportDocument(const aDB: Im3DB;

 anIn: TStream;

 anID: Integer): Boolean; overload;

function m3ImportDocument(const aDB: Im3DB;

 const anIn: AnsiString;

 anID: Integer): Boolean; overload;

function m3IndexRange(const aRange: Im3DBRange;

 const anIndexName: Il3CString;

 aNeedWriteVersions: Boolean;

 aFilesMeter: Tl3ProgressProc;

 const aDB: Im3DB): Integer;

function m3BuildIndexDelta(const aDB: Im3DB;

 const anIndexName: Il3CString;

 aFilesMeter: Tl3ProgressProc;

 aVersions: Tl3IntegerToIntegerMap = nil): Integer;

function m3DBBuildIndexDelta(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc;

 aNestedCall: Boolean;

 aForceFullIndex: Boolean;

 aFrom: Integer = 0;

 aTo: Integer = High(Integer)): Boolean;

function m3BuildFullIndex(const aDB: Im3DB;

 const anIndexName: Il3CString;

 aFilesMeter: Tl3ProgressProc;

 const aDBHelper: Im3DBHelper;

 var aDeltas: Im3FilesEnumerable;

 aForceFullIndex: Boolean;

 aFrom: Integer;

 aTo: Integer): Integer;

procedure m3LoadVersionsFromStream(aVersions: Tl3IntegerToIntegerMap;

 const aStream: IStream);

procedure m3WriteVersionsToStream(const aVersions: Im3IndexedVersions;

 const aStream: IStream);

procedure m3WriteVersions(const aStorage: Im3IndexedStorage;

 const aVersions: Im3IndexedVersions);

procedure m3MergeFromDumper(var aDumper: Im3IndexDumper;

 const anUpdateName: Il3CString;

 aWriteVersions: Boolean;

 aFilesMeter: Tl3ProgressProc;

 const aBaseName: TFileName);

procedure m3CreateSummary(const aStorage: Im3IndexedStorage);

function m3DBMergeIndexDeltas(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc): Boolean; overload;

function m3DBIndexUpdated(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc;

 aForceMerge: Boolean): Boolean;

function m3DBBuildAndMergeIndexDeltas(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc): Boolean;

function m3DBMergeIndexDeltas(const aDBHelper: Im3DBHelper;

 var aDeltas: Im3FilesEnumerable;

 aFilesMeter: Tl3ProgressProc): Boolean; overload;

function m3DBUpdateIndex(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc): Boolean;

function m3ReadModified(const aStorage: Im3IndexedStorage): Il3RangeEnumerable;

procedure m3WriteModified(const aStorage: Im3IndexedStorage;

 const aModified: Il3RangeEnumerable;

 aTimeStamp: TDateTime); overload;

procedure m3DBWaitIndexer;

function m3DBIndexDoc(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc;

 aDocNum: Integer): Boolean;

function m3DBIndexDocs(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc;

 aDocNums: Tl3IntegerList): Boolean;

procedure m3DBIndexDocAsync(const aBaseName: TFileName;

 aDocNum: Integer);

procedure m3DBNeedIndexDoc(const aBaseName: TFileName;

 aDocNum: Integer);

procedure m3DBIndexUpdatedAsync(const aBaseName: TFileName);

procedure m3SetInternalIDtoExternalIDLink(const aBaseName: TFileName;

 anInternalID: Integer;

 anExternalID: Integer);

 {* Устанавливает связь между внутренним номером документа и внешним

https://mdp.garant.ru/pages/viewpage.action?pageId=892273772 }

procedure m3WriteModified(const aStorage: Im3IndexedStorage;

 const aModified: Il3RangeEnumerable); overload;

procedure m3WriteStringToDocument(const aDB: Im3DB;

 aDocID: Integer;

 anAttrID: Integer;

 const aString: Tl3WString);


var g_IndexByParts: Boolean = True;

 {* Индексировать основной индекс КУСКАМИ }

var g_IndexOnlyAttributes: Boolean = False;

 {* Индексировать ТОЛЬКО атрибуты }


//#UC START# *5AEC481D0310publ*

var g_InBuildFullIndex: Boolean = false;

var g_MergeOnlyAttributes: Boolean = false;

//#UC END# *5AEC481D0310publ*


implementation


uses

 l3ImplUses

 , m3DBHelper

 , m3DocumentAddressCachedStore

 , m3DocumentAddressStore

 , m3DocumentAddressCachedStoreProxy

 , m3IDRangeListPrim

 , m3RangedDocumentsList

 , m3ExternalIDtoInternalIDMap

 //#UC START# *5AEC481D0310impl_uses*

 {$If NOT Defined(Linux)}

 , Windows

 {$Else NOT Defined(Linux)}

 // using l3LinWindows instead of Windows

 , l3LinWindows

 {$IfEnd} // NOT Defined(Linux)

 , Math

 , StrUtils

 , DateUtils

 {$If NOT Defined(Linux)}

 , ActiveX

 {$Else NOT Defined(Linux)}

 // using l3LinActiveX instead of ActiveX

 , l3LinActiveX

 {$IfEnd} // NOT Defined(Linux)

 {$If NOT Defined(Linux)}

 , ComObj

 {$Else NOT Defined(Linux)}

 // using l3LinComObj instead of ComObj

 , l3LinComObj

 {$IfEnd} // NOT Defined(Linux)

 //, l3CoreInterfaces

 , m3HashElements

 , m3StorageTools

 , l3Chars

 , l3String

 , l3StringEx

 , l3Date

 , l3Enumerators

 , l3StringsEnumerator

 , l3FileUtils

 , m3DBActions

 , m2COMLib

 , l3Stream

 //, SysUtils

 , m3EvdDocumentIndexBuilder

 , m3StgMgr

 , m4DB

 , m3DB

 , l3Filer

 , l3MemUtils

 , l3Base

 , m2HASLib

 , m3BackupTools

 , m3StorageService

 , l3Prg

 , k2Tags

 , k2Attributes

 , m3SplittedFileStream

 , m3WordAddress

 , m3DocumentAddress

 , m3IdxCla

 , m3IndexSearcherFilteredResult

 , m3IndexConst

 , m3IndexDumper

 , m3IndexDumperJoin

 , m3StorageElementIDList

 , m3StorageHolderList

 , m3BaseStream

 , l3StoreInterfaces

 , l3SysUtils

 //, m3DocumentAddressStore

 //, m3DocumentAddressCachedStore

 , m3Const

 , m3Endings

 , m3DBDocumentPart

 , m3Exceptions

 , k2TagGen

 , m3EvdDocumentAttrsIndexBuilder

 , l3TimeService

 , m3AttrIndexDumper

 , m3AttrIndexDumperJoin

 , m3AttrIndexInterfaces

 , m3AttrIndexSearcher

 , m3DocumentAttrIndexBuilder

 , m3IndexerTypes

 , m3AttrID

 , l3BaseStream

 , m3ProxyIndexStream

;


type

  Tm3DBIndexDocThread = class(TThread)

   private

    f_BaseName : TFileName;

    f_DocNum : Integer;

    f_DocNums : Tl3IntegerList;

   protected

    procedure Execute; override;

   public

    constructor Create(const aBaseName : TFileName; aDocNum : Integer);

    class procedure Run(const aBaseName : TFileName; aDocNum : Integer);

    destructor Destroy; override;

  end;//Tm3DBIndexDocThread


var

  g_IndexDocThreadWaitAdded : Boolean = false;

  g_IndexDocThread : Tm3DBIndexDocThread = nil;

  g_IndexDocThreadstarted : Boolean = false;


type

  Tm3DBIndexUpdatedThread = class(TThread)

   private

    f_BaseName : TFileName;

   protected

    procedure Execute; override;

   public

    constructor Create(const aBaseName : TFileName);

  end;//Tm3DBIndexUpdatedThread


const

 cFakeConst = 1

 //#UC END# *5AEC481D0310impl_uses*

;


function m3ImportDocument(const aDB: Im3DB;

 anIn: TStream;

 anID: Integer): Boolean;

//#UC START# *5AFD5D9D00BB_5AEC481D0310_var*

var

 l_Doc : Im3DBDocument;

 l_Part : Im3DBDocumentPart;

 l_Info : Tm3DBDocumentInfo;

 l_Out : IStream;

//#UC END# *5AFD5D9D00BB_5AEC481D0310_var*

begin

//#UC START# *5AFD5D9D00BB_5AEC481D0310_impl*

 l_Doc := aDB.GetDocument(anID);

 l_Out := l_Doc.Open(m3_saReadWrite);

 try

  Result := l3CopyStream(anIn, l_Out);

 finally

  l_Out := nil;

 end;//try..finally

 l_Part := l_Doc.GetVersion;

 l_Info := l_Part.Info;

 l_Info.rIndexID := 1;

 l_Part.Info := l_Info;

//#UC END# *5AFD5D9D00BB_5AEC481D0310_impl*

end;//m3ImportDocument


function m3ImportDocument(const aDB: Im3DB;

 const anIn: AnsiString;

 anID: Integer): Boolean;

//#UC START# *5AFD622403B9_5AEC481D0310_var*

var

 l_In : Tl3FileStream;

//#UC END# *5AFD622403B9_5AEC481D0310_var*

begin

//#UC START# *5AFD622403B9_5AEC481D0310_impl*

 l_In := Tl3FileStream.Create(anIn, l3_fmRead);

 try

  Result := m3ImportDocument(aDB, l_In, anID);

 finally

  FreeAndNil(l_In);

 end;//try..finally

//#UC END# *5AFD622403B9_5AEC481D0310_impl*

end;//m3ImportDocument


function m3IndexRange(const aRange: Im3DBRange;

 const anIndexName: Il3CString;

 aNeedWriteVersions: Boolean;

 aFilesMeter: Tl3ProgressProc;

 const aDB: Im3DB): Integer;

//#UC START# *5AFEA7650018_5AEC481D0310_var*


 procedure DoDoIndex;

 var

  l_IndexRoot : Im3IndexedStorage;


  procedure DoIndex;

  var

   l_Progress : Il3Progress;

   l_Builder : Tm3EvdDocumentIndexBuilder;

   l_Gen : Tk2TagGenerator;

  begin//DoIndex

   l_Progress := nil;

   try

    if Assigned(aFilesMeter) then

     l_Progress := Tl3ProgressIndicator.Make(aFilesMeter);

    if g_IndexOnlyAttributes then

     l_Builder := nil

    else

     l_Builder := Tm3EvdDocumentIndexBuilder.Create(l_IndexRoot, aNeedWriteVersions, l_Progress);

    try

     l_Gen := nil;

     try

      l_Builder.SetRefTo(l_Gen);

      Tm3EvdDocumentAttrsIndexBuilder.SetTo(l_Gen, Il3CString_ToFileName(anIndexName), aNeedWriteVersions, nil{aDB});

      //Tm3EvdDocumentAttrsIndexBuilder.SetTo(l_Gen, Il3CString_ToFileName(anIndexName), aNeedWriteVersions, aDB);

      if (l_IndexRoot <> nil) then

       m3CreateSummary(l_IndexRoot);

      l_IndexRoot := nil;

      if (aDB = nil) then

       Result := m4IndexRange(aRange, l_Gen)

      else

      if g_InBuildFullIndex then

       Result := m4IndexRange(aRange, l_Gen)

      else

      begin

       aDB.Start(m3_saRead);

       //aDB.Start(m3_saReadWrite);

       try

        Result := m4IndexRange(aRange, l_Gen);

       finally

        aDB.Finish;

       end;//try..finally

       //Result := m4IndexRange(aRange, l_Gen);

      end;//aDB = nil

     finally

      FreeAndNil(l_Gen);

     end;//try..finally

    finally

     FreeAndNil(l_Builder);

    end;//try..finally

   finally

    l_Progress := nil;

   end;//try..finally

  end;//DoIndex


  {$IfDef m3UseProxyIndexStream}

   {$Define m3LocUseProxyIndexStream}

  {$EndIf m3UseProxyIndexStream}


 var

  l_Name : TFileName;

  {$IfDef m3LocUseProxyIndexStream}

  l_Temp : Tl3Stream;

  l_H : Im3StorageHolder;

  {$EndIf m3LocUseProxyIndexStream}

 begin//DoDoIndex

  l_IndexRoot := nil;

  try

   if not g_IndexOnlyAttributes then

   begin

    l_Name := Il3CString_ToFileName(anIndexName);

    m3Endings.InitEndings(ExtractFilePath(l_Name));

    {$IfNDef m3UseVersionedIndexDeltas}

    Tm3StorageManager.DeleteStorageFile(l_Name);

    {$EndIf  m3UseVersionedIndexDeltas}

    {$IfDef m3LocUseProxyIndexStream}

    if not Tm3StorageService.Instance.UseSplitted then

    begin

     l_Temp := Tm3ProxyIndexStream.Create(l_Name);

     try

      l_H := Tm3FullModeExclusiveStorageManager.MakeInterface(l_Temp, c_m3UseVersionedIndexDeltas, def_FullModeStorageUseCompression);

      try

       l_IndexRoot := l_H.Storage;

      finally

       l_H := nil;

      end;//try..finally

     finally

      FreeAndNil(l_Temp);

     end;//try..finally

    end//not Tm3StorageService.Instance.UseSplitted

    else

     l_IndexRoot := Tm3FullModeExclusiveStorageManager.MakeInterface(l_Name, c_m3UseVersionedIndexDeltas);

    {$Else  m3LocUseProxyIndexStream}

    l_IndexRoot := Tm3FullModeExclusiveStorageManager.MakeInterface(l_Name, c_m3UseVersionedIndexDeltas);

    {$EndIf  m3LocUseProxyIndexStream}

   end;//not g_IndexOnlyAttributes


   {$IfNDef m3UseVersionedIndexDeltas}

   if (l_IndexRoot <> nil) then

    l_IndexRoot.ClearAll;

   {$EndIf  m3UseVersionedIndexDeltas}

   if (aDB = nil) then

    DoIndex

   else

   if g_InBuildFullIndex then

    DoIndex

   else

   begin

    aDB.Start(m3_saRead);

    //aDB.Start(m3_saReadWrite);

    try

     DoIndex;

    finally

     aDB.Finish;

    end;//try..finally

   end;//aDB = nil

  finally

   l_IndexRoot := nil;

  end;//try..finally

 end;//DoDoIndex


//#UC END# *5AFEA7650018_5AEC481D0310_var*

begin

//#UC START# *5AFEA7650018_5AEC481D0310_impl*

 Result := -1;

 Assert(aRange <> nil);

 try

  DoDoIndex;

  // - специально выделено, чтобы интерфейсы отпускались

 except

  Result := -1;

  Tm3StorageManager.DeleteStorageFile(anIndexName);

  // - удаляем "провисший" файл

  raise;

 end;//try..except

//#UC END# *5AFEA7650018_5AEC481D0310_impl*

end;//m3IndexRange


function m3BuildIndexDelta(const aDB: Im3DB;

 const anIndexName: Il3CString;

 aFilesMeter: Tl3ProgressProc;

 aVersions: Tl3IntegerToIntegerMap = nil): Integer;

//#UC START# *5AFEAABE01E5_5AEC481D0310_var*

var

 l_Range : Im3DBRange;

//#UC END# *5AFEAABE01E5_5AEC481D0310_var*

begin

//#UC START# *5AFEAABE01E5_5AEC481D0310_impl*

 Result := -1;

 l_Range := nil;

 try

  //l_Range := aDB.ModifiedFiles;

  l_Range := aDB.IndexedFiles.Mul(aDB.ModifiedFiles);

  if (aVersions <> nil) AND not aVersions.Empty then

   l_Range := Tm3FilteredRange.Make(aVersions, l_Range);

  Result := m3IndexRange(l_Range, anIndexName, true, aFilesMeter, aDB);

 finally

  l_Range := nil;

 end;//try..finally

//#UC END# *5AFEAABE01E5_5AEC481D0310_impl*

end;//m3BuildIndexDelta


function m3DBBuildIndexDelta(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc;

 aNestedCall: Boolean;

 aForceFullIndex: Boolean;

 aFrom: Integer = 0;

 aTo: Integer = High(Integer)): Boolean;

//#UC START# *5B07D36103E7_5AEC481D0310_var*


var

 l_Versions : Tl3IntegerToIntegerMap;


 procedure LoadVersions(const aName: String);

 var

  l_S : Im3IndexedStorage;

  l_V : Il3Stream;

 begin//LoadVersions

  l_S := Tm3ReadModeStorageManager.MakeInterface(aName{, false});

  try

   if (l_S <> nil) then

   begin

    l_V := m3COMOpenStream(l_S,

                           l3PCharLen('versions'),

                           m3_saRead,

                           false);

    try

     if (l_V <> nil) then

     begin

      l_S := nil;

      // - типа он нам больше не нужен

      if (l_Versions = nil) then

       l_Versions := Tl3IntegerToIntegerMap.Create;

      m3LoadVersionsFromStream(l_Versions, l_V.As_IStream);

     end;//l_V <> nil

    finally

     l_V := nil;

    end;//try..finally

   end;//l_S <> nil

  finally

   l_S := nil;

  end;//try..finally

 end;//LoadVersions


 function MakeDB(const aName: String): Im3DB;

 begin

  Result := Tm3DB.Make(aName,

         //cbMakeCopy.Checked AND cbFromOld.Checked,

         nil,//Yield,

         nil,//FileMeter.ProgressProc_ev,

         aFilesMeter,//FilesMeter.ProgressProc_ev,

         nil,//FilesProcessed,

         nil //lProgressor.FilesProcessedExProc//FilesProcessedEx

         );

 end;


var

 l_BaseName : String;

 l_IndexName : Il3CString;

 l_PrevIndexName : Il3CString;

 l_NeedDelete : Boolean;

 l_DBHelper : Im3DBHelper;

 l_E : Im3FilesEnumerable;


var

 l_NeedExit : Boolean;


 procedure DoBuildFullIndex;

 var

  l_DB : Im3DB;

 begin//DoBuildFullIndex

  l_NeedExit := false;

  //l_FlagFiler.WriteLn(l_IndexName);

  l_DB := MakeDB(l_BaseName);

  try

   l_NeedDelete := m3BuildFullIndex(l_DB, l_IndexName, aFilesMeter, l_DBHelper, l_E, aForceFullIndex, aFrom, aTo) <= 0;

   l_NeedDelete := false;

   if not Tm3StorageManager.StorageFileExists(l_BaseName + m3_cExchangeExt) then

   // - нету переменной части - дельту строить не надо

   begin

    Result := true;

    l_NeedExit := true;

    Exit;

   end;//not Tm3StorageManager.StorageFileExists(l_BaseName + m3_cExchangeExt)

  finally

   l_DB := nil;

  end;//try..finally

  //l_E := l_DBHelper.GetIndexDeltasFiles;

  // - перечитываем из-за m3BuildFullIndex

 end;//DoBuildFullIndex


 procedure DoBuildIndexDelta;

 var

  l_DB : Im3DB;

  l_US : Boolean;

  l_T1, l_T2 : TDateTime;

 begin//DoBuildIndexDelta

  l_DB := MakeDB(l_BaseName);

  try

   if not aNestedCall then

   begin

    l_T1 := NullDate;

    l_T2 := NullDate;

    l_T1 := Tm3StorageManager.StorageFileDateTime(l_BaseName + m3_cExchangeExt);

    if (l_PrevIndexName <> nil) then

     l_T2 := Tm3StorageManager.StorageFileDateTime(l_PrevIndexName);

    l_US := Tm3StorageService.Instance.SetUseSplitted(false);

    try

     if SameValue(l_T1, NullDate) OR

        SameValue(l_T2, NullDate) OR

        ((DaysBetween(l_T1, l_T2) > 0) AND (CompareValue(l_T1, l_T2) > 0))

        then

      l_NeedDelete := m3BuildIndexDelta(l_DB, l_IndexName, aFilesMeter, nil) <= 0

     else

      l_NeedDelete := m3BuildIndexDelta(l_DB, l_IndexName, aFilesMeter, l_Versions) <= 0;

    finally

     Tm3StorageService.Instance.SetUseSplitted(l_US);

    end;//try..finally

   end//not aNestedCall

   else

   begin

    l_US := Tm3StorageService.Instance.SetUseSplitted(false);

    try

     l_NeedDelete := m3BuildIndexDelta(l_DB, l_IndexName, aFilesMeter, l_Versions) <= 0;

    finally

     Tm3StorageService.Instance.SetUseSplitted(l_US);

    end;//try..finally

   end;//not aNestedCall

  finally

   l_DB := nil;

  end;//try..finally

 end;//DoBuildIndexDelta


var

 l_Files : Im3FilesEnumerator;

//#UC END# *5B07D36103E7_5AEC481D0310_var*

begin

//#UC START# *5B07D36103E7_5AEC481D0310_impl*

 Result := false;

 try

  Assert(aBaseName <> '');

  l_Versions := nil;

  try

   l_NeedDelete := false;

   l_BaseName := Tm3SplittedFileStream.NormalizeFileName(aBaseName);

   l_BaseName := ChangeFileExt(l_BaseName, '');

   //l_Path := ExtractFilePath(l_BaseName);

   l_DBHelper := Tm3DBHelper.Make(l_BaseName);

   try

    Assert(l_DBHelper <> nil);

    l_E := l_DBHelper.GetIndexDeltasFiles;

    if not aNestedCall then

    begin

     if not l_DBHelper.LockDeltas(l_E) then

     begin

      Result := false;

      Exit;

     end;//not l_DBHelper.Lock

    end;//not aNestedCall

    try

     l_IndexName := l_DBHelper.IndexName;

     l_PrevIndexName := nil;

     if not aNestedCall then

     begin

      if aForceFullIndex

         OR not Tm3StorageManager.StorageFileExists(l_IndexName)

         then

      // - тут надо бы построить ПОЛНЫЙ индекс

      begin

       DoBuildFullIndex;

       if l_NeedExit then

        Exit;

      end;//Tm3StorageManager.StorageFileExists(l_IndexName)

     end;//not aNestedCall

     //l_E := l_DBHelper.GetIndexDeltasFiles;

     if (l_E <> nil) then

     begin

      l_Files := l_E.GetEnumerator;

      try

       Assert(l_Files <> nil);

       while l_Files.MoveNext do

       begin

        l_IndexName := l_Files.Current.AsCStr;

        l_PrevIndexName := l_IndexName;

        LoadVersions(Il3CString_ToFileName(l_IndexName));

       end;//while l_Files.MoveNext

      finally

       l_Files := nil;

      end;//try..finally

     end;//l_E <> nil

     l_IndexName := l_DBHelper.NewIndexDeltaName(l_E);

     //l_FlagFiler.WriteLn(l_IndexName);

     //Exit;

     try

      DoBuildIndexDelta;

     except

      Result := false;

      Tm3StorageManager.DeleteStorageFile(l_IndexName);

      // - удаляем "провисший" файл

      raise;

     end;//try..except

     if l_NeedDelete then

      Tm3StorageManager.DeleteStorageFile(l_IndexName)

     else

      l_DBHelper.AddNewIndexDeltaName(l_IndexName, l_E);

    finally

     if not aNestedCall then

      l_DBHelper.UnlockDeltas;

    end;//try..finally

   finally

    l_DBHelper := nil;

   end;//try..finally

  finally

   FreeAndNil(l_Versions);

  end;//try..finally

  Result := true;

 except

  on E: Exception do

  begin

   l3System.Exception2Log(E);

   Result := false;

  end;//on E: Exception

  else

   Result := false;

 end;//try..except

//#UC END# *5B07D36103E7_5AEC481D0310_impl*

end;//m3DBBuildIndexDelta


function m3BuildFullIndex(const aDB: Im3DB;

 const anIndexName: Il3CString;

 aFilesMeter: Tl3ProgressProc;

 const aDBHelper: Im3DBHelper;

 var aDeltas: Im3FilesEnumerable;

 aForceFullIndex: Boolean;

 aFrom: Integer;

 aTo: Integer): Integer;

//#UC START# *5B07DA9600E1_5AEC481D0310_var*

const

 {$IfDef nsTest}

 cLen = 10{50};

 // - так как в базе всего 70+ документов

 {$Else  nsTest}

 cLen = 150000;

 //cLen = 40000{20000}{100000};

 {$EndIf nsTest}

var

 l_Range : Im3DBRange;

 l_SubRange : Tm3SubRange;

 l_From : Integer;

 l_Count : Integer;

 l_IndexName : Il3CString;

 l_US : Boolean;

 l_InBuildFullIndex: Boolean;

 //l_E : Im3FilesEnumerable;

//#UC END# *5B07DA9600E1_5AEC481D0310_var*

begin

//#UC START# *5B07DA9600E1_5AEC481D0310_impl*

 Result := -1;

 l_InBuildFullIndex := g_InBuildFullIndex;

 try

  {$IfDef m3DBCheck}

  g_InBuildFullIndex := true;

  {$Else  m3DBCheck}

  if aForceFullIndex then

   g_InBuildFullIndex := true

  else

   g_InBuildFullIndex := false;

  //g_InBuildFullIndex := true;

  {$EndIf m3DBCheck}

  l_Range := nil;

  try

   if not aForceFullIndex

      AND

      {$IfDef m3IndexByParts}not g_IndexByParts{false}{$Else}true{$EndIf} then

   begin

    l_Range := aDB.IndexedFiles;

    Result := m3IndexRange(l_Range, anIndexName, false, aFilesMeter, aDB);

   end//not g_IndexByParts

   else

   begin

    //l_E := aDBHelper.GetIndexDeltasFiles;

    l_US := Tm3StorageService.Instance.SetUseSplitted(false);

    try

     Result := 0;

     l_From := Max(0, aFrom);

     while (l_From < High(l_From)) do

     begin

      l_Range := aDB.IndexedFiles(1, l_From);

      try

       l_SubRange := Tm3SubRange.Create(l_From, cLen, l_Range);

       try

        l_IndexName := anIndexName;

        if aForceFullIndex

           OR Tm3StorageManager.StorageFileExists(l_IndexName) then

        begin

         l_IndexName := aDBHelper.NewIndexDeltaName(aDeltas);

         l_Count := m3IndexRange(l_SubRange, l_IndexName, false, aFilesMeter, aDB);

         if (l_Count < 0) then

          l_Count := 0;

         if (l_Count <= 0) then

          Tm3StorageManager.DeleteStorageFile(l_IndexName)

         else

          aDBHelper.AddNewIndexDeltaName(l_IndexName, aDeltas);

        end//Tm3StorageManager.StorageFileExists(l_IndexName)

        else

        begin

         l_Count := m3IndexRange(l_SubRange, l_IndexName, false, aFilesMeter, aDB);

         if (l_Count < 0) then

          l_Count := 0;

         if (l_Count <= 0) then

          Tm3StorageManager.DeleteStorageFile(l_IndexName);

        end;//Tm3StorageManager.StorageFileExists(l_IndexName)

        Inc(Result, l_Count);

        if (l_SubRange.Last <= l_From) then

         break;

        {$If not Defined(Linux) OR not Defined(nsTest)}

        l3System.Msg2Log('Indexed: ' + Il3CString_StrEx(l_IndexName) + ' ' + IntToStr(l_From) + ' : ' + IntToStr(l_SubRange.Last));

        {$IfEnd}

        l_From := l_SubRange.Last;

        if (l_From >= aTo) then

         break;

       finally

        FreeAndNil(l_SubRange);

       end;//try..finally

      finally

       l_Range := nil;

      end;//try..finally

     end;//while

    finally

     Tm3StorageService.Instance.SetUseSplitted(l_US);

    end;//try..finally

   end;//not g_IndexByParts

  finally

   l_Range := nil;

  end;//try..finally

 finally

  g_InBuildFullIndex := l_InBuildFullIndex;

 end;//try..finally

//#UC END# *5B07DA9600E1_5AEC481D0310_impl*

end;//m3BuildFullIndex


procedure m3LoadVersionsFromStream(aVersions: Tl3IntegerToIntegerMap;

 const aStream: IStream);

//#UC START# *5B07EABC001C_5AEC481D0310_var*

var

 l_ID : Integer;

 l_Version : Integer;

 l_Return: HRESULT;

 l_Read : Integer;

 l_Index : Integer;

//#UC END# *5B07EABC001C_5AEC481D0310_var*

begin

//#UC START# *5B07EABC001C_5AEC481D0310_impl*

 Assert(aStream <> nil);

 while true do

 begin

  l_Return := aStream.Read(@l_ID, SizeOf(l_ID), @l_Read);

  if (l_Return = S_FALSE) then

   break;

  OleCheck(l_Return);

  if (l_Read <> SizeOf(l_ID)) then

   break;

  l_Return := aStream.Read(@l_Version, SizeOf(l_Version), @l_Read);

  if (l_Return = S_FALSE) then

   break;

  OleCheck(l_Return);

  if (l_Read <> SizeOf(l_Version)) then

   break;


  if (l_Version = Cm3ConstVersion) then

   l_Version := 0;


  if not aVersions.Has(l_ID, l_Index) then

   aVersions.Insert(l_Index, l_ID, l_Version)

  else

  begin

   aVersions.ItemSlot(l_Index)^.rValue := l_Version;

  end;//not l_Versions.Has(l_ID, l_Index)

 end;//while true

//#UC END# *5B07EABC001C_5AEC481D0310_impl*

end;//m3LoadVersionsFromStream


procedure m3WriteVersionsToStream(const aVersions: Im3IndexedVersions;

 const aStream: IStream);

//#UC START# *5B2E22830040_5AEC481D0310_var*

var

 //l_Index : Integer;

 l_It : Tl3IntegerToIntegerMap_Enumerator;

 l_ID : Integer;

 l_Version : Integer;

//#UC END# *5B2E22830040_5AEC481D0310_var*

begin

//#UC START# *5B2E22830040_5AEC481D0310_impl*

 if (aVersions <> nil) then

 begin

  l_It := aVersions.GetEnumerator;

  if (l_It = nil) then

   Exit;

  while l_It.MoveNext do

  //for l_Index := 0 to Pred(aVersions.Count) do

  begin

   //with aVersions.ItemSlot(l_Index)^ do

   begin

    l_ID := l_It.pCurrent^.rKey;

    l_Version := l_It.pCurrent^.rValue;

    m2COMWriteBuffer(aStream, l_ID, SizeOf(l_ID));

    m2COMWriteBuffer(aStream, l_Version, SizeOf(l_Version));

   end;//with aVersions.ItemSlot(l_Index)^

  end;//for l_Index

 end;//aVersions <> nil

//#UC END# *5B2E22830040_5AEC481D0310_impl*

end;//m3WriteVersionsToStream


procedure m3WriteVersions(const aStorage: Im3IndexedStorage;

 const aVersions: Im3IndexedVersions);

//#UC START# *5B2E2F0E02BF_5AEC481D0310_var*

var

 l_Versions : IStream;

//#UC END# *5B2E2F0E02BF_5AEC481D0310_var*

begin

//#UC START# *5B2E2F0E02BF_5AEC481D0310_impl*

 l_Versions := m3COMOpenStream(aStorage,

                               l3PCharLen('versions'),

                               m3_saWrite,

                               //m3_saReadWrite,

                               true{false}).As_IStream;

 try

  if (aVersions <> nil) then

  begin

   m3WriteVersionsToStream(aVersions, l_Versions);

  end;//aVersions <> nil

 finally

  l_Versions := nil;

 end;//try..finally

//#UC END# *5B2E2F0E02BF_5AEC481D0310_impl*

end;//m3WriteVersions


procedure m3MergeFromDumper(var aDumper: Im3IndexDumper;

 const anUpdateName: Il3CString;

 aWriteVersions: Boolean;

 aFilesMeter: Tl3ProgressProc;

 const aBaseName: TFileName);

//#UC START# *5B2E3DB902C7_5AEC481D0310_var*

var

 l_OutHash  : Im3IndexedStorage;

 l_HashInfo : IStream;

 l_StatStream : IStream;

 l_Start : Cardinal;


 function DoHash(const aHashElement: Im3IndexHashElement): Boolean;

 var

  l_HashStorage : Im3IndexedStorage;


  function DoWord(aWord: Tl3PrimString): Boolean;

  var

   l_OutWordStream : Im3DocumentAddressStore;

   //l_Address : Tm3DocumentAddress;

   //l_Result : Im3IndexSearcherResult;

   l_AE : Im3AddressesEnumerable;

  begin//DoWord

   Result := true;

   if Assigned(aFilesMeter) then

    aFilesMeter(piCurrent, aHashElement.ID, l3StrEx(aWord.AsWStr));

   l_OutWordStream := nil;

   try

    //l_Result := nil;

    if (aWord.AsString = '2') then

     l_AE := aHashElement.LemmaAddresses(aWord)

    else

     l_AE := aHashElement.LemmaAddresses(aWord);

    if (l_AE <> nil) then

    begin

     l_OutWordStream := Tm3DocumentAddressCachedStoreProxy.Make(l_HashInfo, l_OutHash, aHashElement.ID, aWord, l_StatStream, 0, l_HashStorage);

     Assert(l_OutWordStream <> nil);

     l_AE.WriteTo(l_OutWordStream);

    end;//l_AE <> nil

(*    if (l_AE <> nil) then

     l_Result := l_AE.GetEnumerator;

    l_AE := nil; // - типа уже можно отпустить


    if (l_Result <> nil) then

    begin

     try

      l_OutWordStream := Tm3DocumentAddressCachedStoreProxy.Make(l_HashInfo, l_OutHash, aHashElement.ID, aWord, l_StatStream, 0, l_HashStorage);

      Assert(l_OutWordStream <> nil);

      while l_Result.GetAddress(l_Address, 0) do

      begin

       l_OutWordStream.PutAddress(@l_Address, SizeOf(l_Address));

      end;//while l_Result.GetAddress

     finally

      l_Result := nil;

     end;//try..finally

    end;//l_Result <> nil*)

   finally

    l_OutWordStream := nil;

   end;//try..finally

  end;//DoWord


 const

  cTimeDelta = 20 * 60 * 1000;

 var

  l_LemmasE : Im3StringsEnumerable;

  l_Lemmas : Im3StringsEnumerator;

  l_TimeDelta : Int64;

 begin//DoHash

  Result := true;

  l_LemmasE := nil;

  l_Lemmas := nil;

  try

   l_LemmasE := aHashElement.Lemmas;

   if (l_LemmasE <> nil) then

   begin

    l_Lemmas := l_LemmasE.GetEnumerator;

    l_LemmasE := nil;

    if (l_Lemmas <> nil) then

    begin

     l_HashStorage := nil;

     //l_HashStorage := m3COMOpenStorage(l_OutHash, aHashElement.ID, m3_saReadWrite, true);

     try

      if Assigned(aFilesMeter) then

       aFilesMeter(piCurrent, aHashElement.ID, '');

      while l_Lemmas.MoveNext do

       DoWord(l_Lemmas.Current);

     finally

      l_HashStorage := nil;

     end;//try..finally

    end;//l_Lemmas <> nil

   end;//l_LemmasE <> nil

  finally

   l_Lemmas := nil;

  end;//try..finally

  l_TimeDelta := cTimeDelta;

  {$IfNDef nsTest}

  if (l3Date.DayOfWeek(SysUtils.Date) <> l3Date.Sunday)

     AND (l3Date.DayOfWeek(SysUtils.Date) <> l3Date.Saturday) then

  begin

   l_TimeDelta := cTimeDelta div 2;

   if (l_TimeDelta <= 1000) then

    l_TimeDelta := cTimeDelta;

  end;//l3Date.DayOfWeek(SysUtils.Date) <> l3Date.Sunday

  {$EndIf  nsTest}

  if l3IsTimeElapsed(l_Start, l_TimeDelta) then

  begin

   // - здесь будем новые дельты строить

   if (aBaseName <> '') then

   begin

    l3System.Msg2Log('Здесь будем новые дельты строить: ' + aBaseName);

    m3DBBuildIndexDelta(aBaseName, nil, true, false{aForceFullIndex});

    {$If Defined(nsTest) OR Defined(m3DBCheck)}

    Tm3StorageHolderList.DropAllFiles;

    // - давайте всё отпустим, чтобы например с ЕО не подраться

    {$IfEnd}

    l3System.Msg2Log('Закончили строить новые дельты: ' + aBaseName);

   end;//aBaseName <> ''

   l_Start := GetTickCount;

   // - новая точка отсчёта

  end;//l3IsTimeElapsed(l_Start, l_TimeDelta)

 end;//DoHash


 var

  l_OutIndex: Im3IndexedStorage;


 procedure CreateHashInfo;

 begin//CreateHashInfo

  l_HashInfo := m3COMOpenStream(l_OutIndex,

                                  l3PCharLen(AnsiString(m3HashInfoName)),

                                  m3_saWrite,

                                  true).As_IStream;

  l_StatStream := m3COMOpenStream(l_OutIndex,

                                  l3PCharLen(AnsiString(m3StatStreamName)),

                                  m3_saWrite,

                                  true).As_IStream;

 end;//CreateHashInfo


 procedure WriteRange;

 var

  l_St : Il3Stream;

 begin//WriteRange

  l_St := m3COMOpenStream(l_OutIndex,

                          l3PCharLen('range'),

                          m3_saWrite,

                          true,

                          false);

  try

   if (l_St <> nil) then

    aDumper.Range.WriteTo(l_St.As_IStream);

  finally

   l_St := nil;

  end;//try..finally

 end;//WriteRange


 {$IfDef m3UseProxyIndexStream}

  {$Define m3LocUseProxyIndexStream}

 {$EndIf m3UseProxyIndexStream}


var

 l_Enum : Im3IndexHashElementEnumerator;

 l_UpdateName : String;

 {$IfDef m3LocUseProxyIndexStream}

 l_Temp : Tl3Stream;

 l_H : Im3StorageHolder;

 {$EndIf m3LocUseProxyIndexStream}

//#UC END# *5B2E3DB902C7_5AEC481D0310_var*

begin

//#UC START# *5B2E3DB902C7_5AEC481D0310_impl*

 l_UpdateName := Il3CString_ToFileName(anUpdateName);

 {$IfNDef m3UseVersionedIndexDeltas}

 if Tm3StorageManager.StorageFileExists(l_UpdateName) then

  Tm3StorageManager.DeleteStorageFile(l_UpdateName);

 {$EndIf m3UseVersionedIndexDeltas}

 {$IfDef m3LocUseProxyIndexStream}

 if not Tm3StorageService.Instance.UseSplitted then

 begin

  l_Temp := Tm3ProxyIndexStream.Create(l_UpdateName);

  try

   l_H := Tm3FullModeExclusiveStorageManager.MakeInterface(l_Temp, c_m3UseVersionedIndexDeltas, def_FullModeStorageUseCompression);

   try

    l_OutIndex := l_H.Storage;

   finally

    l_H := nil;

   end;//try..finally

  finally

   FreeAndNil(l_Temp);

  end;//try..finally

 end//not Tm3StorageService.Instance.UseSplitted

 else

  l_OutIndex := Tm3FullModeExclusiveStorageManager.MakeInterface(l_UpdateName, c_m3UseVersionedIndexDeltas);

 {$Else  m3LocUseProxyIndexStream}

 l_OutIndex := Tm3FullModeExclusiveStorageManager.MakeInterface(l_UpdateName, c_m3UseVersionedIndexDeltas);

 {$EndIf m3LocUseProxyIndexStream}

 try

  if Assigned(aFilesMeter) then

   aFilesMeter(piStart, Cm2HASDefCount, 'Обновление индекса: ' + ExtractFileName(l_UpdateName));

  try

   m3CreateSummary(l_OutIndex);

   if Assigned(aFilesMeter) then

    aFilesMeter(piCurrent, 0, 'Запись списка документов');

   m3WriteModified(l_OutIndex, aDumper.ModifiedDocuments, aDumper.TimeStamp);

   if aWriteVersions then

   begin

    if Assigned(aFilesMeter) then

     aFilesMeter(piCurrent, 0, 'Запись версий документов');

    m3WriteVersions(l_OutIndex, aDumper.IndexedVersions);

   end;//aWriteVersions

   CreateHashInfo;

   WriteRange;

   try

    l_OutHash := m3CreateHashData(l_OutIndex, l3PCharLen(AnsiString(m3HashDataName)));

    try

     l_OutIndex := nil; // - типа можно отпустить

     if Assigned(aFilesMeter) then

      aFilesMeter(piCurrent, 0, 'Обновление индекса: ' + ExtractFileName(l_UpdateName));

     l_Enum := aDumper.GetEnumerator;

     aDumper := nil;

     // - типа можно отпустить

     try

      l_Start := GetTickCount;

      Assert(l_Enum <> nil);

      while l_Enum.MoveNext do

       if not DoHash(l_Enum.Current) then

        break;

     finally

      l_Enum := nil;

     end;//try..finally

    finally

     l_OutHash := nil;

    end;//try..finally

   finally

    l_HashInfo := nil;

   end;//try..finally

  finally

   if Assigned(aFilesMeter) then

    aFilesMeter(piEnd, 0, '');

  end;//try..finally

 finally

  l_OutIndex := nil;

 end;//try..finally

//#UC END# *5B2E3DB902C7_5AEC481D0310_impl*

end;//m3MergeFromDumper


procedure m3CreateSummary(const aStorage: Im3IndexedStorage);

//#UC START# *5B2E42FB02E0_5AEC481D0310_var*

var

 l_Stream : Il3Stream;

//#UC END# *5B2E42FB02E0_5AEC481D0310_var*

begin

//#UC START# *5B2E42FB02E0_5AEC481D0310_impl*

 l_Stream := m3COMOpenStream(aStorage,

                             l3PCharLen(m3SummaryName),

                             m3_saWrite,

                             //m3_saReadWrite,

                             true);

 l_Stream := nil;

//#UC END# *5B2E42FB02E0_5AEC481D0310_impl*

end;//m3CreateSummary


function m3DBMergeIndexDeltas(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc): Boolean;

//#UC START# *5B4F2446023E_5AEC481D0310_var*

var

 l_DBHelper : Im3DBHelper;

 l_E : Im3FilesEnumerable;

//#UC END# *5B4F2446023E_5AEC481D0310_var*

begin

//#UC START# *5B4F2446023E_5AEC481D0310_impl*

 Result := false;

 try

  l_DBHelper := Tm3DBHelper.Make(aBaseName);

  try

   l_E := l_DBHelper.GetIndexDeltasFiles;

   if not l_DBHelper.LockDeltas(l_E) then

   begin

    Result := false;

    Exit;

   end;//not l_DBHelper.Lock

   try

    Result := m3DBMergeIndexDeltas(l_DBHelper, l_E, aFilesMeter);

   finally

    l_DBHelper.UnlockDeltas;

   end;//try..finally

  finally

   l_DBHelper := nil;

  end;//try..finally

 except

  on E: Exception do

  begin

   l3System.Exception2Log(E);

   Result := false;

  end;//on E: Exception

  else

   Result := false;

 end;//try..except

//#UC END# *5B4F2446023E_5AEC481D0310_impl*

end;//m3DBMergeIndexDeltas


function m3DBIndexUpdated(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc;

 aForceMerge: Boolean): Boolean;

//#UC START# *5B4F536603A6_5AEC481D0310_var*


var

 l_WasUpdatedFile : Boolean;


 function IndexUpdated: Boolean;


  function MakeDB(const aName: String): Im3DB;

  begin

   Result := Tm3DB.Make(aName,

          //cbMakeCopy.Checked AND cbFromOld.Checked,

          nil,//Yield,

          nil,//FileMeter.ProgressProc_ev,

          aFilesMeter,//FilesMeter.ProgressProc_ev,

          nil,//FilesProcessed,

          nil //lProgressor.FilesProcessedExProc//FilesProcessedEx

          );

  end;


 const

  cDeleted = '%Deleted:';

 var

  l_DBHelper : Im3DBHelper;

  l_Filer    : Tl3CustomFiler;

  l_Nums     : Tm3StorageElementIDList;

  l_S        : AnsiString;

  l_Num      : Integer;

  l_DB       : Im3DB;

  l_Range    : Im3DBRange;

  l_IndexName : Il3CString;

  l_UpdatedFileName : String;

  l_UpdatedFileNameBack : String;

  l_E : Im3FilesEnumerable;

 begin//IndexUpdated

  Result := false;

  l_DBHelper := Tm3DBHelper.Make(aBaseName);

  try

   l_E := l_DBHelper.GetIndexDeltasFiles;

   if not l_DBHelper.LockDeltas(l_E) then

   begin

    Result := false;

    Exit;

   end;//not l_DBHelper.Lock

   try

    l_UpdatedFileName := aBaseName + '.updated';

    if not FileExists(l_UpdatedFileName) then

    begin

     Result := true;

     // - чтобы дальше дельта индексировалась

     //Result := false;

     Exit;

    end;//not FileExists(l_UpdatedFileName)

    l_WasUpdatedFile := true;

    l_Nums := Tm3StorageElementIDList.Create;

    try

     l_Filer := Tl3DOSFiler.Make(l_UpdatedFileName, l3_fmRead, false);

     try

      l_Filer.Open;

      try

       while not l_Filer.EOF do

       begin

        l_S := l3PCharLen2String(l_Filer.ReadLn);

        if (l_S <> '') then

        begin

         if AnsiStartsText('%', l_S) then

         begin

          if AnsiStartsText(cDeleted, l_S) then

          begin

           System.Delete(l_S, 1, Length(cDeleted));

           if TryStrToInt(l_S, l_Num) then

           begin

            l_Nums.Add(l_Num);

           end;//TryStrToInt(l_S, l_Num)

          end;//AnsiStartsText(cDeleted, l_S)

         end//AnsiStartsText('%', l_S)

         else

         begin

          if TryStrToInt(l_S, l_Num) then

          begin

           l_Nums.Add(l_Num);

           //break;

          end;//TryStrToInt(l_S, l_Num)

         end;//AnsiStartsText('%', l_S)

        end;//l_S <> ''

       end;//while not l_Filer.EOF

      finally

       l_Filer.Close;

      end;//try..finally

     finally

      FreeAndNil(l_Filer);

     end;//try..finally

     if not l_Nums.Empty then

     begin

      l_DB := MakeDB(aBaseName);

      try

       l_Range := l_DB.FilesInList(l_Nums);

       try

        l_DB := nil;

        l_IndexName := l_DBHelper.NewIndexDeltaName(l_E);

        Result := m3IndexRange(l_Range, l_IndexName, true, aFilesMeter, l_DB) > 0;

        if not Result then

         Tm3StorageManager.DeleteStorageFile(l_IndexName)

        else

         l_DBHelper.AddNewIndexDeltaName(l_IndexName, l_E);

       finally

        l_Range := nil;

       end;//try..finally

      finally

       l_DB := nil;

      end;//try..finally

     end//not l_Nums.Empty

     else

      Result := true;

      // - чтобы дальше дельта индексировалась

    finally

     FreeAndNil(l_Nums);

    end;//try..finally

    if Result then

     if (l_UpdatedFileName <> '') then

     begin

      l_UpdatedFileNameBack := l_UpdatedFileName + cBackExt;

      SysUtils.DeleteFile(l_UpdatedFileNameBack);

      SysUtils.RenameFile(l_UpdatedFileName, l_UpdatedFileNameBack);

      //SysUtils.DeleteFile(l_UpdatedFileName);

     end;//l_UpdatedFileName <> ''

   finally

    l_DBHelper.UnlockDeltas;

   end;//try..finally

  finally

   l_DBHelper := nil;

  end;//try..finally

 end;//IndexUpdated


//#UC END# *5B4F536603A6_5AEC481D0310_var*

begin

//#UC START# *5B4F536603A6_5AEC481D0310_impl*

 Result := false;

 l_WasUpdatedFile := false;

 if IndexUpdated then

 begin

  //Exit;

  Result := true;

  //l_WasUpdatedFile := false;

  // - не объединяем дельты - пока не разберусь со скоростью.

  if aForceMerge OR l_WasUpdatedFile then

  begin

   if m3DBBuildAndMergeIndexDeltas(aBaseName, aFilesMeter) then

   begin

    Result := true;

   end;//m3DBBuildAndMergeIndexDeltas(aBaseName, aFilesMeter)

  end//l_WasUpdatedFile

  else

  begin

   if m3DBBuildIndexDelta(aBaseName, aFilesMeter, false, false{aForceFullIndex}) then

   begin

    Result := true;

   end;//m3DBBuildIndexDelta(aBaseName, aFilesMeter, false, false)

  end;//l_WasUpdatedFile

 end;//IndexUpdated

//#UC END# *5B4F536603A6_5AEC481D0310_impl*

end;//m3DBIndexUpdated


function m3DBBuildAndMergeIndexDeltas(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc): Boolean;

//#UC START# *5B505AF40264_5AEC481D0310_var*

//#UC END# *5B505AF40264_5AEC481D0310_var*

begin

//#UC START# *5B505AF40264_5AEC481D0310_impl*

 Result := false;

 if m3DBBuildIndexDelta(aBaseName, aFilesMeter, false, false{aForceFullIndex}) then

 begin

  m3DBMergeIndexDeltas(aBaseName, aFilesMeter);

  Result := true;

 end;//m3DBBuildIndexDelta(l_N1, aFilesMeter, false, false)

//#UC END# *5B505AF40264_5AEC481D0310_impl*

end;//m3DBBuildAndMergeIndexDeltas


function m3DBMergeIndexDeltas(const aDBHelper: Im3DBHelper;

 var aDeltas: Im3FilesEnumerable;

 aFilesMeter: Tl3ProgressProc): Boolean;

//#UC START# *5B5087B00034_5AEC481D0310_var*


var

 l_Attributes : Il3StringsEnumerable;

 l_ContextAttributes : Il3StringsEnumerable;

 l_UpdateName : Il3CString;

 l_BaseName : TFileName;


 function DoMerge: Boolean;


 var

  l_Full : Boolean;


  procedure MergeText;

  var

   l_Dumper : Im3IndexDumper;

   l_US : Boolean;

  begin//MergeText

   l_Dumper := Tm3IndexDumperJoin.MakeFromFiles(aDeltas);

   try

    if (l_Dumper <> nil) then

    begin

     l_US := Tm3StorageService.Instance.SetUseSplitted(false);

     try

      m3MergeFromDumper(l_Dumper,

                        l_UpdateName,

                        not l_Full{true}, // - WriteVersions

                        aFilesMeter,

                        '' // - имя базы

                        );

      l_Dumper := nil;

     finally

      Tm3StorageService.Instance.SetUseSplitted(l_US);

     end;//try..finally

     Result := true;

    end;//l_Dumper <> nil

   finally

    l_Dumper := nil;

   end;//try..finally

  end;//MergeText


 var

  l_AttrDeltas : Im3FilesEnumerable;

  l_AttrUpdateName : Il3CString;


  procedure MergeContextAttr;

  var

   l_ContextAttrDumper : Im3IndexDumper;

   l_US : Boolean;

  begin//MergeContextAttr

   l_ContextAttrDumper := Tm3IndexDumperJoin.MakeFromFiles(l_AttrDeltas);

   try

    if (l_ContextAttrDumper <> nil) then

    begin

     l_US := Tm3StorageService.Instance.SetUseSplitted(false);

     try

      m3MergeFromDumper(l_ContextAttrDumper,

                       l_AttrUpdateName,

                       not l_Full{true}, // - WriteVersions

                       aFilesMeter,

                       '' // - имя базы

                       );

      l_ContextAttrDumper := nil;

     finally

      Tm3StorageService.Instance.SetUseSplitted(l_US);

     end;//try..finally

    end;//l_ContextAttrDumper <> nil

   finally

    l_ContextAttrDumper := nil;

   end;//try..finally

  end;//MergeContextAttr


 var

  l_IsDirect : Boolean;


  procedure MergeAttr;

  var

   l_AttrDumper : Im3AttrIndexDumper;

   l_US : Boolean;

  begin//MergeAttr

   l_AttrDumper := Tm3AttrIndexDumperJoin.MakeFromFiles(l_AttrDeltas, l_IsDirect);

   try

    if (l_AttrDumper <> nil) then

    begin

     l_US := Tm3StorageService.Instance.SetUseSplitted(false);

     try

      Tm3AttrIndexDumper.MakeCopyFromDumper(l_AttrDumper,

                       l_AttrUpdateName,

                       not l_Full{true}, // - WriteVersions

                       aFilesMeter,

                       '',

                       l_IsDirect

                       );

      l_AttrDumper := nil;

     finally

      Tm3StorageService.Instance.SetUseSplitted(l_US);

     end;//try..finally

    end;//l_AttrDumper <> nil

   finally

    l_AttrDumper := nil;

   end;//try..finally

  end;//MergeAttr


  procedure MergeIntegerAttributes;

  var

   l_IsOurDirect : Boolean;

   l_AttributesIt : Il3StringsEnumerator;

   l_AttrBaseName : TFileName;

   l_Count : Integer;

   l_AttrDBHelper : Im3DBHelper;

  begin//MergeIntegerAttributes

   l_Count := 0;

   //if (l_Count >= 2) then

   begin

    for l_IsOurDirect := false to true do

    begin

     l_IsDirect := l_IsOurDirect;

     l_AttributesIt := l_Attributes.GetEnumerator;

     if (l_AttributesIt <> nil) then

     begin

      while l_AttributesIt.MoveNext do

      begin

       l_AttrBaseName := Il3CString_ToFileName(Tm3AttrIndexDumperJoin.MakeNameForAttr(Il3CString_C(l_BaseName), l_AttributesIt.Current.AsString, l_IsDirect));

       l_AttrDBHelper := Tm3DBHelper.Make(l_AttrBaseName);

       try

        l_AttrDBHelper.PurgeIndexDeltas;

        l_AttrDeltas := l_AttrDBHelper.GetIndexDeltasFiles;

        //l_AttrDeltas := Tm3AttrIndexDumperJoin.MakeFilesForAttr(aDeltas, l_AttributesIt.Current.AsString, l_IsDirect);

        l_Count := l_AttrDeltas.Count;

        if (l_Count >= 2) then

        begin

         if l_AttrDBHelper.TryLockDeltas(l_AttrDeltas) then

          try

           l_AttrUpdateName := l_AttrDBHelper.NewIndexDeltaName(l_AttrDeltas);

           //l_AttrUpdateName := Tm3AttrIndexDumperJoin.MakeNameForAttr(l_UpdateName, l_AttributesIt.Current.AsString, l_IsDirect);


           MergeAttr;

           // - СПЕЦИАЛЬНО выделено в процедуру, чтобы интерфейсы ОСВОБОЖДАЛИСЬ


           l_AttrDBHelper.PurgeIndexDeltas;

           l_AttrDBHelper.DeleteIndexDeltas(l_AttrDeltas, true{false});

           l_AttrDBHelper.AddNewIndexDeltaName(l_AttrUpdateName, l_AttrDeltas);

          finally

           l_AttrDBHelper.UnlockDeltas;

          end;//try..finally

        end;//l_Count >= 2

       finally

        l_AttrDBHelper := nil;

       end;//try..finally

      end;//while l_AttributesIt.MoveNext

     end;//l_AttributesIt <> nil

    end;//for l_IsDirect

   end;//l_Count >= 2

  end;//MergeIntegerAttributes


 var

  l_NeedMergeText : Boolean;

  l_AttributesIt : Il3StringsEnumerator;

  l_AttrBaseName : TFileName;

  l_Count : Integer;

  l_AttrDBHelper : Im3DBHelper;

 begin//DoMerge

  Result := false;


  l_Full := false;


  MergeIntegerAttributes;

  // - https://mdp.garant.ru/pages/viewpage.action?pageId=902333600


  l_Count := 0;

  if (aDeltas <> nil) then

  begin

   l_Count := aDeltas.Count;

  end;//aDeltas <> nil

  if (l_Count >= 2) then

  begin

   l_NeedMergeText := not g_MergeOnlyAttributes;

   if l_NeedMergeText then

   begin

    MergeText;

    aDBHelper.DeleteIndexDeltas(aDeltas, true);

    aDBHelper.AddNewIndexDeltaName(l_UpdateName, aDeltas);

   end;//l_NeedMergeText

  end;//l_Count >= 2


  //if (l_Count >= 2) then

  begin

   l_AttributesIt := l_ContextAttributes.GetEnumerator;

   if (l_AttributesIt <> nil) then

    while l_AttributesIt.MoveNext do

    begin

     l_AttrBaseName := Il3CString_ToFileName(Tm3IndexDumperJoin.MakeNameForAttr(Il3CString_C(l_BaseName), l_AttributesIt.Current.AsString));

     l_AttrDBHelper := Tm3DBHelper.Make(l_AttrBaseName);

     try

      l_AttrDBHelper.PurgeIndexDeltas;

      l_AttrDeltas := l_AttrDBHelper.GetIndexDeltasFiles;

      //l_AttrDeltas := Tm3IndexDumperJoin.MakeFilesForAttr(aDeltas, l_AttributesIt.Current.AsString);

      l_Count := l_AttrDeltas.Count;

      if (l_Count >= 2) then

      begin

       if l_AttrDBHelper.TryLockDeltas(l_AttrDeltas) then

        try

         l_AttrUpdateName := l_AttrDBHelper.NewIndexDeltaName(l_AttrDeltas);

         //l_AttrUpdateName := Tm3IndexDumperJoin.MakeNameForAttr(l_UpdateName, l_AttributesIt.Current.AsString);


         MergeContextAttr;

         // - СПЕЦИАЛЬНО выделено в процедуру, чтобы интерфейсы ОСВОБОЖДАЛИСЬ


         l_AttrDBHelper.PurgeIndexDeltas;

         l_AttrDBHelper.DeleteIndexDeltas(l_AttrDeltas, true{false});

         l_AttrDBHelper.AddNewIndexDeltaName(l_AttrUpdateName, l_AttrDeltas);

        finally

         l_AttrDBHelper.UnlockDeltas;

        end;//try..finally

      end;//l_Count >= 2

     finally

      l_AttrDBHelper := nil;

     end;//try..finally

    end;//while l_AttributesIt.MoveNext

  end;//l_Count >= 2


  MergeIntegerAttributes;

  // - https://mdp.garant.ru/pages/viewpage.action?pageId=902333600


 end;//DoMerge


var

 l_Searcher : Im3AttrIndexSearcher;

//#UC END# *5B5087B00034_5AEC481D0310_var*

begin

//#UC START# *5B5087B00034_5AEC481D0310_impl*

 Result := false;

 l_BaseName := aDBHelper.BaseName;

 aDBHelper.PurgeIndexDeltas;

 l_Searcher := Tm3AttrIndexSearcher.Make(l_BaseName);

 try

  l_ContextAttributes := l_Searcher.ContextAttributesNames;

  l_Attributes := l_Searcher.SearcheableAttributesNames;

  l_UpdateName := aDBHelper.NewIndexDeltaName(aDeltas);

  if DoMerge then

  begin

   Result := true;

  end;//DoMerge

 finally

  l_Searcher := nil;

 end;//try..finally

//#UC END# *5B5087B00034_5AEC481D0310_impl*

end;//m3DBMergeIndexDeltas


function m3DBUpdateIndex(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc): Boolean;

//#UC START# *5B60532E0383_5AEC481D0310_var*


var

 l_ContextAttributes : Il3StringsEnumerable;

 l_Attributes : Il3StringsEnumerable;


 function DoDumper(const aDBHelper : Im3DBHelper; const aNewIndexName : Il3CString; const aDeltas: Im3FilesEnumerable): Boolean;


 var

  l_All : Im3FilesEnumerable;


  procedure MergeText;

  var

   l_Dumper : Im3IndexDumper;

  begin//MergeText

   l_Dumper := Tm3IndexDumperJoin.MakeFromFiles(l_All);

   if (l_Dumper <> nil) then

   begin

    try

     m3MergeFromDumper(l_Dumper,

                       aNewIndexName,

                       false, // - WriteVersions

                       aFilesMeter,

                       aBaseName

                       );

     l_Dumper := nil;

     Result := true;

    finally

     l_Dumper := nil;

    end;//try..finally

   end;//l_DeltasDumper <> nil

  end;//MergeText


 var

  l_AttrUpdateName : Il3CString;

  l_AttrDeltas: Im3FilesEnumerable;


  procedure MergeContextAttr;

  var

   l_ContextAttrDumper : Im3IndexDumper;

   l_US : Boolean;

  begin//MergeContextAttr

   l_ContextAttrDumper := Tm3IndexDumperJoin.MakeFromFiles(l_AttrDeltas);

   try

    if (l_ContextAttrDumper <> nil) then

    begin

     Result := true;

     l_US := Tm3StorageService.Instance.SetUseSplitted(false);

     try

      m3MergeFromDumper(l_ContextAttrDumper,

                       l_AttrUpdateName,

                       false, // - WriteVersions

                       aFilesMeter,

                       aBaseName

                       );

      l_ContextAttrDumper := nil;

     finally

      Tm3StorageService.Instance.SetUseSplitted(l_US);

     end;//try..finally

    end;//l_ContextAttrDumper <> nil

   finally

    l_ContextAttrDumper := nil;

   end;//try..finally

  end;//MergeContextAttr


 var

  l_IsDirect : Boolean;


  procedure MergeAttr;

  var

   l_AttrDumper : Im3AttrIndexDumper;

   l_US : Boolean;

  begin//MergeAttr

   l_AttrDumper := Tm3AttrIndexDumperJoin.MakeFromFiles(l_AttrDeltas, l_IsDirect);

   try

    if (l_AttrDumper <> nil) then

    begin

     Result := true;

     l_US := Tm3StorageService.Instance.SetUseSplitted(false);

     try

      Tm3AttrIndexDumper.MakeCopyFromDumper(l_AttrDumper,

                       l_AttrUpdateName,

                       false, // - WriteVersions

                       aFilesMeter,

                       aBaseName,

                       l_IsDirect

                       );

      l_AttrDumper := nil;

     finally

      Tm3StorageService.Instance.SetUseSplitted(l_US);

     end;//try..finally

    end;//l_AttrDumper <> nil

   finally

    l_AttrDumper := nil;

   end;//try..finally

  end;//MergeAttr


 const

  cTimeDelta = 14 * 60 * 60 * 1000;

  // - 14 часов

 var

  l_Start : Cardinal;


  function MergeIntegerAttributes: Boolean;

  var

   l_IsOurDirect : Boolean;

   l_AttributesIt : Il3StringsEnumerator;

   l_AttrBaseName : TFileName;

   l_AttrDBHelper: Im3DBHelper;

   l_NeedJoin : Boolean;

   l_IndexMainFiles : Im3FilesEnumerable;

   l_IndexMainFilesCount : Integer;

   l_AttrDeltasForDelete: Im3FilesEnumerable;

  begin//MergeIntegerAttributes

   Result := true;

   l_NeedJoin := false;

   for l_IsOurDirect := false to true do

   begin

    l_IsDirect := l_IsOurDirect;

    l_AttributesIt := l_Attributes.GetEnumerator;

    if (l_AttributesIt <> nil) then

    begin

     while l_AttributesIt.MoveNext do

     begin

      l_AttrBaseName := Il3CString_ToFileName(Tm3AttrIndexDumperJoin.MakeNameForAttr(Il3CString_C(aBaseName), l_AttributesIt.Current.AsString, l_IsDirect));

      l_AttrDBHelper := Tm3DBHelper.Make(l_AttrBaseName);

      try

       l_AttrDBHelper.PurgeIndexDeltas;

       l_NeedJoin := false;

       l_AttrDeltas := l_AttrDBHelper.GetAllIndexFiles;

       l_AttrDeltasForDelete := l_AttrDBHelper.GetIndexDeltasFiles;

       // - тут надо снимать, чтобы не затереть чужое


       if l_AttrDBHelper.TryLockDeltas(l_AttrDeltas) then

        try

         l_AttrUpdateName := l_AttrDBHelper.NewIndexName;

         if (l_AttrDeltas.Count > 1) then

          l_NeedJoin := true

         else

         begin

          l_IndexMainFiles := l_AttrDBHelper.GetIndexMainFiles;

          if (l_IndexMainFiles = nil) then

           l_IndexMainFilesCount := 0

          else

           l_IndexMainFilesCount := l_IndexMainFiles.Count;

          if (l_IndexMainFilesCount = 0)

             AND (l_AttrDeltas.Count = 1) then

             // - есть только ОДНА дельта, но нету индекса, эту дельту надо перемыть в индекс

             // возможно стоит НЕ ПЕРЕМЫВАТЬ, а объявить дельту ИНДЕКСОМ (переименовать)

           l_NeedJoin := true;

         end;//l_AttrDeltas.Count > 1

         if l_NeedJoin then

          MergeAttr;


         l_AttrDBHelper.PurgeIndexDeltas;


         if l_NeedJoin then

         begin

          l_AttrDBHelper.SetNewIndexName(l_AttrUpdateName);

          l_AttrDBHelper.DeleteIndexDeltas(l_AttrDeltasForDelete, true{false});

         end;//l_NeedJoin

        finally

         l_AttrDBHelper.UnlockDeltas;

        end;//try..finally


       if l3IsTimeElapsed(l_Start, cTimeDelta) then

       begin

        l3System.Msg2Log('Прерываем по таймауту: ' + aBaseName);

        Result := false;

        Exit;

       end;//l3IsTimeElapsed(l_Start, cTimeDelta)

      finally

       l_AttrDBHelper := nil;

      end;//try..finally

     end;//while l_AttributesIt.MoveNext

    end;//l_AttributesIt <> nil

   end;//for l_IsDirect

  end;//MergeIntegerAttributes


 var

  l_AttributesIt : Il3StringsEnumerator;

  l_AttrBaseName : TFileName;

  l_AttrDBHelper: Im3DBHelper;

  l_IndexMainFiles : Im3FilesEnumerable;

  l_IndexMainFilesCount : Integer;

  l_NewIndexName : Il3CString;

  l_Deltas: Im3FilesEnumerable;

  l_AttrDeltasForDelete: Im3FilesEnumerable;

  l_NeedMergeText : Boolean;

  l_NeedJoin : Boolean;

 begin//DoDumper

  Result := false;

  l_NeedJoin := false;

  l_Start := GetTickCount;

  // - новая точка отсчёта


  if not MergeIntegerAttributes then

   Exit;


  l_All := aDBHelper.GetAllIndexFiles;

  if (l_All <> nil) then

  begin

   if (l_All.Count > 1) then

   // - есть что объединять

   begin

    l_NeedMergeText := not g_MergeOnlyAttributes;

    if l_NeedMergeText then

    begin

     MergeText;

     l_NewIndexName := aNewIndexName;

     aDBHelper.SetNewIndexName(l_NewIndexName);

     l_Deltas := aDeltas;

     aDBHelper.DeleteIndexDeltas(l_Deltas, true);

    end;//l_NeedMergeText

    if l3IsTimeElapsed(l_Start, cTimeDelta) then

    begin

     l3System.Msg2Log('Прерываем по таймауту: ' + aBaseName);

     Exit;

    end;//l3IsTimeElapsed(l_Start, cTimeDelta)

   end;//l_All.Count > 1

  end;//l_All <> nil


  l_AttributesIt := l_ContextAttributes.GetEnumerator;

  if (l_AttributesIt <> nil) then

  begin

   while l_AttributesIt.MoveNext do

   begin

    l_AttrBaseName := Il3CString_ToFileName(Tm3IndexDumperJoin.MakeNameForAttr(Il3CString_C(aBaseName), l_AttributesIt.Current.AsString));

    l_AttrDBHelper := Tm3DBHelper.Make(l_AttrBaseName);

    try

     l_AttrDBHelper.PurgeIndexDeltas;

     l_NeedJoin := false;

     l_AttrDeltas := l_AttrDBHelper.GetAllIndexFiles;

     l_AttrDeltasForDelete := l_AttrDBHelper.GetIndexDeltasFiles;

     // - тут надо снимать, чтобы не затереть чужое


     if l_AttrDBHelper.TryLockDeltas(l_AttrDeltas) then

      try

       l_AttrUpdateName := l_AttrDBHelper.NewIndexName;

       if (l_AttrDeltas.Count > 1) then

        l_NeedJoin := true

       else

       begin

        l_IndexMainFiles := l_AttrDBHelper.GetIndexMainFiles;

        if (l_IndexMainFiles = nil) then

         l_IndexMainFilesCount := 0

        else

         l_IndexMainFilesCount := l_IndexMainFiles.Count;

        if (l_IndexMainFilesCount = 0)

           AND (l_AttrDeltas.Count = 1) then

           // - есть только ОДНА дельта, но нету индекса, эту дельту надо перемыть в индекс

           // возможно стоит НЕ ПЕРЕМЫВАТЬ, а объявить дельту ИНДЕКСОМ (переименовать)

         l_NeedJoin := true;

       end;//l_AttrDeltas.Count > 1

       if l_NeedJoin then

        MergeContextAttr;


       l_AttrDBHelper.PurgeIndexDeltas;


       if l_NeedJoin then

       begin

        l_AttrDBHelper.SetNewIndexName(l_AttrUpdateName);

        l_AttrDBHelper.DeleteIndexDeltas(l_AttrDeltasForDelete, true{false});

       end;//l_NeedJoin

      finally

       l_AttrDBHelper.UnlockDeltas;

      end;//try..finally


     if l3IsTimeElapsed(l_Start, cTimeDelta) then

     begin

      l3System.Msg2Log('Прерываем по таймауту: ' + aBaseName);

      Exit;

     end;//l3IsTimeElapsed(l_Start, cTimeDelta)

    finally

     l_AttrDBHelper := nil;

    end;//try..finally

   end;//while l_AttributesIt.MoveNext

  end;//l_AttributesIt <> nil


  if not MergeIntegerAttributes then

   Exit;


 end;//DoDumper


var

 l_UpdateName : Il3CString;

 l_DBHelper : Im3DBHelper;

var

 l_E : Im3FilesEnumerable;

 // - вообще-то надо будет переделать на @l_All

 l_FN : TFileName;

 l_Ext : TFileName;

 l_Searcher : Im3AttrIndexSearcher;

 l_IndexName : Il3CString;

 l_WasIndex : Boolean;

//#UC END# *5B60532E0383_5AEC481D0310_var*

begin

//#UC START# *5B60532E0383_5AEC481D0310_impl*

 Result := false;

 try

  l_DBHelper := Tm3DBHelper.Make(aBaseName);

  try

   l_IndexName := l_DBHelper.IndexName;

   l_WasIndex := Tm3StorageManager.StorageFileExists(l_IndexName);

   m3DBIndexUpdated(aBaseName, aFilesMeter, not l_WasIndex);

   {$If Defined(nsTest) OR Defined(m3DBCheck)}

   Tm3StorageHolderList.DropAllFiles;

   // - это тут зачем?

   // Видимо потому же:

   // - давайте всё отпустим, чтобы например с ЕО не подраться

   {$IfEnd}

  finally

   l_DBHelper := nil;

  end;//try..finally

  l_DBHelper := Tm3DBHelper.Make(aBaseName);

  try

   l_E := l_DBHelper.GetIndexDeltasFiles;

   if not l_DBHelper.LockDeltas(l_E) then

   begin

    Result := false;

    Exit;

   end;//not l_DBHelper.Lock

   try

    l_DBHelper.PurgeIndexDeltas;

    l_UpdateName := l_DBHelper.NewIndexName;

    l_Searcher := Tm3AttrIndexSearcher.Make(aBaseName);

    try

     l_ContextAttributes := l_Searcher.ContextAttributesNames;

     l_Attributes := l_Searcher.SearcheableAttributesNames;

     if DoDumper(l_DBHelper, l_UpdateName, l_E) then

     begin

      Result := true;

     end//DoDumper

     else

     begin

      l_FN := Il3CString_ToFileName(l_UpdateName);

      if not g_IndexByParts then

      begin

       l_Ext := ExtractFileExt(l_FN);

       if (l_Ext = '.idx') then

       // - это я после этго добавил: https://mdp.garant.ru/pages/viewpage.action?pageId=893780516

        Result := true

       else

       if (l_Ext = '.idx001') then

       // - строили ПОЛНЫЙ индекс НЕ частями

        Result := true;

        // - нечего нам объединять

      end;//not g_IndexByParts

     end;//DoDumper

    finally

     l_Searcher := nil;

    end;//try..finally

   finally

    l_DBHelper.UnlockDeltas;

   end;//try..finally

  finally

   l_DBHelper := nil;

  end;//try..finally

 except

  on E: Exception do

  begin

   l3System.Exception2Log(E);

   Result := false;

  end;//on E: Exception

  else

   Result := false;

 end;//try..except

//#UC END# *5B60532E0383_5AEC481D0310_impl*

end;//m3DBUpdateIndex


function m3ReadModified(const aStorage: Im3IndexedStorage): Il3RangeEnumerable;

//#UC START# *5B7C17530137_5AEC481D0310_var*

var

 l_S : Il3Stream;

//#UC END# *5B7C17530137_5AEC481D0310_var*

begin

//#UC START# *5B7C17530137_5AEC481D0310_impl*

 l_S := m3COMOpenStream(aStorage,

                        l3PCharLen('modified'),

                        m3_saRead,

                        false);

 try

  Result := Il3RangeEnumerable_CreateFromStream(l_S);

 finally

  l_S := nil;

 end;//try..finally

//#UC END# *5B7C17530137_5AEC481D0310_impl*

end;//m3ReadModified


procedure m3WriteModified(const aStorage: Im3IndexedStorage;

 const aModified: Il3RangeEnumerable;

 aTimeStamp: TDateTime);

//#UC START# *6602D7070340_5AEC481D0310_var*

var

 l_Stream : IStream;

 l_Now : TDateTime;

//#UC END# *6602D7070340_5AEC481D0310_var*

begin

//#UC START# *6602D7070340_5AEC481D0310_impl*

 if (aModified <> nil) AND not aModified.Empty then

 begin

  l_Stream := m3COMOpenStream(aStorage,

                                l3PCharLen('modified'),

                                m3_saWrite,

                                //m3_saReadWrite,

                                true{false}).As_IStream;

  try

   Il3RangeEnumerable_WriteTo(aModified, l_Stream);

  finally

   l_Stream := nil;

  end;//try..finally


  if SameValue(aTimeStamp, BadDateTime) then

   l_Now := Tl3TimeService.Instance.NowStamp

  else

   l_Now := aTimeStamp;

  l_Stream := m3COMOpenStream(aStorage,

                                l3PCharLen('timestamp'),

                                m3_saWrite,

                                //m3_saReadWrite,

                                true{false}).As_IStream;

  try

   m2COMWriteBuffer(l_Stream, l_Now, SizeOf(l_Now));

  finally

   l_Stream := nil;

  end;//try..finally

 end;//(aModified <> nil) AND not aModified.Empty

//#UC END# *6602D7070340_5AEC481D0310_impl*

end;//m3WriteModified


procedure m3DBWaitIndexer;

//#UC START# *6881D6690242_5AEC481D0310_var*

//#UC END# *6881D6690242_5AEC481D0310_var*

begin

//#UC START# *6881D6690242_5AEC481D0310_impl*

 if (g_IndexDocThread <> nil) then

 begin

  if not g_IndexDocThreadstarted then

   Exit;

  //g_IndexDocThread.Terminate;

  Sleep(50);

  //Sleep(500);

  if (g_IndexDocThread <> nil) then

  begin

   {$IfDef Linux}

   g_IndexDocThread.WaitFor;

   {$Else  Linux}

   g_IndexDocThread.WaitFor;

   {$EndIf Linux}

  end;//g_IndexDocThread <> nil

 end;//g_IndexDocThread <> nil

//#UC END# *6881D6690242_5AEC481D0310_impl*

end;//m3DBWaitIndexer


function m3DBIndexDoc(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc;

 aDocNum: Integer): Boolean;

//#UC START# *6881D7680046_5AEC481D0310_var*


 function MakeDB(const aName: String): Im3DB;

 begin

  Result := Tm3DB.Make(aName,

         //cbMakeCopy.Checked AND cbFromOld.Checked,

         nil,//Yield,

         nil,//FileMeter.ProgressProc_ev,

         aFilesMeter,//FilesMeter.ProgressProc_ev,

         nil,//FilesProcessed,

         nil //lProgressor.FilesProcessedExProc//FilesProcessedEx

         );

 end;


var

 l_Nums : Tm3StorageElementIDList;

 l_IndexName : Il3CString;

 l_E : Im3FilesEnumerable;

 l_DBHelper : Im3DBHelper;

 l_DB       : Im3DB;

 l_Range    : Im3DBRange;

 l_US : Boolean;

//#UC END# *6881D7680046_5AEC481D0310_var*

begin

//#UC START# *6881D7680046_5AEC481D0310_impl*

 l_DBHelper := Tm3DBHelper.Make(aBaseName);

 try

  l_E := l_DBHelper.GetIndexDeltasFiles;

  if not l_DBHelper.LockDeltas(l_E) then

  begin

   Result := false;

   Exit;

  end;//not l_DBHelper.Lock

  try

   l_Nums := Tm3StorageElementIDList.Create;

   try

    l_Nums.Add(aDocNum);

    if not l_Nums.Empty then

    begin

     l_DB := MakeDB(aBaseName);

     try

      l_Range := l_DB.FilesInList(l_Nums);

      try

       l_DB := nil;

       l_IndexName := l_DBHelper.NewIndexDeltaName(l_E);

       l_US := Tm3StorageService.Instance.SetUseSplitted(false);

       try

        Result := m3IndexRange(l_Range, l_IndexName, true, aFilesMeter, l_DB) > 0;

       finally

        Tm3StorageService.Instance.SetUseSplitted(l_US);

       end;//try..finally

       if not Result then

        Tm3StorageManager.DeleteStorageFile(l_IndexName)

       else

        l_DBHelper.AddNewIndexDeltaName(l_IndexName, l_E);

      finally

       l_Range := nil;

      end;//try..finally

     finally

      l_DB := nil;

     end;//try..finally

    end//not l_Nums.Empty

    else

     Result := true;

     // - чтобы дальше дельта индексировалась

   finally

    FreeAndNil(l_Nums);

   end;//try..finally

  finally

   l_DBHelper.UnlockDeltas;

  end;//try..finally

 finally

  l_DBHelper := nil;

 end;//try..finally

//#UC END# *6881D7680046_5AEC481D0310_impl*

end;//m3DBIndexDoc


function m3DBIndexDocs(const aBaseName: TFileName;

 aFilesMeter: Tl3ProgressProc;

 aDocNums: Tl3IntegerList): Boolean;

//#UC START# *6881D86A00A2_5AEC481D0310_var*


 function MakeDB(const aName: String): Im3DB;

 begin

  Result := Tm3DB.Make(aName,

         //cbMakeCopy.Checked AND cbFromOld.Checked,

         nil,//Yield,

         nil,//FileMeter.ProgressProc_ev,

         aFilesMeter,//FilesMeter.ProgressProc_ev,

         nil,//FilesProcessed,

         nil //lProgressor.FilesProcessedExProc//FilesProcessedEx

         );

 end;


var

 l_Nums : Tm3StorageElementIDList;

 l_IndexName : Il3CString;

 l_E : Im3FilesEnumerable;

 l_DBHelper : Im3DBHelper;

 l_DB       : Im3DB;

 l_Range    : Im3DBRange;

 l_US : Boolean;

 l_It : Il3IntegerEnumerator;

//#UC END# *6881D86A00A2_5AEC481D0310_var*

begin

//#UC START# *6881D86A00A2_5AEC481D0310_impl*

 l_DBHelper := Tm3DBHelper.Make(aBaseName);

 try

  l_E := l_DBHelper.GetIndexDeltasFiles;

  if not l_DBHelper.LockDeltas(l_E) then

  begin

   Result := false;

   Exit;

  end;//not l_DBHelper.Lock

  try

   l_Nums := Tm3StorageElementIDList.Create;

   try

    l_It := aDocNums.GetEnumerator;

    if (l_It <> nil) then

     while l_It.MoveNext do

      l_Nums.Add(l_It.Current);

    if not l_Nums.Empty then

    begin

     l_DB := MakeDB(aBaseName);

     try

      l_Range := l_DB.FilesInList(l_Nums);

      try

       l_DB := nil;

       l_IndexName := l_DBHelper.NewIndexDeltaName(l_E);

       l_US := Tm3StorageService.Instance.SetUseSplitted(false);

       try

        Result := m3IndexRange(l_Range, l_IndexName, true, aFilesMeter, l_DB) > 0;

       finally

        Tm3StorageService.Instance.SetUseSplitted(l_US);

       end;//try..finally

       if not Result then

        Tm3StorageManager.DeleteStorageFile(l_IndexName)

       else

        l_DBHelper.AddNewIndexDeltaName(l_IndexName, l_E);

      finally

       l_Range := nil;

      end;//try..finally

     finally

      l_DB := nil;

     end;//try..finally

    end//not l_Nums.Empty

    else

     Result := true;

     // - чтобы дальше дельта индексировалась

   finally

    FreeAndNil(l_Nums);

   end;//try..finally

  finally

   l_DBHelper.UnlockDeltas;

  end;//try..finally

 finally

  l_DBHelper := nil;

 end;//try..finally

//#UC END# *6881D86A00A2_5AEC481D0310_impl*

end;//m3DBIndexDocs


procedure m3DBIndexDocAsync(const aBaseName: TFileName;

 aDocNum: Integer);

//#UC START# *6881D8A1027E_5AEC481D0310_var*

//#UC END# *6881D8A1027E_5AEC481D0310_var*

begin

//#UC START# *6881D8A1027E_5AEC481D0310_impl*

 Tm3DBIndexDocThread.Run(aBaseName, aDocNum);

 //m3DBIndexDoc(aBaseName, nil, aDocNum);

//#UC END# *6881D8A1027E_5AEC481D0310_impl*

end;//m3DBIndexDocAsync


procedure m3DBNeedIndexDoc(const aBaseName: TFileName;

 aDocNum: Integer);

//#UC START# *6881D8E90347_5AEC481D0310_var*

//#UC END# *6881D8E90347_5AEC481D0310_var*

begin

//#UC START# *6881D8E90347_5AEC481D0310_impl*

 m3DBIndexDocAsync(aBaseName, aDocNum);

 // - возможно тут будем выбирать между m3DBIndexDocAsync и m3DBIndexUpdatedAsync

 // в зависимости от наличия залочек.

 // А возможно надо сделать Thread, которая будет крутиться ожидая LockDeltas.

//#UC END# *6881D8E90347_5AEC481D0310_impl*

end;//m3DBNeedIndexDoc


procedure m3DBIndexUpdatedAsync(const aBaseName: TFileName);

//#UC START# *6881D90B0256_5AEC481D0310_var*

var

 l_T : TThread;

//#UC END# *6881D90B0256_5AEC481D0310_var*

begin

//#UC START# *6881D90B0256_5AEC481D0310_impl*

 l_T := Tm3DBIndexUpdatedThread.Create(aBaseName);

 l_T.Resume;

 //m3DBIndexUpdated(aBaseName, nil, false);

//#UC END# *6881D90B0256_5AEC481D0310_impl*

end;//m3DBIndexUpdatedAsync


procedure m3SetInternalIDtoExternalIDLink(const aBaseName: TFileName;

 anInternalID: Integer;

 anExternalID: Integer);

 {* Устанавливает связь между внутренним номером документа и внешним

https://mdp.garant.ru/pages/viewpage.action?pageId=892273772 }

//#UC START# *68D3E25E00B1_5AEC481D0310_var*

var

 l_Searcher : Im3AttrIndexSearcher;

 l_Internal : Integer;

 l_External : Integer;

 //l_BaseNameForAttr : TFileName;

 l_NeedIndex : Boolean;

 l_Builder : Tm3DocumentAttrIndexBuilder;

 l_Range : Tm3IDRange;

 l_A : Tm3AttrID;

 l_Op : Tm3GroupOperation;

 l_M : Tm3RangedDocumentsList;

 l_BN : TFileName;

 l_V : Tl3IntegerToIntegerMap;

 l_DB : Im3DB;

//#UC END# *68D3E25E00B1_5AEC481D0310_var*

begin

//#UC START# *68D3E25E00B1_5AEC481D0310_impl*

 Assert(anInternalID > 0);

 Assert(anExternalID > 0);

 l_NeedIndex := false;

 l_DB := Tm3DB.Make(aBaseName);

 try

  l_Searcher := Tm3AttrIndexSearcher.Make(l_DB);

  //l_Searcher := Tm3AttrIndexSearcher.Make(aBaseName);

  try

   l_External := -1;

   l_Internal := l_Searcher.ExternalHandleToInternalHandle(anExternalID);

   if (l_Internal = anInternalID) then

   begin

   // - связь есть

    l_External := l_Searcher.InternalHandleToExternalHandle(anInternalID);

    if (l_External = anExternalID) then

    // - проверяем в ОБЕ стороны

     Exit;

   end;//l_Internal = anInternalID

   l_NeedIndex := true;

 (*  with Tm3ExternalIDtoInternalIDMap.Instance do

   begin

    Lock;

    try

     l_NeedIndex := false;

     if not Has(anExternalID) then

     begin

      Add(anExternalID, anInternalID);

      // Тут ещё индексировать будем:

      l_NeedIndex := true;

     end//not Has(anExternalID)

     else

     begin

      if (ValueSlotByKey(anExternalID)^ <> anInternalID) then

      begin

       ValueSlotByKey(anExternalID)^ := anInternalID;

       // Тут ещё индексировать будем (замену):

       l_NeedIndex := true;

      end;//ValueSlotByKey(anExternalID)^ <> anInternalID

     end;//not Has(anExternalID)

    finally

     Unlock;

    end;//try..finally

   end;//with Tm3ExternalIDtoInternalIDMap.Instance*)


   if l_NeedIndex then

   begin

    //l_BaseNameForAttr := Il3CString_ToFileName(Tm3AttrIndexDumperJoin.MakeNameForAttr(Il3CString_C(aBaseName), Tk2Attributes.Instance.NameByID(k2_tiExternalHandle)));

    l_Range := Tl3Range_C(anInternalID);

    l_A := Tm3AttrID_C(k2_tiExternalHandle, '');

    l_Op := m3_gopSet;

    l_M := Tm3RangedDocumentsList.Create;

    try

     l_M.Add(anInternalID);

     l_V := nil;

     l_BN := aBaseName + '.idxdelta';

     l_Builder := Tm3DocumentAttrIndexBuilder.Create(l_BN, l_A, l_M.AsEnumerable, l_V, @l_Range, false, l_Op, l_DB);

     try

      l_Builder.AddHandle(anInternalID, anExternalID);

      l_Builder.Save;

     finally

      FreeAndNil(l_Builder);

     end;//try..finally

     l_Builder := Tm3DocumentAttrIndexBuilder.Create(l_BN, l_A, l_M.AsEnumerable, l_V, @l_Range, true, l_Op, l_DB);

     try

      l_Builder.AddHandle(anExternalID, anInternalID);

      l_Builder.Save;

     finally

      FreeAndNil(l_Builder);

     end;//try..finally

    finally

     FreeAndNil(l_M);

    end;//try..finally

   end;//l_NeedIndex

  finally

   l_Searcher := nil;

  end;//try..finally

 finally

  l_DB := nil;

 end;//try..finally

//#UC END# *68D3E25E00B1_5AEC481D0310_impl*

end;//m3SetInternalIDtoExternalIDLink


procedure m3WriteModified(const aStorage: Im3IndexedStorage;

 const aModified: Il3RangeEnumerable);

//#UC START# *68EAABFC012B_5AEC481D0310_var*

//#UC END# *68EAABFC012B_5AEC481D0310_var*

begin

//#UC START# *68EAABFC012B_5AEC481D0310_impl*

 m3WriteModified(aStorage, aModified, BadDateTime);

//#UC END# *68EAABFC012B_5AEC481D0310_impl*

end;//m3WriteModified


procedure m3WriteStringToDocument(const aDB: Im3DB;

 aDocID: Integer;

 anAttrID: Integer;

 const aString: Tl3WString);

//#UC START# *68F083AE00D4_5AEC481D0310_var*

var

 l_S : Tl3Str;

 l_Doc : Im3DBDocument;

 l_DocStream : IStream;

//#UC END# *68F083AE00D4_5AEC481D0310_var*

begin

//#UC START# *68F083AE00D4_5AEC481D0310_impl*

 Assert(aDB <> nil);

 if (anAttrID < 0) then

  Exit;

 if (anAttrID > k2_tiLast) then

  Exit;

 l_Doc := aDB.GetDocument(aDocID);

 if (l_Doc <> nil) then

 begin

  try

   l_DocStream := l_Doc.Open(m3_saWrite, m3_dsNames, anAttrID);

  except

   on Em3NoStreamForWrite do

    l_DocStream := nil;

  end;//try..except

  try

   if (l_DocStream <> nil) then

   begin

    if not l3IsNil(aString) then

    begin

     l_S.Init(aString, CP_Unicode);

     try

      m2COMWriteBuffer(l_DocStream, l_S.S^, l_S.SLen * SizeOf(WideChar));

     finally

      l_S.Clear;

     end;//try..finally

    end;//not l3IsNil(aString)

   end;//l_DocStream <> nil

  finally

   l_DocStream := nil;

  end;//try..finally

 end;//l_Doc <> nil

//#UC END# *68F083AE00D4_5AEC481D0310_impl*

end;//m3WriteStringToDocument


constructor Tm3FilteredRange.Create(aVersions: Tl3IntegerToIntegerMap;

 const anOther: Im3DBRange);

//#UC START# *5B06E840019F_5B06E7C7009D_var*

//#UC END# *5B06E840019F_5B06E7C7009D_var*

begin

//#UC START# *5B06E840019F_5B06E7C7009D_impl*

 Assert(aVersions <> nil);

 Assert(anOther <> nil);

 inherited Create;

 aVersions.SetRefTo(f_Versions);

 f_Other := anOther;

//#UC END# *5B06E840019F_5B06E7C7009D_impl*

end;//Tm3FilteredRange.Create


class function Tm3FilteredRange.MakePrim(aVersions: Tl3IntegerToIntegerMap;

 const anOther: Im3DBRange): Im3DBRange;

var

 l_Inst : Tm3FilteredRange;

begin

 l_Inst := Create(aVersions, anOther);

 try

  Result := l_Inst;

 finally

  l_Inst.Free;

 end;//try..finally

end;//Tm3FilteredRange.MakePrim


class function Tm3FilteredRange.Make(aVersions: Tl3IntegerToIntegerMap;

 const anOther: Im3DBRange): Im3DBRange;

//#UC START# *5B5080160141_5B06E7C7009D_var*

//#UC END# *5B5080160141_5B06E7C7009D_var*

begin

//#UC START# *5B5080160141_5B06E7C7009D_impl*

 if (aVersions = nil) OR aVersions.Empty then

  Result := anOther

 else

  Result := MakePrim(aVersions, anOther);

//#UC END# *5B5080160141_5B06E7C7009D_impl*

end;//Tm3FilteredRange.Make


{$If NOT Defined(l3NoSRT)}

function Tm3FilteredRange.SetRefTo(var thePlace: Tm3FilteredRange): Boolean;

begin

 if (thePlace = Self) then

  Result := false

 else

 begin

  Result := true;

  thePlace.Free;

  thePlace := Self.Use;

 end;//thePlace = Self

end;//Tm3FilteredRange.SetRefTo

{$IfEnd} // NOT Defined(l3NoSRT)


function Tm3FilteredRange.DB: Im3DB;

//#UC START# *4720847C026F_5B06E7C7009D_var*

//#UC END# *4720847C026F_5B06E7C7009D_var*

begin

//#UC START# *4720847C026F_5B06E7C7009D_impl*

 Result := f_Other.DB;

//#UC END# *4720847C026F_5B06E7C7009D_impl*

end;//Tm3FilteredRange.DB


function Tm3FilteredRange.Mul(const aRange: Im3DBRange): Im3DBRange;

 {* Пересекает выборки. }

//#UC START# *472718E303CA_5B06E7C7009D_var*

//#UC END# *472718E303CA_5B06E7C7009D_var*

begin

//#UC START# *472718E303CA_5B06E7C7009D_impl*

 Result := Self;

 Assert(false);

//#UC END# *472718E303CA_5B06E7C7009D_impl*

end;//Tm3FilteredRange.Mul


{$If NOT Defined(m3NoEVD)}

function Tm3FilteredRange.Iterate(const aFilter: Im3TagGenerator;

 aNeedWriteToBase: Boolean;

 const aParts: Tm3DocPartSet = m3_AllDocParts): Integer;

//#UC START# *472719650005_5B06E7C7009D_var*

//#UC END# *472719650005_5B06E7C7009D_var*

begin

//#UC START# *472719650005_5B06E7C7009D_impl*

 Result := m3IterateRangeViaFilter(Self, aFilter, aNeedWriteToBase, aParts, Self.DB);

//#UC END# *472719650005_5B06E7C7009D_impl*

end;//Tm3FilteredRange.Iterate

{$IfEnd} // NOT Defined(m3NoEVD)


function Tm3FilteredRange.IterateF(anAction: Tm3FilerAction;

 const aParts: Tm3DocPartSet = m3_AllDocParts): Integer;

//#UC START# *47271A4301A1_5B06E7C7009D_var*


var

 l_Count  : Integer absolute Result;


 function DoFiler(aFiler: Tl3CustomFiler; const anIndex : Tm3DBStreamIndex): Boolean;

 var

  l_IDSource : Im3VersionedStorageElementIDSource;

  l_ID : Tm3VersionedStorageElementID;

  l_Ver : Integer;

  l_Index : Integer;

 begin//DoFiler

  if Supports(aFiler.COMStream, Im3VersionedStorageElementIDSource, l_IDSource) then

   try

    l_ID := l_IDSource.VersionedID;

    if f_Versions.Has(l_ID.rID, l_Index) then

    begin

     l_Ver := f_Versions.ItemSlot(l_Index).rValue;

     if (l_Ver = l_ID.rVersion) then

     begin

      Result := true;

      Exit;

     end;//l_Ver = l_ID.rVersion

    end;//f_Versions.Has(l_ID.rID.rID, l_Index)

   finally

    l_IDSource := nil;

   end;//try..finally

  Result := anAction(aFiler, anIndex);

  if Result then

   Inc(l_Count);

 end;//DoFiler


//#UC END# *47271A4301A1_5B06E7C7009D_var*

begin

//#UC START# *47271A4301A1_5B06E7C7009D_impl*

 Result := 0;

 try

  {Result := }f_Other.IterateF(m3L2FilerAction(@DoFiler), aParts);

  //Result := Iterate(anAction, aParts);

 finally

  m3FreeFilerAction(anAction);

 end;//try..finally

//#UC END# *47271A4301A1_5B06E7C7009D_impl*

end;//Tm3FilteredRange.IterateF


function Tm3FilteredRange.IterateF(anAction: Tm3DocumentAction;

 const aParts: Tm3DocPartSet = m3_AllDocParts): Integer;

//#UC START# *47271A8703B1_5B06E7C7009D_var*

//#UC END# *47271A8703B1_5B06E7C7009D_var*

begin

//#UC START# *47271A8703B1_5B06E7C7009D_impl*

 try

  Result := 0;

  Assert(false);

  //Result := Iterate(anAction, aParts);

 finally

  m3FreeDocumentAction(anAction);

 end;//try..finally

//#UC END# *47271A8703B1_5B06E7C7009D_impl*

end;//Tm3FilteredRange.IterateF


function Tm3FilteredRange.CopyTo(const aDB: Im3DB;

 aMode: Tm3DBCopyMode = m3_cmRewrite;

 const aParts: Tm3DocPartSet = m3_AllDocParts): Boolean;

 {* переливает выборку в другую базу. }

//#UC START# *47271A9E01AC_5B06E7C7009D_var*

//#UC END# *47271A9E01AC_5B06E7C7009D_var*

begin

//#UC START# *47271A9E01AC_5B06E7C7009D_impl*

 Result := false;

 Assert(false);

//#UC END# *47271A9E01AC_5B06E7C7009D_impl*

end;//Tm3FilteredRange.CopyTo


procedure Tm3FilteredRange.Cleanup;

 {* Функция очистки полей объекта. }

//#UC START# *479731C50290_5B06E7C7009D_var*

//#UC END# *479731C50290_5B06E7C7009D_var*

begin

//#UC START# *479731C50290_5B06E7C7009D_impl*

 FreeAndNil(f_Versions);

 inherited;

//#UC END# *479731C50290_5B06E7C7009D_impl*

end;//Tm3FilteredRange.Cleanup


procedure Tm3FilteredRange.ClearFields;

begin

 f_Other := nil;

 inherited;

end;//Tm3FilteredRange.ClearFields


constructor Tm3SubRange.Create(aFrom: Integer;

 aMaxLen: Integer;

 const anOther: Im3DBRange);

//#UC START# *5DA721EF0369_5DA721C700EC_var*

//#UC END# *5DA721EF0369_5DA721C700EC_var*

begin

//#UC START# *5DA721EF0369_5DA721C700EC_impl*

 Assert(anOther <> nil);

 f_Other := anOther;

 f_From := aFrom;

 f_MaxLen := aMaxLen;

 f_Last := f_From;

 inherited Create;

//#UC END# *5DA721EF0369_5DA721C700EC_impl*

end;//Tm3SubRange.Create


class function Tm3SubRange.Make(aFrom: Integer;

 aMaxLen: Integer;

 const anOther: Im3DBRange): Im3DBRange;

var

 l_Inst : Tm3SubRange;

begin

 l_Inst := Create(aFrom, aMaxLen, anOther);

 try

  Result := l_Inst;

 finally

  l_Inst.Free;

 end;//try..finally

end;//Tm3SubRange.Make


{$If NOT Defined(l3NoSRT)}

function Tm3SubRange.SetRefTo(var thePlace: Tm3SubRange): Boolean;

begin

 if (thePlace = Self) then

  Result := false

 else

 begin

  Result := true;

  thePlace.Free;

  thePlace := Self.Use;

 end;//thePlace = Self

end;//Tm3SubRange.SetRefTo

{$IfEnd} // NOT Defined(l3NoSRT)


function Tm3SubRange.DB: Im3DB;

//#UC START# *4720847C026F_5DA721C700EC_var*

//#UC END# *4720847C026F_5DA721C700EC_var*

begin

//#UC START# *4720847C026F_5DA721C700EC_impl*

 Result := f_Other.DB;

//#UC END# *4720847C026F_5DA721C700EC_impl*

end;//Tm3SubRange.DB


function Tm3SubRange.Mul(const aRange: Im3DBRange): Im3DBRange;

 {* Пересекает выборки. }

//#UC START# *472718E303CA_5DA721C700EC_var*

//#UC END# *472718E303CA_5DA721C700EC_var*

begin

//#UC START# *472718E303CA_5DA721C700EC_impl*

 Result := Self;

 Assert(false);

//#UC END# *472718E303CA_5DA721C700EC_impl*

end;//Tm3SubRange.Mul


{$If NOT Defined(m3NoEVD)}

function Tm3SubRange.Iterate(const aFilter: Im3TagGenerator;

 aNeedWriteToBase: Boolean;

 const aParts: Tm3DocPartSet = m3_AllDocParts): Integer;

//#UC START# *472719650005_5DA721C700EC_var*

//#UC END# *472719650005_5DA721C700EC_var*

begin

//#UC START# *472719650005_5DA721C700EC_impl*

 Result := m3IterateRangeViaFilter(Self, aFilter, aNeedWriteToBase, aParts, Self.DB);

//#UC END# *472719650005_5DA721C700EC_impl*

end;//Tm3SubRange.Iterate

{$IfEnd} // NOT Defined(m3NoEVD)


function Tm3SubRange.IterateF(anAction: Tm3FilerAction;

 const aParts: Tm3DocPartSet = m3_AllDocParts): Integer;

//#UC START# *47271A4301A1_5DA721C700EC_var*


var

 l_Count  : Integer absolute Result;

 l_PrevID : Integer;

 l_Len    : Integer;


 function DoFiler(aFiler: Tl3CustomFiler; const anIndex : Tm3DBStreamIndex): Boolean;

 var

  l_ID : Integer;

 begin//DoFiler

  l_ID := anIndex.rID;

  if (l_ID < f_From) then

  begin

   Result := true;

   Exit;

  end;//l_ID < f_From

  if (l_ID <> l_PrevID) then

  begin

   Inc(l_Len);

   if (l_Len >= f_MaxLen) then

   begin

    Result := false;

    Exit;

   end;//l_Len >= f_MaxLen

  end;//l_ID <> l_PrevID

  Result := anAction(aFiler, anIndex);

  if Result then

  begin

   l_PrevID := l_ID;

   Inc(l_Count);

  end;//Result

 end;//DoFiler


//#UC END# *47271A4301A1_5DA721C700EC_var*

begin

//#UC START# *47271A4301A1_5DA721C700EC_impl*

 Result := 0;

 try

  l_PrevID := -1;

  l_Len := 0;

  {Result := }f_Other.IterateF(m3L2FilerAction(@DoFiler), aParts);

  if (l_PrevID = -1) then

   // - ни разу не попали

   f_Last := High(Integer)

  else

  if (l_Len < f_MaxLen) AND not f_Other.DB.Stopped then

   f_Last := High(Integer)

  else

   f_Last := l_PrevID + 1;

  //Result := Iterate(anAction, aParts);

 finally

  m3FreeFilerAction(anAction);

 end;//try..finally

//#UC END# *47271A4301A1_5DA721C700EC_impl*

end;//Tm3SubRange.IterateF


function Tm3SubRange.IterateF(anAction: Tm3DocumentAction;

 const aParts: Tm3DocPartSet = m3_AllDocParts): Integer;

//#UC START# *47271A8703B1_5DA721C700EC_var*

//#UC END# *47271A8703B1_5DA721C700EC_var*

begin

//#UC START# *47271A8703B1_5DA721C700EC_impl*

 try

  Result := 0;

  Assert(false);

  //Result := Iterate(anAction, aParts);

 finally

  m3FreeDocumentAction(anAction);

 end;//try..finally

//#UC END# *47271A8703B1_5DA721C700EC_impl*

end;//Tm3SubRange.IterateF


function Tm3SubRange.CopyTo(const aDB: Im3DB;

 aMode: Tm3DBCopyMode = m3_cmRewrite;

 const aParts: Tm3DocPartSet = m3_AllDocParts): Boolean;

 {* переливает выборку в другую базу. }

//#UC START# *47271A9E01AC_5DA721C700EC_var*

//#UC END# *47271A9E01AC_5DA721C700EC_var*

begin

//#UC START# *47271A9E01AC_5DA721C700EC_impl*

 Result := false;

 Assert(false);

//#UC END# *47271A9E01AC_5DA721C700EC_impl*

end;//Tm3SubRange.CopyTo


procedure Tm3SubRange.Cleanup;

 {* Функция очистки полей объекта. }

//#UC START# *479731C50290_5DA721C700EC_var*

//#UC END# *479731C50290_5DA721C700EC_var*

begin

//#UC START# *479731C50290_5DA721C700EC_impl*

 f_Other := nil;

 inherited;

//#UC END# *479731C50290_5DA721C700EC_impl*

end;//Tm3SubRange.Cleanup


procedure Tm3SubRange.ClearFields;

begin

 f_Other := nil;

 inherited;

end;//Tm3SubRange.ClearFields


//#UC START# *5DA721C700ECimpl*

procedure IndexDocThreadWait;

{$IfDef Linux}

(*var

 l_Count : Integer;

 l_Then : TDateTime;*)

{$EndIf Linux}

begin//IndexDocThreadWait

 if (g_IndexDocThread <> nil) then

 begin

  if not g_IndexDocThreadstarted then

   Exit;

  //g_IndexDocThread.Terminate;

  {$IfDef Linux}

  g_IndexDocThread.WaitFor;

(*  l_Then := Now;

  l_Count := 0;

  while (g_IndexDocThread <> nil) do

  begin

   Sleep(100);

   //Sleep(1000);

   Inc(l_Count);

   if (l_Count > 60) then

   //if (l_Count > 1000) then

    break;

   if (MinutesBetween(Now, l_Then) > 5) then

    break;

  end;//g_IndexDocThread <> nil*)

  {$Else  Linux}

  g_IndexDocThread.WaitFor;

  {$EndIf Linux}

 end;//g_IndexDocThread <> nil

end;//IndexDocThreadWait


constructor Tm3DBIndexDocThread.Create(const aBaseName : TFileName; aDocNum : Integer);

begin

 if (g_IndexDocThread = nil) then

 begin

  if not g_IndexDocThreadWaitAdded then

  begin

   g_IndexDocThreadWaitAdded := true;

   Tl3MemUtils.AddExitProc(IndexDocThreadWait);

  end;//not g_IndexDocThreadWaitAdded

  g_IndexDocThread := Self;

 end;//g_IndexDocThread = nil

 inherited Create(true);

 f_BaseName := aBaseName;

 f_DocNum := aDocNum;

 Self.FreeOnTerminate := true;

 //Self.Resume;

end;


destructor Tm3DBIndexDocThread.Destroy;

begin

 inherited;

 l3System.EnterGlobalCS;

 try

  FreeAndNil(f_DocNums);

  if (g_IndexDocThread = Self) then

  begin

   g_IndexDocThread := nil;

   g_IndexDocThreadstarted := false;

  end;//g_IndexDocThread = Self

 finally

  l3System.LeaveGlobalCS;

 end;//try..finally

end;


class procedure Tm3DBIndexDocThread.Run(const aBaseName : TFileName; aDocNum : Integer);

var

 l_T : TThread;

begin

 l3System.EnterGlobalCS;

 try

  if (g_IndexDocThread = nil) then

  begin

   l_T := Self.Create(aBaseName, aDocNum);

   l_T.Resume;

  end//g_IndexDocThread = nil

  else

  begin

   if (g_IndexDocThread.f_DocNums = nil) then

    g_IndexDocThread.f_DocNums := Tl3IntegerList.Create;

   g_IndexDocThread.f_DocNums.Add(aDocNum);

  end;//g_IndexDocThread = nil

 finally

  l3System.LeaveGlobalCS;

 end;//try..finally

end;


procedure Tm3DBIndexDocThread.Execute;

var

 l_DocNums : Tl3IntegerList;

 l_DBHelper : Im3DBHelper;

 l_E : Im3FilesEnumerable;

 l_P : Tl3ProgressProc;

begin

 try

  if (g_IndexDocThread = Self) then

   g_IndexDocThreadstarted := true;

  m3DBIndexDoc(f_BaseName, nil, f_DocNum);

  l_DocNums := nil;

  while true do

  begin

   begin

    // Асинхронно запускать объединение дельт. Если их типа много накопилось.

    // m3DBMergeIndexDeltas

    l_DBHelper := Tm3DBHelper.Make(f_BaseName);

    try

     l_E := l_DBHelper.GetIndexDeltasFiles;

     if (l_E <> nil) then

      if (l_E.Count > 5) then

      begin

       l_P := nil;

       m3DBMergeIndexDeltas(f_BaseName, l_P);

      end;//l_E.Count > 5

    finally

     l_DBHelper := nil;

    end;//try..finally

   end;

   if (f_DocNums <> nil) then

   begin

    l3System.EnterGlobalCS;

    try

     if (f_DocNums <> nil) then

     begin

      if not f_DocNums.Empty then

      begin

       f_DocNums.SetRefTo(l_DocNums);

       FreeAndNil(f_DocNums);

      end;//not f_DocNums.Empty

     end;//f_DocNums <> nil

    finally

     l3System.LeaveGlobalCS;

    end;//try..finally

   end;//f_DocNums <> nil

   if (l_DocNums <> nil) then

   begin

    try

     if not l_DocNums.Empty then

     begin

      m3DBIndexDocs(f_BaseName, nil, l_DocNums);

      continue;

     end;//not l_DocNums.Empty

    finally

     FreeAndNil(l_DocNums);

    end;//try..finally

   end;//l_DocNums <> nil

   break;

  end;//while true

 except

  if (g_IndexDocThread = Self) then

   g_IndexDocThread := nil;

 end;//try..except

end;


constructor Tm3DBIndexUpdatedThread.Create(const aBaseName : TFileName);

begin

 inherited Create(true);

 f_BaseName := aBaseName;

 Self.FreeOnTerminate := true;

 //Self.Resume;

end;


procedure Tm3DBIndexUpdatedThread.Execute;

begin

 m3DBIndexUpdated(f_BaseName, nil, false);

end;


//#UC END# *5DA721C700ECimpl*


//#UC START# *5AEC481D0310impl*

//#UC END# *5AEC481D0310impl*



//#UC START# *5AEC481D0310forDiagramm*

(*

*)

//#UC END# *5AEC481D0310forDiagramm*


end.


Комментариев нет:

Отправить комментарий