Trailing-Edge
-
PDP-10 Archives
-
BB-JF18A-BM
-
sources/rms/utlvfy.b36
There are 11 other files named utlvfy.b36 in the archive. Click here to see a list.
%TITLE 'U T L V F Y -- verify ISAM index structure'
!<BLF/REQUIRE 'RMSBLF.REQ'>
MODULE utlvfy ( ! Verify ISAM index structure
IDENT = '2.0'
) =
BEGIN
!
!
! COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1980, 1986.
! ALL RIGHTS RESERVED.
!
! THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND
! COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH
! THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR
! ANY OTHER COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE
! AVAILABLE TO ANY OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE
! SOFTWARE IS HEREBY TRANSFERRED.
!
! THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT
! NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL
! EQUIPMENT CORPORATION.
!
! DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF
! ITS SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
!
!
!++
! FACILITY: RMS UTILITY (RMSUTL)
!
! ABSTRACT: This module contains scanning loop for VERIFY.
! It also does fix-ups and shows progress.
!
!
! AUTHOR: S. Cohen CREATION DATE: 23 May 80
!
!
! MODIFIED BY:
!
! Ron Lusk, 3-Feb-84 : VERSION 2.0
! 423 - Fix up for version 2: reformat, cleanup.
! 454 - In SCANBKT, do not scan a bucket with no
! entries, and set PPLEV1 to 0 so that the
! "previous bucket" checks do not take place
! when the succeeding bucket is scanned.
! 455 - Change progress reporting in SCNBKT to use
! RMSM2 formats and routine. This way, all
! key datatypes may be reported.
!
!--
! REQUIRE FILES:
!
REQUIRE 'rmslus';
LIBRARY 'rmslib';
LIBRARY 'utlext';
!<BLF/MACRO>
! MACROS:
!
! U$*** - INTERFACE TO $UTLINT (EXTERNAL ACCESS TO RMS ROUTINES)
!
! U$BOTH - CALL $UTLINT & FALL THRU
! U$IFF - CALL $UTLINT IN RMS & TEST FOR FAILURE
! U$IFS - CALL $UTLINT IN RMS & TEST FOR SUCCESS
! U$SUC - CALL $UTLINT & ABORT IF FAILURE
!
! THESE MACROS ACCEPT A $UTLINT FUNCTION CODE MINUS THE U$
! THE REMAINING ARGUMENTS ARE FUNCTION-DEPENDENT
! THE MACROS ASSUME THAT THERE IS A STRUCT CALLED "ARGBLK"
! U$SUC USES THE ERROR LABEL "INTERR"
!
MACRO
u$both (funct) =
bld_arg_lst (argblk, %NAME (u$, funct), %REMAINING);
$utlint (argblk, bugerr) !NOTE NO ERR LABEL
%;
!<BLF/NOMACRO>
MACRO
u$iff (funct) =
u$both (funct, %REMAINING);
IF .argblk EQL 0 !FAIL TEST
%;
MACRO
u$ifs (funct) =
u$both (funct, %REMAINING);
IF .argblk NEQ 0 !SUCC TEST
%;
!<BLF/MACRO>
MACRO
u$suc (funct) =
BEGIN
bld_arg_lst (argblk, %NAME (u$, funct), %REMAINING);
$utlint (argblk, interr);
END
%;
MACRO ! !A423
! !A423
! $$COMMA yields a comma if any arguments are passed !A423
! to it. It is used in macros which call routines !A423
! with a variable number of parameters; when a !A423
! routine is called as "SUMRTN (A, B, %REMAINING)", !A423
! if %REMAINING is null, the comma after "B" will !A423
! evoke a "Null expression appears..." error. Using !A423
! "SUMRTN (A, B $$COMMA[%REMAINING] %REMAINING)" is !A423
! cumbersome, but avoids this error. !A423
! !A423
$$comma [] =
, %;
!
! ERRUX - EXPLANATION LINE OF ERR FOR UNEXP RMS ERROR
!
MACRO
errux =
verinfo (utlurf, ! .
zown, ! .
rmevec [.$field (sts, .rab) - er$bas], ! .
.$field (stv, .rab)) ! .
%;
! FIXING - TEST IF V$FIX APPLIES
! UNCING - TEST IF UNCLUTTER APPLIES
!
MACRO
fixing =
(.v$fix NEQ 0) %;
MACRO
uncing =
(.sc$case EQL sc_uncl) %;
! GB_VERIF - GET A BKT & OUTPUT APPROP MESSAGES IF NECES
!
MACRO
gb_verif (page$, size$, lock$, bd$) =
BEGIN
LOCAL
R1;
R1 = bd$get (page$, size$, lock$, bd$, bbm_none);
IF .R1 LSS 0 THEN %NAME ('TRAP.U') (); ! Fake an UNWIND
!+
! See if the bucket is clobbered (the test is
! a kludge) and tell the user about it if so.
!-
IF .R1 GTR 1 ! Clobbered?
THEN
verrarb (utlpnv, 0, page$, .R1, .kdb [kdbref]); ! Tell user
END
%;
!
! MODBKT - PERFORM ACTION AND SET BKT UPD FLAG
!
MACRO
modbkt (action, bd) =
BEGIN
action;
setupd (bd); !INDIC MUST WRITE
END
%;
!
! VERR - OUTPUTS "USUAL" MSG DESCRIBING PROBLEM DETECTED DIRECTLY BY UTLVFY
!
MACRO
verr (text$) =
verrarb (utlvem, 0, UPLIT (%ASCIZ text$), .ent_rfa) %;
! VERRFIX - OUTPUTS "USUAL" MSG FOR FIXABLE PROBLEM
!
MACRO
verrfix (text$) =
verrarb (utlvef, 1, UPLIT (%ASCIZ text$), .ent_rfa, .fixsfx) %;
! VERRC - OUTPUTS "USUAL" MSG FOR PROBLEM DETECTED IN FILE BY CALL TO RMS
!
MACRO
verrc (text$) =
BEGIN
verrarb (utlvem, 0, UPLIT (%ASCIZ text$), .ent_rfa);
errux
END
%;
!<BLF/NOMACRO>
!
! VERRARB - OUTPUTS ARBITRARY VERIFY ERR MSG TO REPORT FILE
!
MACRO
verrarb (fmt, fixable) =
BEGIN
EXTERNAL
%NAME (fmt);
%IF NOT fixable
%THEN
currerr = .currerr + 1; !BUMP ERRS DETECTED FOR CURR BKT
IF fixing OR uncing !TERMINATE UPDATES FOR SAFETY
THEN
BEGIN !YES
verinfo (utlcrs); !CHANGING TO RETR MODE
sc$case = sc_ver; !UNCL OFF
v$fix = 0; !FIX OFF
fixsfx = UPLIT (%ASCIZ' [Fixable]');
END;
%FI
!+
! IF statement is a kludge to insure that
! an ADJSP immediately follows the
! PUSHJ 17,TX$TOUT instruction.
!-
IF tx$rpt NEQ 0 !
THEN
tx$rpt (%REMAINING $$comma[%REMAINING] %NAME (fmt) );
v$err = v$err + 1; !FOR SUMMARY MSG
END
%;
!
! VERINFO - OUTPUTS INFO MSG TO REPORT FILE
!
MACRO
verinfo (fmt) =
BEGIN
EXTERNAL
%NAME (fmt);
!+
! IF statement is a kludge to insure that
! an ADJSP immediately follows the
! PUSHJ 17,TX$TOUT instruction.
!-
IF tx$rpt NEQ 0 !
THEN
tx$rpt (%REMAINING $$comma[%REMAINING] %NAME (fmt) );
END
%;
!
! EQUATED SYMBOLS:
!
LITERAL
flgdup = xb$dup, ! KLUDGE FOR "DUPLICATES" MACRO
rfmfix = fb$fix, ! DITTO "SIZEOFDATARECRD"
sidhsz = sidrhdrsize; ! SHORTHAND
BIND
stplen = stript + 1; !2ND WD OF STRING PTR
OWN !STATIC VARIABLES FOR MODULE
argblk : BLOCK [5], !SPACE FOR ARGLISTS OF $UTLINT RMS ENTRY
bdcurr : BLOCK [bdsize], !BKT DESC, FOR CURR P OF SCAN
bdlink : BLOCK [bdsize], !BKT DESC, FOR P LINKED TO CURR P
bkt : REF BLOCK, !POINTER TO CURR BKT
cid, !ID OF CURR ENTRY IN BKT
cpage, !FILE PAGE AT WHICH CURR BKT LOCATED
currerr, !# OF NON-FIXABLE PROBS FOR CURR BKT
dupstill, !IF ON, KEYVAL SEEN IN EARLIER BKT STILL HIGHEST SEEN SO FAR
end_rfa, !PHYS ADDR OF RFA TERM SCAN
ent_hikey, !ENTPTR FOR ENTRY WITH HI KEY IN BKT
entptr : REF BLOCK, !PTR TO CURR ENTRY IN BKT
ent_rfa, !PHYS ADDR OF CURR REC
ent_rrv, !RRV ADDR (IE. USER RFA ADDR) OF ENTRY
fixsfx, !PTR TO ASCIZ STRING INDICATING FIX INFO
hsz_prim, !# OF WDS IN HDR OF UDR (3=V, 2=F)
kref, !KEY OF REFERENCE FOR SCAN
oopage, !P# OF OUT-OF-ORD KEY (TO RECOG BKT LP)
ppage, !FILE PAGE AT WHICH PREV BKT LOCATED
pplev1, !LEVEL 1 ENT OF PATH ON PREV KEY ACC
progress, !IN EFFECT SCANCNT MOD V$PREQ
pt1 : REF BLOCK, !TEMP PTR
rdp : BLOCK [rdsize], !REC DESC PKT CONTAINS KVAL PTR/LEN
rdp_rfa : BLOCK [rdsize], !RECDESC FOR FBYRFA/RRV
rdp_key : BLOCK [rdsize], !FOR CKEYKU CALL (SIDR,UDR)
scancnt, !CNT OF RECS SEEN
sidr_occ, !ON IF SIDR HAS AT LEAST 1 NON-NULL RFA
zown : INITIAL (0); !0 VALUE = NULL STRING
%SBTTL 'VR$CLEAN - insure cleanliness(?)'
GLOBAL ROUTINE vr$clean : NOVALUE =
BEGIN
bd$put (bdcurr, 0); ! Insure things clean
bd$put (bdlink, 0);
RETURN;
END;
%SBTTL 'VR$SCAN - initiate data scan'
GLOBAL ROUTINE vr$scan (rfa_low, rfa_hi) : NOVALUE =
! VR$SCAN - INIT SCAN OF DATA BKTS
! INPUTS:
! RFA_LOW = THE PHYS ADDR OF ENTRY TO START SCAN
! RFA_HI = PHYS ADDR OF ENTRY THAT TERMINATES SCAN (0 FOR EOF)
! (SEE V$ACC, V$FIX, V$PREQ, & RAB IN EXTERNALS TOO)
! NOTES:
! SET CONSTANT USER AND DATA STRUCT INFO (EG. KEY-OF-REF).
! LOCATE BKT OF LOWER LIMIT OF SCAN & GET PTR TO ITS BKT.
! SET KDB & USE IT TO SET KEY'S PARAMETERS.
! ESTABLISH RFA OF UPPER LIMIT OF SCAN.
! SET KEY PARAMATERS NEEDED IN SCAN.
! CALL APPROP SCANNING ROUTINE
BEGIN
IF fixing !SET FIX MSG
THEN
fixsfx = UPLIT (%ASCIZ' [Fixing]')
ELSE
fixsfx = UPLIT (%ASCIZ' [Fixable]');
end_rfa = .rfa_hi; !MAKE IT GLOBALLY AVAIL
kref = .$field (krf, .rab); !MAKE IT EASY TO USE KEY OF REF
$field (rac, .rab) = rb$key; !WILL DO KEY ACCESSES
pt1 = .fst [fstkdb]; !GET PRIM KEY KDB
hsz_prim = .pt1 [kdbhsz]; !NEEDED BY ACCUDR
cpage = bucketofrfa (.rfa_low); !GET PAGE $FIND WENT TO
gb_verif (.cpage, .kdb [kdbdbkz], 0, bdcurr); !GET BKT ADDR OR BARF
bkt = .bdcurr [bkdbktadr]; !PRESERVE IT
bd$put (bdlink, 0); !INSURE CLEAN
rdp_rfa [rdrecptr] = 0; !START AT BEGIN OF BKT
rdp_rfa [rdrfa] = .rfa_low; ! AND LOOK FOR THIS
u$both (fbyrfa, rdp_rfa, bdlink); !GET LOC OF THIS ENTRY
IF .argblk EQL 0
THEN
BEGIN
txtout (utlsnf); !STARTING REC NOT FOUND
entptr = .bkt + bhhdrsize; !START AT TOP OF PAGE
END
ELSE
entptr = .rdp_rfa [rdrecptr]; !SET OFFSET
rdp [rdusersize] = .ksizb; !FOR MOVEKEY & CKEYKU
rdp [rduserptr] = buf$k1; !PT TO KEYVAL TOO
rdp_key [rdusersize] = .ksizb; !SET KEY LEN
IF .sc$case EQL sc_space
THEN
scansp () !DO SPACE SCAN
ELSE
scanver (); !DO VERIF SCAN
RETURN;
END;
%SBTTL 'SCANSP - loop for SPACE scan'
GLOBAL ROUTINE scansp : NOVALUE =
! SCANSP - LOOP FOR SPACE SCAN
! NOTES:
! TABULATE TOTAL SPACE AVAILABLE & SPACE USED, REPORT AS PERCENTAGE
BEGIN
LOCAL
cspace;
LOCAL
freept;
LOCAL
sizrec;
LOCAL
tspace;
LOCAL
uspace;
LOCAL
t1; !TEMP FOR SHORT-TERM USE
tspace = 0; !NO SPACE TABUL YET
cspace = 0;
uspace = 0; repeat
BEGIN !EXIT ONLY ON ERR OR EOF
tspace = .tspace + (.kdb [kdbdbkz]*pagesize) - bhhdrsize;
uspace = .uspace + .bkt [bhnextbyte] - bhhdrsize; !INCREM TOT & USED SPACE SEEN
IF .kref EQL 0 !IF PRIM KEY
THEN
BEGIN !SCAN FOR CLUTTER TOO
freept = .bkt + .bkt [bhnextbyte]; !SCAN TO END OF DATA
WHILE .entptr LSS .freept !SCAN TO 1ST FREE WD
DO
BEGIN
IF rrvflag (entptr) NEQ 0
THEN
BEGIN !REPRES CHUNK OF CLUT
cspace = .cspace + .freept - .entptr;
EXITLOOP; !RRV'S AT END OF BKT
END;
sizrec = sizeofdatarecrd (entptr); !GET # OF WDS IN CURR ENT
IF deleteflag (entptr) NEQ 0 THEN cspace = .cspace + .sizrec; !TACK ON SIZE OF DEL REC
entptr = .entptr + .sizrec; !HOP TO NEXT ENTRY
END; !END ITER OF MAIN LOOP
END; !END CLUTTER CHK
t1 = chkflag (bkt [bhflags], bhflgend); !PRESERVE END STATUS
cpage = .bkt [bhnextbkt]; !GET P# OF NEXT BKT FOR GETBKT
bd$put (bdcurr, 0); !RELEASE BKT
IF .t1 NEQ 0
THEN
BEGIN !REACHED END OF DATA BKTS
scancnt = (.uspace*100)/.tspace; !COMPUTE PERCENTAGE
verinfo (utlssc, .kref, .scancnt); !REPORT COMM RESULTS OF SCAN
IF .kref EQL 0 !DO MORE FOR PRIM KEY
THEN
BEGIN
scancnt = (.cspace*100)/.tspace;
verinfo (utlbcl, .scancnt);
END;
$flush (.rab); !MAKE RMS FILE UP TO DATE
RETURN;
END;
gb_verif (.cpage, .kdb [kdbdbkz], 0, bdcurr); !GET PTR TO NEXT BKT
bkt = .bdcurr [bkdbktadr]; !PRESERVE IT
entptr = .bkt + bhhdrsize; !PT TO 1ST ENTRY
END; !LOOP
RETURN; !INACCESSIBLE (BLISS STUPID)
END; !END ROUTINE
%SBTTL 'SCANVER - top-level VERIFY loop'
GLOBAL ROUTINE scanver : NOVALUE =
! SCANVER - TOP-LEVEL LOOP FOR VERIFY SCAN
! NOTES:
! DOES BKT I/O & DETECTS EOF
! CALLS SCANBKT TO DO ALL RECORD-LEVEL WORK
BEGIN
LOCAL
t1; !TEMP FOR SHORT-TERM USE
ent_rfa = -1; !IN CASE 1ST DATA BKT EMPTY...
!PREVENTS END_RFA=EOF=0 MATCHING
dupstill = 1; !INDIC 1ST KEY
oopage = 0; !NO PAGE SCANNED YET
pplev1 = 0; !INDIC NO PREV BKT YET
progress = 0; !INIT STATE OF SCAN
scancnt = 0; !INIT CNT OF RECS SCANNED
repeat
BEGIN !EXIT ONLY ON ERR OR EOF
cid = 0; !INIT CURR ID
currerr = 0; !INDIC NO ERRS FOR CURR BKT YET
IF .bkt [bhbtype] NEQ btypedata THEN verr ('Bucket type not DATA'); !ASSUME TYPCOD WRONG
IF .oopage EQL .cpage !BKTS IN LOOP?
THEN
verinfo (utlask, .kref); !YES, TELL USER
IF .oopage EQL .cpage !SEP TEST TO OUTWIT BLISS
THEN
RETURN; !... AND ABORT SCAN
scanbkt (); !PROCESS ENTRIES IN BKT
t1 = chkflag (bkt [bhflags], bhflgend); !PRESERVE END STATUS
ppage = .cpage; !SAVE P# OF PREV BKT
cpage = .bkt [bhnextbkt]; !GET P# OF NEXT BKT FOR GETBKT
bd$put (bdcurr, 0); !RELEASE BKT
IF .end_rfa EQL -1 THEN RETURN; !FIXING A SINGLE REC
IF .t1 NEQ 0 OR .ent_rfa EQL .end_rfa
THEN
BEGIN !REACHED END OF DATA BKTS
verinfo (utlvcm, .kref, .scancnt); !REPORT RESULT OF SCAN
$flush (.rab); !MAKE RMS FILE UP TO DATE
RETURN;
END;
gb_verif (.cpage, .kdb [kdbdbkz], 0, bdcurr); !GET PTR TO NEXT BKT
bkt = .bdcurr [bkdbktadr]; !PRESERVE IT
entptr = .bkt + bhhdrsize; !PT TO 1ST ENTRY
END; !LOOP
RETURN; !INACCESSIBLE (BLISS STUPID)
END; !END ROUTINE
%SBTTL 'SCANBKT - scan entries in a bucket'
GLOBAL ROUTINE scanbkt : NOVALUE =
!
! SCANBKT - SCANS THE ENTRIES IN A BKT
! INPUTS
! BKT = PTR TO CURRENT PAGE
! ENTPTR = PTR TO 1ST ENTRY TO PROC IN CURR BKT
! NOTES:
! VERIFIES KEYS ASCEND PROPERLY
! CHKS IF BKT'S RECORDS CAN BE FOUND BY KEY ACCESS
! VERIFIES INTER-PAGE LINKS VS. KEY-GREATER ACCESS
! DOES RRV BACKCHECK (CORRECTING PROB IF V$FIX)
! DOES UDR/SIDR COMPAT CHKS IF APPLIC (SEE ACCUDR)
BEGIN
LABEL
iter,
current,
idxseq;
LOCAL
currdup, ! Number of undeleted !A047
! records with current
! key value
cval; ! Key comparison case variable
LITERAL
!
! CVAL is set to one of these values based on the
! comparison of the record key currently processed
! with the key of the previous record.
!
cval$k_higher = 1, ! New key higher than old
cval$k_ok_dup = 2, ! New key is legal duplicate
cval$k_lower = 3, ! New key is lower than old
cval$k_err_dup = 4; ! New key is illegal duplicate
!+
! Scan bucket from entry position (at initial value of
! ENTPTR) until we reach the end of the data in the bucket.
!
! Note: the label ITER is used to terminate processing in
! a particular iteration of the loop WITHOUT leaving the
! loop construct (by the "LEAVE iter" expression in the
! loop body). If an EXITLOOP had been used, the processing
! would resume after the loop body; with the LEAVE, processing
! resumes at the top of the loop (at ITER).
!-
WHILE .entptr LSS .bkt + .bkt [bhnextbyte] DO
iter :
BEGIN
!+
! If we have 3 or more non-fixable errors in this bucket,
! then abort this bucket-scan and change PPLEV1 to show
! no previous bucket.
!-
IF .currerr GEQ 3 !TREAT PAGE AS GARBAGE?
THEN
BEGIN !YES
verinfo (utlasb); !ABORT SCAN
pplev1 = 0; !REINIT NEXT/DOWN PTR CONSIS CHK
RETURN;
END;
current :
BEGIN
!+
! Report progress count: reset the progress counter,
! checkpoint the file, and type a message (with current
! key string) on the report device.
!-
IF .progress EQL .v$preq !TIME TO REPORT?
THEN
BEGIN !YES
LOCAL
strarg : VECTOR [3]; !ARGUMENTS FOR OUTPUT !A455
progress = 0; !RESET IT
$flush (.rab); !MAKE RMS FILE UP TO DATE
stript<rh> = buf$k1; !PT TO CURR KEY STRING
strarg [0] = .stript; ! Pointer for RMSM2 !A455
strarg [1] = .ksizb; ! Length in bytes !A455
strarg [2] = .ktype; ! Datatype !A455
verinfo (utlvpr, strarg); ! Type message !A455
rp$put (); !EMPTY RPT BUFFER
$flush (.outrab); !FORCE RPT DATA TO DSK TOO
END;
!+
! If we are verifying a range of keys, see if
! we have reached the high end of the range.
!-
cid = .entptr [drrecordid]; !SET CURR ID
ent_rfa = makerfa (.cpage, .cid); !SET ITS RFA TOO
IF .end_rfa EQL .ent_rfa !HIT 1ST OUT OF BNDS KEY?
THEN
RETURN; !YES
!+
! Exit the loop when we hit the RRVs.
!-
IF rrvflag (entptr) NEQ 0 THEN EXITLOOP; !RRV'S AT END OF BKT
!+
! If the current entry is deleted and we are
! fixing up the file, expunge the entry from
! the bucket and go back and scan the next entry.
!
! Note: see comment above on "LEAVE iter".
!-
IF deleteflag (entptr) NEQ 0 AND fixing
THEN
BEGIN !DELETED & FIXING
delent (bdcurr, .entptr); !ZAP IT
LEAVE iter; !NEXT REC NOW HAS CURR ENTPTR
END;
!+
! If we are scanning an alternate key, then
! check that we can access the UDR (ACCUDR also
! checks that the alternate key matches the
! key field in the UDR). If we are in the
! primary index, retrieve this entry's RRV address.
!-
IF .kref NEQ 0 !2NDARY KEY?
THEN
accudr () !ACC USER DATA RECORD
ELSE
ent_rrv = .entptr [drrrvaddress]; !WILL SET CKEY IF HI-ER KEY
!+
! Compare the previous record's key with the key
! of the record we are now processing. Set CVAL
! to indicate that the new key is greater than
! the last, a legal duplicate, an illegal duplicate,
! or (worst of all) lower than the previous key.
! Processing will continue based on CVAL in the
! next section of code.
!
! Note: usage of BEGIN and END here is gratuitous,
! to delineate the code whereunto this comment pertains.
!-
BEGIN
!
! Set up the arguments for the $UTLINT call,
! choosing the function based on whether
! this is a primary or secondary key.
!
bld_arg_lst (argblk, 0, rdp, .entptr + .kdb [kdbhsz]);
IF .kref EQL 0 !IS IT PRIM KEY?
THEN
argblk [rms_func] = u$ckeyku !COMPARE KEY STRING TO UDR
ELSE
argblk [rms_func] = u$ckeykk; !COMP KS TO SIDR KEY
$utlint (argblk, bugerr); !COMPARE KVCURR TO KEY OF REC
!+
! Set CVAL to reflect the comparison results.
!-
IF .argblk NEQ 0 !TRUE IF RECORD HAS EQL OR HIGHER KEY
THEN
IF lssflag (rdp) NEQ 0
THEN
cval = cval$k_higher !HIGHER ALW OK, PLUS CALL MOVEKEY
ELSE
IF duplicates !EQL KEYS, DUPS OK?
THEN
cval = cval$k_ok_dup !OK, BUT NO MOVEKEY
ELSE
cval = cval$k_err_dup !DUPS NOT OK, ERR
ELSE
cval = cval$k_lower; !REC LOWER NEVER OK
!+
! If we are still processing a record in this bucket
! which is a duplicate of a record in a previous bucket
! (i.e., this is a continuation bucket) then mark it
! as a "higher" record for the sake of argument.
!-
IF .dupstill EQL 1 !THEN 1ST TIME THRU
THEN
cval = cval$k_higher; !TREAT 1ST KEY AS "HIGHER"
END; ! End of comparison code
!+
! We have determined what relation this record (or,
! better, this key) has to the previous record.
! Now, process it accordingly, flagging errors
! where necessary.
!-
CASE .cval FROM cval$k_higher TO cval$k_err_dup OF
SET
[cval$k_higher] :
!+
! New key is higher than old, so store it
! away for future comparisons, among other
! things.
!-
BEGIN !NEW HI KEY
IF .kref EQL 0 !COPY KEYVAL, PR/SEC DEP
THEN
u$suc (movekey, .entptr + .kdb [kdbhsz], buf$k1) !COPY KEY VAL FOR NEXT ITER
ELSE
movewords (.entptr + .kdb [kdbhsz], buf$k1, .ksizw); !JUST BLT THE KEY
ent_hikey = .entptr; !SAVE ADDR OF ENTRY WITH HI KEY
currdup = 0; ![%47] CLEAR CNT OF UNDEL DUPS
dupstill = 0; !CLEAR FLAG
END;
[cval$k_ok_dup] :
!+
! Key is a valid duplicate, don't do anything.
!-
0; !NO-OP FOR EQUAL KEYS
[cval$k_lower] :
!+
! Key is lower than previous record, so use it
! as the key for future comparisons (to minimize
! error messages in case the previous key was
! illegitimately high, rather than this key being
! unduly low) and give the user an error message.
!-
BEGIN
currdup = 0; ![%47] CLEAR CNT OF UNDEL DUPS
IF .entptr EQL .bkt + bhhdrsize !1ST ENT ON PAGE?
THEN
oopage = .cpage; !THEN SAVE FOR LOOP DETECTION
verr ('Key value out of order');
IF .kref EQL 0 !COPY KEYVAL, PR/SEC DEP
THEN
u$suc (movekey, .entptr + .kdb [kdbhsz], buf$k1) !COPY KEY VAL FOR NEXT ITER
ELSE
movewords (.entptr + .kdb [kdbhsz], buf$k1, .ksizw); !JUST BLT THE KEY
END;
[cval$k_err_dup] :
!+
! Key is invalid duplicate, so give an error unless
! the record is deleted. Notice that the use of
! CURRDUP prevents output of multiple error messages
! for a chain of duplicate records with the same key.
!-
BEGIN
IF .currdup NEQ 0 AND deleteflag (entptr) EQL 0 ![%47] DONT CONSID DEL RECS
THEN
verr ('Duplicate key encountered');
END;
TES;
!+
! If we have a deleted record, then we can skip over
! the key- and RFA-access checks and move on.
!-
IF deleteflag (entptr) NEQ 0 ! Deleted record?
THEN
LEAVE current; ! Skip access checks
currdup = .currdup + 1; ![%47] INCR CNT OF UNDEL DUPS
IF .v$acc EQL true THEN acckey (); !ACC UDR BY 2NDARY KEYS
IF .kref EQL 0 THEN accrrv (); !DO RRV BACK CHECK FOR UDR
!IF UNCLUT, DEL RRV IF 1
END; !END "CURRENT" BLK
IF .end_rfa EQL -1 THEN RETURN; !FIXING A SINGLE REC
entptr = .entptr + sizeofdatarecrd (entptr); !HOP TO NEXT ENTRY
END; !END ITER OF MAIN LOOP
!+
! We have come through much suffering to the end of the
! bucket. If we have scanned more than one key, and have
! not scanned a whole bucket of duplicate keys, then, we
! must perform several checks on this bucket as a whole.
!-
IF .dupstill EQL 0 !1ST KEY OR SAME HI KEY AS LAST BKT?
THEN
BEGIN !NO
!
! Write out the bucket and then search for the
! highest key in the bucket (which we stored away).
!
bd$put (bdlink, 0); !INSURE CLEAN SLATE
rdp [rdrecptr] = 0; !INSURE SEARCH NOT "RESUMED"
u$both (fnddata, rdp, bdlink); !FIND HI KEY REC (EVEN IF DEL)
!+
! If the search is unsuccessful, give an error message.
! Otherwise, keep checking.
!-
IF .argblk EQL 0
THEN
verrc ('Key access aborted') !OOPS, NOTHING FND
ELSE
!+
! We found a record; if it is not an exact match of
! our high entry, then we have another error.
!-
IF chkflag (rdp [rdstatus], ! Check for exact key match
(rdflglss OR rdflgpst)) NEQ 0 OR ! ...
.rdp [rdrecptr] NEQ .ent_hikey ! and exactly same record.
THEN
verr ('Key access failed'); ! Not a match - print message
!+
! If there is a previous bucket, then do
! some consistency checks against it.
!
! Note: a previous bucket is indicated by
! non-zero PPLEV1.
!-
IF .pplev1 NEQ 0 !1ST TIME THRU OR AFT BKT ABORT?
THEN
idxseq :
BEGIN !NO, SEE IF NOW AT NEXT IDX ENT
!+
! If the same index entry points to the
! previous bucket and to this bucket,
! then we need not continue with the
! consistency checks.
!
! Note: if this is the case, it means we
! have an inefficient index, which we may
! want to think about in a future extension
! to UNCLUTTER.
!-
IF .path [1, wrd] EQL .pplev1 ! Same index entry
THEN
LEAVE idxseq; ! Stop checking
!+
! Tweak PPLEV1 to point at what should be the
! index entry pointing at the current bucket
! (i.e., add the length of an index record
! to it). If PPLEV1 then points at the
! hoped-for entry (check by looking at PATH),
! further consistency checks are unnecessary.
!-
BEGIN
!
! Update PPLEV1 to point at current index record.
!
pplev1<pathoffset> = .pplev1<pathoffset> + .ksizw + 1;
IF .path [1, wrd] EQL .pplev1 AND ! Same index entry and
.cpage EQL .path [0, pathbkt] ! same data bucket?
THEN
LEAVE idxseq; ! Yes - all is OK
END;
!+
! If all has gone well, we are here because the
! previous index entry and the current one are in
! different index buckets. If such is the case,
! get the previous bucket and do some consistency
! checks between the buckets.
!-
gb_verif (.pplev1<pathbkt>, .kdb [kdbibkz], 0, bdlink); !GET IDX BKT TO CHK LAST ENTRY
pt1 = .bdlink [bkdbktadr]; !MAKE PTR TO IDX BKT
!+
! At this point, PPLEV1 contains the path information
! pointing to the first free space in the previous
! index bucket (i.e., just past the last index record).
! PT1 points to the header of that index bucket.
!-
IF
BEGIN
!
! Is the previous index record
! not last in its bucket?
!
.pplev1<pathoffset> NEQ .pt1 [bhnextbyte] OR !
!
! Is the current index record
! not in the succeeding bucket?
!
.path [1, pathbkt] NEQ .pt1 [bhnextbkt] OR !
!
! Is the current index record
! not first in its bucket?
!
.path [1, pathoffset] NEQ bhhdrsize OR !
!
! Is the current data page incorrect?
!
.cpage NEQ .path [0, pathbkt] !
END
THEN
!
! If any of the above are true, then
! we have a bad link in the file
! somewhere, and the user should know.
!
verrarb (utlbnc, 0, .ppage, .cpage); !BKT NOT IN DATA BKT CHAIN
END;
pplev1 = .path [1, wrd]; !SAV JUST FOUND INFO
dupstill = true; !RESET "NO GTR KEY YET"
END; !END IF NOT .DUPSTILL
!+ !A454
! If the data bucket we just scanned has no entries
! (as in the case of all records deleted and expunged),
! set PPLEV1 to 0, to indicate that there are
! to be no "previous bucket" consistency checks
! when the next bucket is verified.
!-
IF .bkt [bhnextbyte] EQL bhhdrsize ! Header only? !A454
THEN ! !A454
pplev1 = 0; ! No previous bucket !A454
RETURN;
END; !END SCANBKT
%SBTTL 'ACCKEY - access UDR by alternate keys'
GLOBAL ROUTINE acckey : NOVALUE =
!
! ACCKEY - ACCESSES UDR BY THE SPECIFIED 2NDARY KEYS
! INPUTS:
! BKT, ENTPTR
! NOTES:
! ACCKEY CALLED ONLY IF KREF IS 0.
! LOOPS THRU THE KDB'S FOR THE FILE, DOES A KEY ACCESS FOR EACH KEY
! AFTER MOVEKEY-ING IT TO A BUFFER.
! HOWEVER THE KEY ACCESS IS OMITTED IF THE RECORD ISNT LONG ENOUGH.
! IF CANT FIND THE REC BY KEY,
! THEN PUTSIDR IS CALLED IF V$FIX APPLIES
! ELSE THE RRV IS ELIMINATED IF UNCLUTTERING.
BEGIN
LABEL
iter;
LOCAL
sts; !TEMP FOR RMS STATUS
rdp_key [rduserptr] = buf$k2; !MAKE KEY ADDR PASSABLE
$field (kbf, .rab) = buf$k2; !USER-LEVEL TOO
$field (rop, .rab) = rb$rah OR rb$nrp; !DO EXACT MATCH (READAHEAD TOO)
!NRP FOR PHYS ADDR OF SIDR
WHILE true DO
iter :
BEGIN
kdb = .kdb [kdbnxt]; !GET NEXT 2NDARY KEY
IF .kdb EQL 0 THEN EXITLOOP; !PAST LAST KEY
IF .entptr [drrecsize] LSS .kdb [kdbminrsz] THEN LEAVE iter; !REC TOO SHORT TO ACC BY CUR KEY
u$suc (getkdb, .kdb [kdbref]); !RESET CURR KDB IN RMS
u$suc (movekey, .entptr + .hsz_prim, buf$k2); !COPY KEY VAL FOR THIS KEY
$field (krf, .rab) = .kdb [kdbref]; !SET KRF OF CUR KEY
rdp_key [rdusersize] = .kdb [kdbksz]; !MAKE KEY SIZE PASSABLE
$field (ksz, .rab) = .kdb [kdbksz]; !RESET AT USER-LEVEL TOO
$find (.rab); !DO KEY ACCESS
sts = .$field (sts, .rab); !GET STS FLD
IF .$field (sts, .rab) EQL su$suc
THEN
IF sidrscan () !IS THIS PARTIC REC IN SIDR ARR
THEN
BEGIN !YES
IF uncing !ELIM RRV'S?
THEN
sidradj (); !RESET SIDR PTR TO ENT_RFA
LEAVE iter; !DONE WITH THIS ENTRY
END;
IF .sts NEQ er$rnf !FISHY FAILURE?
AND .sts NEQ su$suc !SIDRSCAN MAY HAVE FAILED
THEN
BEGIN !YES
verrarb (utlakf, 0, .kdb [kdbref], .ent_rfa, zown); !ACC BY KEY N FAILED FOR X/Y
errux !INEXPLIC ERR, TELL USER
END
ELSE
BEGIN !ONE OR THE OTHER FAILED
verrarb (utlakf, 1, .kdb [kdbref], .ent_rfa, .fixsfx); !ACC BY KEY N FAILED FOR X/Y
IF fixing THEN sidrput (); !PUT OUT NEEDED SIDR ENTRY
END;
END; !END KDB LOOP
m$kdb (0); !RESET PRIM KEY
rdp_key [rdusersize] = .ksizb; !SET KEY LEN
RETURN;
END;
%SBTTL 'ACCRRV - verify RRV record'
GLOBAL ROUTINE accrrv : NOVALUE =
!
! ACCRRV - VERIFIES THAT REC'S RRV (IF ONE) PTS AT IT.
! INPUTS:
! CURRENT ENTRY AS SET BY SCANBKT
! NOTES:
! IF UNCLUTTER MODE APPLIES, THE CHECK IS N/A BECAUSE
! THE RRV IS SIMPLY DELETED, AND THE UDR PTED AT ITSELF.
! IF FIX AND NOT UNCLUT, A BAD PTR IN AN RRV IS SIMPLY RESET
BEGIN
progress = .progress + 1; !BUMP CNT OF RECS PROCESSED
scancnt = .scancnt + 1; !BUMP CNT OF RECS PROCESSED
IF .ent_rrv EQL .ent_rfa THEN RETURN; !IF REC NEVER MOVED, NO BACK CHK TO MAKE
rdp_rfa [rdrecptr] = 0; !CLEAR STARTING POS
rdp_rfa [rdrfa] = .ent_rrv; !PREP TO PASS IT
bd$put (bdlink, 0); !INSURE CLEAN
u$both (fbyrfa, rdp_rfa, bdlink); !GET RRV OF THIS UDR
IF .argblk NEQ 0 AND NOT uncing !AND CHK IF NOT SUBSUMED
THEN
BEGIN !RRV FOUND
pt1 = .rdp_rfa [rdrecptr]; !GET PTR TO RRV
IF rrvflag (pt1) EQL 0 !PTS BACK AT UDR?
THEN
BEGIN !YES, OOPS
verr ('Data record identified by back pointer');
RETURN;
END;
IF .pt1 [drrrvaddress] NEQ .ent_rfa
THEN
BEGIN !RRV/UDR OUT OF SYNC
verrfix ('POINTER entry does not point at data record');
IF fixing !
THEN
BEGIN
! modbkt (pt1 [drrrvaddress] = .ent_rfa, bdlink);
pt1 [drrrvaddress] = .ent_rfa; ! Point RRV at right place
setupd (bdlink);
END;
END;
END
ELSE
BEGIN !BAD RRV OR ZAPPING ALL RRV'S
IF uncing OR fixing !EITHER TYPE OF MODS ALLOWED?
THEN
BEGIN
! modbkt (entptr [drrrvaddress] = .ent_rfa, bdcurr);
entptr [drrrvaddress] = .ent_rfa; ! Yes -- point back at self
setupd (bdcurr);
END;
IF NOT uncing
THEN
BEGIN !BAD RRV
verrfix ('POINTER entry not found');
ent_rrv = .ent_rfa; !INDIC RRV NO GOOD
IF .v$acc EQL true THEN acckey (); !RE-ACC TO DEAL WITH BAD RRV
END
ELSE
IF .argblk NEQ 0 !
THEN
delent (bdlink, !
.rdp_rfa [rdrecptr]); !ZAP THE RRV IF IT WAS FND
END; !END OF "IF NO RRV"
RETURN;
END;
%SBTTL 'ACCUDR - compare SIDR key w/ UDR key'
GLOBAL ROUTINE accudr : NOVALUE =
!
! ACCUDR - COMPARES SIDR KEY VALUE TO ASSOC PRIM DATA KEY VALUE
! NOTES:
! SCANS ALL DUPS IN A PARTIC SIDR.
! FOR EACH, IT MUST GET THE PRIMARY DATA BKT,
! DO THE COMPARE AND REACT TO AN INEQUALITY.
! IF NOFIX, IT JUST PRINTS AN ERR MSG.
! IF V$FIX, IT 0'S THE BAD PTR
BEGIN
LOCAL
i; !LOOP CTR
LABEL
iter,
exist;
sidr_occ = 0; !PRESUME EMPTY ENTRY
scancnt = .scancnt + 1; !BUMP CNT OF SIDRS PROCESSED
progress = .progress + 1; !BUMP CNT OF SIDRS PROCESSED
INCR i FROM sidhsz + .ksizw TO .entptr [sidrrecsize] + sidhsz - 1 DO
iter :
BEGIN !EXA EACH RFA IN SIDR VECTOR
rdp_rfa [rdrfa] = .entptr [.i, wrd]; !SET ARG FOR FBYRRV
rdp_rfa [rdrecptr] = 0; !CLEAR OUTPUT ARG
IF .entptr [.i, wrd] EQL 0 THEN LEAVE iter; !NO ENTRY IN THIS SLOT
exist :
BEGIN !EXISTING ENTRY
sidr_occ = 1; !INDIC SIDR NOT EMPTY
rdp_key [rduserptr] = 0; ! Kludgey distinction
! between bad key/record
bd$put (bdlink, 0); ! Insure clean
u$both (fbyrrv, rdp_rfa, bdlink); ! Get pointer to UDR
IF .argblk EQL 0 THEN LEAVE exist; !NO MATCHING UDR
pt1 = .rdp_rfa [rdrecptr]; !MATER PTR TO UDR
IF deleteflag (pt1) NEQ 0 THEN LEAVE exist; !UDR DELETED
rdp_key [rduserptr] = .entptr + sidhsz; !SET PTR TO DATA
u$both (ckeyku, rdp_key, .pt1 + .hsz_prim);
IF .argblk EQL 0 OR ! .
lssflag (rdp_key) NEQ 0 ! Compare KVCURR to key of rec
THEN
LEAVE exist; !KEY VALUES DONT MATCH
IF .sidr_occ GEQ 0 !SEEN A VALID ENTRY YET?
THEN
sidr_occ = -1 !NO, INDIC NON-EMPTY & VALID
ELSE
IF noduplicates THEN verr ('Duplicate key encountered');
LEAVE iter; !ALL OK, DONE THIS ENTRY
END; !EXIST
!BEGIN "NO LONGER EXIST"
verrarb (utlnmr, 1, .i + 1 - sidhsz - .ksizw, .rdp_rfa [rdrfa], .ent_rfa, .fixsfx);
!NO MAT UDR FOR SIDR ELEM N
IF fixing !
THEN
BEGIN
! modbkt (entptr [.i, wrd] = 0, bdcurr);
entptr [.i, wrd] = 0; ! Assume UDR deleted or
! key update
setupd (bdcurr);
END;
IF .rdp_key [rduserptr] NEQ 0 !REACH HERE CAUSE KEYS NOT MAT?
THEN
verrarb (utlakm, 1, .kref, .rdp_rfa [rdrfa]); !YES, INDIC 2ND ERR LIKELY
!END "NO LONGER EXIST"
END; !END ITER
! IF .SIDR_OCC EQL 0 AND UNCING !EMPTY AND UNCLUTTERING?
! THEN DELENT (BDCURR, .ENTPTR); !YES, ELIM THE EMPTY SIDR
IF .sidr_occ EQL 0 !EMPTY RFA LIST?
THEN
verinfo (utlerl, .ent_rfa); !YES, TELL USER
RETURN;
END;
%SBTTL 'DELENT - expunge bucket entry'
GLOBAL ROUTINE delent (bkdesc, recptr) : NOVALUE =
! DELENT - EXPUNGES ANY TYPE OF ENTRY FROM A BKT
! INPUTS:
! BKDESC OF THE BKT THAT CONTAINS THE ENTRY
! RECPTR = ADDR IN BUFFER OF THE ENTRY TO BE EXPUNGED
! NOTES:
! IF ENTRY IS UDR, THEN ITS RRV IS ALSO EXPUNGED, IF ONE
BEGIN
MAP
bkdesc : REF BLOCK;
MAP
recptr : REF BLOCK;
bk$dent (.cpage, !
.bkdesc [bkdbktadr], .recptr); ! Common routine does the work
setupd (bkdesc); !INDIC BKT UPD
RETURN;
END;
%SBTTL 'SIDRADJ - reset SIDR RFA on UNCLUTTER'
GLOBAL ROUTINE sidradj : NOVALUE =
! SIDRADJ - RESET RFA IN SIDR TO POINT AT ACTU REC IF NECES
! NOTES:
! ADJUSTMENT APPLIES ONLY IF UNCLUTTERING.
! RDP_KEY AND BDLINK ARE SETUP IN SIDRSCAN.
BEGIN
IF .ent_rrv EQL .ent_rfa !BACK PTR = ACTU ADDR?
THEN
RETURN; !YES, SO NO UN-INDIRECTING TO DO
pt1 = .rdp_key [rdrecptr]; !GET ADDR OF SIDR PTING AT CURR REC
pt1 [.rdp_key [rdsidrelement], wrd] = .ent_rfa;
setupd (bdlink); !SET FLAG SAYING BKT MODIFIED
RETURN;
END; !END SIDRADJ
%SBTTL 'SIDRPUT - create missing SIDR entry'
GLOBAL ROUTINE sidrput : NOVALUE =
! SIDRPUT - CREATE SIDR ENTRY FOR UDR THAT LACKED ONE
BEGIN
IF uncing
THEN
rdp_rfa [rdrrv] = .ent_rfa !MAP TO REC ITSELF
ELSE
rdp_rfa [rdrrv] = .ent_rrv; !USE RRV, IF ONE
$field (rbf, .rab) = .entptr + .hsz_prim; !PUTSIDR USES UDR TO LOC KEY VALUE
u$both (putsidr, rdp_rfa); !INSERT KEY IN SIDR
IF .argblk EQL 0 THEN verr ('Could not insert key into secondary index');
RETURN;
END;
%SBTTL 'SIDRSCAN - check that RFA is in SIDR'
GLOBAL ROUTINE sidrscan =
! SIDRSCAN - INSURE THAT RFA AS WELL AS KEY WAS IN THE SIDR
! RETURNS:
! TRUE IF RECORD IS IN SIDR ARRAY, SETTING UP RDP_KEY AND BDLINK (USUALLY)
! FALSE OTHERWISE
! NOTES:
! IF DUPLICATES NOT ALLOWED, THE $FIND DATA IS ADEQUATE.
! OTHERWISE HAVE TO USE RMS ROUTINES TO ACTU SCAN SIDRS.
! HOWEVER IF UNCLUT, THEN RMS ROUTINES ALWAYS CALLED SO THAT RD&BD SETUP.
! THUS ONLY TIME THEY ARE NOT SETUP IS NO UNC & $FIND ADEQ.
BEGIN
IF NOT uncing
THEN
BEGIN !THEN TRY TO DO QUICK CHK
IF .$field (rfa, .rab) EQL .ent_rrv THEN RETURN true; !IT WAS 1ST OR NO DUPS
IF noduplicates THEN RETURN false; !NO DUPS, SO SCAN CANT SUCC
END;
rdp_key [rdrecptr] = 0; !CLEAR STARTING POS
rdp_key [rdrfa] = .rst [rstnrp]; !PASS RFA OF THE FND SIDR
!START DUP SRCH WITH THIS REC
bd$put (bdlink, 0); !INSURE CLEAN
u$suc (fbyrfa, rdp_key, bdlink); !PHYS LOCATE SIDR FROM ITS RFA
rdp_key [rdflags] = rdflgretex; !RET ON MATCH, DONT CORRECT IT
rdp_key [rdrrv] = .ent_rrv; !COMPARE AGAINST THIS
u$both (chkdup, rdp_key, bdlink); !SCAN FOR RFA MATCH
IF .argblk EQL 0
THEN
RETURN true !FAILURE=MATCH
ELSE
RETURN false; !SUCC=SCANNED ENTIRE SIDR
END;
END
ELUDOM