You've already forked lazarus-ccr
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5438 8e941d3f-bd1b-0410-a28a-d453659cc2b4
396 lines
15 KiB
PHP
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;
|
|
|