-
-
Notifications
You must be signed in to change notification settings - Fork 234
/
Copy pathvalidation.cpp
3409 lines (2793 loc) · 95.7 KB
/
validation.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* PROGRAM: JRD Access Method
* MODULE: validation.cpp
* 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
==============================
Deej Bredenberg
March 16, 1994
Updated: 1996-Dec-11 David Schnepper
I. TERMINOLOGY
The following terminology will be helpful to understand in this discussion:
record fragment: The smallest recognizable piece of a record; multiple
fragments can be linked together to form a single version.
record version: A single version of a record representing an INSERT, UPDATE
or DELETE by a particular transaction (note that deletion
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
variable-length array on each data page stores the
offsets to the stored records on
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
Here are all the options for gfix which have to do with validation, and
what they do:
gfix switch dpb parameter
----------- -------------
-validate isc_dpb_verify (gds__dpb_verify prior to 4.0)
Invoke validation and repair. All other switches modify this switch.
-full isc_dpb_records
Visit all records. Without this switch, only page structures will be
validated, which does involve some limited checking of records.
-mend isc_dpb_repair
Attempts to mend the database where it can to make it viable for reading;
does not guarantee to retain data.
-no_update isc_dpb_no_update
Specifies that orphan pages not be released, and allocated pages not
be marked in use when found to be free. Actually a misleading switch
name since -mend will update the database, but if -mend is not specified
and -no_update is specified, then no updates will occur to the database.
-ignore isc_dpb_ignore
Tells the engine to ignore checksums in fetching pages. Validate will
report on the checksums, however. Should probably not even be a switch,
it should just always be in effect. Otherwise checksums will disrupt
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
-- Object "database_filename.fdb" is in use"
If other processes or servers are attached to the database, validate
waits for the exclusive lock on the database (i.e. waits for every
other server to get out of the database).
NOTE: Ordinarily when processes gain exclusive access to the database,
all active transactions are marked as dead on the Transaction Inventory
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
the entire database (described below). During this phase, all pages visited
are stored in a bitmap for later use during the garbage collection phase.
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)"
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
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:
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
define pag_log 10 // OBSOLETE. Write ahead log page: 4.0 only
2. Checksum
If -ignore is specified, the checksum is specifically checked in
validate instead of in the engine. If the checksum is found to
be wrong, the error:
"Checksum error on page xxx"
is returned. This is harmless when found by validate, and the page
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
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:
"Page xxx doubly allocated"
is returned. This should catch the case when a page of the same type
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
If any pages in the page inventory were not visited
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
If any pages marked free in the page inventory were in fact
found to be in use during validation, the following error
will be returned:
"Page xxx is use but marked free" (sic)
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.
2. Page Inventory pages.
3. System Change Number pages.
4. Transaction Inventory pages
If the system relation RDB$PAGES could not be read or did not
contain any TIP pages, the message:
"Transaction inventory pages lost"
will be returned. If a particular page is missing from the
sequence as established by RDB$PAGE_SEQUENCE, then the following
message will be returned:
"Transaction inventory page lost, sequence xxx"
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
would have been on that page are assumed committed.
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.
B. Relation Walking
All the relations in the database are walked. For each relation, all
indices defined on the relation are fetched, and all pointer and
data pages associated with the relation are fetched (see below).
But first, the metadata is scanned from RDB$RELATIONS to fetch the
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
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
all child data pages are walked (see below). If a pointer page cannot
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:
"Pointer page xxx is inconsistent"
If each pointer page does not point to the next pointer page as
stored in the RDB$PAGE_SEQUENCE field in RDB$PAGES, the following
error is returned:
"Pointer page (sequence xxx) inconsistent"
E. Data Pages
Each of the data pages referenced by the pointer page is fetched.
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
cause a whole page of data to be lost.
The data page is corrupt at the page level if it is not marked as
part of the current relation, or if it is not marked as being in
the proper sequence. If either of these conditions occurs, the
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
of records stored on page. If the slot is non-zero, the record
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"
where "line" means the slot number.
NOTE: If this condition is encountered, the data page is considered
corrupt at the page level (and thus will be removed from its
pointer page if -mend is specified).
G. Record Validation
The record at each slot is looked at for basic validation, regardless
of whether -full is specified or not. The fragment could be any of the
following:
1. Back Version
If the fragment is marked as a back version, then it is skipped.
It will be fetched as part of its record.
2. Corrupt
If the fragment is determined to be corrupt for any reason, and -mend
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:
"Record xxx is marked as damaged"
where xxx is the record number.
4. Bad Transaction
If the record is marked with a transaction id greater than the last
transaction started in the database, the following error is returned:
"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
involves retrieving all versions, and all fragments of each
particular version. In other
words, the entire logical record will be retrieved.
1. Back Versions
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.
If the slot number of the back version is greater than the max
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
message is returned:
"Chain for record xxx is broken"
2. Incomplete
If the record header is marked as incomplete, it means that there
are additional fragments to be fetched--the record was too large
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:
"Fragmented record xxx is corrupt"
Once the full record has been retrieved, the length of the format is
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.)
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.
I. Blob Walking
If the slot on the data page points to a blob record, then the blob
is fetched (even without -full). This has several cases, corresponding
to the various blob levels. (See the "Engine Internals" document for a
discussion of blob levels.)
Level Action
----- -----------------------------------------------------------------
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.
2 All pages pointed to by the blob pointer pages are fetched and
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
page does not point back to the lead page, the following error
is returned:
"Warning: blob xxx appears inconsistent"
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:
"Blob xxx is corrupt"
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
is fetched and all indices on the page fetched.
For each index, the btree pages are fetched from top-down, left to
right.
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
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
ascending order.
If a visited page is not part of the specified relation and index,
the following error is returned:
"Index xxx is corrupt at page xxx"
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
btr_sibling updated, the following error is returned
"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
we compare this bitmap to the bitmap of all records in the
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
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
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
actually printed. The record is simply marked as damaged. It is only
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,
you would not see this error message; once the full validation is done,
the message will be returned even if you do not specify -full.
B. Damaged Blobs
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
as damaged will not be fetched and backed up. (Why this is done
differently for blobs than for records I cannot say.
Perhaps it was viewed as too difficult to try to retrieve a damaged blob.)
*/
#include "firebird.h"
#include "memory_routines.h"
#include <stdio.h>
#include <stdarg.h>
#include "../jrd/jrd.h"
#include "../jrd/pag.h"
#include "firebird/impl/inf_pub.h"
#include "../jrd/val.h"
#include "../jrd/btr.h"
#include "../jrd/btn.h"
#include "../jrd/cch.h"
#include "../jrd/tra.h"
#include "../jrd/sqz.h"
#include "../jrd/svc.h"
#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"
#include "../yvalve/gds_proto.h"
#include "../common/isc_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/ods_proto.h"
#include "../jrd/tra_proto.h"
#include "../jrd/val_proto.h"
#include "../jrd/validation.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/db_alias.h"
#include "../jrd/intl_proto.h"
#include "../jrd/lck_proto.h"
#ifdef DEBUG_VAL_VERBOSE
#include "../jrd/dmp_proto.h"
/* Control variable for verbose output during debug of
validation.
0 == logged errors only
1 == logical output also
2 == physical page output also */
static USHORT VAL_debug_level = 0;
#endif
using namespace Jrd;
using namespace Ods;
using namespace Firebird;
#ifdef DEBUG_VAL_VERBOSE
static void print_rhd(USHORT, const rhd*);
#endif
static SimilarToRegex* createPatternMatcher(thread_db* tdbb, const char* pattern)
{
SimilarToRegex* matcher = NULL;
try
{
if (pattern)
{
const int len = strlen(pattern);
//// TODO: Should this be different than trace and replication
//// and use case sensitive matcher?
matcher = FB_NEW_POOL(*tdbb->getDefaultPool()) SimilarToRegex(
*tdbb->getDefaultPool(), 0,
pattern, len,
"\\", 1);
}
}
catch (const Exception& ex)
{
Arg::StatusVector status(ex);
status << Arg::Gds(isc_random) << Arg::Str(pattern);
status.raise();
}
return matcher;
}
static void explain_pp_bits(const UCHAR bits, Firebird::string& names)
{
if (bits & ppg_dp_full)
names = "full";
if (bits & ppg_dp_large)
{
if (!names.empty())
names.append(", ");
names.append("large");
}
if (bits & ppg_dp_swept)
{
if (!names.empty())
names.append(", ");
names.append("swept");
}
if (bits & ppg_dp_secondary)
{
if (!names.empty())
names.append(", ");
names.append("secondary");
}
if (bits & ppg_dp_empty)
{
if (!names.empty())
names.append(", ");
names.append("empty");
}
}
bool VAL_validate(thread_db* tdbb, USHORT switches)
{
/**************************************
*
* 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)
att->att_validation = FB_NEW_POOL (*att->att_pool) Validation(tdbb);
USHORT flags = 0;
if (switches & isc_dpb_records)
flags |= Validation::VDR_records;
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();
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
; // error
break;
default:
break;
}
}
ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE);
if (!userName.isEmpty())
{
dpb.insertString(isc_dpb_trusted_auth, userName);
}
dpb.insertTag(isc_dpb_no_garbage_collect);
PathName expandedFilename;
if (expandDatabaseName(dbName, expandedFilename, NULL))
expandedFilename = dbName;
if (dbName != expandedFilename)
dpb.insertString(isc_dpb_org_filename, dbName);
FbLocalStatus status;
AutoPlugin<JProvider> jProv(JProvider::getInstance());
RefPtr<JAttachment> jAtt(jProv->attachDatabase(&status, expandedFilename.c_str(), dpb.getBufferLength(), dpb.getBuffer()));
if (status->getState() & IStatus::STATE_ERRORS)
{
svc->getStatusAccessor().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++;
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->getStatusAccessor().setServiceStatus(status->getErrors());
ret_code = FB_FAILURE;
}
dbb->deletePool(val_pool);
jAtt->detach(&status);
return ret_code;
}
int VAL_service(Firebird::UtilSvc* svc)
{
svc->getStatusAccessor().init();
int exit_code = FB_SUCCESS;
try
{
exit_code = validate(svc);
}
catch (const Exception& ex)
{
FbLocalStatus status;
ex.stuffException(&status);
svc->getStatusAccessor().setServiceStatus(status->getErrors());
exit_code = FB_FAILURE;
}
svc->started();
return exit_code;
}
namespace Jrd
{
const Validation::MSG_ENTRY Validation::vdr_msg_table[VAL_MAX_ERROR] =
{
{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 %" SQUADFORMAT}, // 15
{true, isc_info_record_errors, "Fragmented record %" SQUADFORMAT" is corrupt"},
{true, isc_info_record_errors, "Record %" SQUADFORMAT" is wrong length"},
{true, isc_info_ipage_errors, "Missing index root page"},
{true, isc_info_tpage_errors, "Transaction inventory pages lost"},
{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"},
{true, isc_info_dpage_errors, "Data page %" ULONGFORMAT" {sequence %" ULONGFORMAT"} marked as secondary but contains primary record versions"},
{true, isc_info_tpage_errors, "Transaction inventory page %" ULONGFORMAT" {sequence %" ULONGFORMAT"} has transaction with non-zero state which number is greater than Next transaction"}
};
Validation::Validation(thread_db* tdbb, UtilSvc* uSvc)
: vdr_cond_idx(*tdbb->getDefaultPool()),
vdr_used_bdbs(*tdbb->getDefaultPool())
{
vdr_tdbb = tdbb;
vdr_max_page = 0;
vdr_flags = 0;
vdr_errors = 0;
vdr_warns = 0;
vdr_fixed = 0;
vdr_max_transaction = 0;
vdr_rel_backversion_counter = 0;
vdr_backversion_pages = NULL;
vdr_rel_chain_counter = 0;
vdr_chain_pages = NULL;
vdr_rel_records = NULL;
vdr_idx_records = NULL;
vdr_page_bitmap = NULL;
vdr_service = uSvc;
vdr_lock_tout = -10;
if (uSvc) {
parse_args(tdbb);
}
output("Validation started\n\n");
}
Validation::~Validation()
{
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);
///s.printf("%04d-%02d-%02d %02d:%02d:%02d.%04d ",
s.printf("%02d:%02d:%02d.%02d ",
///now.tm_year + 1900, now.tm_mon + 1, now.tm_mday,
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());
}