Files
lazarus-ccr/components/flashfiler/sourcelaz/ffsrridx.inc
2016-12-07 13:31:59 +00:00

396 lines
15 KiB
PHP

{*********************************************************}
{* FlashFiler: rebuild index include file *}
{*********************************************************}
(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TurboPower FlashFiler
*
* The Initial Developer of the Original Code is
* TurboPower Software
*
* Portions created by the Initial Developer are Copyright (C) 1996-2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** *)
function TffServerEngine.TableRebuildIndex(aDatabaseID : TffDatabaseID;
const aTableName : TffTableName;
const aIndexName : TffName;
aIndexID : LongInt;
var aRebuildID : LongInt): TffResult;
var
DB : TffSrDatabase;
RebuildParamsPtr : PffSrRebuildParams;
CursorID : TffCursorID;
RecordInfo : TffRecordInfo;
StartedTrans : Boolean;
TransID : TffTransID;
begin
if IsReadOnly then begin {!!.01 - Start}
Result := DBIERR_TABLEREADONLY;
Exit;
end else {!!.01 - End}
Result := DBIERR_NONE;
aRebuildID := -1;
StartedTrans := False;
RebuildParamsPtr := nil;
try
Result := CheckDatabaseIDAndGet(aDatabaseID, DB);
if Result <> DBIERR_NONE then
Exit;
try
Result := DB.NotifyExtenders(ffeaBeforeRebuildInx, ffeaTabRebuildInxFail);
{ Exit if the extenders give us an error. }
if Result <> DBIERR_NONE then
Exit;
FFGetMem(RebuildParamsPtr, SizeOf(RebuildParamsPtr^)); {!!.13}
try
FillChar(RebuildParamsPtr^, SizeOf(RebuildParamsPtr^), 0);
with RebuildParamsPtr^ do begin
rpDB := TffSrDatabase.Create(DB.Engine,
DB.Session,
DB.Folder,
DB.Alias,
DB.OpenMode,
DB.ShareMode,
DB.Timeout,
DB.CheckSpace); {!!.11}
rpDB.State := ffosActive;
{ Update the folder's reference count. }
DB.Folder.IncRefCount;
rpTableName := aTableName;
rpIndexName := aIndexName;
rpIndexID := aIndexID;
try
{ Open the table for exclusive write access; TableRebuildIndexPrim
is responsible for closing the cursor. }
Result := TableOpen(rpDB.DatabaseID,
aTableName, false, aIndexName, aIndexID,
omReadWrite, smExclusive, rpDB.Timeout, CursorID,
nil);
if Result <> DBIERR_NONE then Abort;
seCheckCursorIDAndGet(CursorID, TffSrBaseCursor(rpCursor));
rpCursor.State := ffosActive;
rpCursor.CloseTable := True;
try
{ Start an implicit, read-only transaction. }
if not assigned(rpDB.Transaction) then begin
Result := seTransactionStart(rpDB, false,
ffcl_TrImplicit, TransID);
StartedTrans := (Result = DBIERR_NONE);
end;
if Result <> DBIERR_NONE then Abort;
{ Get the total nondeleted records in the table }
FFTblGetRecordInfo(rpCursor.Table.Files[0],
rpDB.TransactionInfo, RecordInfo);
if StartedTrans then begin
seTransactionCommit(rpDB);
StartedTrans := False;
end;
rpRebuildStatus := RebuildRegister
(TffSrClient(rpDB.Client).ClientID,
RecordInfo.riRecCount);
aRebuildID := rpRebuildStatus.RebuildID;
{ Lock the table; TableRebuildIndexPrim is responsible for
releasing the lock. }
Result := TableLockAcquire(CursorID, ffltWriteLock);
if Result <> DBIERR_NONE then Abort;
try
{ Create a separate thread for the reindex operation }
TffSrReindexThread.Create(Self, RebuildParamsPtr);
{ The thread constructor is responsible for deallocating this
memory block }
RebuildParamsPtr := nil;
except
TableLockRelease(CursorID, false);
raise;
end;
except
rpCursor.State := ffosInactive;
CursorClose(CursorID);
raise;
end;
except
rpDB.State := ffosInactive;
RebuildDeregister(aRebuildID);
raise;
end;
end;
except
raise;
end;
finally
DB.Deactivate;
end;
except
on E : Exception do begin
if Result = DBIERR_NONE then
Result := ConvertServerException(E, FEventLog);
{Begin !!.13}
if Assigned(RebuildParamsPtr) then begin
if StartedTrans then
seTransactionRollback(RebuildParamsPtr^.rpDB);
FFFreeMem(RebuildParamsPtr, SizeOf(RebuildParamsPtr^));
end; { if }
{End !!.13}
end;
end;
end;
{--------}
function TffServerEngine.seTableRebuildIndexPrim(aRebuildParamsPtr: PffSrRebuildParams): TffResult;
const
{ Action intervals }
// aiFlush = 10; { every x records, flush dirty pages } {Deleted !!.05}
aiSnapshot = 10; { every x records, update the status snapshot }
aiYield = 10; { every x records, yield for other messages (16-bit) }
var
aiFlush : integer; {!!.05}
RecordInfo: TffRecordInfo;
RecordBuf: PffByteArray;
BufLength: LongInt;
Dict: TffServerDataDict;
OurTable: TffSrTable;
OurIndexID: longint;
OurIndexFileNumber: longint;
RefNr: TffInt64;
Compare: TffKeyCompareFunc;
OurKey: PffByteArray;
BuildKey: TffKeyBuildFunc;
CmpData : TffCompareData;
TransID : TffTransID;
RecordsRead: LongInt;
RecordsWritten: LongInt;
NextFlushPoint: LongInt;
NextSnapshotPoint: LongInt;
{$IFNDEF ThreadedRebuilds}
NextYieldPoint: LongInt;
{$ENDIF}
IsComposite : Boolean;
begin
Result := DBIERR_NONE;
FFSetRetry(0); {!!.03}
with aRebuildParamsPtr^ do begin
RecordsRead := 0;
RecordsWritten := 0;
try
try
try
try
rpCursor.Timeout := 0;
rpDB.Timeout := 0;
{ Capture data dictionary, etc }
OurIndexID := rpCursor.IndexID;
OurTable := TffSrTable(rpCursor.Table);
Dict := OurTable.Dictionary;
OurIndexFileNumber := Dict.IndexFileNumber[OurIndexID];
{ Set up the compare method for the index }
if (Dict.IndexType[OurIndexID] = itComposite) then begin
Compare := FFKeyCompareComposite;
IsComposite := true;
end
else begin
Compare := OurTable.stGetUserCompareKey(OurIndexID);
IsComposite := false;
end;
{ Start transaction -- to ensure all data changes are written to
the file }
Result := seTransactionStart(rpDB, False, False, TransID);
if Result <> DBIERR_NONE then Exit;
try
with CmpData do begin
cdKeyLen := Dict.IndexKeyLength[OurIndexID];
cdIndex := OurIndexID;
cdDict := Dict;
cdFldCnt := 0;
cdPartLen := 0;
cdAscend := Dict.IndexIsAscending[OurIndexID];
cdNoCase := Dict.IndexIsCaseInsensitive[OurIndexID];
end;
{ Remove all the keys in the existing index }
rpCursor.ClearIndex;
if (Result <> DBIERR_NONE) then
FFRaiseExceptionNoData(EffServerException, ffStrResServer, fferrUnknownCursor);
{ Post the dirty pages for the deleted keys }
if seTransactionCommitSubset(rpDB) <> DBIERR_NONE then
FFRaiseExceptionNoData(EffServerException, ffStrResServer,
fferrTransactionFailed);
{ Get the size of the record buffer }
FFTblGetRecordInfo(OurTable.Files[0],
rpDB.TransactionInfo, RecordInfo);
BufLength := RecordInfo.riRecLength;
{ Allocate a record buffer }
FFGetMem(RecordBuf, BufLength);
{Begin !!.05}
{ Figure out how many records are to be processed before
flushing. }
aiFlush := (ffcl_1MB div BufLength);
{End !!.05}
try
{ Allocate key buffer }
FFGetMem(OurKey, CmpData.cdKeyLen);
try
NextFlushPoint := aiFlush;
NextSnapshotPoint := aiSnapshot;
{$IFNDEF ThreadedRebuilds}
NextYieldPoint := aiYield;
{$ENDIF}
RefNr.iLow := 0;
RefNr.iHigh := 0;
{ Loop through all the nondeleted records... }
OurTable.GetNextRecordSeq(rpDB.TransactionInfo,
RefNr, RecordBuf);
while (not (RefNr.iLow = 0) and (RefNr.iHigh = 0)) do begin
Inc(RecordsRead);
{ Reindexing the Sequential Access Index }
if OurIndexID = 0 then begin
if OurTable.stInsertKeyPrim
(OurIndexFileNumber,
rpDB.TransactionInfo,
RefNr,
PffByteArray(@RefNr),
FFKeyCompareI64,
@CmpData) then
Inc(RecordsWritten)
else
FFRaiseExceptionNoData(EffServerException, ffStrResServer,
fferrKeyPresent);
end
else begin
{ Reindexing a composite index }
if IsComposite then begin
Result := OurTable.stBuildCompositeKey(OurIndexID,
RecordBuf,
OurKey,
0,
0);
end
{ Reindexing a user-defined index }
else begin
BuildKey := OurTable.stGetUserBuildKey(OurIndexID);
if not BuildKey(OurIndexID,
RecordBuf,
OurKey^,
CmpData.cdKeyLen) then
Result := DBIERR_KEYVIOL;
end;
if Result <> DBIERR_NONE then
Abort;
if OurTable.stInsertKeyPrim
(OurIndexFileNumber,
rpDB.TransactionInfo,
RefNr,
OurKey,
Compare,
@CmpData) then
Inc(RecordsWritten)
else
Abort;
end;
{ See if it's time to flush our work so far }
if RecordsRead >= NextFlushPoint then begin
Inc(NextFlushPoint, aiFlush);
if seTransactionCommitSubset(rpDB) <> DBIERR_NONE then
FFRaiseExceptionNoData(EffServerException,
ffStrResServer,
fferrTransactionFailed);
end;
{ See if it's time to update the status packet }
if RecordsRead >= NextSnapshotPoint then begin
Inc(NextSnapshotPoint, aiSnapshot);
rpRebuildStatus.MakeSnapshot(RecordsRead,
RecordsWritten,
DBIERR_NONE);
end;
{$IFNDEF ThreadedRebuilds}
{ See if it's time to yield for other messages }
if RecordsRead >= NextYieldPoint then begin
Inc(NextYieldPoint, aiYield);
Application.ProcessMessages;
end;
{$ENDIF}
OurTable.GetNextRecordSeq(rpDB.TransactionInfo,
RefNr, RecordBuf);
end;
finally
FFFreeMem(OurKey, CmpData.cdKeyLen);
end;
finally
FFFreeMem(RecordBuf, BufLength);
end;
finally
if seTransactionCommit(rpDB) <> DBIERR_NONE then
FFRaiseExceptionNoData(EffServerException,
ffStrResServer,
fferrTransactionFailed);
end;
finally
rpCursor.RelTableLock(false);
end;
finally
rpCursor.State := ffosInactive;
CursorClose(rpCursor.CursorID);
rpDB.State := ffosInactive;
rpDB.Free;
end;
except
on E : Exception do begin
if Result = DBIERR_NONE then
Result := ConvertServerExceptionEx(E, FEventLog, {!!.01}
bseGetReadOnly); {!!.01}
end;
end;
finally
rpRebuildStatus.MakeSnapshot(RecordsRead,
RecordsWritten,
Result);
RebuildDeregister(rpRebuildStatus.RebuildID);
end;
end;
end;