Subject Re: [IB-Architect] Is there really any way...?
Author Jim Starkey
At 09:38 PM 4/26/00 +1000, Helen Borrie wrote:
>I've had several people suggest a topic for the IBDH along the lines of...
>
>"We need a topic that describes the structure of the database so that I can
>open the .gdb with a suitable editor and retrieve good data from a corrupt
>database."
>
>In my high-handed way, I've tended just to say to myself "Pooph! another
>of THOSE!" I've always assumed (rightly or wrongly) that it would be like
>looking for a needle in a haystack, given the relative randomness of
>storage of rows, never mind tables, indexes, etc.
>
>But - is there any way?
>

Your instincts are corrected. There is no way to get anything out of
an Interbase database without understanding the entire on-disk structure.
This is not to say that a database walking GUI couldn't be built, but
it would be limited to a single ODS version and couldn't share a
database file with a running engine.

For the record, to find a particular field in a specific record
the utility would have to:

1. Read the header pages to get the ground rules and initial
pointer page of RDB$PAGES.
2. Process the table RDB$PAGES (see below) to find the
pointer page for RDB$RELATIONS (relation id is a known
constant).
3. Process the table RDB$RELATIONS to get the relation id of
the target table.
4. Go back to RDB$PAGES to get the pointer pages for the
target table.
5. Process RDB$PAGES to find the pointer pages for RDB$RELATION_FIELDS
and RDB$FIELDS (relation ids are constants).
6. Process RDB$RELATION_FIELDS and RDB$FIELDS to get the logical
structure of the target table.
7. Compute the states of interest transactions: start with the
the oldest interesting transaction on the header page, process
RDB$PAGES to find the transaction inventory pages (TIPs).
8. Starting with the first pointer page for the target table
search successive data pages.
9. For each data page scan record segments skipping deleted
record stubs, blobs, and non-leading record fragments to
find record headers.
10. For a candidate record header see if it's creating
transaction was commited. If not, follow the back version
pointer, remembering to expand the record to apply a delta
if necessary.
11. To expand a segment, it if first necessary to allocate
a buffer to hold the expanded record. To allocate the
buffer, you need to know the length. Get the format
version number from the header and process RDB$FORMATS
to find the record format (go back to RDB$PAGES to the
the pointer pages for RDB$FORMATS, etc.). Allocate the
the buffer. Expand the compressed segment into the the
buffer. If the record is fragmented, chase the tail
fragments, expanding each.
12. If we were following a back version, apply the compressed
delta to the expanded version of the previous version. If
the format versions changed (unlikely but possible), deal
with it.
13. Once the target record has been assembled, index into
the format to compute the descriptor for the target field.
14. If the target field is a blob, fetch the blob header. If
it is level 0, the data is there. Otherwise, chase down
and assemble the blob pages (no, blobs are not compressed).

Doable. But perhaps a little much for an editor.

Jim Starkey