Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions uBtreePage.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
unit uBtreePage;

interface

uses
Windows, SysUtils, uStructs;

const
pag_left = $01; // ��� ����� ����� �������� � ������
pag_root = $02; // ��� �������� ��������
pag_leaf = $04; // ��� �������� ��������

key_delete = $01; // ���� ������� ��� ��������
key_right = $02; // ���� ��������� �� ������ ���� (���������� ����)
key_bias = $04; // ���� �������� ��������� (bias key)
key_dupeq = $08; // ���� ������� ��� �������� (��� ���������� ��������)

type
// ������������� ����� ��������� B-Tree ��������
TBtreePageHeader = packed record
pag_header: TPag; // 0x00-0x0F - ����������� ���������
pag_next: SLong; // 0x10-0x13 - ��������� �� ��������� �������� (����)
pag_sibling: SLong;// 0x14-0x17 - ��������� �� �������� ��������
pag_parent: SLong; // 0x18-0x1B - ��������� �� ������������ ��������
pag_count: UShort; // 0x1C-0x1D - ���������� ������ �� ��������
pag_flags: UChar; // 0x1E - ����� ��������
pag_fill: UChar; // 0x1F - ������� ���������� (�� ������������)
// pag_keys ���������� � ����� 0x20
end;

// ��� ��� ������������� ������ �����
TBtreeKey = record
Length: UShort;
Flags: SChar;
Data: TBytes; // ������ �����
PageOrRecordNumber: SLong; // ��� ���������� ����� - ����� ��������, ��� ������� - ����� ������
end;

// ��� ��� ������� ������
TBtreeKeyArray = array of TBtreeKey;

// ����� ��� ������� B-Tree ��������
TBtreePageAnalyzer = class
private
FPageSize: Integer;
FPageNumber: ULong; // ����� ����� B-Tree �������� (��������� ��� ���������)
FHeader: TBtreePageHeader;
FPageData: TBytes; // ������ ���� �������� (��� ���������� ������)

// ���������� ������� ��� ��������� ����� �� ��������
function GetKeyAtOffset(Offset: Integer): TBtreeKey;
public
// ����������� - ��������� ����� ��������, ������ �������� � ����� ��������
constructor Create(const PageBuffer: TBytes; PageSize: Integer; PageNum: ULong);

// �������� ������ ���� ������ �� ��������
function GetKeys: TBtreeKeyArray;

// �������� ����� ��������
property Flags: UChar read FHeader.pag_flags;
// �������� ���������� ������
property Count: UShort read FHeader.pag_count;
// �������� ����� ��������� ��������
property NextPage: SLong read FHeader.pag_next;
// �������� ����� �������� ��������
property SiblingPage: SLong read FHeader.pag_sibling;
// �������� ����� ������������ ��������
property ParentPage: SLong read FHeader.pag_parent;
// ��� �������� ��������?
property IsLeaf: Boolean read (FHeader.pag_flags and pag_leaf) <> 0;
// ��� �������� ��������?
property IsRoot: Boolean read (FHeader.pag_flags and pag_root) <> 0;
// ��� ����� ����� ��������?
property IsLeftmost: Boolean read (FHeader.pag_flags and pag_left) <> 0;

end;

implementation

constructor TBtreePageAnalyzer.Create(const PageBuffer: TBytes; PageSize: Integer; PageNum: ULong);
var
ExpectedMinSize: Integer;
begin
if (PageSize < MIN_PAGE_SIZE) or (PageSize > MAX_PAGE_SIZE) or
(Length(PageBuffer) <> PageSize) then
raise Exception.Create('Invalid page size or buffer size for B-Tree page.');

ExpectedMinSize := SizeOf(TBtreePageHeader);
if Length(PageBuffer) < ExpectedMinSize then
raise Exception.Create('Page buffer too small for B-Tree header.');

// �������� ������������� ����� ���������
Move(PageBuffer[0], FHeader, SizeOf(TBtreePageHeader));

// ��������� ��� ��������
if FHeader.pag_header.pag_type <> $07 then
raise Exception.Create('Not a B-Tree Index Page.');

FPageSize := PageSize;
FPageNumber := PageNum;
FPageData := Copy(PageBuffer); // �������� ��� ������ ��������
end;

