InterBase - статьи

Подробный формат страницы данных и записи


Рассмотрим формат страницы данных и записи более подробно.

Выборочно информацию возьмем из файла ods . h ( on disk structure ) из поставки Interbase ( firebird ) и опишем ее более подробно.

/* Page types */

#define pag_data 5 /* Data page */

Указывается, что страница данных идентифицируется номером 5.

#ifdef _CRAY

#define MIN_PAGE_SIZE 4096

#else

#define MIN_PAGE_SIZE 1024

#endif

#define MAX_PAGE_SIZE 16384

#define DEFAULT_PAGE_SIZE 4096

В данном листинге определяются размеры страницы: её максимальный и минимальный размер, а так же размер по умолчанию.

/* Basic page header */

Основной заголовок страницы

typedef struct pag {

SCHAR pag_type;

SCHAR pag_flags;

USHORT pag_checksum;

ULONG pag_generation;

ULONG pag_seqno; /*WAL seqno of last update*/

ULONG pag_offset; /*WAL offset of last update*/

} *PAG;

Определяется: тип страницы, флаги, контрольная сумма, номер последней измененной последовательности и смещение последнего изменения.

/* Data Page */

Определяется заголовок страницы данных

typedef struct dpg {

struct pag dpg_header;

SLONG dpg_sequence;

USHORT dpg_relation;

USHORT dpg_count;

struct dpg_repeat

{

USHORT dpg_offset;

USHORT dpg_length;

} dpg_rpt [1];

} *DPG;

Сначала идет стандартный заголовок страницы, далее следует номер последовательности в отношении, номер отношения, количество записей на странице. Далее следует повторяющаяся структура { смещение, длина } фрагмента записи.

/* Record header */

Заголовок записи

typedef struct rhd {

SLONG rhd_transaction;/* transaction id */

SLONG rhd_b_page; /* back pointer */

USHORT rhd_b_line; /* back line */

USHORT rhd_flags; /* flags, etc */

UCHAR rhd_format; /* format version */

#ifdef _CRAY

UCHAR rhd_pad [7];

#endif

UCHAR rhd_data [1];

} *RHD;

В заголовке записи определен номер транзакции, указатель на старую версию записи, флаги, версию формата записи и непосредственно данные.

Так же из файла ods . h можно узнать структуру фрагментированных записей и структуру записей поля blob . Но в данной статье мы не будем их рассматривать.

Далее определяются флаги записей

# define rhd _ deleted 1

Запись логически удалена

#define rhd_chain 2

Запись является старой версией

#define rhd_fragment 4

Запись является фрагментом

#define rhd_incomplete 8

Запись неполная

#define rhd_blob 16

Поле типа blob

#define rhd_delta 32

Предыдущая версия, различия только

#define rhd_large 64

Объект является большим

#define rhd_damaged 128

Объект известен , как поврежденный

#define rhd_gc_active 256

Мусор, мертвая версия записи


Как говорилось, ранее данные в записях сжаты методом кодирования переменной длины ( RLE ). Суть метода заключается в том, что положительное число указывает на количество следующих байт, которые непосредственно следует прочитать, отрицательное же число указывает, что следующий байт нужно повторить abs ( n ) (где abs - модуль числа) раз.

Заголовок хранит информацию о транзакции, которая требуется для реализации multi - generational архитектуры, в нормальной записи заголовок будет только содержать указатель на старую версию.

После заголовка и перед данными идет вектор нулевых бит на каждое поле (добитое до 8 (битовой) байтовой границы) в таблице. Если флаг установлен, тогда поле равно нулю, пока данные установлены в 0 для улучшения компрессии. Это означает что заголовок состоит из эффективной длины 16 байт и 3 байта были добавлены для выравнивания. Байты для нулевого битового вектора добавляются по необходимости, в зависимости от количества полей, определенных в таблице.

В результате того, что резервируется место для версий, когда заканчивается место на странице, остается возможность того, что новая версия записи будет сохранена на этой же странице.

При восстановлении данных не следует забывать о внутреннем формате представления чисел, даты и времени и кодировке строк, дабы заранее не разочароваться в возможности восстановления.

Напомню вам, что на платформе intel при записи числа типа word (два байта), сначала записывается младший байт, а затем старший, например число 0 x 5684 будет представлено на диске, как 84:56. Аналогично сохраняются и числа типа long .

Формат даты является необычным по сравнению со стандартными типами записи дат, поэтому немного остановимся на его описании.

Для хранения даты и времени в Interbase существует тип date, его внутреннее представление таково. Это запись из двух 32-разрядных знаковых целых чисел. В первом числе хранится число дней, прошедших с 17 ноября 1858, а во втором - время в десятых долях миллисекунды, прошедшее после полуночи.

Для перевода в стандартный тип Delphi - TDateTime, который объявлен как TDateTime = type Double, где целая часть - это число дней, прошедших с 30 декабря 1899, а дробная часть - время, прошедшее после полуночи (.0 = 0:00; .25 = 6:00; .5 = 12:00; .75 = 18:00) можно воспользоваться простой формулой

DateTime := Days - IBDateDelta + MSec10 / MSecsPerDay10, где

Days - количество дней в формате Interbase ;

IBDateDelta = 15018 - разница в днях между датами Delphi и Interbase;

MSec 10 - время в десятых долях миллисекунды, прошедшее после полуночи;

MSecsPerDay10 = Количество миллисекунд в сутках * 10

На данный момент мы в достаточной степени узнали необходимую информацию о том, каким образом и где хранится информация в препарируемой нами базе данных. Изучив подробно все структуры используемые СУБД было бы возможно написать программу и воочию посмотреть и восстановить все данные своими руками. Но, как говорится, <Кесарю - кесарево>, я же предпочитаю воспользоваться стандартной программной isq ( wisq , кому как нравится) и движком interbase (ведь не зря его писали?) если, конечно база данных не повреждена, и тем самым ускорить процесс восстановления информации.


Содержание раздела