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

329 lines
13 KiB
PHP

{*********************************************************}
{* FlashFiler: table restructure 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.TableRestructure(aDatabaseID : TffDatabaseID;
const aTableName : TffTableName;
aDictionary : TffDataDictionary;
aFieldMap : TffStringList;
var aRebuildID : LongInt): TffResult;
var
DB : TffSrDatabase;
RebuildParamsPtr: PffSrRebuildParams;
TargetBasename: TffFileName;
RecordInfo: TffRecordInfo;
CursorID: TffCursorID;
SourceClosed: Boolean;
SourceDeleted: Boolean;
I, L: Integer;
Inx: Integer;
KeyProcItem : TffKeyProcItem;
StartedTrans : boolean;
function ValidateFieldMap(aSourceCursorID,
aTargetCursorID: LongInt;
aFieldExMap: TffSrFieldMapList): TffResult;
var
I: Integer;
begin
Result := DBIERR_NONE;
with aFieldExMap do begin
for I := 0 to Count - 1 do begin
Result := seConvertSingleField(nil,
nil,
aSourceCursorID,
aTargetCursorID,
SourceField[I].Number,
TargetField[I].Number,
nil,
0);
if Result <> DBIERR_NONE then
FFRaiseException(EffServerException, ffStrResServer, fferrBadFieldXform, [SourceField[I].Name, TargetField[I].Name]);
end;
end;
end;
function SetTargetBasename(Path: TffPath; Root: TffFileName): TffFileName;
var
I: Integer;
begin
I := 0;
repeat
Inc(I);
Result := Root + IntToStr(I);
until not FFFileExists(FFMakeFullFilename(Path,
FFMakeFileNameExt(Result,
ffc_ExtForData)));
end;
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
SourceClosed := False;
SourceDeleted := False;
Result := CheckDatabaseIDAndGet(aDatabaseID, DB);
if Result <> DBIERR_NONE then
Exit;
try
Result := DB.NotifyExtenders(ffeaBeforeTabRestruct, ffeaTabRestructFail);
{ Exit if the extenders give us an error. }
if Result <> DBIERR_NONE then
exit;
FFGetMem(RebuildParamsPtr, SizeOf(RebuildParamsPtr^)); {!!.13}
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 := '';
rpIndexID := 0;
{ Open the table for exclusive write access; TablePackPrim
is responsible for closing the cursor. }
Result := TableOpen(rpDB.DatabaseID,
aTableName, False, '', 0,
omReadWrite, smExclusive, DB.Timeout, CursorID,
nil);
if Result <> DBIERR_NONE then Abort;
seCheckCursorIDAndGet(CursorID, TffSrBaseCursor(rpCursor));
rpCursor.State := ffosActive;
rpCursor.CloseTable := True;
{ Get the total nondeleted records in the table }
FFTblGetRecordInfo(rpCursor.Table.Files[0],
rpDB.TransactionInfo,
RecordInfo);
try
{ Check the source and target indexes. Can't preserve data if the
sequence of user-defined indexes has been altered. }
if Assigned(aFieldMap) then
with rpCursor.Table.Dictionary do begin
if IndexCount > aDictionary.IndexCount then begin
L := aDictionary.IndexCount;
{ Deleting user-defined indexes is OK }
end
else begin
L := IndexCount;
{ see if we've added user-defined indexes }
for I := L to aDictionary.IndexCount - 1 do
if (aDictionary.IndexType[I] = itUserDefined) and
(RecordInfo.riRecCount > 0) then begin {!!.13}
Result := DBIERR_INVALIDRESTROP;
Abort; { to trigger exception handlers }
end;
end;
{ See if there have been changes to existing user-defined indexes }
for I := 1 to L - 1 do
if (aDictionary.IndexType[I] = itUserDefined) and
((IndexType[I] <> itUserDefined) or
(IndexKeyLength[I] <> aDictionary.IndexKeyLength[I])) then begin
Result := DBIERR_INVALIDRESTROP;
Abort; { to trigger exception handlers }
end;
end;
{ Setup the destination file(s). Use a basename of _REST<x>
where <x> starts at 1 and is incremented upward until a
nonexistant filename is found. }
TargetBasename := SetTargetBasename(DB.Folder.Path, '_REST');
{ Setup the new (temporary) table }
Result := TableBuild(rpDB.DatabaseID, False, TargetBasename, False,
aDictionary);
if Result <> DBIERR_NONE then Abort;
try
{ If a field map is given to us, then pass info on so the
data can be reorganized. Otherwise, we just restructure
the table and lose all data. }
if Assigned(aFieldMap) then begin
{ The underlying primitive method is responsible for
releasing this memory block }
rpFieldMap := TffSrFieldMapList.Create(rpCursor.Table.Dictionary,
aDictionary);
try
{ Bind the user-defined index routines (if any) to the
target table }
for I := 1 to aDictionary.IndexCount - 1 do
if (aDictionary.IndexType[I] = itUserDefined) then
with Configuration do begin
with rpCursor.Table do
Inx := KeyProcList.KeyProcIndex(Folder.Path, BaseName,
i);
if (Inx <> -1) then begin
KeyProcItem := KeyProcList[Inx];
with KeyProcItem do begin
Link;
AddKeyProc(DB.Folder.Path, TargetBasename, I,
DLLName, BuildKeyName, CompareKeyName);
end;
end else
FFRaiseExceptionNoData(EffServerException,
ffStrResServer,
fferrResolveTableLinks);
end;
try
{ Open the destination table for exclusive write access;
TablePackPrim is responsible for closing the cursor }
Result := TableOpen(rpDB.DatabaseID,
TargetBasename, False, '', 0,
omReadWrite, smExclusive, DB.Timeout,
CursorID, nil);
if Result <> DBIERR_NONE then Abort;
seCheckCursorIDAndGet(CursorID,
TffSrBaseCursor(rpTargetCursor));
rpTargetCursor.State := ffosActive;
rpTargetCursor.Table.AddAttribute(fffaBLOBChainSafe); {!!.03}
rpTargetCursor.CloseTable := True;
finally
{ Get rid of the temporary user-defined index bindings }
for I := 1 to aDictionary.IndexCount - 1 do
if (aDictionary.IndexType[I] = itUserDefined) then
with Configuration do
if KeyProcList.KeyProcExists(DB.Folder.Path,
TargetBaseName, I) then
KeyProcList.DeleteKeyProc(DB.Folder.Path,
TargetBaseName, I);
end;
try
{ Expand the field map from string list to structured list }
Result := rpFieldMap.AddStringList(aFieldMap);
if Result <> DBIERR_NONE then Abort;
{ Validate the field map; translate into extended field map }
ValidateFieldMap(rpCursor.CursorID,
rpTargetCursor.CursorID,
rpFieldMap);
rpRebuildStatus := RebuildRegister
(TffSrClient(rpDB.Client).ClientID,
RecordInfo.riRecCount);
try
aRebuildID := rpRebuildStatus.RebuildID;
{ Create a separate thread for the restructure operation }
TffSrRestructureThread.Create(Self, RebuildParamsPtr);
{ The thread constructor is responsible for deallocating
this memory block }
RebuildParamsPtr := nil;
except
RebuildDeregister(aRebuildID);
raise;
end;
except
rpTargetCursor.State := ffosInactive;
CursorClose(rpTargetCursor.CursorID);
raise;
end;
except
rpFieldMap.Free;
raise;
end;
end { if Assigned(aFieldMap) }
else begin
{ No field map was given, so we just restructure the
table and lose the data }
{ Close the source cursor; the locks are cleared for us }
rpCursor.State := ffosInactive;
CursorClose(rpCursor.CursorID);
SourceClosed := True; { Defuse the exception handler }
{ Delete the original files, rename the working file }
Result := TableDelete(rpDB.DatabaseID, aTableName);
SourceDeleted := True; { Defuse the exception handler }
{ Rename the temporary work table to the original tablename }
if Result = DBIERR_NONE then
Result := TableRename(rpDB.DatabaseID, TargetBasename, aTableName);
{ Deallocate the parameters buffer }
FFFreeMem(RebuildParamsPtr, SizeOf(RebuildParamsPtr^)); {!!.13}
RebuildParamsPtr := nil; { Defuse the exception handler }
end;{ if not Assigned(aFieldMap) }
{ Clean up after ourselves if there are exeptions }
except
{ Clean up the files }
if not SourceDeleted then
seTableDeletePrim(rpDB, TargetBaseName);
raise;
end;
except
rpCursor.State := ffosInactive;
if not SourceClosed then
CursorClose(rpCursor.CursorID);
rpDB.State := ffosInactive;
raise;
end;
end;
finally
DB.Deactivate;
end;
except
on E : Exception do begin
if Result = DBIERR_NONE then
Result := ConvertServerExceptionEx(E, FEventLog, bseGetReadOnly); {!!.01}
{Begin !!.13}
if Assigned(RebuildParamsPtr) then begin
if StartedTrans then
seTransactionRollback(RebuildParamsPtr^.rpDB);
FFFreeMem(RebuildParamsPtr, SizeOf(RebuildParamsPtr^));
end; { if }
{End !!.13}
end;
end;
end;