function TBtreePageAnalyzer.GetKeyAtOffset(Offset: Integer): TBtreeKey;
var
KeyLength: UShort;
KeyFlags: SChar;
KeyDataOffset, KeyPageOffset: Integer;
begin
// ��������� �������
if (Offset + 2 {key_length} + 1 {key_flags} > Length(FPageData)) then
raise Exception.Create('Invalid offset for key in B-Tree page.');

// ������ key_length (2 �����)
KeyLength := PWord(@FPageData[Offset])^;
Inc(Offset, 2);

// ������ key_flags (1 ����)
KeyFlags := PShortInt(@FPageData[Offset])^;
Inc(Offset, 1);

// ���������, �� ������� �� ����� ������ �� �������
if (Offset + KeyLength + 4 {key_page} > Length(FPageData)) then
raise Exception.Create('Key length exceeds page boundary.');

// �������� key_data
SetLength(Result.Data, KeyLength);
if KeyLength > 0 then
Move(FPageData[Offset], Result.Data[0], KeyLength);
Inc(Offset, KeyLength);

// ������ key_page (4 �����)
Result.PageOrRecordNumber := PLongInt(@FPageData[Offset])^;

// ��������� ���� ����������
Result.Length := KeyLength;
Result.Flags := KeyFlags;
end;

function TBtreePageAnalyzer.GetKeys: TBtreeKeyArray;
var
i: Integer;
CurrentOffset: Integer;
Keys: TBtreeKeyArray;
begin
SetLength(Keys, FHeader.pag_count);

// �������� ������ ������� ������
CurrentOffset := SizeOf(TBtreePageHeader);

for i := 0 to FHeader.pag_count - 1 do
begin
Keys[i] := GetKeyAtOffset(CurrentOffset);

// ��������� �������� ���������� �����
// key_length(2) + key_flags(1) + key_data(key_length) + key_page(4)
CurrentOffset := CurrentOffset + 2 + 1 + Keys[i].Length + 4;
end;

Result := Keys;
end;

end.
226 changes: 226 additions & 0 deletions uDatabaseStats.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
unit uDatabaseStats;

interface

uses
Classes, uPageAnalyzer, uDataPage, uTipPage, uBtreePage;

type
SChar = Shortint;
SShort = Smallint;
UShort = Word;
SLong = Longint;
ULong = LongWord;
UChar = type Byte;

TPageTypeStats = record
HeaderPages: ULong;
PipPages: ULong;
TipPages: ULong;
PointerPages: ULong;
DataPages: ULong;
IndexRootPages: ULong;
IndexBtreePages: ULong;
BlobPages: ULong;
GeneratorPages: ULong;
WalPages: ULong;
UnknownPages: ULong;
end;


TTransactionStats = record
Active: ULong;
Limbo: ULong;
Dead: ULong;
Committed: ULong;
Total: ULong;
end;


TRelationStats = record
RelationID: ULong;
PageCount: ULong; // ���������� Data Pages, ������������� ���������
RecordCount: ULong; // ������: ���������� ������� (������� �������� ����������)
end;
TRelationStatsArray = array of TRelationStats;

TDatabaseStats = class
private
FPageAnalyzer: TPageAnalyzer;
FPageStats: TPageTypeStats;
FTransactionStats: TTransactionStats;
FRelationStats: TRelationStatsArray;
FStatsCalculated: Boolean;

// ���������� ������� ��� �������� ����� �������
procedure CalculatePageStats;
// ���������� ������� ��� �������� ���������� �� TIP
procedure CalculateTransactionStats;
// ���������� ������� ��� �������� ���������� �� ���������� (����������)
procedure CalculateRelationStats;
public
// ����������� - ��������� TPageAnalyzer
constructor Create(APageAnalyzer: TPageAnalyzer);
// ����������
destructor Destroy; override;

// ��������� ��� ����������
procedure CalculateStats;

// �������� ���������� �� ����� �������
property PageStats: TPageTypeStats read FPageStats;
// �������� ���������� �� �����������
property TransactionStats: TTransactionStats read FTransactionStats;
// �������� ���������� �� ����������
property RelationStats: TRelationStatsArray read FRelationStats;
// ���������� ��� ���������?
property StatsCalculated: Boolean read FStatsCalculated;
end;

implementation

uses
SysUtils;

constructor TDatabaseStats.Create(APageAnalyzer: TPageAnalyzer);
begin
inherited Create;
FPageAnalyzer := APageAnalyzer;
FStatsCalculated := False;
end;

