2001-05-23 15:26:42 +02:00
/*
* PROGRAM : JRD Access Method
2004-12-17 07:21:39 +01:00
* MODULE : validation . cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION : Validation and garbage collection
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 ( 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.Inprise.com/IPL.html
*
* 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 was created by Inprise Corporation
* and its predecessors . Portions created by Inprise Corporation are
* Copyright ( C ) Inprise Corporation .
*
* All Rights Reserved .
* Contributor ( s ) : ______________________________________ .
*/
/*
Database Validation and Repair
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
Deej Bredenberg
March 16 , 1994
2008-12-05 02:20:14 +01:00
Updated : 1996 - Dec - 11 David Schnepper
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
I . TERMINOLOGY
2001-05-23 15:26:42 +02:00
The following terminology will be helpful to understand in this discussion :
2008-12-05 02:20:14 +01:00
record fragment : The smallest recognizable piece of a record ; multiple
2001-05-23 15:26:42 +02:00
fragments can be linked together to form a single version .
record version : A single version of a record representing an INSERT , UPDATE
2008-12-05 02:20:14 +01:00
or DELETE by a particular transaction ( note that deletion
2001-05-23 15:26:42 +02:00
of a record causes a new version to be stored as a
deleted stub ) .
record chain : A linked list of record versions chained together to
represent a single logical " record " .
slot : The line number of the record on page . A
2008-12-05 02:20:14 +01:00
variable - length array on each data page stores the
offsets to the stored records on
2001-05-23 15:26:42 +02:00
that page , and the slot is an index into that array .
For more information on data page format , see my paper on the internals
of the InterBase Engine .
II . COMMAND OPTIONS
2008-12-05 02:20:14 +01:00
Here are all the options for gfix which have to do with validation , and
2001-05-23 15:26:42 +02:00
what they do :
2008-12-05 02:20:14 +01:00
gfix switch dpb parameter
- - - - - - - - - - - - - - - - - - - - - - - -
2001-05-23 15:26:42 +02:00
- validate isc_dpb_verify ( gds__dpb_verify prior to 4.0 )
Invoke validation and repair . All other switches modify this switch .
2008-12-05 02:20:14 +01:00
- full isc_dpb_records
2001-05-23 15:26:42 +02:00
Visit all records . Without this switch , only page structures will be
2008-12-05 02:20:14 +01:00
validated , which does involve some limited checking of records .
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
- mend isc_dpb_repair
2001-05-23 15:26:42 +02:00
Attempts to mend the database where it can to make it viable for reading ;
does not guarantee to retain data .
2008-12-05 02:20:14 +01:00
- no_update isc_dpb_no_update
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
Specifies that orphan pages not be released , and allocated pages not
2001-05-23 15:26:42 +02:00
be marked in use when found to be free . Actually a misleading switch
2008-12-05 02:20:14 +01:00
name since - mend will update the database , but if - mend is not specified
2001-05-23 15:26:42 +02:00
and - no_update is specified , then no updates will occur to the database .
- ignore isc_dpb_ignore
2008-12-05 02:20:14 +01:00
Tells the engine to ignore checksums in fetching pages . Validate will
2001-05-23 15:26:42 +02:00
report on the checksums , however . Should probably not even be a switch ,
2008-12-05 02:20:14 +01:00
it should just always be in effect . Otherwise checksums will disrupt
2001-05-23 15:26:42 +02:00
the validation . Customers should be advised to always use it .
NOTE : Unix 4.0 ( ODS 8.0 ) does not have on - page checksums , and all
platforms under ODS 9.0 ( NevaStone & above ) does not have
checksums .
III . OPERATION
Validation runs only with exclusive access to the database , to ensure
that database structures are not modified during validation . On attach ,
validate attempts to obtain an exclusive lock on the database .
If other attachments are already made locally or through the same multi -
client server , validate gives up with the message :
" Lock timeout during wait transaction
2003-03-17 13:06:48 +01:00
- - Object " database_filename.fdb " is in use "
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
If other processes or servers are attached to the database , validate
waits for the exclusive lock on the database ( i . e . waits for every
2001-05-23 15:26:42 +02:00
other server to get out of the database ) .
2008-12-05 02:20:14 +01:00
NOTE : Ordinarily when processes gain exclusive access to the database ,
all active transactions are marked as dead on the Transaction Inventory
2001-05-23 15:26:42 +02:00
Pages . This feature is turned off for validation .
IV . PHASES OF VALIDATION
There are two phases to the validation , the first of which is a walk through
2008-12-05 02:20:14 +01:00
the entire database ( described below ) . During this phase , all pages visited
are stored in a bitmap for later use during the garbage collection phase .
2001-05-23 15:26:42 +02:00
A . Visiting Pages
During the walk - through phase , any page that is fetched goes through a
basic validation :
1. Page Type Check
Each page is check against its expected type . If the wrong type
page is found in the page header , the message :
" Page xxx wrong type (expected xxx encountered xxx) "
2008-12-05 02:20:14 +01:00
is returned . This could represent a ) a problem with the database
being overwritten , b ) a bug with InterBase page allocation mechanisms
in which one page was written over another , or c ) a page which was
2001-05-23 15:26:42 +02:00
allocated but never written to disk ( most likely if the encountered
page type was 0 ) .
The error does not tell you what page types are what , so here
they are for reference :
2004-04-29 16:51:02 +02:00
define pag_undefined 0 // purposely undefined
define pag_header 1 // Database header page
define pag_pages 2 // Page inventory page
define pag_transactions 3 // Transaction inventory page
define pag_pointer 4 // Pointer page
define pag_data 5 // Data page
define pag_root 6 // Index root page
define pag_index 7 // Index (B-tree) page
define pag_blob 8 // Blob data page
define pag_ids 9 // Gen-ids
2009-11-28 09:04:04 +01:00
define pag_log 10 // OBSOLETE. Write ahead log page: 4.0 only
2001-05-23 15:26:42 +02:00
2. Checksum
If - ignore is specified , the checksum is specifically checked in
2008-12-05 02:20:14 +01:00
validate instead of in the engine . If the checksum is found to
2001-05-23 15:26:42 +02:00
be wrong , the error :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Checksum error on page xxx "
is returned . This is harmless when found by validate , and the page
2008-12-05 02:20:14 +01:00
will still continue to be validated - - if data structures can be
validated on page , they will be . If - mend is specified , the page
will be marked for write , so that when the page is written to disk
at the end of validation the checksum will automatically be
2001-05-23 15:26:42 +02:00
recalculated .
Note : For 4.0 only Windows & NLM platforms keep page checksums .
3. Revisit
We check each page fetched against the page bitmap to make sure we
have not visited already . If we have , the error :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Page xxx doubly allocated "
2008-12-05 02:20:14 +01:00
is returned . This should catch the case when a page of the same type
2001-05-23 15:26:42 +02:00
is allocated for two different purposes .
Data pages are not checked with the Revisit mechanism - when walking
record chains and fragments they are frequently revisited .
B . Garbage Collection
During this phase , the Page Inventory ( PIP ) pages are checked against the
bitmap of pages visited . Two types of errors can be detected during
this phase .
1. Orphan Pages
2008-12-05 02:20:14 +01:00
If any pages in the page inventory were not visited
2001-05-23 15:26:42 +02:00
during validation , the following error will be returned :
" Page xxx is an orphan "
If - no_update was not specified , the page will be marked as free
on the PIP .
2. Improperly Freed Pages
2008-12-05 02:20:14 +01:00
If any pages marked free in the page inventory were in fact
found to be in use during validation , the following error
2001-05-23 15:26:42 +02:00
will be returned :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Page xxx is use but marked free " ( sic )
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
If - no_update was not specified , the page will be marked in use
on the PIP .
NOTE : If errors were found during the validation phase , no changes will
be made to the PIP pages . This assumes that we did not have a chance to
visit all the pages because invalid structures were detected .
V . WALK - THROUGH PHASE
A . Page Fetching
In order to ensure that all pages are fetched during validation , the
following pages are fetched just for the most basic validation :
1. The header page ( and for 4.0 any overflow header pages ) .
2. Log pages for after - image journalling ( 4.0 only ) .
3. Page Inventory pages .
4. Transaction Inventory pages
If the system relation RDB $ PAGES could not be read or did not
2008-12-05 02:20:14 +01:00
contain any TIP pages , the message :
2001-05-23 15:26:42 +02:00
" Transaction inventory pages lost "
2008-12-05 02:20:14 +01:00
will be returned . If a particular page is missing from the
2001-05-23 15:26:42 +02:00
sequence as established by RDB $ PAGE_SEQUENCE , then the following
message will be returned :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Transaction inventory page lost, sequence xxx "
2008-12-05 02:20:14 +01:00
If - mend is specified , then a new TIP will be allocated on disk and
stored in RDB $ PAGES in the proper sequence . All transactions which
2001-05-23 15:26:42 +02:00
would have been on that page are assumed committed .
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
If a TIP page does not point to the next one in sequence , the
following message will be returned :
" Transaction inventory pages confused, sequence xxx "
5. Generator pages as identified in RDB $ PAGES .
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
B . Relation Walking
All the relations in the database are walked . For each relation , all
2008-12-05 02:20:14 +01:00
indices defined on the relation are fetched , and all pointer and
2001-05-23 15:26:42 +02:00
data pages associated with the relation are fetched ( see below ) .
But first , the metadata is scanned from RDB $ RELATIONS to fetch the
2008-12-05 02:20:14 +01:00
format of the relation . If this information is missing or
corrupted the relation cannot be walked .
If any bugchecks are encountered from the scan , the following
2001-05-23 15:26:42 +02:00
message is returned :
" bugcheck during scan of table xxx (<table_name>) "
This will prevent any further validation of the relation .
NOTE : For views , the metadata is scanned but nothing further is done .
C . Index Walking
Prior to 4.5 ( NevaStone ) Indices were walked before data pages .
In NevaStone Index walking was moved to after data page walking .
Please refer to the later section entitled " Index Walking " .
D . Pointer Pages
All the pointer pages for the relation are walked . As they are walked
2008-12-05 02:20:14 +01:00
all child data pages are walked ( see below ) . If a pointer page cannot
2001-05-23 15:26:42 +02:00
be found , the following message is returned :
" Pointer page (sequence xxx) lost "
If the pointer page is not part of the relation we expected or
if it is not marked as being in the proper sequence , the following
message is returned :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Pointer page xxx is inconsistent "
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
If each pointer page does not point to the next pointer page as
2008-12-05 02:20:14 +01:00
stored in the RDB $ PAGE_SEQUENCE field in RDB $ PAGES , the following
2001-05-23 15:26:42 +02:00
error is returned :
" Pointer page (sequence xxx) inconsistent "
E . Data Pages
Each of the data pages referenced by the pointer page is fetched .
2008-12-05 02:20:14 +01:00
If any are found to be corrupt at the page level , and - mend is
specified , the page is deleted from its pointer page . This will
2001-05-23 15:26:42 +02:00
cause a whole page of data to be lost .
The data page is corrupt at the page level if it is not marked as
2008-12-05 02:20:14 +01:00
part of the current relation , or if it is not marked as being in
the proper sequence . If either of these conditions occurs , the
2001-05-23 15:26:42 +02:00
following error is returned :
" Data page xxx (sequence xxx) is confused "
F . Slot Validation
Each of the slots on the data page is looked at , up to the count
2008-12-05 02:20:14 +01:00
of records stored on page . If the slot is non - zero , the record
2001-05-23 15:26:42 +02:00
fragment at the specified offset is retrieved . If the record
begins before the end of the slots array , or continues off the
end of the page , the following error is returned :
" Data page xxx (sequence xxx), line xxx is bad "
2008-12-05 02:20:14 +01:00
where " line " means the slot number .
NOTE : If this condition is encountered , the data page is considered
2001-05-23 15:26:42 +02:00
corrupt at the page level ( and thus will be removed from its
pointer page if - mend is specified ) .
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
G . Record Validation
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
The record at each slot is looked at for basic validation , regardless
2008-12-05 02:20:14 +01:00
of whether - full is specified or not . The fragment could be any of the
2001-05-23 15:26:42 +02:00
following :
1. Back Version
2008-12-05 02:20:14 +01:00
If the fragment is marked as a back version , then it is skipped .
2001-05-23 15:26:42 +02:00
It will be fetched as part of its record .
2. Corrupt
2008-12-05 02:20:14 +01:00
If the fragment is determined to be corrupt for any reason , and - mend
2001-05-23 15:26:42 +02:00
is specified , then the record header is marked as damaged .
3. Damaged
If the fragment is marked damaged already from a previous visit or
a previous validation , the following error is returned :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Record xxx is marked as damaged "
2008-12-05 02:20:14 +01:00
where xxx is the record number .
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
4. Bad Transaction
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
If the record is marked with a transaction id greater than the last
2001-05-23 15:26:42 +02:00
transaction started in the database , the following error is returned :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Record xxx has bad transaction xxx "
H . Record Walking
If - full is specified , and the fragment is the first fragment in a logical
record , then the record at this slot number is fully retrieved . This
2008-12-05 02:20:14 +01:00
involves retrieving all versions , and all fragments of each
particular version . In other
2001-05-23 15:26:42 +02:00
words , the entire logical record will be retrieved .
1. Back Versions
2008-12-05 02:20:14 +01:00
If there are any back versions , they are visited at this point .
If the back version is on another page , the page is fetched but
not validated since it will be walked separately .
2001-05-23 15:26:42 +02:00
If the slot number of the back version is greater than the max
2008-12-05 02:20:14 +01:00
records on page , or there is no record stored at that slot number ,
or it is a blob record , or it is a record fragment , or the
fragment itself is invalid , the following error
2001-05-23 15:26:42 +02:00
message is returned :
" Chain for record xxx is broken "
2. Incomplete
If the record header is marked as incomplete , it means that there
2008-12-05 02:20:14 +01:00
are additional fragments to be fetched - - the record was too large
2001-05-23 15:26:42 +02:00
to be stored in one slot .
A pointer is stored in the record to the next fragment in the list .
For fragmented records , all fragments are fetched to form a full
record version . If any of the fragments is not in a valid position ,
or is not the correct length , the following error is returned :
2008-12-05 02:20:14 +01:00
" Fragmented record xxx is corrupt "
2001-05-23 15:26:42 +02:00
Once the full record has been retrieved , the length of the format is
2008-12-05 02:20:14 +01:00
checked against the expected format stored in RDB $ FORMATS ( the
format number is stored with the record , representing the exact
format of the relation at the time the record was stored . )
2001-05-23 15:26:42 +02:00
If the length of the reconstructed record does not match
the expected format length , the following error is returned :
" Record xxx is wrong length "
For delta records ( record versions which represent updates to the record )
this check is not made .
2008-12-05 02:20:14 +01:00
I . Blob Walking
2001-05-23 15:26:42 +02:00
If the slot on the data page points to a blob record , then the blob
2008-12-05 02:20:14 +01:00
is fetched ( even without - full ) . This has several cases , corresponding
to the various blob levels . ( See the " Engine Internals " document for a
2001-05-23 15:26:42 +02:00
discussion of blob levels . )
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
Level Action
2008-12-05 02:20:14 +01:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2001-05-23 15:26:42 +02:00
0 These are just records on page , and no further validation is done .
1 All the pages pointed to by the blob record are fetched and
validated in sequence .
2008-12-05 02:20:14 +01:00
2 All pages pointed to by the blob pointer pages are fetched and
2001-05-23 15:26:42 +02:00
validated .
3 The blob page is itself a blob pointer page ; all its children
are fetched and validated .
For each blob page found , some further validation is done . If the
2008-12-05 02:20:14 +01:00
page does not point back to the lead page , the following error
2001-05-23 15:26:42 +02:00
is returned :
" Warning: blob xxx appears inconsistent "
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
where xxx corresponds to the blob record number . If any of the blob pages
are not marked in the sequence we expect them to be in , the following
error is returned :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Blob xxx is corrupt "
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
Tip : the message for the same error in level 2 or 3 blobs is slightly
different :
" Blob xxx corrupt "
If we have lost any of the blob pages in the sequence , the following error
is returned :
" Blob xxx is truncated "
If the fetched blob is determined to be corrupt for any of the above
reasons , and - mend is specified , then the blob record is marked as
damaged .
J . Index Walking
In 4.5 ( NevaStone ) Index walking was moved to after the completion
of data page walking .
The indices for the relation are walked . If the index root page
is missing , the following message is returned :
" Missing index root page "
and the indices are not walked . Otherwise the index root page
2008-12-05 02:20:14 +01:00
is fetched and all indices on the page fetched .
2001-05-23 15:26:42 +02:00
For each index , the btree pages are fetched from top - down , left to
2008-12-05 02:20:14 +01:00
right .
2001-05-23 15:26:42 +02:00
Basic validation is made on non - leaf pages to ensure that each node
on page points to another index page . If - full validation is specified
then the lower level page is fetched to ensure it is starting index
2008-12-05 02:20:14 +01:00
entry is consistent with the parent entry .
On leaf pages , the records pointed to by the index pages are not
fetched , the keys are looked at to ensure they are in correct
2001-05-23 15:26:42 +02:00
ascending order .
If a visited page is not part of the specified relation and index ,
the following error is returned :
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Index xxx is corrupt at page xxx "
2008-12-05 02:20:14 +01:00
If there are orphan child pages , i . e . a child page does not have its entry
as yet in the parent page , however the child ' s left sibling page has it ' s
2001-05-23 15:26:42 +02:00
btr_sibling updated , the following error is returned
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
" Index xxx has orphan child page at page xxx "
If the page does not contain the number of nodes we would have
expected from its marked length , the following error is returned :
" Index xxx is corrupt on page xxx "
While we are walking leaf pages , we keep a bitmap of all record
numbers seen in the index . At the conclusion of the index walk
2008-12-05 02:20:14 +01:00
we compare this bitmap to the bitmap of all records in the
2001-05-23 15:26:42 +02:00
relation ( calculated during data page / Record Validation phase ) .
If the bitmaps are not equal then we have a corrupt index
and the following error is reported :
" Index %d is corrupt (missing entries) "
We do NOT check that each version of each record has a valid
index entry - nor do we check that the stored key for each item
in the index corresponds to a version of the specified record .
K . Relation Checking
We count the number of backversions seen while walking pointer pages ,
and separately count the number of backversions seen while walking
record chains . If these numbers do not match it indicates either
" orphan " backversion chains or double - linked chains . If this is
see the following error is returned :
" Relation has %ld orphan backversions (%ld in use) "
Currently we do not try to correct this condition , mearly report
it . For " orphan " backversions the space can be reclaimed by
a backup / restore . For double - linked chains a SWEEP should
remove all the backversions .
VI . ADDITIONAL NOTES
A . Damaged Records
2008-12-05 02:20:14 +01:00
If any corruption of a record fragment is seen during validation , the
record header is marked as " damaged " . As far as I can see , this has no
effect on the engine per se . Records marked as damaged will still be
2001-05-23 15:26:42 +02:00
retrieved by the engine itself . There is some question in my mind as
to whether this record should be retrieved at all during a gbak .
If a damaged record is visited , the following error message will appear :
" Record xxx is marked as damaged "
Note that when a damaged record is first detected , this message is not
2008-12-05 02:20:14 +01:00
actually printed . The record is simply marked as damaged . It is only
2001-05-23 15:26:42 +02:00
thereafter when the record is visited that this message will appear .
So I would postulate that unless a full validation is done at some point ,
2008-12-05 02:20:14 +01:00
you would not see this error message ; once the full validation is done ,
2001-05-23 15:26:42 +02:00
the message will be returned even if you do not specify - full .
B . Damaged Blobs
2008-12-05 02:20:14 +01:00
Blob records marked as damaged cannot be opened and will not be deleted
from disk . This means that even during backup the blob structures marked
2001-05-23 15:26:42 +02:00
as damaged will not be fetched and backed up . ( Why this is done
2008-12-05 02:20:14 +01:00
differently for blobs than for records I cannot say .
2001-05-23 15:26:42 +02:00
Perhaps it was viewed as too difficult to try to retrieve a damaged blob . )
*/
2001-07-29 19:42:23 +02:00
# include "firebird.h"
2003-08-13 12:45:10 +02:00
# include "memory_routines.h"
2004-04-29 00:43:34 +02:00
# include <stdio.h>
2001-05-23 15:26:42 +02:00
# include <stdarg.h>
# include "../jrd/jrd.h"
# include "../jrd/pag.h"
2014-09-28 11:43:57 +02:00
# include "../jrd/inf_pub.h"
2001-05-23 15:26:42 +02:00
# include "../jrd/val.h"
# include "../jrd/btr.h"
2003-12-01 03:37:25 +01:00
# include "../jrd/btn.h"
2001-05-23 15:26:42 +02:00
# include "../jrd/cch.h"
# include "../jrd/rse.h"
# include "../jrd/tra.h"
2015-06-19 14:07:41 +02:00
# include "../jrd/svc.h"
2001-05-23 15:26:42 +02:00
# include "../jrd/btr_proto.h"
# include "../jrd/cch_proto.h"
# include "../jrd/dpm_proto.h"
# include "../jrd/err_proto.h"
# include "../jrd/jrd_proto.h"
2010-10-12 10:02:57 +02:00
# include "../yvalve/gds_proto.h"
2015-03-20 19:02:30 +01:00
# include "../common/isc_proto.h"
2001-05-23 15:26:42 +02:00
# include "../jrd/met_proto.h"
2015-10-19 15:32:02 +02:00
# include "../jrd/ods_proto.h"
2001-05-23 15:26:42 +02:00
# include "../jrd/tra_proto.h"
# include "../jrd/val_proto.h"
2014-09-28 11:43:57 +02:00
# include "../jrd/validation.h"
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
# include "../common/classes/ClumpletWriter.h"
2015-07-05 12:36:09 +02:00
# include "../common/db_alias.h"
2015-06-19 14:07:41 +02:00
# include "../jrd/intl_proto.h"
# include "../jrd/lck_proto.h"
# include "../jrd/Collation.h"
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
# include "../jrd/dmp_proto.h"
/* Control variable for verbose output during debug of
2008-12-05 02:20:14 +01:00
validation .
2001-05-23 15:26:42 +02:00
0 = = logged errors only
1 = = logical output also
2 = = physical page output also */
static USHORT VAL_debug_level = 0 ;
# endif
2004-03-20 15:57:40 +01:00
using namespace Jrd ;
using namespace Ods ;
2015-06-19 14:07:41 +02:00
using namespace Firebird ;
2004-03-20 15:57:40 +01:00
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
# ifdef DEBUG_VAL_VERBOSE
static void print_rhd ( USHORT , const rhd * ) ;
# endif
2012-02-07 04:17:52 +01:00
2015-06-19 14:07:41 +02:00
static PatternMatcher * createPatternMatcher ( thread_db * tdbb , const char * pattern )
{
PatternMatcher * matcher = NULL ;
try
{
if ( pattern )
{
const int len = strlen ( pattern ) ;
Collation * obj = INTL_texttype_lookup ( tdbb , CS_UTF8 ) ;
matcher = obj - > createSimilarToMatcher ( * tdbb - > getDefaultPool ( ) ,
2015-06-29 03:02:52 +02:00
( const UCHAR * ) pattern , len , ( UCHAR * ) " \\ " , 1 ) ;
2015-06-19 14:07:41 +02:00
}
}
catch ( const Exception & ex )
{
Arg : : StatusVector status ( ex ) ;
status < < Arg : : Gds ( isc_random ) < < Arg : : Str ( pattern ) ;
status . raise ( ) ;
}
return matcher ;
}
2014-09-28 11:43:57 +02:00
static void explain_pp_bits ( const UCHAR bits , Firebird : : string & names )
2012-02-07 04:17:52 +01:00
{
2014-09-28 11:43:57 +02:00
if ( bits & ppg_dp_full )
names = " full " ;
2012-02-07 04:17:52 +01:00
2014-09-28 11:43:57 +02:00
if ( bits & ppg_dp_large )
{
2014-10-12 04:25:02 +02:00
if ( ! names . empty ( ) )
2014-09-28 11:43:57 +02:00
names . append ( " , " ) ;
names . append ( " large " ) ;
}
2012-02-07 04:17:52 +01:00
2014-09-28 11:43:57 +02:00
if ( bits & ppg_dp_swept )
{
2014-10-12 04:25:02 +02:00
if ( ! names . empty ( ) )
2014-09-28 11:43:57 +02:00
names . append ( " , " ) ;
names . append ( " swept " ) ;
}
2004-05-24 19:31:47 +02:00
2014-09-28 11:43:57 +02:00
if ( bits & ppg_dp_secondary )
{
2014-10-12 04:25:02 +02:00
if ( ! names . empty ( ) )
2014-09-28 11:43:57 +02:00
names . append ( " , " ) ;
names . append ( " secondary " ) ;
}
2004-05-04 00:42:47 +02:00
2014-10-12 04:25:02 +02:00
if ( bits & ppg_dp_empty )
2014-09-28 11:43:57 +02:00
{
2014-10-12 04:25:02 +02:00
if ( ! names . empty ( ) )
2014-09-28 11:43:57 +02:00
names . append ( " , " ) ;
names . append ( " empty " ) ;
}
}
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
bool VAL_validate ( thread_db * tdbb , USHORT switches )
2012-02-07 04:17:52 +01:00
{
2014-09-28 11:43:57 +02:00
/**************************************
*
* V A L _ v a l i d a t e
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Validate a database .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
SET_TDBB ( tdbb ) ;
Attachment * att = tdbb - > getAttachment ( ) ;
if ( ! att - > att_validation )
2015-10-12 16:26:00 +02:00
att - > att_validation = FB_NEW_POOL ( * att - > att_pool ) Validation ( tdbb ) ;
2015-06-19 14:07:41 +02:00
USHORT flags = 0 ;
if ( switches & isc_dpb_records )
flags | = Validation : : VDR_records ;
2014-09-28 11:43:57 +02:00
2015-06-19 14:07:41 +02:00
if ( switches & isc_dpb_repair )
flags | = Validation : : VDR_repair ;
if ( ! ( switches & isc_dpb_no_update ) )
flags | = Validation : : VDR_update ;
return att - > att_validation - > run ( tdbb , flags ) ;
}
static int validate ( Firebird : : UtilSvc * svc )
{
PathName dbName ;
string userName ;
const Switches valSwitches ( val_option_in_sw_table , FB_NELEM ( val_option_in_sw_table ) , false , true ) ;
const char * * argv = svc - > argv . begin ( ) ;
const char * const * end = svc - > argv . end ( ) ;
2015-06-29 03:02:52 +02:00
2015-06-19 14:07:41 +02:00
for ( + + argv ; argv < end ; argv + + )
{
if ( ! * argv )
continue ;
const Switches : : in_sw_tab_t * sw = valSwitches . findSwitch ( * argv ) ;
if ( ! sw )
continue ;
switch ( sw - > in_sw )
{
case IN_SW_VAL_DATABASE :
* argv = NULL ;
argv + + ;
if ( argv < end & & * argv )
dbName = * argv ;
else
2015-06-29 03:02:52 +02:00
; // error
2015-06-19 14:07:41 +02:00
break ;
default :
break ;
}
}
ClumpletWriter dpb ( ClumpletReader : : Tagged , MAX_DPB_SIZE , isc_dpb_version1 ) ;
if ( ! userName . isEmpty ( ) )
{
dpb . insertString ( isc_dpb_trusted_auth , userName ) ;
}
2015-07-05 12:36:09 +02:00
PathName expandedFilename ;
if ( expandDatabaseName ( dbName , expandedFilename , NULL ) )
expandedFilename = dbName ;
if ( dbName ! = expandedFilename )
dpb . insertPath ( isc_dpb_org_filename , dbName ) ;
2015-06-19 14:07:41 +02:00
FbLocalStatus status ;
RefPtr < JProvider > jProv ( JProvider : : getInstance ( ) ) ;
RefPtr < JAttachment > jAtt ;
2015-07-05 12:36:09 +02:00
jAtt . assignRefNoIncr ( jProv - > attachDatabase ( & status , expandedFilename . c_str ( ) , dpb . getBufferLength ( ) , dpb . getBuffer ( ) ) ) ;
2015-06-19 14:07:41 +02:00
if ( status - > getState ( ) & IStatus : : STATE_ERRORS )
{
svc - > setServiceStatus ( status - > getErrors ( ) ) ;
return FB_FAILURE ;
}
Attachment * att = jAtt - > getHandle ( ) ;
Database * dbb = att - > att_database ;
svc - > started ( ) ;
MemoryPool * val_pool = NULL ;
int ret_code = FB_SUCCESS ;
try
{
// should be EngineContextHolder but it is declared in jrd.cpp
BackgroundContextHolder tdbb ( dbb , att , & status , FB_FUNCTION ) ;
att - > att_use_count + + ;
tdbb - > tdbb_flags | = TDBB_sweeper ;
val_pool = dbb - > createPool ( ) ;
Jrd : : ContextPoolHolder context ( tdbb , val_pool ) ;
Validation control ( tdbb , svc ) ;
control . run ( tdbb , Validation : : VDR_records | Validation : : VDR_online | Validation : : VDR_partial ) ;
att - > att_use_count - - ;
}
catch ( const Exception & ex )
{
att - > att_use_count - - ;
ex . stuffException ( & status ) ;
svc - > setServiceStatus ( status - > getErrors ( ) ) ;
ret_code = FB_FAILURE ;
}
dbb - > deletePool ( val_pool ) ;
jAtt - > detach ( & status ) ;
return ret_code ;
}
int VAL_service ( Firebird : : UtilSvc * svc )
{
svc - > initStatus ( ) ;
int exit_code = FB_SUCCESS ;
try
{
exit_code = validate ( svc ) ;
}
catch ( const Exception & ex )
{
FbLocalStatus status ;
ex . stuffException ( & status ) ;
svc - > setServiceStatus ( status - > getErrors ( ) ) ;
exit_code = FB_FAILURE ;
}
svc - > started ( ) ;
2015-06-19 15:55:27 +02:00
return exit_code ;
2012-02-07 04:17:52 +01:00
}
2001-05-23 15:26:42 +02:00
2006-02-23 06:08:26 +01:00
2014-09-28 11:43:57 +02:00
namespace Jrd
2006-05-04 02:03:52 +02:00
{
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
const Validation : : MSG_ENTRY Validation : : vdr_msg_table [ VAL_MAX_ERROR ] =
{
2016-03-17 11:15:08 +01:00
{ true , isc_info_page_errors , " Page % " ULONGFORMAT " wrong type (expected %s encountered %s) " } , // 0
{ true , isc_info_page_errors , " Checksum error on page % " ULONGFORMAT } ,
{ true , isc_info_page_errors , " Page % " ULONGFORMAT " doubly allocated " } ,
{ true , isc_info_page_errors , " Page % " ULONGFORMAT " is used but marked free " } ,
{ false , fb_info_page_warns , " Page % " ULONGFORMAT " is an orphan " } ,
{ false , fb_info_bpage_warns , " Blob % " SQUADFORMAT " appears inconsistent " } , // 5
{ true , isc_info_bpage_errors , " Blob % " SQUADFORMAT " is corrupt " } ,
{ true , isc_info_bpage_errors , " Blob % " SQUADFORMAT " is truncated " } ,
{ true , isc_info_record_errors , " Chain for record % " SQUADFORMAT " is broken " } ,
{ true , isc_info_dpage_errors , " Data page % " ULONGFORMAT " {sequence % " ULONGFORMAT " } is confused " } ,
{ true , isc_info_dpage_errors , " Data page % " ULONGFORMAT " {sequence % " ULONGFORMAT " }, line % " ULONGFORMAT " is bad " } , // 10
{ true , isc_info_ipage_errors , " Index %d is corrupt on page % " ULONGFORMAT " level %d at offset % " ULONGFORMAT " . File: %s, line: %d \n \t " } ,
{ true , isc_info_ppage_errors , " Pointer page {sequence % " ULONGFORMAT " } lost " } ,
{ true , isc_info_ppage_errors , " Pointer page % " ULONGFORMAT " {sequence % " ULONGFORMAT " } inconsistent " } ,
{ true , isc_info_record_errors , " Record % " SQUADFORMAT " is marked as damaged " } ,
{ true , isc_info_record_errors , " Record % " SQUADFORMAT " has bad transaction % " ULONGFORMAT } , // 15
{ true , isc_info_record_errors , " Fragmented record % " SQUADFORMAT " is corrupt " } ,
{ true , isc_info_record_errors , " Record % " SQUADFORMAT " is wrong length " } ,
2014-09-28 11:43:57 +02:00
{ true , isc_info_ipage_errors , " Missing index root page " } ,
{ true , isc_info_tpage_errors , " Transaction inventory pages lost " } ,
2016-03-17 11:15:08 +01:00
{ true , isc_info_tpage_errors , " Transaction inventory page lost, sequence % " ULONGFORMAT } , // 20
{ true , isc_info_tpage_errors , " Transaction inventory pages confused, sequence % " ULONGFORMAT } ,
{ false , fb_info_record_warns , " Relation has % " UQUADFORMAT " orphan backversions {% " UQUADFORMAT " in use} " } ,
{ true , isc_info_ipage_errors , " Index %d is corrupt {missing entries for record % " SQUADFORMAT " } " } ,
{ false , fb_info_ipage_warns , " Index %d has orphan child page at page % " ULONGFORMAT } ,
{ true , isc_info_ipage_errors , " Index %d has a circular reference at page % " ULONGFORMAT } , // 25
{ true , isc_info_page_errors , " SCN's page % " ULONGFORMAT " {sequence % " ULONGFORMAT " } inconsistent " } ,
{ false , fb_info_page_warns , " Page % " ULONGFORMAT " has SCN % " ULONGFORMAT " while at SCN's page it is % " ULONGFORMAT } ,
{ true , isc_info_bpage_errors , " Blob % " SQUADFORMAT " has unknown level %d instead of {0, 1, 2} " } ,
{ false , fb_info_ipage_warns , " Index %d has inconsistent left sibling pointer, page % " ULONGFORMAT " level %d at offset % " ULONGFORMAT } ,
{ false , fb_info_ipage_warns , " Index %d misses node on page % " ULONGFORMAT " level %d at offset % " ULONGFORMAT } , // 30
{ false , fb_info_pip_warns , " PIP % " ULONGFORMAT " (seq %d) have wrong pip_min (% " ULONGFORMAT " ). Correct is % " ULONGFORMAT } ,
{ false , fb_info_pip_warns , " PIP % " ULONGFORMAT " (seq %d) have wrong pip_extent (% " ULONGFORMAT " ). Correct is % " ULONGFORMAT } ,
{ false , fb_info_pip_warns , " PIP % " ULONGFORMAT " (seq %d) have wrong pip_used (% " ULONGFORMAT " ). Correct is % " ULONGFORMAT } ,
{ false , fb_info_ppage_warns , " Pointer page % " ULONGFORMAT " {sequence % " ULONGFORMAT " } bits {0x%02X %s} are not consistent with data page % " ULONGFORMAT " {sequence % " ULONGFORMAT " } state {0x%02X %s} " } ,
{ true , fb_info_pip_errors , " Data page % " ULONGFORMAT " marked as free in PIP (% " ULONGFORMAT " :% " ULONGFORMAT " ) " } ,
{ true , isc_info_ppage_errors , " Data page % " ULONGFORMAT " is not in PP (% " ULONGFORMAT " ). Slot (%d) is not found " } ,
{ true , isc_info_ppage_errors , " Data page % " ULONGFORMAT " is not in PP (% " ULONGFORMAT " ). Slot (%d) has value % " ULONGFORMAT } ,
{ true , isc_info_ppage_errors , " Pointer page is not found for data page % " ULONGFORMAT " . dpg_sequence (% " ULONGFORMAT " ) is invalid " }
2014-09-28 11:43:57 +02:00
} ;
2006-02-23 06:08:26 +01:00
2015-07-26 21:56:40 +02:00
Validation : : Validation ( thread_db * tdbb , UtilSvc * uSvc ) :
vdr_used_bdbs ( * tdbb - > getDefaultPool ( ) )
2014-09-28 11:43:57 +02:00
{
2015-06-19 14:07:41 +02:00
vdr_tdbb = tdbb ;
2014-09-28 11:43:57 +02:00
vdr_max_page = 0 ;
vdr_flags = 0 ;
vdr_errors = 0 ;
2014-10-02 00:00:35 +02:00
vdr_warns = 0 ;
vdr_fixed = 0 ;
2014-09-28 11:43:57 +02:00
vdr_max_transaction = 0 ;
vdr_rel_backversion_counter = 0 ;
2015-11-26 10:28:31 +01:00
vdr_backversion_pages = NULL ;
2014-09-28 11:43:57 +02:00
vdr_rel_chain_counter = 0 ;
2015-11-26 10:28:31 +01:00
vdr_chain_pages = NULL ;
2014-09-28 11:43:57 +02:00
vdr_rel_records = NULL ;
vdr_idx_records = NULL ;
vdr_page_bitmap = NULL ;
2015-06-19 14:07:41 +02:00
vdr_service = uSvc ;
vdr_tab_incl = vdr_tab_excl = NULL ;
vdr_idx_incl = vdr_idx_excl = NULL ;
vdr_lock_tout = - 10 ;
if ( uSvc ) {
parse_args ( tdbb ) ;
}
output ( " Validation started \n \n " ) ;
2014-09-28 11:43:57 +02:00
}
2006-02-23 06:08:26 +01:00
2015-06-19 14:07:41 +02:00
Validation : : ~ Validation ( )
{
delete vdr_tab_incl ;
delete vdr_tab_excl ;
delete vdr_idx_incl ;
delete vdr_idx_excl ;
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
output ( " Validation finished \n " ) ;
}
void Validation : : parse_args ( thread_db * tdbb )
{
Switches local_sw_table ( val_option_in_sw_table , FB_NELEM ( val_option_in_sw_table ) , true , true ) ;
const char * * argv = vdr_service - > argv . begin ( ) ;
const char * const * end = vdr_service - > argv . end ( ) ;
for ( + + argv ; argv < end ; argv + + )
{
if ( ! * argv )
continue ;
string arg ( * argv ) ;
Switches : : in_sw_tab_t * sw = local_sw_table . findSwitchMod ( arg ) ;
if ( ! sw )
continue ;
if ( sw - > in_sw_state )
{
string s ;
s . printf ( " Switch %s specified more than once " , sw - > in_sw_name ) ;
( Arg : : Gds ( isc_random ) < < Arg : : Str ( s ) ) . raise ( ) ;
}
sw - > in_sw_state = true ;
switch ( sw - > in_sw )
{
case IN_SW_VAL_TAB_INCL :
case IN_SW_VAL_TAB_EXCL :
case IN_SW_VAL_IDX_INCL :
case IN_SW_VAL_IDX_EXCL :
case IN_SW_VAL_LOCK_TIMEOUT :
* argv + + = NULL ;
if ( argv > = end | | ! ( * argv ) )
{
string s ;
s . printf ( " Switch %s requires value " , sw - > in_sw_name ) ;
( Arg : : Gds ( isc_random ) < < Arg : : Str ( s ) ) . raise ( ) ;
}
break ;
default :
break ;
}
switch ( sw - > in_sw )
{
case IN_SW_VAL_TAB_INCL :
vdr_tab_incl = createPatternMatcher ( tdbb , * argv ) ;
break ;
case IN_SW_VAL_TAB_EXCL :
vdr_tab_excl = createPatternMatcher ( tdbb , * argv ) ;
break ;
case IN_SW_VAL_IDX_INCL :
vdr_idx_incl = createPatternMatcher ( tdbb , * argv ) ;
break ;
case IN_SW_VAL_IDX_EXCL :
vdr_idx_excl = createPatternMatcher ( tdbb , * argv ) ;
break ;
case IN_SW_VAL_LOCK_TIMEOUT :
{
char * end = ( char * ) * argv ;
vdr_lock_tout = - strtol ( * argv , & end , 10 ) ;
if ( end & & * end )
{
string s ;
s . printf ( " Value (%s) is not a valid number " , * argv ) ;
( Arg : : Gds ( isc_random ) < < Arg : : Str ( s ) ) . raise ( ) ;
}
}
break ;
default :
break ;
}
}
}
void Validation : : output ( const char * format , . . . )
{
if ( ! vdr_service )
return ;
va_list params ;
va_start ( params , format ) ;
string s ;
tm now ;
int ms ;
TimeStamp : : getCurrentTimeStamp ( ) . decode ( & now , & ms ) ;
2015-06-29 03:02:52 +02:00
///s.printf("%04d-%02d-%02d %02d:%02d:%02d.%04d ",
2015-06-19 14:07:41 +02:00
s . printf ( " %02d:%02d:%02d.%02d " ,
2015-06-29 03:02:52 +02:00
///now.tm_year + 1900, now.tm_mon + 1, now.tm_mday,
2015-06-19 14:07:41 +02:00
now . tm_hour , now . tm_min , now . tm_sec , ms / 100 ) ;
vdr_service - > outputVerbose ( s . c_str ( ) ) ;
s . vprintf ( format , params ) ;
va_end ( params ) ;
vdr_service - > outputVerbose ( s . c_str ( ) ) ;
}
bool Validation : : run ( thread_db * tdbb , USHORT flags )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2014-09-28 11:43:57 +02:00
* r u n
2001-05-23 15:26:42 +02:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Validate a database .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
vdr_tdbb = tdbb ;
2008-01-29 19:07:37 +01:00
MemoryPool * val_pool = NULL ;
2007-12-03 16:46:39 +01:00
Database * dbb = tdbb - > getDatabase ( ) ;
2014-10-02 00:00:35 +02:00
Firebird : : PathName fileName = tdbb - > getAttachment ( ) - > att_filename ;
2001-05-23 15:26:42 +02:00
2014-10-12 04:25:02 +02:00
try
2014-09-28 11:43:57 +02:00
{
2009-06-27 08:23:36 +02:00
val_pool = dbb - > createPool ( ) ;
Jrd : : ContextPoolHolder context ( tdbb , val_pool ) ;
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
vdr_flags = flags ;
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// initialize validate errors
2014-10-02 00:00:35 +02:00
vdr_errors = vdr_warns = vdr_fixed = 0 ;
2014-09-28 11:43:57 +02:00
for ( USHORT i = 0 ; i < VAL_MAX_ERROR ; i + + )
vdr_err_counts [ i ] = 0 ;
2001-05-23 15:26:42 +02:00
2009-06-27 08:23:36 +02:00
tdbb - > tdbb_flags | = TDBB_sweeper ;
2009-12-21 00:13:01 +01:00
2014-10-02 00:00:35 +02:00
gds__log ( " Database: %s \n \t Validation started " , fileName . c_str ( ) ) ;
2014-09-28 11:43:57 +02:00
walk_database ( ) ;
2014-10-02 00:00:35 +02:00
if ( vdr_errors | | vdr_warns )
2014-09-28 11:43:57 +02:00
vdr_flags & = ~ VDR_update ;
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
if ( ! ( vdr_flags & VDR_online ) & & ! ( vdr_flags & VDR_partial ) ) {
garbage_collect ( ) ;
}
2015-07-06 00:03:09 +02:00
if ( vdr_fixed )
{
const USHORT flushFlags = ( ( dbb - > dbb_flags & DBB_shared ) & & ( vdr_flags & VDR_online ) ) ?
FLUSH_SYSTEM : FLUSH_FINI ;
CCH_flush ( tdbb , flushFlags , 0 ) ;
}
2001-05-23 15:26:42 +02:00
2009-06-27 08:23:36 +02:00
tdbb - > tdbb_flags & = ~ TDBB_sweeper ;
2014-09-28 11:43:57 +02:00
cleanup ( ) ;
2014-10-02 00:00:35 +02:00
2014-10-12 04:25:02 +02:00
gds__log ( " Database: %s \n \t Validation finished: %d errors, %d warnings, %d fixed " ,
2014-10-02 00:00:35 +02:00
fileName . c_str ( ) , vdr_errors , vdr_warns , vdr_fixed ) ;
2001-12-24 03:51:06 +01:00
} // try
2009-06-27 08:23:36 +02:00
catch ( const Firebird : : Exception & ex )
{
2015-03-27 18:51:19 +01:00
ex . stuffException ( tdbb - > tdbb_status_vector ) ;
2014-10-02 00:00:35 +02:00
Firebird : : string err ;
err . printf ( " Database: %s \n \t Validation aborted " , fileName . c_str ( ) ) ;
2015-03-20 19:02:30 +01:00
iscLogStatus ( err . c_str ( ) , tdbb - > tdbb_status_vector ) ;
2014-10-02 00:00:35 +02:00
2014-09-28 11:43:57 +02:00
cleanup ( ) ;
2008-01-29 11:11:52 +01:00
dbb - > deletePool ( val_pool ) ;
2001-12-24 03:51:06 +01:00
tdbb - > tdbb_flags & = ~ TDBB_sweeper ;
2004-03-07 08:58:55 +01:00
return false ;
2001-12-24 03:51:06 +01:00
}
2008-01-29 11:11:52 +01:00
dbb - > deletePool ( val_pool ) ;
2004-03-07 08:58:55 +01:00
return true ;
2001-05-23 15:26:42 +02:00
}
2001-12-24 03:51:06 +01:00
2014-09-28 11:43:57 +02:00
void Validation : : cleanup ( )
{
delete vdr_page_bitmap ;
vdr_page_bitmap = NULL ;
delete vdr_rel_records ;
vdr_rel_records = NULL ;
delete vdr_idx_records ;
vdr_idx_records = NULL ;
}
ULONG Validation : : getInfo ( UCHAR item )
{
ULONG ret = 0 ;
for ( int i = 0 ; i < VAL_MAX_ERROR ; i + + )
2014-10-25 20:39:45 +02:00
{
2014-09-28 11:43:57 +02:00
if ( vdr_msg_table [ i ] . info_item = = item )
ret + = vdr_err_counts [ i ] ;
2014-10-25 20:39:45 +02:00
}
2014-09-28 11:43:57 +02:00
return ret ;
}
Validation : : RTN Validation : : corrupt ( int err_code , const jrd_rel * relation , . . . )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o r r u p t
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Corruption has been detected .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
fb_assert ( sizeof ( vdr_msg_table ) / sizeof ( vdr_msg_table [ 0 ] ) = = VAL_MAX_ERROR ) ;
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
Attachment * att = vdr_tdbb - > getAttachment ( ) ;
if ( err_code < VAL_MAX_ERROR )
vdr_err_counts [ err_code ] + + ;
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
const TEXT * err_string = err_code < VAL_MAX_ERROR ? vdr_msg_table [ err_code ] . msg : " Unknown error code " ;
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
string s ;
2007-09-04 10:34:20 +02:00
va_list ptr ;
2014-09-28 11:43:57 +02:00
const char * fn = att - > att_filename . c_str ( ) ;
2001-05-23 15:26:42 +02:00
2007-09-04 10:34:20 +02:00
va_start ( ptr , relation ) ;
2014-09-28 11:43:57 +02:00
s . vprintf ( err_string , ptr ) ;
2007-09-04 10:34:20 +02:00
va_end ( ptr ) ;
2001-05-23 15:26:42 +02:00
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level > = 0 )
2007-09-04 10:34:20 +02:00
{
if ( relation )
{
fprintf ( stdout , " LOG: \t Database: %s \n \t %s in table %s (%d) \n " ,
2014-09-28 11:43:57 +02:00
fn , s . c_str ( ) , relation - > rel_name . c_str ( ) , relation - > rel_id ) ;
2007-09-04 10:34:20 +02:00
}
else
2014-09-28 11:43:57 +02:00
fprintf ( stdout , " LOG: \t Database: %s \n \t %s \n " , fn , s . c_str ( ) ) ;
2007-09-04 10:34:20 +02:00
}
2001-05-23 15:26:42 +02:00
# endif
2014-10-02 00:00:35 +02:00
if ( vdr_msg_table [ err_code ] . error )
{
+ + vdr_errors ;
s . insert ( 0 , " Error: " ) ;
}
else
{
+ + vdr_warns ;
s . insert ( 0 , " Warning: " ) ;
}
2001-05-23 15:26:42 +02:00
2007-09-04 10:34:20 +02:00
if ( relation )
{
gds__log ( " Database: %s \n \t %s in table %s (%d) " ,
2014-09-28 11:43:57 +02:00
fn , s . c_str ( ) , relation - > rel_name . c_str ( ) , relation - > rel_id ) ;
2007-09-04 10:34:20 +02:00
}
else
2014-09-28 11:43:57 +02:00
gds__log ( " Database: %s \n \t %s " , fn , s . c_str ( ) ) ;
2007-09-04 10:34:20 +02:00
2015-06-19 14:07:41 +02:00
s . append ( " \n " ) ;
output ( s . c_str ( ) ) ;
2001-05-23 15:26:42 +02:00
return rtn_corrupt ;
}
2003-10-09 04:02:42 +02:00
2014-10-12 04:25:02 +02:00
Validation : : FETCH_CODE Validation : : fetch_page ( bool mark , ULONG page_number ,
2015-06-19 14:07:41 +02:00
USHORT type , WIN * window , void * aPage_pointer )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f e t c h _ p a g e
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
2014-09-28 11:43:57 +02:00
* Fetch page and return type of illness , if any . If " mark " is true ,
* check for doubly allocated pages and account for page use .
2001-05-23 15:26:42 +02:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
if ( - - vdr_tdbb - > tdbb_quantum < 0 )
2015-06-19 14:07:41 +02:00
{
2014-09-28 11:43:57 +02:00
JRD_reschedule ( vdr_tdbb , 0 , true ) ;
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
if ( vdr_service & & vdr_service - > finished ( ) )
{
CCH_unwind ( vdr_tdbb , false ) ;
Arg : : Gds ( isc_att_shutdown ) . raise ( ) ;
}
}
2001-05-23 15:26:42 +02:00
window - > win_page = page_number ;
window - > win_flags = 0 ;
2015-06-19 14:07:41 +02:00
pag * * page_pointer = reinterpret_cast < pag * * > ( aPage_pointer ) ;
2015-07-26 21:56:40 +02:00
FB_SIZE_T pos ;
if ( vdr_used_bdbs . find ( page_number , pos ) )
{
vdr_used_bdbs [ pos ] . count + + ;
BufferDesc * bdb = vdr_used_bdbs [ pos ] . bdb ;
fb_assert ( bdb - > bdb_page = = PageNumber ( DB_PAGE_SPACE , page_number ) ) ;
window - > win_bdb = bdb ;
* page_pointer = window - > win_buffer = bdb - > bdb_buffer ;
}
else
{
* page_pointer = CCH_FETCH_NO_SHADOW ( vdr_tdbb , window ,
( vdr_flags & VDR_online ? LCK_read : LCK_write ) ,
0 ) ;
2015-10-23 07:27:49 +02:00
vdr_used_bdbs . add ( UsedBdb ( window - > win_bdb ) ) ;
2015-07-26 21:56:40 +02:00
}
2001-05-23 15:26:42 +02:00
2009-12-21 00:13:01 +01:00
if ( ( * page_pointer ) - > pag_type ! = type & & type ! = pag_undefined )
2009-06-27 08:23:36 +02:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PAG_WRONG_TYPE , 0 , page_number ,
2012-06-28 13:46:21 +02:00
pagtype ( type ) . c_str ( ) , pagtype ( ( * page_pointer ) - > pag_type ) . c_str ( ) ) ;
2001-05-23 15:26:42 +02:00
return fetch_type ;
}
2014-09-28 11:43:57 +02:00
if ( ! mark )
2001-05-23 15:26:42 +02:00
return fetch_ok ;
2009-08-13 12:35:56 +02:00
// If "damaged" flag was set, checksum may be incorrect. Check.
2001-05-23 15:26:42 +02:00
2009-06-27 08:23:36 +02:00
if ( ( dbb - > dbb_flags & DBB_damaged ) & & ! CCH_validate ( window ) )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PAG_CHECKSUM_ERR , 0 , page_number ) ;
if ( vdr_flags & VDR_repair )
CCH_MARK ( vdr_tdbb , window ) ;
2001-05-23 15:26:42 +02:00
}
2012-02-07 04:17:52 +01:00
vdr_max_page = MAX ( vdr_max_page , page_number ) ;
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// For walking back versions & record fragments on data pages we
// sometimes will fetch the same page more than once. In that
// event we don't report double allocation. If the page is truely
// double allocated (to more than one relation) we'll find it
2009-12-23 22:43:37 +01:00
// when the on-page relation id doesn't match.
2009-12-21 00:13:01 +01:00
// We also don't test SCN's pages here. If it double allocated this
// will be detected when wrong page reference will be fetched with
// non pag_scns type.
2001-05-23 15:26:42 +02:00
2009-12-23 22:43:37 +01:00
if ( type ! = pag_data & & type ! = pag_scns & &
2012-02-07 04:17:52 +01:00
PageBitmap : : test ( vdr_page_bitmap , page_number ) )
2009-06-27 08:23:36 +02:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PAG_DOUBLE_ALLOC , 0 , page_number ) ;
2001-05-23 15:26:42 +02:00
return fetch_duplicate ;
}
2009-12-21 00:13:01 +01:00
// Check SCN's page
if ( page_number )
{
2009-12-24 12:48:17 +01:00
const PageManager & pageMgr = dbb - > dbb_page_manager ;
2009-12-21 00:13:01 +01:00
const ULONG scn_seq = page_number / pageMgr . pagesPerSCN ;
const ULONG scn_slot = page_number % pageMgr . pagesPerSCN ;
const ULONG scn_page_num = PageSpace : : getSCNPageNum ( dbb , scn_seq ) ;
const ULONG page_scn = ( * page_pointer ) - > pag_scn ;
2009-12-22 01:08:49 +01:00
2009-12-21 00:13:01 +01:00
WIN scns_window ( DB_PAGE_SPACE , scn_page_num ) ;
2009-12-23 22:43:37 +01:00
scns_page * scns = ( scns_page * ) * page_pointer ;
2009-12-22 01:08:49 +01:00
2009-12-21 00:13:01 +01:00
if ( scn_page_num ! = page_number ) {
2014-09-28 11:43:57 +02:00
fetch_page ( mark , scn_page_num , pag_scns , & scns_window , & scns ) ;
2009-12-21 00:13:01 +01:00
}
2009-12-22 01:08:49 +01:00
if ( scns - > scn_pages [ scn_slot ] ! = page_scn )
2009-12-21 00:13:01 +01:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PAG_WRONG_SCN , 0 , page_number , page_scn , scns - > scn_pages [ scn_slot ] ) ;
2009-12-21 00:13:01 +01:00
2014-09-28 11:43:57 +02:00
if ( vdr_flags & VDR_update )
2009-12-21 00:13:01 +01:00
{
2009-12-23 22:43:37 +01:00
WIN * win = ( scn_page_num = = page_number ) ? window : & scns_window ;
2014-09-28 11:43:57 +02:00
CCH_MARK ( vdr_tdbb , win ) ;
2009-12-21 00:13:01 +01:00
scns - > scn_pages [ scn_slot ] = page_scn ;
2014-10-02 00:00:35 +02:00
vdr_fixed + + ;
2009-12-21 00:13:01 +01:00
}
}
if ( scn_page_num ! = page_number ) {
2015-07-26 21:56:40 +02:00
release_page ( & scns_window ) ;
2009-12-21 00:13:01 +01:00
}
}
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
PBM_SET ( vdr_tdbb - > getDefaultPool ( ) , & vdr_page_bitmap , page_number ) ;
2001-05-23 15:26:42 +02:00
return fetch_ok ;
}
2003-10-09 04:02:42 +02:00
2015-07-26 21:56:40 +02:00
void Validation : : release_page ( WIN * window )
{
FB_SIZE_T pos ;
if ( ! vdr_used_bdbs . find ( window - > win_page . getPageNum ( ) , pos ) )
{
fb_assert ( false ) ;
return ; // BUG
}
fb_assert ( vdr_used_bdbs [ pos ] . bdb = = window - > win_bdb ) ;
if ( ! - - vdr_used_bdbs [ pos ] . count )
{
CCH_RELEASE ( vdr_tdbb , window ) ;
vdr_used_bdbs . remove ( pos ) ;
}
}
2014-09-28 11:43:57 +02:00
void Validation : : garbage_collect ( )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g a r b a g e _ c o l l e c t
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* The database has been walked ; compare the page inventory against
* the bitmap of pages visited .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2006-05-22 00:07:35 +02:00
PageManager & pageSpaceMgr = dbb - > dbb_page_manager ;
PageSpace * pageSpace = pageSpaceMgr . findPageSpace ( DB_PAGE_SPACE ) ;
fb_assert ( pageSpace ) ;
2008-12-05 02:20:14 +01:00
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2001-05-23 15:26:42 +02:00
2012-02-07 04:17:52 +01:00
for ( ULONG sequence = 0 , number = 0 ; number < vdr_max_page ; sequence + + )
2006-02-23 06:08:26 +01:00
{
2011-01-21 18:55:04 +01:00
const ULONG page_number = sequence ? sequence * pageSpaceMgr . pagesPerPIP - 1 : pageSpace - > pipFirst ;
2006-02-23 06:08:26 +01:00
page_inv_page * page = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( false , page_number , pag_pages , & window , & page ) ;
2003-12-11 11:33:30 +01:00
UCHAR * p = page - > pip_bits ;
2006-05-22 00:07:35 +02:00
const UCHAR * const end = p + pageSpaceMgr . bytesBitPIP ;
2012-02-07 04:17:52 +01:00
while ( p < end & & number < vdr_max_page )
2009-01-03 10:14:29 +01:00
{
2003-12-11 11:33:30 +01:00
UCHAR byte = * p + + ;
2009-01-03 10:14:29 +01:00
for ( int i = 8 ; i ; - - i , byte > > = 1 , number + + )
{
2012-02-07 04:17:52 +01:00
if ( PageBitmap : : test ( vdr_page_bitmap , number ) )
2009-01-03 10:14:29 +01:00
{
2009-06-27 08:23:36 +02:00
if ( byte & 1 )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PAG_IN_USE , 0 , number ) ;
if ( vdr_flags & VDR_update )
2009-06-27 08:23:36 +02:00
{
2014-09-28 11:43:57 +02:00
CCH_MARK ( vdr_tdbb , & window ) ;
2001-05-23 15:26:42 +02:00
p [ - 1 ] & = ~ ( 1 < < ( number & 7 ) ) ;
2014-10-02 00:00:35 +02:00
vdr_fixed + + ;
2001-05-23 15:26:42 +02:00
}
DEBUG ;
}
}
2014-09-28 11:43:57 +02:00
else if ( ! ( byte & 1 ) & & ( vdr_flags & VDR_records ) )
2009-01-03 10:14:29 +01:00
{
2009-08-13 12:35:56 +02:00
// Page is potentially an orphan - but don't declare it as such
// unless we think we walked all pages
2009-06-27 08:23:36 +02:00
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PAG_ORPHAN , 0 , number ) ;
if ( vdr_flags & VDR_update )
2009-06-27 08:23:36 +02:00
{
2014-09-28 11:43:57 +02:00
CCH_MARK ( vdr_tdbb , & window ) ;
2001-05-23 15:26:42 +02:00
p [ - 1 ] | = 1 < < ( number & 7 ) ;
2014-10-02 00:00:35 +02:00
vdr_fixed + + ;
2001-05-23 15:26:42 +02:00
}
DEBUG ;
}
2003-12-11 11:33:30 +01:00
}
2001-05-23 15:26:42 +02:00
}
2003-12-11 11:33:30 +01:00
const UCHAR test_byte = p [ - 1 ] ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2003-12-11 11:33:30 +01:00
if ( test_byte & 0x80 )
2001-05-23 15:26:42 +02:00
break ;
}
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2009-08-13 12:35:56 +02:00
// Dump verbose output of all the pages fetched
2007-09-04 10:34:20 +02:00
if ( VAL_debug_level > = 2 )
{
2012-02-07 04:17:52 +01:00
if ( vdr_page_bitmap - > getFirst ( ) )
2009-08-14 03:58:22 +02:00
{
2007-09-04 10:34:20 +02:00
do {
2012-02-07 04:17:52 +01:00
ULONG dmp_page_number = vdr_page_bitmap - > current ( ) ;
2007-09-04 10:34:20 +02:00
DMP_page ( dmp_page_number , dbb - > dbb_page_size ) ;
2012-02-07 04:17:52 +01:00
} while ( vdr_page_bitmap - > getNext ( ) ) ;
2009-08-14 03:58:22 +02:00
}
2004-11-07 11:47:20 +01:00
}
2001-05-23 15:26:42 +02:00
# endif
}
2003-10-09 04:02:42 +02:00
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2003-12-22 11:00:59 +01:00
static void print_rhd ( USHORT length , const rhd * header )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p r i n t _ r h d
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Debugging routine to print a
* Record Header Data .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-06-27 08:23:36 +02:00
if ( VAL_debug_level )
{
2016-03-17 11:15:08 +01:00
fprintf ( stdout , " rhd: len %d TX % " SQUADFORMAT " format %d " ,
2015-10-19 15:32:02 +02:00
length , Ods : : getTraNum ( header ) , ( int ) header - > rhd_format ) ;
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " BP %d/%d flags 0x%x " ,
2001-05-23 15:26:42 +02:00
header - > rhd_b_page , header - > rhd_b_line , header - > rhd_flags ) ;
2009-06-27 08:23:36 +02:00
if ( header - > rhd_flags & rhd_incomplete )
{
2009-01-03 10:14:29 +01:00
const rhdf * fragment = ( rhdf * ) header ;
2008-12-19 12:12:28 +01:00
fprintf ( stdout , " FP %d/%d " , fragment - > rhdf_f_page , fragment - > rhdf_f_line ) ;
2004-11-07 11:47:20 +01:00
}
2008-12-19 12:12:28 +01:00
fprintf ( stdout , " %s " , ( header - > rhd_flags & rhd_deleted ) ? " DEL " : " " ) ;
fprintf ( stdout , " %s " , ( header - > rhd_flags & rhd_chain ) ? " CHN " : " " ) ;
fprintf ( stdout , " %s " , ( header - > rhd_flags & rhd_fragment ) ? " FRG " : " " ) ;
fprintf ( stdout , " %s " , ( header - > rhd_flags & rhd_incomplete ) ? " INC " : " " ) ;
fprintf ( stdout , " %s " , ( header - > rhd_flags & rhd_blob ) ? " BLB " : " " ) ;
fprintf ( stdout , " %s " , ( header - > rhd_flags & rhd_delta ) ? " DLT " : " " ) ;
fprintf ( stdout , " %s " , ( header - > rhd_flags & rhd_large ) ? " LRG " : " " ) ;
fprintf ( stdout , " %s " , ( header - > rhd_flags & rhd_damaged ) ? " DAM " : " " ) ;
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " \n " ) ;
2004-11-07 11:47:20 +01:00
}
2001-05-23 15:26:42 +02:00
}
# endif
2003-10-09 04:02:42 +02:00
2014-10-12 04:25:02 +02:00
Validation : : RTN Validation : : walk_blob ( jrd_rel * relation , const blh * header , USHORT length ,
2014-09-28 11:43:57 +02:00
RecordNumber number )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ b l o b
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk a blob .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2009-06-27 08:23:36 +02:00
if ( VAL_debug_level )
{
2004-04-29 00:43:34 +02:00
fprintf ( stdout ,
2001-05-23 15:26:42 +02:00
" walk_blob: level %d lead page %d max pages %d max segment %d \n " ,
header - > blh_level , header - > blh_lead_page ,
header - > blh_max_sequence , header - > blh_max_segment ) ;
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " count %d, length %d sub_type %d \n " ,
2001-05-23 15:26:42 +02:00
header - > blh_count , header - > blh_length ,
header - > blh_sub_type ) ;
2004-11-07 11:47:20 +01:00
}
2001-05-23 15:26:42 +02:00
# endif
2012-02-15 04:44:17 +01:00
switch ( header - > blh_level )
{
case 0 :
// Level 0 blobs have no work to do.
2001-05-23 15:26:42 +02:00
return rtn_ok ;
2012-02-15 04:44:17 +01:00
case 1 :
case 2 :
break ;
default :
2014-09-28 11:43:57 +02:00
corrupt ( VAL_BLOB_UNKNOWN_LEVEL , relation , number . getValue ( ) , header - > blh_level ) ;
2012-02-15 04:44:17 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// Level 1 blobs are a little more complicated
2006-05-22 00:07:35 +02:00
WIN window1 ( DB_PAGE_SPACE , - 1 ) , window2 ( DB_PAGE_SPACE , - 1 ) ;
2015-06-19 14:07:41 +02:00
window1 . win_flags = window2 . win_flags = WIN_garbage_collector ;
2001-05-23 15:26:42 +02:00
2011-01-21 18:55:04 +01:00
const ULONG * pages1 = header - > blh_page ;
const ULONG * const end1 = pages1 + ( ( USHORT ) ( length - BLH_SIZE ) > > SHIFTLONG ) ;
2012-02-15 04:44:17 +01:00
ULONG sequence = 0 ;
2001-05-23 15:26:42 +02:00
2012-02-15 04:44:17 +01:00
for ( ; pages1 < end1 ; pages1 + + )
2009-01-03 10:14:29 +01:00
{
2004-02-02 12:02:12 +01:00
blob_page * page1 = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , * pages1 , pag_blob , & window1 , & page1 ) ;
2012-09-05 21:32:34 +02:00
if ( page1 - > blp_lead_page ! = header - > blh_lead_page ) {
2014-09-28 11:43:57 +02:00
corrupt ( VAL_BLOB_INCONSISTENT , relation , number . getValue ( ) ) ;
2012-09-05 21:32:34 +02:00
}
2009-06-27 08:23:36 +02:00
if ( ( header - > blh_level = = 1 & & page1 - > blp_sequence ! = sequence ) )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_BLOB_CORRUPT , relation , number . getValue ( ) ) ;
2015-07-26 21:56:40 +02:00
release_page ( & window1 ) ;
2001-05-23 15:26:42 +02:00
return rtn_corrupt ;
}
if ( header - > blh_level = = 1 )
sequence + + ;
2009-06-27 08:23:36 +02:00
else
{
2011-01-21 18:55:04 +01:00
const ULONG * pages2 = page1 - > blp_page ;
const ULONG * const end2 = pages2 + ( page1 - > blp_length > > SHIFTLONG ) ;
2009-06-27 08:23:36 +02:00
for ( ; pages2 < end2 ; pages2 + + , sequence + + )
{
2004-02-02 12:02:12 +01:00
blob_page * page2 = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , * pages2 , pag_blob , & window2 , & page2 ) ;
2008-12-19 12:12:28 +01:00
if ( page2 - > blp_lead_page ! = header - > blh_lead_page | | page2 - > blp_sequence ! = sequence )
2004-12-24 09:52:39 +01:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_BLOB_CORRUPT , relation , number . getValue ( ) ) ;
2015-07-26 21:56:40 +02:00
release_page ( & window1 ) ;
release_page ( & window2 ) ;
2001-05-23 15:26:42 +02:00
return rtn_corrupt ;
}
2015-07-26 21:56:40 +02:00
release_page ( & window2 ) ;
2001-05-23 15:26:42 +02:00
}
}
2015-07-26 21:56:40 +02:00
release_page ( & window1 ) ;
2001-05-23 15:26:42 +02:00
}
if ( sequence - 1 ! = header - > blh_max_sequence )
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_BLOB_TRUNCATED , relation , number . getValue ( ) ) ;
2001-05-23 15:26:42 +02:00
return rtn_ok ;
}
2003-10-09 04:02:42 +02:00
2014-10-12 04:25:02 +02:00
Validation : : RTN Validation : : walk_chain ( jrd_rel * relation , const rhd * header ,
2014-09-28 11:43:57 +02:00
RecordNumber head_number )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ c h a i n
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Make sure chain of record versions is completely intact .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-12-11 11:33:30 +01:00
# ifdef DEBUG_VAL_VERBOSE
USHORT counter = 0 ;
# endif
2001-05-23 15:26:42 +02:00
2011-01-21 18:55:04 +01:00
ULONG page_number = header - > rhd_b_page ;
2003-12-11 11:33:30 +01:00
USHORT line_number = header - > rhd_b_line ;
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2015-06-19 14:07:41 +02:00
window . win_flags = WIN_garbage_collector ;
2001-05-23 15:26:42 +02:00
2009-01-03 10:14:29 +01:00
while ( page_number )
{
2003-12-11 11:33:30 +01:00
const bool delta_flag = ( header - > rhd_flags & rhd_delta ) ? true : false ;
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " BV %02d: " , + + counter ) ;
2001-05-23 15:26:42 +02:00
# endif
2012-02-07 04:17:52 +01:00
vdr_rel_chain_counter + + ;
2006-02-23 06:08:26 +01:00
data_page * page = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , page_number , pag_data , & window , & page ) ;
2015-11-26 10:28:31 +01:00
if ( page - > dpg_relation ! = relation - > rel_id )
{
release_page ( & window ) ;
return corrupt ( VAL_DATA_PAGE_CONFUSED , relation , page_number , page - > dpg_sequence ) ;
}
vdr_rel_chain_counter + + ;
PBM_SET ( vdr_tdbb - > getDefaultPool ( ) , & vdr_chain_pages , page_number ) ;
2004-02-02 12:02:12 +01:00
const data_page : : dpg_repeat * line = & page - > dpg_rpt [ line_number ] ;
2012-02-07 04:33:32 +01:00
header = ( const rhd * ) ( ( UCHAR * ) page + line - > dpg_offset ) ;
2008-12-19 12:12:28 +01:00
if ( page - > dpg_count < = line_number | | ! line - > dpg_length | |
2001-05-23 15:26:42 +02:00
( header - > rhd_flags & ( rhd_blob | rhd_fragment ) ) | |
2015-11-26 10:28:31 +01:00
! ( header - > rhd_flags & rhd_chain ) | |
2014-09-28 11:43:57 +02:00
walk_record ( relation , header , line - > dpg_length ,
2008-12-05 02:20:14 +01:00
head_number , delta_flag ) ! = rtn_ok )
2003-12-11 11:33:30 +01:00
{
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_REC_CHAIN_BROKEN , relation , head_number . getValue ( ) ) ;
2001-05-23 15:26:42 +02:00
}
page_number = header - > rhd_b_page ;
line_number = header - > rhd_b_line ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
}
return rtn_ok ;
}
2003-10-09 04:02:42 +02:00
2014-09-28 11:43:57 +02:00
void Validation : : walk_database ( )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ d a t a b a s e
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Jrd : : Attachment * attachment = vdr_tdbb - > getAttachment ( ) ;
2001-05-23 15:26:42 +02:00
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2009-06-27 08:23:36 +02:00
if ( VAL_debug_level )
{
2004-04-29 00:43:34 +02:00
fprintf ( stdout ,
2009-11-18 13:02:00 +01:00
" walk_database: %s \n ODS: %d.%d \n Page size %d \n " ,
dbb - > dbb_filename . c_str ( ) , dbb - > dbb_ods_version , dbb - > dbb_minor_version ,
2001-05-23 15:26:42 +02:00
dbb - > dbb_page_size ) ;
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
# endif
2014-09-28 11:43:57 +02:00
DPM_scan_pages ( vdr_tdbb ) ;
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2004-07-10 05:20:33 +02:00
header_page * page = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , HEADER_PAGE , pag_header , & window , & page ) ;
2012-02-07 04:17:52 +01:00
vdr_max_transaction = page - > hdr_next_transaction ;
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
if ( vdr_flags & VDR_online ) {
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2015-06-19 14:07:41 +02:00
}
if ( ! ( vdr_flags & VDR_partial ) )
{
walk_header ( page - > hdr_next_page ) ;
walk_pip ( ) ;
walk_scns ( ) ;
walk_tip ( page - > hdr_next_transaction ) ;
walk_generators ( ) ;
}
2001-05-23 15:26:42 +02:00
2005-12-02 08:35:34 +01:00
vec < jrd_rel * > * vector ;
2011-05-09 12:15:19 +02:00
for ( USHORT i = 0 ; ( vector = attachment - > att_relations ) & & i < vector - > count ( ) ; i + + )
2005-12-02 08:35:34 +01:00
{
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2015-06-19 14:07:41 +02:00
if ( i > dbb - > dbb_max_sys_rel ) // Why not system flag instead?
2001-05-23 15:26:42 +02:00
VAL_debug_level = 2 ;
# endif
2005-12-02 08:35:34 +01:00
jrd_rel * relation = ( * vector ) [ i ] ;
2015-06-19 14:07:41 +02:00
if ( relation & & relation - > rel_flags & REL_check_existence )
relation = MET_lookup_relation_id ( vdr_tdbb , i , false ) ;
2003-12-22 11:00:59 +01:00
if ( relation )
2015-06-19 14:07:41 +02:00
{
// Can't validate system relations online as they could be modified
// by system transaction which not acquires relation locks
if ( ( vdr_flags & VDR_online ) & & relation - > isSystem ( ) )
continue ;
if ( vdr_tab_incl )
{
vdr_tab_incl - > reset ( ) ;
2015-06-29 03:02:52 +02:00
if ( ! vdr_tab_incl - > process ( ( UCHAR * ) relation - > rel_name . c_str ( ) , relation - > rel_name . length ( ) ) | |
2015-06-19 14:07:41 +02:00
! vdr_tab_incl - > result ( ) )
2015-06-29 03:02:52 +02:00
{
2015-06-19 14:07:41 +02:00
continue ;
2015-06-29 03:02:52 +02:00
}
2015-06-19 14:07:41 +02:00
}
if ( vdr_tab_excl )
{
vdr_tab_excl - > reset ( ) ;
2015-06-29 03:02:52 +02:00
if ( ! vdr_tab_excl - > process ( ( UCHAR * ) relation - > rel_name . c_str ( ) , relation - > rel_name . length ( ) ) | |
2015-06-19 14:07:41 +02:00
vdr_tab_excl - > result ( ) )
2015-06-29 03:02:52 +02:00
{
2015-06-19 14:07:41 +02:00
continue ;
2015-06-29 03:02:52 +02:00
}
2015-06-19 14:07:41 +02:00
}
2015-07-08 11:56:04 +02:00
// We can't realiable track double allocated page's when validating online.
// All we can check is that page is not double allocated at the same relation.
if ( vdr_flags & VDR_online )
vdr_page_bitmap - > clear ( ) ;
2015-06-19 14:07:41 +02:00
string relName ;
relName . printf ( " Relation %d (%s) " , relation - > rel_id , relation - > rel_name . c_str ( ) ) ;
output ( " %s \n " , relName . c_str ( ) ) ;
int errs = vdr_errors ;
2014-09-28 11:43:57 +02:00
walk_relation ( relation ) ;
2015-06-19 14:07:41 +02:00
errs = vdr_errors - errs ;
if ( ! errs )
output ( " %s is ok \n \n " , relName . c_str ( ) ) ;
else
output ( " %s : %d ERRORS found \n \n " , relName . c_str ( ) , errs ) ;
}
2001-05-23 15:26:42 +02:00
}
2015-06-19 14:07:41 +02:00
if ( ! ( vdr_flags & VDR_online ) ) {
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2015-06-19 14:07:41 +02:00
}
2001-05-23 15:26:42 +02:00
}
2003-10-09 04:02:42 +02:00
2014-10-12 04:25:02 +02:00
Validation : : RTN Validation : : walk_data_page ( jrd_rel * relation , ULONG page_number ,
2014-09-28 11:43:57 +02:00
ULONG sequence , UCHAR & pp_bits )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ d a t a _ p a g e
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk a single data page .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2015-06-19 14:07:41 +02:00
window . win_flags = WIN_garbage_collector ;
2004-07-10 05:20:33 +02:00
data_page * page = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , page_number , pag_data , & window , & page ) ;
2001-05-23 15:26:42 +02:00
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2009-06-27 08:23:36 +02:00
if ( VAL_debug_level )
{
2004-04-29 00:43:34 +02:00
fprintf ( stdout ,
2001-05-23 15:26:42 +02:00
" walk_data_page: page %d rel %d seq %d count %d \n " ,
page_number , page - > dpg_relation , page - > dpg_sequence ,
page - > dpg_count ) ;
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
# endif
2008-12-19 12:12:28 +01:00
if ( page - > dpg_relation ! = relation - > rel_id | | page - > dpg_sequence ! = sequence )
2003-12-22 11:00:59 +01:00
{
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_DATA_PAGE_CONFUSED , relation , page_number , sequence ) ;
2001-05-23 15:26:42 +02:00
}
2014-09-28 11:43:57 +02:00
pp_bits = 0 ;
const UCHAR dp_flags = page - > dpg_header . pag_flags ;
// Evaluate what flags should be set on PP
2014-10-12 04:25:02 +02:00
if ( dp_flags & dpg_full )
2014-09-28 11:43:57 +02:00
pp_bits | = ppg_dp_full ;
if ( dp_flags & dpg_large )
pp_bits | = ppg_dp_large ;
if ( dp_flags & dpg_swept )
pp_bits | = ppg_dp_swept ;
if ( dp_flags & dpg_secondary )
pp_bits | = ppg_dp_secondary ;
if ( page - > dpg_count = = 0 )
pp_bits | = ppg_dp_empty ;
2009-08-13 12:35:56 +02:00
// Walk records
2001-05-23 15:26:42 +02:00
2012-02-07 04:17:52 +01:00
const UCHAR * const end_page = ( UCHAR * ) page + dbb - > dbb_page_size ;
2004-02-02 12:02:12 +01:00
const data_page : : dpg_repeat * const end = page - > dpg_rpt + page - > dpg_count ;
2012-09-05 21:32:34 +02:00
RecordNumber number ( ( SINT64 ) sequence * dbb - > dbb_max_records ) ;
2001-05-23 15:26:42 +02:00
2012-09-05 21:32:34 +02:00
for ( const data_page : : dpg_repeat * line = page - > dpg_rpt ; line < end ; line + + , number . increment ( ) )
2004-02-02 12:02:12 +01:00
{
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2009-06-27 08:23:36 +02:00
if ( VAL_debug_level )
{
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " Slot %02d (%d,%d): " ,
2008-12-25 07:09:37 +01:00
line - page - > dpg_rpt , line - > dpg_offset , line - > dpg_length ) ;
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
# endif
2009-01-03 10:14:29 +01:00
if ( line - > dpg_length )
{
2012-02-07 04:17:52 +01:00
rhd * header = ( rhd * ) ( ( UCHAR * ) page + line - > dpg_offset ) ;
if ( ( UCHAR * ) header < ( UCHAR * ) end | | ( UCHAR * ) header + line - > dpg_length > end_page )
2003-12-22 11:00:59 +01:00
{
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_DATA_PAGE_LINE_ERR , relation , page_number ,
2011-01-21 18:55:04 +01:00
sequence , ( ULONG ) ( line - page - > dpg_rpt ) ) ;
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
if ( header - > rhd_flags & rhd_chain )
2015-11-26 10:28:31 +01:00
{
2012-02-07 04:17:52 +01:00
vdr_rel_backversion_counter + + ;
2015-11-26 10:28:31 +01:00
PBM_SET ( vdr_tdbb - > getDefaultPool ( ) , & vdr_backversion_pages , page_number ) ;
}
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// Record the existance of a primary version of a record
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
if ( ( vdr_flags & VDR_records ) & &
2003-12-22 11:00:59 +01:00
! ( header - > rhd_flags & ( rhd_chain | rhd_fragment | rhd_blob ) ) )
{
2009-08-13 12:35:56 +02:00
// Only set committed (or limbo) records in the bitmap. If there
// is a backversion then at least one of the record versions is
// committed. If there's no backversion then check transaction
2016-03-14 10:11:04 +01:00
// state of the lone primary record version. Unless it is already deleted.
2001-05-23 15:26:42 +02:00
if ( header - > rhd_b_page )
2014-09-28 11:43:57 +02:00
RBM_SET ( vdr_tdbb - > getDefaultPool ( ) , & vdr_rel_records , number . getValue ( ) ) ;
2016-03-14 10:11:04 +01:00
else if ( ( header - > rhd_flags & rhd_deleted ) = = 0 )
2009-06-27 08:23:36 +02:00
{
2015-10-19 15:32:02 +02:00
const TraNumber transaction = Ods : : getTraNum ( header ) ;
const int state = ( transaction < dbb - > dbb_oldest_transaction ) ?
tra_committed : TRA_fetch_state ( vdr_tdbb , transaction ) ;
2001-05-23 15:26:42 +02:00
if ( state = = tra_committed | | state = = tra_limbo )
2014-09-28 11:43:57 +02:00
RBM_SET ( vdr_tdbb - > getDefaultPool ( ) , & vdr_rel_records , number . getValue ( ) ) ;
2001-05-23 15:26:42 +02:00
}
}
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2009-06-27 08:23:36 +02:00
if ( VAL_debug_level )
{
2001-05-23 15:26:42 +02:00
if ( header - > rhd_flags & rhd_chain )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " (backvers) " ) ;
2001-05-23 15:26:42 +02:00
if ( header - > rhd_flags & rhd_fragment )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " (fragment) " ) ;
2001-05-23 15:26:42 +02:00
if ( header - > rhd_flags & ( rhd_fragment | rhd_chain ) )
print_rhd ( line - > dpg_length , header ) ;
}
# endif
if ( ! ( header - > rhd_flags & rhd_chain ) & &
2014-09-28 11:43:57 +02:00
( ( header - > rhd_flags & rhd_large ) | | ( vdr_flags & VDR_records ) ) )
2003-12-22 11:00:59 +01:00
{
2004-02-02 12:02:12 +01:00
const RTN result = ( header - > rhd_flags & rhd_blob ) ?
2014-09-28 11:43:57 +02:00
walk_blob ( relation , ( const blh * ) header , line - > dpg_length , number ) :
walk_record ( relation , header , line - > dpg_length , number , false ) ;
2015-07-26 21:56:40 +02:00
2014-09-28 11:43:57 +02:00
if ( ( result = = rtn_corrupt ) & & ( vdr_flags & VDR_repair ) )
2006-03-07 06:44:13 +01:00
{
2014-09-28 11:43:57 +02:00
CCH_MARK ( vdr_tdbb , & window ) ;
2001-05-23 15:26:42 +02:00
header - > rhd_flags | = rhd_damaged ;
2014-10-02 00:00:35 +02:00
vdr_fixed + + ;
2001-05-23 15:26:42 +02:00
}
}
}
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
else if ( VAL_debug_level )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " (empty) \n " ) ;
2001-05-23 15:26:42 +02:00
# endif
}
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " ------------------------------------ \n " ) ;
2001-05-23 15:26:42 +02:00
# endif
return rtn_ok ;
}
2003-10-09 04:02:42 +02:00
2014-09-28 11:43:57 +02:00
void Validation : : walk_generators ( )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ g e n e r a t o r s
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk the page inventory pages .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2001-05-23 15:26:42 +02:00
2003-12-22 11:00:59 +01:00
vcl * vector = dbb - > dbb_gen_id_pages ;
2009-06-27 08:23:36 +02:00
if ( vector )
{
2003-12-22 11:00:59 +01:00
vcl : : iterator ptr , end ;
2009-01-03 10:14:29 +01:00
for ( ptr = vector - > begin ( ) , end = vector - > end ( ) ; ptr < end ; + + ptr )
{
2009-06-27 08:23:36 +02:00
if ( * ptr )
{
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " walk_generator: page %d \n " , * ptr ) ;
2001-05-23 15:26:42 +02:00
# endif
2009-11-07 08:52:48 +01:00
// It doesn't make a difference generator_page or pointer_page because it's not used.
2009-11-12 02:38:48 +01:00
generator_page * page = NULL ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , * ptr , pag_ids , & window , & page ) ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
}
2003-12-11 11:33:30 +01:00
}
}
2001-05-23 15:26:42 +02:00
}
2003-10-09 04:02:42 +02:00
2014-09-28 11:43:57 +02:00
void Validation : : walk_header ( ULONG page_num )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ h e a d e r
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk the overflow header pages
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-06-27 08:23:36 +02:00
while ( page_num )
{
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " walk_header: page %d \n " , page_num ) ;
2001-05-23 15:26:42 +02:00
# endif
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2006-02-23 06:08:26 +01:00
header_page * page = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , page_num , pag_header , & window , & page ) ;
2001-05-23 15:26:42 +02:00
page_num = page - > hdr_next_page ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
}
}
2003-10-09 04:02:42 +02:00
2014-09-28 11:43:57 +02:00
Validation : : RTN Validation : : walk_index ( jrd_rel * relation , index_root_page & root_page , USHORT id )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ i n d e x
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk all btree pages left - to - right and top - down .
2008-12-05 02:20:14 +01:00
* Check all the pointers and keys for consistency
* relative to each other , and check sibling pointers .
2001-05-23 15:26:42 +02:00
*
2008-12-05 02:20:14 +01:00
* NOTE : id is the internal index id , relative for each
2001-05-23 15:26:42 +02:00
* relation . It is 1 less than the user level index id .
* So errors are reported against index id + 1
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2001-05-23 15:26:42 +02:00
2016-01-27 12:11:38 +01:00
const ULONG page_number = root_page . irt_rpt [ id ] . getRoot ( ) ;
2005-05-03 17:45:23 +02:00
if ( ! page_number ) {
return rtn_ok ;
}
const bool unique = ( root_page . irt_rpt [ id ] . irt_flags & ( irt_unique | idx_primary ) ) ;
2012-05-11 21:38:36 +02:00
const bool descending = ( root_page . irt_rpt [ id ] . irt_flags & irt_descending ) ;
2005-05-03 17:45:23 +02:00
temporary_key nullKey , * null_key = 0 ;
2009-11-24 12:42:56 +01:00
if ( unique )
2005-05-03 17:45:23 +02:00
{
const bool isExpression = root_page . irt_rpt [ id ] . irt_flags & irt_expression ;
if ( isExpression )
root_page . irt_rpt [ id ] . irt_flags & = ~ irt_expression ;
2006-01-14 08:00:44 +01:00
2005-05-03 17:45:23 +02:00
index_desc idx ;
2014-09-28 11:43:57 +02:00
BTR_description ( vdr_tdbb , relation , & root_page , & idx , id ) ;
2005-05-03 17:45:23 +02:00
if ( isExpression )
root_page . irt_rpt [ id ] . irt_flags | = irt_expression ;
2006-01-14 08:00:44 +01:00
null_key = & nullKey ;
2014-09-28 11:43:57 +02:00
BTR_make_null_key ( vdr_tdbb , & idx , null_key ) ;
2005-05-03 17:45:23 +02:00
}
2011-01-21 18:55:04 +01:00
ULONG next = page_number ;
ULONG down = page_number ;
2004-03-28 11:10:30 +02:00
temporary_key key ;
2001-05-23 15:26:42 +02:00
key . key_length = 0 ;
2011-01-21 18:55:04 +01:00
ULONG previous_number = 0 ;
2015-09-29 12:04:48 +02:00
UCHAR level = 255 ;
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
RecordBitmap : : reset ( vdr_idx_records ) ;
2003-12-01 03:37:25 +01:00
bool firstNode = true ;
2005-05-03 17:45:23 +02:00
bool nullKeyNode = false ; // current node is a null key of unique index
bool nullKeyHandled = ! ( unique & & null_key ) ; // null key of unique index was handled
2003-12-01 03:37:25 +01:00
IndexNode node , lastNode ;
2007-02-15 11:21:09 +01:00
PageBitmap visited_pages ; // used to check circular page references, Diane Downie 2007-02-09
2001-05-23 15:26:42 +02:00
2006-04-19 09:10:53 +02:00
while ( next )
{
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2015-06-19 14:07:41 +02:00
window . win_flags = WIN_garbage_collector ;
2006-02-23 06:08:26 +01:00
btree_page * page = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , next , pag_index , & window , & page ) ;
2008-12-05 02:20:14 +01:00
// remember each page for circular reference detection
visited_pages . set ( next ) ;
2015-07-26 21:56:40 +02:00
//if ((next != page_number) &&
// (page->btr_header.pag_flags & BTR_FLAG_COPY_MASK) != (flags & BTR_FLAG_COPY_MASK))
//{
// corrupt(VAL_INDEX_PAGE_CORRUPT, relation,
// id + 1, next, page->btr_level, 0, __FILE__, __LINE__);
//}
2015-09-29 12:04:48 +02:00
if ( level = = 255 ) {
level = page - > btr_level ;
}
else if ( level ! = page - > btr_level )
{
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
id + 1 , next , page - > btr_level , 0 , __FILE__ , __LINE__ ) ;
}
2004-07-10 05:20:33 +02:00
const bool leafPage = ( page - > btr_level = = 0 ) ;
2008-12-05 02:20:14 +01:00
2008-12-19 12:12:28 +01:00
if ( page - > btr_relation ! = relation - > rel_id | | page - > btr_id ! = ( UCHAR ) ( id % 256 ) )
2003-08-18 23:13:56 +02:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation , id + 1 ,
2011-05-09 12:15:19 +02:00
next , page - > btr_level , 0 , __FILE__ , __LINE__ ) ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
return rtn_corrupt ;
}
2008-12-05 02:20:14 +01:00
2012-03-14 15:33:41 +01:00
UCHAR * pointer = page - > btr_nodes ;
2012-03-13 11:31:28 +01:00
// Check if firstNodeOffset is not out of page area.
2012-03-14 15:33:41 +01:00
if ( BTR_SIZE + page - > btr_jump_size > page - > btr_length )
2012-03-13 11:31:28 +01:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) ( pointer - ( UCHAR * ) page ) ,
__FILE__ , __LINE__ ) ;
2012-03-13 11:31:28 +01:00
}
2012-03-14 15:33:41 +01:00
UCHAR n = page - > btr_jump_count ;
2015-08-09 22:57:19 +02:00
USHORT jumpersSize = 0 , jumpDataLen = 0 ;
2012-03-13 11:31:28 +01:00
IndexNode checknode ;
IndexJumpNode jumpNode ;
while ( n )
2003-12-01 03:37:25 +01:00
{
2012-03-13 11:31:28 +01:00
pointer = jumpNode . readJumpNode ( pointer ) ;
jumpersSize + = jumpNode . getJumpNodeSize ( ) ;
// Check if jump node offset is inside page.
2012-03-14 15:33:41 +01:00
if ( ( jumpNode . offset < BTR_SIZE + page - > btr_jump_size ) | |
2012-03-13 11:31:28 +01:00
( jumpNode . offset > page - > btr_length ) )
2003-12-01 03:37:25 +01:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) ( pointer - ( UCHAR * ) page ) ,
__FILE__ , __LINE__ ) ;
2003-12-01 03:37:25 +01:00
}
2012-03-13 11:31:28 +01:00
else
2009-01-03 10:14:29 +01:00
{
2012-03-13 11:31:28 +01:00
// Check if jump node has same length as data node prefix.
checknode . readNode ( ( UCHAR * ) page + jumpNode . offset , leafPage ) ;
2015-08-16 15:53:57 +02:00
if ( ( jumpNode . prefix + jumpNode . length ) ! = checknode . prefix )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) jumpNode . offset ,
__FILE__ , __LINE__ ) ;
2003-12-01 03:37:25 +01:00
}
2012-03-13 11:31:28 +01:00
2015-08-09 22:57:19 +02:00
// First jump node should have zero prefix
2015-08-16 15:53:57 +02:00
if ( n = = page - > btr_jump_count & & jumpNode . prefix )
{
2015-08-09 22:57:19 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
id + 1 , next , page - > btr_level , ( ULONG ) jumpNode . offset ,
__FILE__ , __LINE__ ) ;
}
// jump node prefix can't be more than previous jump data length
2015-08-16 15:53:57 +02:00
if ( n ! = page - > btr_jump_count & & jumpNode . prefix > jumpDataLen )
{
2015-08-09 22:57:19 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
id + 1 , next , page - > btr_level , ( ULONG ) jumpNode . offset ,
__FILE__ , __LINE__ ) ;
}
jumpDataLen = jumpNode . prefix + jumpNode . length ;
2003-12-01 03:37:25 +01:00
}
2012-03-13 11:31:28 +01:00
n - - ;
2003-12-01 03:37:25 +01:00
}
2001-05-23 15:26:42 +02:00
2015-08-16 15:53:57 +02:00
if ( jumpersSize > page - > btr_jump_size )
{
2015-08-09 22:57:19 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
id + 1 , next , page - > btr_level , ( ULONG ) page - > btr_jump_size + BTR_SIZE ,
__FILE__ , __LINE__ ) ;
}
2003-12-01 03:37:25 +01:00
// go through all the nodes on the page and check for validity
2012-03-14 15:33:41 +01:00
pointer = page - > btr_nodes + page - > btr_jump_size ;
2012-02-11 15:34:13 +01:00
if ( firstNode )
2012-01-26 08:01:45 +01:00
lastNode . readNode ( pointer , leafPage ) ;
2001-05-23 15:26:42 +02:00
2012-02-07 04:17:52 +01:00
const UCHAR * const endPointer = ( ( UCHAR * ) page + page - > btr_length ) ;
2009-01-03 10:14:29 +01:00
while ( pointer < endPointer )
{
2012-01-26 08:01:45 +01:00
pointer = node . readNode ( pointer , leafPage ) ;
2005-04-15 08:25:28 +02:00
if ( pointer > endPointer ) {
break ;
}
2003-12-01 03:37:25 +01:00
2015-06-19 14:07:41 +02:00
if ( node . prefix > key . key_length )
{
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
id + 1 , next , page - > btr_level , node . nodePointer - ( UCHAR * ) page , __FILE__ , __LINE__ ) ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2015-06-19 14:07:41 +02:00
return rtn_corrupt ;
}
2009-06-27 08:23:36 +02:00
const UCHAR * p ;
const UCHAR * q ;
USHORT l ; // temporary variable for length
2003-12-01 03:37:25 +01:00
// make sure the current key is not less than the previous key
2008-12-05 02:20:14 +01:00
bool duplicateNode = ! firstNode & & ! node . isEndLevel & &
2008-12-19 12:12:28 +01:00
( key . key_length = = ( node . length + node . prefix ) ) ;
2003-12-01 03:37:25 +01:00
q = node . data ;
p = key . key_data + node . prefix ;
l = MIN ( node . length , ( USHORT ) ( key . key_length - node . prefix ) ) ;
2009-01-03 10:14:29 +01:00
for ( ; l ; l - - , p + + , q + + )
{
2009-06-27 08:23:36 +02:00
if ( * p > * q )
{
2005-05-03 17:45:23 +02:00
duplicateNode = false ;
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) ( q - ( UCHAR * ) page ) ,
__FILE__ , __LINE__ ) ;
2003-12-01 03:37:25 +01:00
}
2009-06-27 08:23:36 +02:00
else if ( * p < * q )
{
2005-05-03 17:45:23 +02:00
duplicateNode = false ;
2001-05-23 15:26:42 +02:00
break ;
2003-12-01 03:37:25 +01:00
}
}
2012-05-11 21:38:36 +02:00
// Two checks below are about case where one (shorter) key is
// a full prefix of another (longer) key, for example:
// 'aa' and 'aaa', '' and 'a', etc
2012-05-26 20:05:56 +02:00
// in ascending index short key is less than long key ('aa' < 'aaa')
2012-05-11 21:38:36 +02:00
// the only exception is end-of-level node with zero length
2012-05-26 20:05:56 +02:00
if ( ! firstNode & & ! descending & & ! node . isEndLevel & &
2012-05-11 21:38:36 +02:00
node . prefix < key . key_length & & node . length = = 0 )
{
duplicateNode = false ;
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) ( node . nodePointer - ( UCHAR * ) page ) ,
2012-05-11 21:38:36 +02:00
__FILE__ , __LINE__ ) ;
}
2012-05-26 20:05:56 +02:00
// in descending index short key is greater than long key ('aaa' < 'aa')
// the only exception is first node after start-of-level node at
2012-05-11 21:38:36 +02:00
// non-leaf level (also known as degenerate node)
2012-05-26 20:05:56 +02:00
if ( ! firstNode & & descending & &
2012-05-11 21:38:36 +02:00
node . prefix = = key . key_length & & node . length > 0 )
{
2012-05-26 20:05:56 +02:00
bool ok = ( page - > btr_left_sibling = = 0 & & page - > btr_level > 0 & &
2012-05-11 21:38:36 +02:00
key . key_length = = 0 ) ;
if ( ok )
{
IndexNode first ;
const UCHAR * p = first . readNode ( page - > btr_nodes + page - > btr_jump_size , false ) ;
ok = ( node . nodePointer = = p ) ;
}
if ( ! ok )
{
duplicateNode = false ;
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) ( node . nodePointer - ( UCHAR * ) page ) ,
2012-05-11 21:38:36 +02:00
__FILE__ , __LINE__ ) ;
}
}
2009-06-27 08:23:36 +02:00
if ( ! duplicateNode & & nullKeyNode )
{
2005-05-03 17:45:23 +02:00
nullKeyHandled = true ;
nullKeyNode = false ;
}
2009-12-23 22:43:37 +01:00
if ( node . recordNumber . getValue ( ) > = 0 & & ! firstNode & & ! node . isEndLevel )
2003-12-01 03:37:25 +01:00
{
// If this node is equal to the previous one and it's
// not a MARKER, record number should be same or higher.
2009-06-27 08:23:36 +02:00
if ( duplicateNode )
{
2005-05-03 17:45:23 +02:00
if ( ( ! unique | | ( unique & & nullKeyNode ) ) & &
2008-12-05 02:20:14 +01:00
( node . recordNumber < lastNode . recordNumber ) )
2005-05-03 17:45:23 +02:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) ( node . nodePointer - ( UCHAR * ) page ) ,
2012-02-11 15:34:13 +01:00
__FILE__ , __LINE__ ) ;
2003-12-01 03:37:25 +01:00
}
}
2001-05-23 15:26:42 +02:00
2005-05-03 17:45:23 +02:00
lastNode = node ;
2003-12-01 03:37:25 +01:00
}
2001-05-23 15:26:42 +02:00
2003-12-01 03:37:25 +01:00
// save the current key
2007-09-04 10:34:20 +02:00
memcpy ( key . key_data + node . prefix , node . data , node . length ) ;
//key.key_length = key.key_data + node.prefix + node.length - key.key_data;
key . key_length = node . prefix + node . length ;
2001-05-23 15:26:42 +02:00
2005-05-03 17:45:23 +02:00
if ( ! nullKeyHandled & & ! nullKeyNode & & ! duplicateNode )
{
nullKeyNode = ( leafPage | | ( ! leafPage & & ! firstNode ) ) & &
2008-12-05 02:20:14 +01:00
! node . isEndLevel & & ( null_key - > key_length = = key . key_length ) & &
2005-05-03 17:45:23 +02:00
( memcmp ( null_key - > key_data , key . key_data , null_key - > key_length ) = = 0 ) ;
}
2012-02-11 15:34:13 +01:00
if ( firstNode )
2005-05-03 17:45:23 +02:00
firstNode = false ;
2008-12-05 02:20:14 +01:00
2012-02-11 15:34:13 +01:00
if ( node . isEndBucket | | node . isEndLevel )
2001-05-23 15:26:42 +02:00
break ;
2003-12-01 03:37:25 +01:00
// Record the existance of a primary version of a record
2014-09-28 11:43:57 +02:00
if ( leafPage & & ( vdr_flags & VDR_records ) )
RBM_SET ( vdr_tdbb - > getDefaultPool ( ) , & vdr_idx_records , node . recordNumber . getValue ( ) ) ;
2001-05-23 15:26:42 +02:00
2003-12-01 03:37:25 +01:00
// fetch the next page down (if full validation was specified)
2014-09-28 11:43:57 +02:00
if ( ! leafPage & & ( vdr_flags & VDR_records ) )
2006-02-23 06:08:26 +01:00
{
2011-01-21 18:55:04 +01:00
const ULONG down_number = node . pageNumber ;
2004-09-28 08:28:38 +02:00
const RecordNumber down_record_number = node . recordNumber ;
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
// Note: mark == false for the fetch_page() call here
2008-12-05 02:20:14 +01:00
// as we don't want to mark the page as visited yet - we'll
2003-12-01 03:37:25 +01:00
// mark it when we visit it for real later on
2006-05-22 00:07:35 +02:00
WIN down_window ( DB_PAGE_SPACE , - 1 ) ;
2015-06-19 14:07:41 +02:00
down_window . win_flags = WIN_garbage_collector ;
2006-02-23 06:08:26 +01:00
btree_page * down_page = 0 ;
2014-09-28 11:43:57 +02:00
fetch_page ( false , down_number , pag_index , & down_window , & down_page ) ;
2004-07-10 05:20:33 +02:00
const bool downLeafPage = ( down_page - > btr_level = = 0 ) ;
2003-12-01 03:37:25 +01:00
// make sure the initial key is greater than the pointer key
2012-03-14 15:33:41 +01:00
UCHAR * downPointer = down_page - > btr_nodes + down_page - > btr_jump_size ;
2001-05-23 15:26:42 +02:00
2003-12-01 03:37:25 +01:00
IndexNode downNode ;
2012-01-26 08:01:45 +01:00
downPointer = downNode . readNode ( downPointer , downLeafPage ) ;
2001-05-23 15:26:42 +02:00
2003-12-01 03:37:25 +01:00
p = downNode . data ;
2001-05-23 15:26:42 +02:00
q = key . key_data ;
2003-12-01 03:37:25 +01:00
l = MIN ( key . key_length , downNode . length ) ;
2009-06-27 08:23:36 +02:00
for ( ; l ; l - - , p + + , q + + )
{
if ( * p < * q )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) ( node . nodePointer - ( UCHAR * ) page ) ,
2012-02-11 15:34:13 +01:00
__FILE__ , __LINE__ ) ;
2001-05-23 15:26:42 +02:00
}
2012-02-11 15:34:13 +01:00
else if ( * p > * q )
2001-05-23 15:26:42 +02:00
break ;
}
2008-12-05 02:20:14 +01:00
// Only check record-number if this isn't the first page in
2003-12-01 03:37:25 +01:00
// the level and it isn't a MARKER.
2005-12-14 01:06:39 +01:00
// Also don't check on primary/unique keys, because duplicates aren't
// sorted on recordnumber, except for NULL keys.
2009-12-22 11:30:49 +01:00
if ( down_page - > btr_left_sibling & &
2009-01-03 10:14:29 +01:00
! ( downNode . isEndBucket | | downNode . isEndLevel ) & & ( ! unique | | nullKeyNode ) )
2003-12-01 03:37:25 +01:00
{
// Check record number if key is equal with node on
2008-12-05 02:20:14 +01:00
// pointer page. In that case record number on page
2003-12-01 03:37:25 +01:00
// down should be same or larger.
if ( ( l = = 0 ) & & ( key . key_length = = downNode . length ) & &
( downNode . recordNumber < down_record_number ) )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation ,
2013-08-14 11:27:24 +02:00
id + 1 , next , page - > btr_level , ( ULONG ) ( node . nodePointer - ( UCHAR * ) page ) ,
2012-02-11 15:34:13 +01:00
__FILE__ , __LINE__ ) ;
2003-12-01 03:37:25 +01:00
}
}
2001-05-23 15:26:42 +02:00
2003-12-01 03:37:25 +01:00
// check the left and right sibling pointers against the parent pointers
2009-06-27 08:23:36 +02:00
if ( previous_number ! = down_page - > btr_left_sibling )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_BAD_LEFT_SIBLING , relation ,
2014-03-12 16:51:12 +01:00
id + 1 , next , page - > btr_level , ( ULONG ) ( node . nodePointer - ( UCHAR * ) page ) ) ;
2003-12-01 03:37:25 +01:00
}
2012-01-26 08:01:45 +01:00
downNode . readNode ( pointer , leafPage ) ;
2011-01-21 18:55:04 +01:00
const ULONG next_number = downNode . pageNumber ;
2003-12-01 03:37:25 +01:00
2008-12-05 02:20:14 +01:00
if ( ! ( downNode . isEndBucket | | downNode . isEndLevel ) & &
( next_number ! = down_page - > btr_sibling ) )
2003-12-01 03:37:25 +01:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_MISSES_NODE , relation ,
2014-03-12 16:51:12 +01:00
id + 1 , next , page - > btr_level , ( ULONG ) ( node . nodePointer - ( UCHAR * ) page ) ) ;
2003-12-01 03:37:25 +01:00
}
2004-03-07 22:50:53 +01:00
if ( downNode . isEndLevel & & down_page - > btr_sibling ) {
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_ORPHAN_CHILD , relation , id + 1 , next ) ;
2003-12-01 03:37:25 +01:00
}
2001-05-23 15:26:42 +02:00
previous_number = down_number ;
2015-07-26 21:56:40 +02:00
release_page ( & down_window ) ;
2001-05-23 15:26:42 +02:00
}
}
2009-06-27 08:23:36 +02:00
if ( pointer ! = endPointer | | page - > btr_length > dbb - > dbb_page_size )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_PAGE_CORRUPT , relation , id + 1 ,
2013-08-14 11:27:24 +02:00
next , page - > btr_level , ( ULONG ) ( pointer - ( UCHAR * ) page ) , __FILE__ , __LINE__ ) ;
2003-12-01 03:37:25 +01:00
}
2001-05-23 15:26:42 +02:00
2009-06-27 08:23:36 +02:00
if ( next = = down )
{
if ( page - > btr_level )
{
2003-12-01 03:37:25 +01:00
IndexNode newPageNode ;
2012-03-14 15:33:41 +01:00
pointer = page - > btr_nodes + page - > btr_jump_size ;
newPageNode . readNode ( pointer , false ) ;
2003-12-01 03:37:25 +01:00
down = newPageNode . pageNumber ;
2008-12-05 02:20:14 +01:00
}
2012-02-11 15:34:13 +01:00
else
2003-12-01 03:37:25 +01:00
down = 0 ;
}
2001-05-23 15:26:42 +02:00
2009-06-27 08:23:36 +02:00
if ( ! ( next = page - > btr_sibling ) )
{
2001-05-23 15:26:42 +02:00
next = down ;
2015-09-29 12:04:48 +02:00
level - - ;
2001-05-23 15:26:42 +02:00
key . key_length = 0 ;
previous_number = 0 ;
2005-05-03 17:45:23 +02:00
firstNode = true ;
nullKeyNode = false ;
nullKeyHandled = ! ( unique & & null_key ) ;
2001-05-23 15:26:42 +02:00
}
2007-02-14 09:57:34 +01:00
// check for circular referenes
2008-12-05 02:20:14 +01:00
if ( next & & visited_pages . test ( next ) )
2007-02-14 09:57:34 +01:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_INDEX_CYCLE , relation , id + 1 , next ) ;
2007-02-14 09:57:34 +01:00
next = 0 ;
}
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
}
2003-12-01 03:37:25 +01:00
// If the index & relation contain different sets of records we
// have a corrupt index
2014-09-28 11:43:57 +02:00
if ( vdr_flags & VDR_records )
2009-06-27 08:23:36 +02:00
{
2012-02-07 04:17:52 +01:00
RecordBitmap : : Accessor accessor ( vdr_rel_records ) ;
2011-05-11 03:18:28 +02:00
2008-12-05 02:20:14 +01:00
if ( accessor . getFirst ( ) )
2011-05-11 03:18:28 +02:00
{
do
{
2006-04-06 10:18:53 +02:00
SINT64 next_number = accessor . current ( ) ;
2011-05-11 03:18:28 +02:00
2012-02-07 04:17:52 +01:00
if ( ! RecordBitmap : : test ( vdr_idx_records , next_number ) )
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_INDEX_MISSING_ROWS , relation , id + 1 , next_number ) ;
2006-04-06 10:18:53 +02:00
} while ( accessor . getNext ( ) ) ;
2011-05-11 03:18:28 +02:00
}
2001-05-23 15:26:42 +02:00
}
return rtn_ok ;
}
2003-09-13 14:03:11 +02:00
2014-09-28 11:43:57 +02:00
void Validation : : walk_pip ( )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ p i p
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk the page inventory pages .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
PageManager & pageSpaceMgr = dbb - > dbb_page_manager ;
2007-09-04 10:34:20 +02:00
const PageSpace * pageSpace = pageSpaceMgr . findPageSpace ( DB_PAGE_SPACE ) ;
2006-05-22 00:07:35 +02:00
fb_assert ( pageSpace ) ;
2004-07-10 05:20:33 +02:00
page_inv_page * page = 0 ;
2001-05-23 15:26:42 +02:00
2009-01-03 10:14:29 +01:00
for ( USHORT sequence = 0 ; true ; sequence + + )
{
2011-01-21 18:55:04 +01:00
const ULONG page_number =
2009-12-17 11:25:23 +01:00
sequence ? sequence * pageSpaceMgr . pagesPerPIP - 1 : pageSpace - > pipFirst ;
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " walk_pip: page %d \n " , page_number ) ;
2001-05-23 15:26:42 +02:00
# endif
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , page_number , pag_pages , & window , & page ) ;
ULONG pipMin = MAX_ULONG ;
ULONG pipExtent = MAX_ULONG ;
ULONG pipUsed = 0 ;
UCHAR * bytes = page - > pip_bits ;
const UCHAR * end = ( UCHAR * ) page + dbb - > dbb_page_size ;
for ( ; bytes < end ; bytes + + )
{
if ( * bytes = = 0 )
{
pipUsed = ( bytes - page - > pip_bits + 1 ) * 8 ;
continue ;
}
if ( * bytes = = 0xFF & & pipExtent = = MAX_ULONG )
pipExtent = ( bytes - page - > pip_bits ) * 8 ;
2014-10-12 04:25:02 +02:00
if ( pipMin = = MAX_ULONG )
2014-09-28 11:43:57 +02:00
{
UCHAR mask = 1 ;
for ( int i = 0 ; i < 8 ; i + + , mask < < = 1 )
{
if ( * bytes & mask )
{
pipMin = ( bytes - page - > pip_bits ) * 8 + i ;
break ;
}
}
}
if ( * bytes ! = 0xFF )
{
UCHAR mask = 0x80 ;
for ( int i = 8 ; i > 0 ; i - - , mask > > = 1 )
{
if ( ( * bytes & mask ) = = 0 )
{
pipUsed = ( bytes - page - > pip_bits ) * 8 + i ;
break ;
}
}
}
}
if ( pipMin = = MAX_ULONG ) {
pipMin = pageSpaceMgr . pagesPerPIP ;
}
if ( pipExtent = = MAX_ULONG ) {
pipExtent = pageSpaceMgr . pagesPerPIP ;
}
2014-10-02 00:00:35 +02:00
bool fixme = false ;
2014-10-12 04:25:02 +02:00
if ( pipMin < page - > pip_min )
2014-10-02 00:00:35 +02:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PIP_WRONG_MIN , NULL , page_number , sequence , page - > pip_min , pipMin ) ;
2014-10-02 00:00:35 +02:00
fixme = ( vdr_flags & VDR_update ) ;
2014-09-28 11:43:57 +02:00
}
2014-10-12 04:25:02 +02:00
if ( pipExtent < page - > pip_extent )
2014-10-02 00:00:35 +02:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PIP_WRONG_EXTENT , NULL , page_number , sequence , page - > pip_extent , pipExtent ) ;
2014-10-02 00:00:35 +02:00
fixme = ( vdr_flags & VDR_update ) ;
2014-09-28 11:43:57 +02:00
}
2014-10-12 04:25:02 +02:00
if ( pipUsed > page - > pip_used )
2014-10-02 00:00:35 +02:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_PIP_WRONG_USED , NULL , page_number , sequence , page - > pip_used , pipUsed ) ;
2014-10-02 00:00:35 +02:00
fixme = ( vdr_flags & VDR_update ) ;
}
if ( fixme )
{
CCH_MARK ( vdr_tdbb , & window ) ;
if ( pipMin < page - > pip_min )
{
page - > pip_min = pipMin ;
vdr_fixed + + ;
}
if ( pipExtent < page - > pip_extent )
{
page - > pip_extent = pipExtent ;
vdr_fixed + + ;
}
if ( pipUsed > page - > pip_used )
{
page - > pip_used = pipUsed ;
vdr_fixed + + ;
}
2014-09-28 11:43:57 +02:00
}
2006-05-22 00:07:35 +02:00
const UCHAR byte = page - > pip_bits [ pageSpaceMgr . bytesBitPIP - 1 ] ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
if ( byte & 0x80 )
break ;
}
}
2003-10-09 04:02:42 +02:00
2014-09-28 11:43:57 +02:00
Validation : : RTN Validation : : walk_pointer_page ( jrd_rel * relation , ULONG sequence )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ p o i n t e r _ p a g e
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
2009-08-13 12:35:56 +02:00
* Walk a pointer page for a relation . Return rtn_ok if there are more to go .
2001-05-23 15:26:42 +02:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
const vcl * vector = relation - > getBasePages ( ) - > rel_pages ;
2001-05-23 15:26:42 +02:00
2012-02-11 15:34:13 +01:00
if ( ! vector | | sequence > = vector - > count ( ) )
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_P_PAGE_LOST , relation , sequence ) ;
2001-05-23 15:26:42 +02:00
2004-07-10 05:20:33 +02:00
pointer_page * page = 0 ;
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2015-06-19 14:07:41 +02:00
window . win_flags = WIN_garbage_collector ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , ( * vector ) [ sequence ] , pag_pointer , & window , & page ) ;
2001-05-23 15:26:42 +02:00
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level )
2008-12-25 15:25:01 +01:00
{
2008-12-25 07:09:37 +01:00
fprintf ( stdout , " walk_pointer_page: page %d relation %d sequence %d \n " ,
2001-12-24 03:51:06 +01:00
( * vector ) [ sequence ] , relation - > rel_id , sequence ) ;
2008-12-25 15:25:01 +01:00
}
2001-05-23 15:26:42 +02:00
# endif
2009-08-13 12:35:56 +02:00
// Give the page a quick once over
2001-05-23 15:26:42 +02:00
2008-12-19 12:12:28 +01:00
if ( page - > ppg_relation ! = relation - > rel_id | | page - > ppg_sequence ! = sequence )
2003-12-11 11:33:30 +01:00
{
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_P_PAGE_INCONSISTENT , relation , ( * vector ) [ sequence ] , sequence ) ;
2003-12-11 11:33:30 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// Walk the data pages (someday we may optionally walk pages with "large objects"
2001-05-23 15:26:42 +02:00
2011-01-21 18:55:04 +01:00
ULONG seq = sequence * dbb - > dbb_dp_per_pp ;
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
UCHAR * bits = ( UCHAR * ) ( page - > ppg_page + dbb - > dbb_dp_per_pp ) ;
bool marked = false ;
2004-02-02 12:02:12 +01:00
USHORT slot = 0 ;
2011-01-21 18:55:04 +01:00
for ( ULONG * pages = page - > ppg_page ; slot < page - > ppg_count ; slot + + , pages + + , seq + + )
2003-12-22 11:00:59 +01:00
{
2009-06-27 08:23:36 +02:00
if ( * pages )
{
2014-09-28 11:43:57 +02:00
UCHAR new_pp_bits = 0 ;
const RTN result = walk_data_page ( relation , * pages , seq , new_pp_bits ) ;
if ( result ! = rtn_ok & & ( vdr_flags & VDR_repair ) )
2009-06-27 08:23:36 +02:00
{
2014-09-28 11:43:57 +02:00
if ( ! marked )
{
CCH_MARK ( vdr_tdbb , & window ) ;
marked = true ;
}
2001-05-23 15:26:42 +02:00
* pages = 0 ;
2014-10-02 00:00:35 +02:00
vdr_fixed + + ;
2001-05-23 15:26:42 +02:00
}
2014-09-28 11:43:57 +02:00
if ( * pages )
{
UCHAR & pp_bits = PPG_DP_BITS_BYTE ( bits , slot ) ;
if ( pp_bits ! = new_pp_bits )
{
Firebird : : string s_pp , s_dp ;
explain_pp_bits ( pp_bits , s_pp ) ;
explain_pp_bits ( new_pp_bits , s_dp ) ;
2014-10-12 04:25:02 +02:00
corrupt ( VAL_P_PAGE_WRONG_BITS , relation ,
page - > ppg_header . pag_pageno , sequence , pp_bits , s_pp . c_str ( ) ,
2014-09-28 11:43:57 +02:00
* pages , seq , new_pp_bits , s_dp . c_str ( ) ) ;
if ( ( vdr_flags & VDR_update ) )
{
if ( ! marked )
{
CCH_MARK ( vdr_tdbb , & window ) ;
marked = true ;
}
pp_bits = new_pp_bits ;
2014-10-02 00:00:35 +02:00
vdr_fixed + + ;
2014-09-28 11:43:57 +02:00
}
}
}
2001-05-23 15:26:42 +02:00
}
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// If this is the last pointer page in the relation, we're done
2001-05-23 15:26:42 +02:00
2009-06-27 08:23:36 +02:00
if ( page - > ppg_header . pag_flags & ppg_eof )
{
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
return rtn_eof ;
}
2009-08-13 12:35:56 +02:00
// Make sure the "next" pointer agrees with the pages relation
2001-05-23 15:26:42 +02:00
2011-01-21 18:55:04 +01:00
if ( + + sequence > = vector - > count ( ) | |
2001-12-24 03:51:06 +01:00
( page - > ppg_next & & page - > ppg_next ! = ( * vector ) [ sequence ] ) )
2001-05-23 15:26:42 +02:00
{
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2015-06-19 14:07:41 +02:00
if ( vdr_flags & VDR_online )
{
// relation could be extended before we acquired its lock in PR mode
2015-06-29 03:02:52 +02:00
// let's re-read pointer pages and check again
2015-06-19 14:07:41 +02:00
DPM_scan_pages ( vdr_tdbb ) ;
vector = relation - > getBasePages ( ) - > rel_pages ;
- - sequence ;
2015-10-12 16:26:00 +02:00
if ( ! vector | | sequence > = vector - > count ( ) ) {
2015-06-19 14:07:41 +02:00
return corrupt ( VAL_P_PAGE_LOST , relation , sequence ) ;
}
fetch_page ( false , ( * vector ) [ sequence ] , pag_pointer , & window , & page ) ;
+ + sequence ;
2015-10-12 16:26:00 +02:00
const bool error = ( sequence > = vector - > count ( ) ) | |
2015-06-19 14:07:41 +02:00
( page - > ppg_next & & page - > ppg_next ! = ( * vector ) [ sequence ] ) ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2015-06-19 14:07:41 +02:00
if ( ! error )
return rtn_ok ;
}
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_P_PAGE_INCONSISTENT , relation , page - > ppg_next , sequence ) ;
2001-05-23 15:26:42 +02:00
}
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
return rtn_ok ;
}
2003-10-09 04:02:42 +02:00
2001-05-23 15:26:42 +02:00
2014-10-12 04:25:02 +02:00
Validation : : RTN Validation : : walk_record ( jrd_rel * relation , const rhd * header , USHORT length ,
2014-09-28 11:43:57 +02:00
RecordNumber number , bool delta_flag )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ r e c o r d
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk a record .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2009-06-27 08:23:36 +02:00
if ( VAL_debug_level )
{
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " record: number %ld (%d/%d) " ,
2001-05-23 15:26:42 +02:00
number ,
2007-12-03 16:46:39 +01:00
( USHORT ) number / tdbb - > getDatabase ( ) - > dbb_max_records ,
( USHORT ) number % tdbb - > getDatabase ( ) - > dbb_max_records ) ;
2001-05-23 15:26:42 +02:00
print_rhd ( length , header ) ;
2004-11-07 11:47:20 +01:00
}
2001-05-23 15:26:42 +02:00
# endif
2009-06-27 08:23:36 +02:00
if ( header - > rhd_flags & rhd_damaged )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_REC_DAMAGED , relation , number . getValue ( ) ) ;
2001-05-23 15:26:42 +02:00
return rtn_ok ;
}
2015-10-19 15:32:02 +02:00
const TraNumber transaction = Ods : : getTraNum ( header ) ;
if ( transaction > vdr_max_transaction )
corrupt ( VAL_REC_BAD_TID , relation , number . getValue ( ) , transaction ) ;
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// If there's a back pointer, verify that it's good
2001-05-23 15:26:42 +02:00
2009-06-27 08:23:36 +02:00
if ( header - > rhd_b_page & & ! ( header - > rhd_flags & rhd_chain ) )
{
2014-09-28 11:43:57 +02:00
const RTN result = walk_chain ( relation , header , number ) ;
2001-05-23 15:26:42 +02:00
if ( result ! = rtn_ok )
return result ;
}
2009-08-13 12:35:56 +02:00
// If the record is a fragment, not large, or we're not interested in
// chasing records, skip the record
2001-05-23 15:26:42 +02:00
2014-03-22 21:51:24 +01:00
if ( ( header - > rhd_flags & ( rhd_fragment | rhd_deleted ) ) | |
2014-09-28 11:43:57 +02:00
! ( ( header - > rhd_flags & rhd_large ) | | ( vdr_flags & VDR_records ) ) )
2006-03-07 06:44:13 +01:00
{
return rtn_ok ;
}
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// Pick up what length there is on the fragment
2001-05-23 15:26:42 +02:00
2006-04-19 09:10:53 +02:00
const rhdf * fragment = ( rhdf * ) header ;
2001-05-23 15:26:42 +02:00
2006-04-19 09:10:53 +02:00
const char * p ;
2004-02-02 12:02:12 +01:00
const char * end ;
2009-06-27 08:23:36 +02:00
if ( header - > rhd_flags & rhd_incomplete )
{
2008-12-19 12:12:28 +01:00
p = ( SCHAR * ) fragment - > rhdf_data ;
2014-07-17 23:17:15 +02:00
end = p + length - offsetof ( rhdf , rhdf_data [ 0 ] ) ;
2001-05-23 15:26:42 +02:00
}
2009-06-27 08:23:36 +02:00
else
{
2008-12-19 12:12:28 +01:00
p = ( SCHAR * ) header - > rhd_data ;
2014-07-17 23:17:15 +02:00
end = p + length - offsetof ( rhd , rhd_data [ 0 ] ) ;
2001-05-23 15:26:42 +02:00
}
2013-08-21 09:40:31 +02:00
ULONG record_length = 0 ;
2001-05-23 15:26:42 +02:00
2009-06-27 08:23:36 +02:00
while ( p < end )
{
2008-12-19 12:12:28 +01:00
const signed char c = * p + + ;
2009-06-27 08:23:36 +02:00
if ( c > = 0 )
{
2001-05-23 15:26:42 +02:00
record_length + = c ;
p + = c ;
}
2009-06-27 08:23:36 +02:00
else
{
2001-05-23 15:26:42 +02:00
record_length - = c ;
p + + ;
}
2004-02-02 12:02:12 +01:00
}
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// Next, chase down fragments, if any
2001-05-23 15:26:42 +02:00
2011-01-21 18:55:04 +01:00
ULONG page_number = fragment - > rhdf_f_page ;
2004-02-02 12:02:12 +01:00
USHORT line_number = fragment - > rhdf_f_line ;
2006-04-19 09:10:53 +02:00
USHORT flags = fragment - > rhdf_flags ;
2001-05-23 15:26:42 +02:00
2004-07-10 05:20:33 +02:00
data_page * page = 0 ;
2009-01-03 10:14:29 +01:00
while ( flags & rhd_incomplete )
{
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2015-06-19 14:07:41 +02:00
window . win_flags = WIN_garbage_collector ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , page_number , pag_data , & window , & page ) ;
2004-02-02 12:02:12 +01:00
const data_page : : dpg_repeat * line = & page - > dpg_rpt [ line_number ] ;
2001-05-23 15:26:42 +02:00
if ( page - > dpg_relation ! = relation - > rel_id | |
2006-03-07 06:44:13 +01:00
line_number > = page - > dpg_count | | ! ( length = line - > dpg_length ) )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_REC_FRAGMENT_CORRUPT , relation , number . getValue ( ) ) ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
return rtn_corrupt ;
}
2012-02-07 04:17:52 +01:00
fragment = ( rhdf * ) ( ( UCHAR * ) page + line - > dpg_offset ) ;
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2009-06-27 08:23:36 +02:00
if ( VAL_debug_level )
{
2008-12-19 12:12:28 +01:00
fprintf ( stdout , " fragment: pg %d/%d " , page_number , line_number ) ;
2006-04-19 09:10:53 +02:00
print_rhd ( line - > dpg_length , ( rhd * ) fragment ) ;
2001-05-23 15:26:42 +02:00
}
# endif
2009-06-27 08:23:36 +02:00
if ( fragment - > rhdf_flags & rhd_incomplete )
{
2012-02-07 04:33:32 +01:00
p = ( SCHAR * ) fragment - > rhdf_data ;
2014-07-17 23:17:15 +02:00
end = p + line - > dpg_length - offsetof ( rhdf , rhdf_data [ 0 ] ) ;
2001-05-23 15:26:42 +02:00
}
2009-06-27 08:23:36 +02:00
else
{
2012-02-07 04:33:32 +01:00
p = ( SCHAR * ) ( ( rhd * ) fragment ) - > rhd_data ;
2014-07-17 23:17:15 +02:00
end = p + line - > dpg_length - offsetof ( rhd , rhd_data [ 0 ] ) ;
2001-05-23 15:26:42 +02:00
}
2009-06-27 08:23:36 +02:00
while ( p < end )
{
2008-01-16 11:17:51 +01:00
const signed char c = * p + + ;
2009-06-27 08:23:36 +02:00
if ( c > = 0 )
{
2001-05-23 15:26:42 +02:00
record_length + = c ;
p + = c ;
}
2009-06-27 08:23:36 +02:00
else
{
2001-05-23 15:26:42 +02:00
record_length - = c ;
p + + ;
}
2004-02-02 12:02:12 +01:00
}
2001-05-23 15:26:42 +02:00
page_number = fragment - > rhdf_f_page ;
line_number = fragment - > rhdf_f_line ;
flags = fragment - > rhdf_flags ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
}
2009-08-13 12:35:56 +02:00
// Check out record length and format
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
const Format * format = MET_format ( vdr_tdbb , relation , header - > rhd_format ) ;
2001-05-23 15:26:42 +02:00
if ( ! delta_flag & & record_length ! = format - > fmt_length )
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_REC_WRONG_LENGTH , relation , number . getValue ( ) ) ;
2001-05-23 15:26:42 +02:00
return rtn_ok ;
}
2001-12-24 03:51:06 +01:00
2015-11-26 10:28:31 +01:00
# define DECOMPOSE(n, divisor, q, r) {r = n % divisor; q = n / divisor;}
void restoreFlags ( UCHAR * byte , UCHAR flags , bool empty )
{
UCHAR bit = PPG_DP_BIT_MASK ( slot , ppg_dp_full ) ;
if ( flags & dpg_full )
* byte | = bit ;
else
* byte & = ~ bit ;
bit = PPG_DP_BIT_MASK ( slot , ppg_dp_large ) ;
if ( flags & dpg_large )
* byte | = bit ;
else
* byte & = ~ bit ;
bit = PPG_DP_BIT_MASK ( slot , ppg_dp_swept ) ;
if ( flags & dpg_swept )
* byte | = bit ;
else
* byte & = ~ bit ;
bit = PPG_DP_BIT_MASK ( slot , ppg_dp_secondary ) ;
if ( flags & dpg_secondary )
* byte | = bit ;
else
* byte & = ~ bit ;
bit = PPG_DP_BIT_MASK ( slot , ppg_dp_empty ) ;
if ( empty )
* byte | = bit ;
else
* byte & = ~ bit ;
}
void Validation : : checkDPinPP ( jrd_rel * relation , SLONG page_number )
{
/**************************************
*
* Functional description
* Check if data page presented in pointer page .
* If not we try to fix it by setting pointer page slot in the page_number .
* Early in walk_chain we observed that this page in related to the relation so we skip such kind of check here .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
WIN window ( DB_PAGE_SPACE , page_number ) ;
data_page * dpage ;
fetch_page ( false , page_number , pag_data , & window , & dpage ) ;
const SLONG sequence = dpage - > dpg_sequence ;
const bool dpEmpty = ( dpage - > dpg_count = = 0 ) ;
release_page ( & window ) ;
USHORT slot ;
ULONG pp_sequence ;
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
DECOMPOSE ( sequence , dbb - > dbb_dp_per_pp , pp_sequence , slot ) ;
const vcl * vector = relation - > getBasePages ( ) - > rel_pages ;
pointer_page * ppage = 0 ;
if ( pp_sequence < vector - > count ( ) )
{
fetch_page ( false , ( * vector ) [ pp_sequence ] , pag_pointer , & window , & ppage ) ;
if ( slot > = ppage - > ppg_count )
{
corrupt ( VAL_DATA_PAGE_SLOT_NOT_FOUND , relation , page_number , window . win_page . getPageNum ( ) , slot ) ;
if ( vdr_flags & VDR_update & & slot < dbb - > dbb_dp_per_pp )
{
CCH_MARK ( vdr_tdbb , & window ) ;
for ( USHORT i = ppage - > ppg_count ; i < slot ; i + + )
{
ppage - > ppg_page [ i ] = 0 ;
// Clear control fields
UCHAR * byte = & PPG_DP_BITS_BYTE ( ( UCHAR * ) & ppage - > ppg_page [ dbb - > dbb_dp_per_pp ] , slot ) ;
* byte = 0 ;
}
ppage - > ppg_page [ slot ] = page_number ;
ppage - > ppg_count = slot + 1 ;
// Restore control fields
UCHAR * byte = & PPG_DP_BITS_BYTE ( ( UCHAR * ) & ppage - > ppg_page [ dbb - > dbb_dp_per_pp ] , slot ) ;
restoreFlags ( byte , dpage - > dpg_header . pag_flags , dpEmpty ) ;
vdr_fixed + + ;
}
}
else if ( page_number ! = ppage - > ppg_page [ slot ] )
{
corrupt ( VAL_DATA_PAGE_SLOT_BAD_VAL , relation , page_number , window . win_page . getPageNum ( ) , slot , ppage - > ppg_page [ slot ] ) ;
if ( vdr_flags & VDR_update & & ! ppage - > ppg_page [ slot ] )
{
CCH_MARK ( vdr_tdbb , & window ) ;
ppage - > ppg_page [ slot ] = page_number ;
// Restore control fields
UCHAR * byte = & PPG_DP_BITS_BYTE ( ( UCHAR * ) & ppage - > ppg_page [ dbb - > dbb_dp_per_pp ] , slot ) ;
restoreFlags ( byte , dpage - > dpg_header . pag_flags , dpEmpty ) ;
vdr_fixed + + ;
}
}
}
else
corrupt ( VAL_DATA_PAGE_HASNO_PP , relation , page_number , dpage - > dpg_sequence ) ;
release_page ( & window ) ;
}
void Validation : : checkDPinPIP ( jrd_rel * relation , SLONG page_number )
{
/**************************************
*
* Functional description
* Check if data page presented in page inventory page .
* If not we try to fix it by clearing the necessary bit on the page .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
PageManager & pageMgr = dbb - > dbb_page_manager ;
PageSpace * pageSpace = pageMgr . findPageSpace ( DB_PAGE_SPACE ) ;
fb_assert ( pageSpace ) ;
const SLONG sequence = page_number / pageMgr . pagesPerPIP ;
const SLONG relative_bit = page_number % pageMgr . pagesPerPIP ;
WIN pip_window ( DB_PAGE_SPACE , ( sequence = = 0 ) ? pageSpace - > pipFirst : sequence * pageMgr . pagesPerPIP - 1 ) ;
page_inv_page * pages ;
fetch_page ( false , pip_window . win_page . getPageNum ( ) , pag_pages , & pip_window , & pages ) ;
if ( pages - > pip_bits [ relative_bit > > 3 ] & ( 1 < < ( relative_bit & 7 ) ) )
{
corrupt ( VAL_DATA_PAGE_ISNT_IN_PIP , relation , page_number , pip_window . win_page . getPageNum ( ) , relative_bit ) ;
if ( vdr_flags & VDR_update )
{
CCH_MARK ( vdr_tdbb , & pip_window ) ;
pages - > pip_bits [ relative_bit > > 3 ] & = ~ ( 1 < < ( relative_bit & 7 ) ) ;
vdr_fixed + + ;
}
}
release_page ( & pip_window ) ;
}
2001-12-24 03:51:06 +01:00
2014-09-28 11:43:57 +02:00
Validation : : RTN Validation : : walk_relation ( jrd_rel * relation )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ r e l a t i o n
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk all pages associated with a given relation .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
// If relation hasn't been scanned, do so now
2001-05-23 15:26:42 +02:00
2008-12-19 12:12:28 +01:00
if ( ! ( relation - > rel_flags & REL_scanned ) | | ( relation - > rel_flags & REL_being_scanned ) )
2003-12-22 11:00:59 +01:00
{
2014-09-28 11:43:57 +02:00
MET_scan_relation ( vdr_tdbb , relation ) ;
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
2007-09-30 12:53:01 +02:00
// skip deleted relations
if ( relation - > rel_flags & ( REL_deleted | REL_deleting ) ) {
return rtn_ok ;
}
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level )
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " walk_relation: id %d Format %d %s %s \n " ,
2001-05-23 15:26:42 +02:00
relation - > rel_id , relation - > rel_current_fmt ,
2005-05-12 20:28:04 +02:00
relation - > rel_name . c_str ( ) , relation - > rel_owner_name . c_str ( ) ) ;
2001-05-23 15:26:42 +02:00
# endif
2009-08-13 12:35:56 +02:00
// If it's a view, external file or virtual table, skip this
2001-05-23 15:26:42 +02:00
2006-07-28 10:29:26 +02:00
if ( relation - > rel_view_rse | | relation - > rel_file | | relation - > isVirtual ( ) ) {
2001-05-23 15:26:42 +02:00
return rtn_ok ;
}
2015-06-19 14:07:41 +02:00
AutoLock lckRead ( vdr_tdbb ) ;
jrd_rel : : GCExclusive lckGC ( vdr_tdbb , relation ) ;
2015-06-29 03:02:52 +02:00
if ( vdr_flags & VDR_online )
2015-06-19 14:07:41 +02:00
{
lckRead = jrd_rel : : createLock ( vdr_tdbb , NULL , relation , LCK_relation , false ) ;
if ( ! LCK_lock ( vdr_tdbb , lckRead , LCK_PR , vdr_lock_tout ) )
{
output ( " Acquire relation lock failed \n " ) ;
vdr_errors + + ;
return rtn_ok ;
}
if ( ! lckGC . acquire ( vdr_lock_tout ) )
{
output ( " Acquire garbage collection lock failed \n " ) ;
vdr_errors + + ;
return rtn_ok ;
}
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2015-06-29 03:02:52 +02:00
header_page * page = NULL ;
fetch_page ( false , ( SLONG ) HEADER_PAGE , pag_header , & window , & page ) ;
2015-06-19 14:07:41 +02:00
vdr_max_transaction = page - > hdr_next_transaction ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2015-06-19 14:07:41 +02:00
}
2001-05-23 15:26:42 +02:00
2009-08-13 12:35:56 +02:00
// Walk pointer and selected data pages associated with relation
2001-05-23 15:26:42 +02:00
2014-09-28 11:43:57 +02:00
vdr_rel_backversion_counter = 0 ;
2015-11-26 10:28:31 +01:00
PageBitmap : : reset ( vdr_backversion_pages ) ;
2014-09-28 11:43:57 +02:00
vdr_rel_chain_counter = 0 ;
2015-11-26 10:28:31 +01:00
PageBitmap : : reset ( vdr_chain_pages ) ;
2014-09-28 11:43:57 +02:00
RecordBitmap : : reset ( vdr_rel_records ) ;
2012-02-11 15:34:13 +01:00
2011-01-21 18:55:04 +01:00
for ( ULONG sequence = 0 ; true ; sequence + + )
2009-06-27 08:23:36 +02:00
{
2015-06-19 14:07:41 +02:00
const vcl * vector = relation - > getBasePages ( ) - > rel_pages ;
const int ppCnt = vector ? vector - > count ( ) : 0 ;
output ( " process pointer page %4d of %4d \n " , sequence , ppCnt ) ;
2014-09-28 11:43:57 +02:00
const RTN result = walk_pointer_page ( relation , sequence ) ;
2012-02-11 15:34:13 +01:00
if ( result = = rtn_eof )
2001-05-23 15:26:42 +02:00
break ;
2012-02-11 15:34:13 +01:00
if ( result ! = rtn_ok )
2001-05-23 15:26:42 +02:00
return result ;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
// Walk indices for the relation
2014-09-28 11:43:57 +02:00
walk_root ( relation ) ;
2001-05-23 15:26:42 +02:00
2015-06-19 14:07:41 +02:00
lckGC . release ( ) ;
2015-11-26 10:28:31 +01:00
// Compare backversion pages and chain pages
if ( ( vdr_flags & VDR_records ) & &
vdr_chain_pages & & ( vdr_rel_chain_counter > vdr_rel_backversion_counter ) )
{
if ( vdr_backversion_pages )
{
PageBitmap : : Accessor c ( vdr_chain_pages ) ;
PageBitmap : : Accessor b ( vdr_backversion_pages ) ;
2015-12-06 23:49:24 +01:00
if ( c . getFirst ( ) & & b . getFirst ( ) )
{
for ( bool next = true ; next ; next = c . getNext ( ) )
2015-11-26 10:28:31 +01:00
{
if ( c . current ( ) = = b . current ( ) )
b . getNext ( ) ;
2015-12-06 23:49:24 +01:00
else if ( ( c . current ( ) < b . current ( ) ) | | ! b . getNext ( ) )
{
2015-11-26 10:28:31 +01:00
//fprintf(stdout, "chain page was visited not via data pages %d\n", c.current());
checkDPinPP ( relation , c . current ( ) ) ;
checkDPinPIP ( relation , c . current ( ) ) ;
}
}
}
}
else
{
PageBitmap : : Accessor c ( vdr_chain_pages ) ;
2015-12-06 23:49:24 +01:00
2015-11-26 10:28:31 +01:00
if ( c . getFirst ( ) )
{
2015-12-06 23:49:24 +01:00
for ( bool next = true ; next ; next = c . getNext ( ) )
2015-11-26 10:28:31 +01:00
{
//fprintf(stdout, "chain page was visited not via data pages %d\n", c.current());
checkDPinPP ( relation , c . current ( ) ) ;
checkDPinPIP ( relation , c . current ( ) ) ;
}
}
}
}
2001-12-24 03:51:06 +01:00
// See if the counts of backversions match
2014-09-28 11:43:57 +02:00
if ( ( vdr_flags & VDR_records ) & &
2015-11-26 10:28:31 +01:00
( vdr_rel_backversion_counter > vdr_rel_chain_counter ) )
2001-05-23 15:26:42 +02:00
{
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_REL_CHAIN_ORPHANS , relation ,
2012-02-07 04:17:52 +01:00
vdr_rel_backversion_counter - vdr_rel_chain_counter , vdr_rel_chain_counter ) ;
2001-05-23 15:26:42 +02:00
}
2001-12-24 03:51:06 +01:00
} // try
2009-01-03 10:14:29 +01:00
catch ( const Firebird : : Exception & )
{
2015-06-19 14:07:41 +02:00
if ( ! ( vdr_flags & VDR_online ) )
{
const char * msg = relation - > rel_name . length ( ) > 0 ?
" bugcheck during scan of table %d (%s) " :
" bugcheck during scan of table %d " ;
gds__log ( msg , relation - > rel_id , relation - > rel_name . c_str ( ) ) ;
}
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-12-24 03:51:06 +01:00
if ( VAL_debug_level )
2007-09-04 10:34:20 +02:00
{
char s [ 256 ] ;
SNPRINTF ( s , sizeof ( s ) , msg , relation - > rel_id , relation - > rel_name . c_str ( ) ) ;
2004-04-29 00:43:34 +02:00
fprintf ( stdout , " LOG: \t %s \n " , s ) ;
2007-09-04 10:34:20 +02:00
}
2001-12-24 03:51:06 +01:00
# endif
2004-03-01 04:35:23 +01:00
throw ;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
return rtn_ok ;
}
2001-12-24 03:51:06 +01:00
2014-09-28 11:43:57 +02:00
Validation : : RTN Validation : : walk_root ( jrd_rel * relation )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ r o o t
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk index root page for a relation as well as any indices .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-08-13 12:35:56 +02:00
// If the relation has an index root, walk it
2006-05-22 00:07:35 +02:00
RelationPages * relPages = relation - > getBasePages ( ) ;
2001-05-23 15:26:42 +02:00
2012-02-11 15:34:13 +01:00
if ( ! relPages - > rel_index_root )
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_INDEX_ROOT_MISSING , relation ) ;
2001-05-23 15:26:42 +02:00
2004-07-10 05:20:33 +02:00
index_root_page * page = 0 ;
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , relPages - > rel_index_root , pag_root , & window , & page ) ;
2001-05-23 15:26:42 +02:00
2012-02-11 15:34:13 +01:00
for ( USHORT i = 0 ; i < page - > irt_count ; i + + )
2015-06-19 14:07:41 +02:00
{
2016-01-27 12:11:38 +01:00
if ( page - > irt_rpt [ i ] . getRoot ( ) = = 0 )
2015-06-19 14:07:41 +02:00
continue ;
MetaName index ;
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2015-06-19 14:07:41 +02:00
MET_lookup_index ( vdr_tdbb , index , relation - > rel_name , i + 1 ) ;
fetch_page ( false , relPages - > rel_index_root , pag_root , & window , & page ) ;
if ( vdr_idx_incl )
{
vdr_idx_incl - > reset ( ) ;
2015-06-29 03:02:52 +02:00
if ( ! vdr_idx_incl - > process ( ( UCHAR * ) index . c_str ( ) , index . length ( ) ) | | ! vdr_idx_incl - > result ( ) )
2015-06-19 14:07:41 +02:00
continue ;
}
if ( vdr_idx_excl )
{
vdr_idx_excl - > reset ( ) ;
2015-06-29 03:02:52 +02:00
if ( ! vdr_idx_excl - > process ( ( UCHAR * ) index . c_str ( ) , index . length ( ) ) | | vdr_idx_excl - > result ( ) )
2015-06-19 14:07:41 +02:00
continue ;
}
output ( " Index %d (%s) \n " , i + 1 , index . c_str ( ) ) ;
2014-09-28 11:43:57 +02:00
walk_index ( relation , * page , i ) ;
2015-06-19 14:07:41 +02:00
}
2001-05-23 15:26:42 +02:00
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
return rtn_ok ;
}
2003-10-09 04:02:42 +02:00
2014-09-28 11:43:57 +02:00
Validation : : RTN Validation : : walk_tip ( TraNumber transaction )
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* w a l k _ t i p
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk transaction inventory pages .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2001-05-23 15:26:42 +02:00
2005-12-02 08:35:34 +01:00
const vcl * vector = dbb - > dbb_t_pages ;
2012-02-11 15:34:13 +01:00
if ( ! vector )
2014-09-28 11:43:57 +02:00
return corrupt ( VAL_TIP_LOST , 0 ) ;
2001-05-23 15:26:42 +02:00
2004-07-10 05:20:33 +02:00
tx_inv_page * page = 0 ;
2006-05-22 00:07:35 +02:00
const ULONG pages = transaction / dbb - > dbb_page_manager . transPerTIP ;
2001-05-23 15:26:42 +02:00
2009-01-03 10:14:29 +01:00
for ( ULONG sequence = 0 ; sequence < = pages ; sequence + + )
{
2009-06-27 08:23:36 +02:00
if ( ! ( * vector ) [ sequence ] | | sequence > = vector - > count ( ) )
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_TIP_LOST_SEQUENCE , 0 , sequence ) ;
if ( ! ( vdr_flags & VDR_repair ) )
2001-05-23 15:26:42 +02:00
continue ;
2014-10-02 00:00:35 +02:00
2014-09-28 11:43:57 +02:00
TRA_extend_tip ( vdr_tdbb , sequence ) ;
2001-05-23 15:26:42 +02:00
vector = dbb - > dbb_t_pages ;
2014-10-02 00:00:35 +02:00
vdr_fixed + + ;
2001-05-23 15:26:42 +02:00
}
2003-12-11 11:33:30 +01:00
2006-05-22 00:07:35 +02:00
WIN window ( DB_PAGE_SPACE , - 1 ) ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , ( * vector ) [ sequence ] , pag_transactions , & window , & page ) ;
2001-05-23 15:26:42 +02:00
2003-02-11 03:13:55 +01:00
# ifdef DEBUG_VAL_VERBOSE
2001-05-23 15:26:42 +02:00
if ( VAL_debug_level )
2008-12-19 12:12:28 +01:00
fprintf ( stdout , " walk_tip: page %d next %d \n " , ( * vector ) [ sequence ] , page - > tip_next ) ;
2001-05-23 15:26:42 +02:00
# endif
2001-12-24 03:51:06 +01:00
if ( page - > tip_next & & page - > tip_next ! = ( * vector ) [ sequence + 1 ] )
2001-05-23 15:26:42 +02:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_TIP_CONFUSED , 0 , sequence ) ;
2001-05-23 15:26:42 +02:00
}
2015-07-26 21:56:40 +02:00
release_page ( & window ) ;
2001-05-23 15:26:42 +02:00
}
return rtn_ok ;
}
2009-12-21 00:13:01 +01:00
2014-09-28 11:43:57 +02:00
Validation : : RTN Validation : : walk_scns ( )
2009-12-21 00:13:01 +01:00
{
/**************************************
*
* w a l k _ s c n s
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Functional description
* Walk SCN inventory pages .
*
* Don ' t check scn_pages array - its checked when other pages are fetched .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-09-28 11:43:57 +02:00
Database * dbb = vdr_tdbb - > getDatabase ( ) ;
2009-12-21 00:13:01 +01:00
2009-12-23 22:43:37 +01:00
PageManager & pageMgr = dbb - > dbb_page_manager ;
PageSpace * pageSpace = pageMgr . findPageSpace ( DB_PAGE_SPACE ) ;
2009-12-21 00:13:01 +01:00
const ULONG lastPage = pageSpace - > lastUsedPage ( ) ;
const ULONG cntSCNs = lastPage / pageMgr . pagesPerSCN + 1 ;
for ( ULONG sequence = 0 ; sequence < cntSCNs ; sequence + + )
{
const ULONG scnPage = pageSpace - > getSCNPageNum ( sequence ) ;
WIN scnWindow ( pageSpace - > pageSpaceID , scnPage ) ;
2009-12-23 22:43:37 +01:00
scns_page * scns = NULL ;
2014-09-28 11:43:57 +02:00
fetch_page ( true , scnPage , pag_scns , & scnWindow , & scns ) ;
2009-12-21 00:13:01 +01:00
2009-12-22 01:08:49 +01:00
if ( scns - > scn_sequence ! = sequence )
2009-12-21 00:13:01 +01:00
{
2014-09-28 11:43:57 +02:00
corrupt ( VAL_SCNS_PAGE_INCONSISTENT , 0 , scnPage , sequence ) ;
2009-12-21 00:13:01 +01:00
2014-09-28 11:43:57 +02:00
if ( vdr_flags & VDR_update )
2009-12-21 00:13:01 +01:00
{
2014-09-28 11:43:57 +02:00
CCH_MARK ( vdr_tdbb , & scnWindow ) ;
2009-12-21 00:13:01 +01:00
scns - > scn_sequence = sequence ;
2014-10-02 00:00:35 +02:00
vdr_fixed + + ;
2009-12-21 00:13:01 +01:00
}
}
2015-07-26 21:56:40 +02:00
release_page ( & scnWindow ) ;
2009-12-21 00:13:01 +01:00
}
return rtn_ok ;
}
2014-09-28 11:43:57 +02:00
} // namespace Jrd