destructor TDatabaseStats.Destroy;
begin
inherited;
end;

procedure TDatabaseStats.CalculatePageStats;
var
PageNum: ULong;
PageInfo: TPageInfo;
begin

FillChar(FPageStats, SizeOf(FPageStats), 0);

for PageNum := 0 to FPageAnalyzer.GetLastPageNumber do
begin
PageInfo := FPageAnalyzer.GetPageInfo(PageNum);
case PageInfo.Header.pag_type of
pag_header: Inc(FPageStats.HeaderPages);
pag_pip: Inc(FPageStats.PipPages);
pag_tip: Inc(FPageStats.TipPages);
pag_pointer: Inc(FPageStats.PointerPages);
pag_data: Inc(FPageStats.DataPages);
pag_index_root: Inc(FPageStats.IndexRootPages);
pag_index_bt: Inc(FPageStats.IndexBtreePages);
pag_blob: Inc(FPageStats.BlobPages);
pag_generator: Inc(FPageStats.GeneratorPages);
pag_wal: Inc(FPageStats.WalPages);
else
Inc(FPageStats.UnknownPages);
end;
end;
end;

procedure TDatabaseStats.CalculateTransactionStats;
var
PageNum: ULong;
PageInfo: TPageInfo;
TipAnalyzer: TTIPAnalyzer;
TxInfoArray: TTransactionInfoArray;
i: Integer;
begin
// �������������� ��������
FillChar(FTransactionStats, SizeOf(FTransactionStats), 0);

for PageNum := 0 to FPageAnalyzer.GetLastPageNumber do
begin
PageInfo := FPageAnalyzer.GetPageInfo(PageNum);
if PageInfo.Header.pag_type = pag_tip then
begin
TipAnalyzer := TTIPAnalyzer.Create(PageInfo.Buffer, PageInfo.Size, PageInfo.Number);
try
TxInfoArray := TipAnalyzer.GetTransactionInfo(); // �������� ��� ���������� �� ��������
for i := 0 to High(TxInfoArray) do
begin
case TxInfoArray[i].State of
tsActive: Inc(FTransactionStats.Active);
tsLimbo: Inc(FTransactionStats.Limbo);
tsDead: Inc(FTransactionStats.Dead);
tsCommitted: Inc(FTransactionStats.Committed);
end;
Inc(FTransactionStats.Total);
end;
finally
TipAnalyzer.Free;
end;
end;
end;
end;

procedure TDatabaseStats.CalculateRelationStats;
var
PageNum: ULong;
PageInfo: TPageInfo;
DataPageHeader: TData_Page_Fixed_Part;
Fragments: TRecordFragmentsArray;
FoundRelation: Boolean;
i, j: Integer;
begin
SetLength(FRelationStats, 0);

for PageNum := 0 to FPageAnalyzer.GetLastPageNumber do
begin
PageInfo := FPageAnalyzer.GetPageInfo(PageNum);
if PageInfo.Header.pag_type = pag_data then
begin
if Length(PageInfo.Buffer) >= SizeOf(TData_Page_Fixed_Part) then
begin
DataPageHeader := TData_Page_Fixed_Part(PageInfo.Buffer[0]);
var RelationID := DataPageHeader.dpg_relation;

FoundRelation := False;
for j := 0 to High(FRelationStats) do
begin
if FRelationStats[j].RelationID = RelationID then
begin
Inc(FRelationStats[j].PageCount);
FoundRelation := True;
Break;
end;
end;


if not FoundRelation then
begin
SetLength(FRelationStats, Length(FRelationStats) + 1);
FRelationStats[High(FRelationStats)].RelationID := RelationID;
FRelationStats[High(FRelationStats)].PageCount := 1;
FRelationStats[High(FRelationStats)].RecordCount := 0;
end;


Fragments := ExtractDataFragments(PageInfo.Buffer, PageInfo.Size);

for j := 0 to High(FRelationStats) do
begin
if FRelationStats[j].RelationID = RelationID then
begin
Inc(FRelationStats[j].RecordCount, Length(Fragments));
Break;
end;
end;
end;
end;
end;
end;

procedure TDatabaseStats.CalculateStats;
begin
CalculatePageStats;
CalculateTransactionStats;
CalculateRelationStats;
FStatsCalculated := True;
end;

end.
Loading