Trailing-Edge
-
PDP-10 Archives
-
RMS-10_T10_704_FT2_880425
-
10,7/rms10/rmssrc/rmsudr.b36
There are 11 other files named rmsudr.b36 in the archive. Click here to see a list.
MODULE UDR =
BEGIN
GLOBAL BIND UDRV = 1^24 + 0^18 + 14; !EDIT DATE: 17-FEB-77
%([
FUNCTION: THIS MODULE CONTAINS ROUTINES WHICH PROCESS
USER DATA RECORDS WITHIN AN RMS-20 INDEXED FILE.
AUTHOR: S. BLOUNT
THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
!COPYRIGHT (C) 1977, 1979 BY DIGITAL EQUIPMENT CORPORATION
********** TABLE OF CONTENTS **************
ROUTINE FUNCTION
======= ========
MAKEUDR MAKE A USER DATA RECORD
SDATABKT SEARCH A USER DATA BUCKET
INSRTUDR INSERT A USER DATA RECORD
CHKDUP CHECK FOR DUPLICATE KEY VALUES
REVISION HISTORY:
EDIT DATE WHO PURPOSE
==== ==== === ========
1 24-AUG-76 JK ADD 'UPDRRVS' ROUTINE.
2 1-SEP-76 JK REPLACE REFS TO ZERO ID BY 'NULLID'.
3 1-SEP-76 JK FIX 'UPDRRVS'.
4 1-SEP-76 JK FIX 'UPDRRVS' -- 'UPDBKD' SHOULD BE MAPPED 'FORMAT'.
5 2-SEP-76 JK REMOVE EDIT 3 (EDIT 4 FOUND REAL CULPRIT).
6 2-SEP-76 JK REMOVE EDIT 5, REINSTATE EDIT 3, UPDATE RRV REC. CORRECTLY.
7 2-SEP-76 JK 'UPDRRVS' NOW HANDLES "RRV NOT FOUND" CORRECTLY.
8 3-SEP-76 JK RMSUDR SPLIT INTO RMSUDR, RMSUD2, RMSSPT.
9 5-NOV-76 SB MAKE SDATABKT RETURN EMPTY ON NON-EX BKT
10 9-NOV-76 SB ADD IDOFRFA INSTEAD OF IDMASK
11 9-DEC-76 SB CLEAN-UP, TAKE OUT CHECK FOR SAME BKT ON UPDATE
12 13-JAN-77 SB FIX BUG IN SDATABKT WHERE ENTIRE RFA IS
CHECKED FOR 0 INSTEAD OF ONLY THE ID.
13 17-FEB-77 SB IF 3-BKT SPLIT & DUP SEEN, MARK AS 2-BKT SPLIT
*************************************************
* *
* NEW REVISION HISTORY *
* *
*************************************************
****************** Start RMS-10 V1.1 *********************
********************* TOPS-10 ONLY ***********************
PRODUCT MODULE SPR
EDIT EDIT QAR DESCRIPTION
====== ====== ===== ===========
100 14 Dev Make declarations for routine names
be EXTERNAL ROUTINE so RMS will compile
under BLISS V4 (RMT, 10/22/85).
***** END OF REVISION HISTORY *****
])%
%([ FORWARD DECLARATIONS ])%
%(< NONE >)%
%([ EXTERNAL DECLARATIONS ])%
EXTERNAL ROUTINE
CRASH, ! DEBUGGING
CKEYKK, ! COMPARE CONTIGUOUS KEYS
MOVEKEY, ! MOVE A KEY STRING VALUE
SPLIT, ! SPLIT A DATA BUCKET
ALCRFA, ! ALLOCATE A NEW RFA FOR A BKT
FNDREC,
SKIPRECORD, ! SKIP A DATA RECORD
UPDRRVS, ! UPDATE RRV RECORDS AFTER A SPLIT
COMPRESS, ! COMPRESS A DATA BKT
FBYRFA, ! FIND A RECORD GIVEN ITS RFA
DUMPHEADER, ! DUMP A BUCKET HEADER
DUMPRD, ! DUMP A RECORD DESCRIPTOR
ALCBKT, ! ALLOCATE A NEW BUCKET
PUTBKT, ! RELEASE A BUCKET
CKEYKU, ! COMPARE KEY (KEY TO UDR)
DUMP; ! SAME
%([ ERROR MESSAGES REFERENCED IN THIS MODULE ])%
EXTERNAL
MSGINPUT, ! BAD INPUT VALUES
MSGFAILURE, ! ROUTINE FAILED
MSGFLAGS, ! BAD FLAG VALUES
MSGCOUNT, ! A BAD COUNT VALUE
MSGLOOP, ! WENT THROUGH LOOP TOO MANY TIMES
MSGCANTGETHERE; ! BAD LOGIC FLOW
REQUIRE 'RMSREQ';
EXTDECLARATIONS;
! MAKEUDR
! =======
! THIS ROUTINE CREATES A USER DATA RECORD ( UDR ) IN AN
! INDEXED FILE. IT PERFORMS NO INDEX MODIFICATION
! OR TRAVERSAL. IT ALSO DOES NOT MODIFY THE CONTENTS OF
! THE BUCKET HEADER IN ANY WAY.
! INPUT:
! RECORD DESCRIPTOR:
! RECPTR ADDRESS IN BUCKET TO WRITE RECORD
! ADDRESS OF USER DATA RECORD
!
! FIELDS WITHIN THE RST WHICH ARE USED:
! RSZ SIZE OF RECORD IN BYTES
! RSZW SIZE OF RECORD IN WORDS
!
! OUTPUT:
! TRUE ALWAYS
! INPUT ARGUMENTS MODIFIED:
! RECORD DESCRIPTOR:
! RFA RFA OF NEW RECORD
! RRV SET TO BE THE RRV ADDRESS (SAME AS RFA)
GLOBAL ROUTINE MAKEUDR ( RECDESC, USERRECORDPTR ): NOVALUE =
BEGIN
ARGUMENT (RECDESC,BASEADD); ! RECORD DESCRIPTOR
ARGUMENT (USERRECORDPTR,BASEADD); ! USER RECORD ADDRESS
REGISTER
RECORDPTR, ! POINTER TO THE DATA RECORD
RECORDRFA; ! RFA OF NEW RECORD
MAP
RECDESC: POINTER,
USERRECORDPTR: POINTER,
RECORDPTR: POINTER;
TRACE ('MAKEUDR' );
%([ SET UP SOME MISCELLANEOUS STUFF ])%
RECORDPTR = .RECDESC [ RDRECPTR ]; ! FETCH ADDRESS OF RECORD
%([ STORE FLAGS INTO DATA RECORD ])%
RECORDPTR [ DRFLAGS ] = DEFDRFLAGS; ! USE DEFAULT FLAGS
%([ SET UP THE RFA AND THE ID VALUE IN THE RECORD ])%
RECORDRFA = .RECDESC [ RDRFA ]; ! GET IT FROM REC DESC
RECORDPTR [ DRRRVADDRESS ] = .RECORDRFA; ! STORE IN RECORD
RECDESC [ RDRRV ] = .RECORDRFA; ! PUT BACK IN RRV ADDRESS
RECORDPTR [ DRRECORDID ] = .RECORDRFA < LH >; ! 'LH' CORRESPONDS TO RFAID
IF NOT FIXEDLENGTH %(RECORDS)%
THEN %( WE MUST STORE THE SIZE OF THIS RECORD )%
BEGIN
RECORDPTR [ DRRECSIZE ] = .RST [ RSTRSZ ];
RECORDPTR [ DRRESERVED ] = ZERO; ! JUST FOR SAFETY
INC ( RECORDPTR, 1 ) ! BUMP TO DATA
END; %(OF IF NOT FIXEDLENGTH)%
%([ MOVE POINTER TO THE RECORD DATA ])%
RECORDPTR = .RECORDPTR + FIXHDRSIZE;
%([ MOVE THE USER RECORD INTO THE FILE ])%
MOVEWORDS ( %(FROM)% .USERRECORDPTR,
%(TO)% .RECORDPTR,
%(SIZE)% .RST [ RSTRSZW ] );
RETURN
END; %( OF MAKEUDR )%
! SDATABKT
! ========
! ROUTINE TO SEARCH A USER DATA BUCKET.
! THIS ROUTINE WILL SEARCH A USER DATA BUCKET FOR
! TWO CONDITIONS:
! 1. MATCHING KEY VALUE
! 2. MATCHING ID VALUE
!
! FOR KEY SEARCHES, THE ROUTINE WILL STOP WHEN A
! RECORD KEY VALUE IS FOUND WHICH IS GEQ TO THE
! SEARCH KEY VALUE, AND THE STATUS FLAGS IN THE
! RECORD DESCRIPTOR WILL BE SET ACCORDINGLY.
! FOR KEY SEARCHES, THE SEARCH WILL TERMINATE IF AN
! RRV RECORD IS FOUND.
!
! FOR ID SEARCHES, THE ROUTINE WILL STOP WHEN AN
! EXACT MATCH IS FOUND, OR WHEN THE END OF THE
! BUCKET IS REACHED. FOR ID SEARCHES, THE SEARCH WILL
! NOT TERMINATE WHEN AN RRV RECORD IS FOUND.
! INPUT:
! RECORD DESCRIPTOR
! RECPTR PLACE TO START THE SEARCH
! .GTR. 0 ==> ADDRESS TO START SEARCH
! .EQL. 0 ==> START SEARCH AT TOP OF BKT
! .LSS. 0 ==> SEARCH ONLY FIRST RECORD
! USERPTR ADDRESS OF USER DATA RECORD/KEY STRING
! USERSIZE SIZE OF DATA RECORD/KEY STRING
! RFA CONTAINS THE ID TO SEARCH FOR, OR 0 FOR KEY SEARCH
! FLAGS
! <NONE>
!
! BUCKET DESCRIPTOR OF BUCKET
! OUTPUT STATUS:
! TRUE: SEARCH TERMINATED NORMALLY
! FLGLSS MAY BE SET IF MATCH WAS .LSS.
! FALSE: SEARCH TERMINATED ABNORMALLY
! (I.E. RECORD NOT FOUND)
! INPUT ARGUMENTS MODIFIED:
! RECORD DESCRIPTOR
! RECPTR ADDRESS OF SEARCH TERMINATION
! LASTRECPTR ADDRESS OF RECORD BEFORE CURRENT ONE
! STATUS FLAGPST
! FLAGLSS
! NOTES ON INPUT ARGUMENTS:
!
! 1. IF RECPTR IS LESS THAN 0, IT MEANS THAT THIS
! ROUTINE IS TO SEARCH ONLY THE FIRST RECORD IN
! THE NEXT BUCKET. THIS OCCURS WHEN AN INDEX
! TREE ERROR IS DETECTED (THE INDEX RECORD KEY IS
! GREATER THAN THE KEY OF THE LAST RECORD IN THE
! BUCKET WHICH IT POINTS TO). BY CHECKING ONLY THE
! FIRST RECORD IN THE NEXT BUCKET, WE CAN DETERMINE
! THE CORRECT POSITIONING FOR THE OPERATION.
!
!NOTE ON OUTPUT ARGUMENTS:
! 1) IF FLGPASTLAST IS SET ON RETURN, RECPTR WILL POINT
! TO THE RECORD POSITION FOLLOWING THE LAST DATA RECORD (EITHER
! END OF BUCKET OR 1ST RRV ) ON KEY SEARCHES. ON ID SEARCHES,
! THE SEARCH TERMINATION WILL ONLY BE AT THE END OF BUCKET.
!
! 2 ) IF THE SEARCH TERMINATES AT THE FIRST RECORD, THEN
! LASTRECPTR WILL POINT TO THE FIRST RECORD (AS WILL RECPTR).
! ROUTINES CALLED:
! CKEYUU
! CKEYUI
GLOBAL ROUTINE SDATABKT ( RECDESC, BKTDESC ) =
BEGIN
ARGUMENT (RECDESC,BASEADD); ! RECORD DESCRIPTOR
ARGUMENT (BKTDESC,BASEADD); ! BUCKET DESCRIPTOR
MAP
RECDESC: POINTER,
BKTDESC: POINTER;
REGISTER
MOVINGPTR: POINTER,
SEARCHID, ! ID OF RFA TO SEARCH FOR
SEARCHRFA, ! ENTIRE RFA TO SEARCH FOR
TEMPAC;
LOCAL
ENDPTR: POINTER, ! ADDR OF END OF BUCKET
HEADERSIZE, ! SIZE OF RECORD HEADER
STOPATFIRSTFLAG, ! FLAG FOR SEARCH
SIDRFLAG, ! ON IF SCANNING SIDR'S
DUMMY, ! DUMMY LOCAL VARIABLE
SAVESTATUS,
DATAPTR: POINTER; ! PTR TO DATA RECORD
TRACE ( 'SDATABKT' );
%([ SET UP FOR BUCKET SEARCH ])%
SIDRFLAG = ( STOPATFIRSTFLAG = FALSE);
IF .KDB [ KDBREF ] ISNT REFPRIMARY THEN SIDRFLAG = TRUE;
CLRFLAG ( RECDESC [ RDSTATUS ], RDFLGLSS+RDFLGEMPTY+RDFLGPST+RDFLGDELETE);
ENDPTR = .BKTDESC [ BKDBKTADR ]; ! BEGIN TO COMPUTE END
%([ IF THIS IS NOT A DATA BUCKET, THEN EITHER THERE IS
A BUG, OR THE USER IS DOING RFA ACCESS WITH A BAD
RFA, BUT WE CAN'T TELL WHICH ])%
IF .ENDPTR [ BHBTYPE ] ISNT BTYPEDATA THEN BADRETURN;
IF ( MOVINGPTR = .RECDESC [ RDRECPTR ] ) LEQ ZERO
THEN %(WE SHOULD START AT TOP OF BUCKET)%
BEGIN
IF .MOVINGPTR LSS ZERO THEN STOPATFIRSTFLAG = TRUE;
MOVINGPTR = .ENDPTR + BHHDRSIZE;
RECDESC [ RDRECPTR ] = (RECDESC [ RDLASTRECPTR ] = .MOVINGPTR)
END; %(OF START AT TOP OF BUCKET)%
%([ CHECK FOR EMPTY OR NON-EXISTENT BUCKET ])%
IF ( TEMPAC = .ENDPTR [ BHNEXTBYTE ] ) LEQ BHHDRSIZE
THEN %(WE HAVE AN EMPTY BUCKET)%
BEGIN
SETFLAG ( RECDESC [ RDSTATUS ], RDFLGEMPTY + RDFLGPST );
BADRETURN
END;
%([ NOW, RESET THE END POINTER TO THE END OF DATA ])%
ENDPTR = .ENDPTR + .TEMPAC; ! FIND NEXT FREE BYTE
%([ FETCH THE SEARCH ID AND DETERMINE IF THIS IS AN ID SEARCH ])%
SEARCHRFA = .RECDESC [ RDRFA ]; ! GET WHOLE RFA
SEARCHID = IDOFRFA ( .SEARCHRFA ); ! EXTRACT ID FROM RFA
HEADERSIZE = .KDB [ KDBHSZ ]; ! ABD SIZE OF HEADER
%([ CHECK IF THE STARTING ADDRESS IS PAST THE ENDING ADDRESS ])%
IF .MOVINGPTR GEQ .ENDPTR
THEN %(WE MUST SET SOME STATUS BITS AND EXIT)%
BEGIN
IF .MOVINGPTR GTR .ENDPTR THEN RMSBUG ( MSGINPUT );
SETPASTLASTFLAG ( RECDESC );
BADRETURN
END; %(OF IF MOVINGPTR GTR ENDPTR)%
%([ THIS IS MAIN SEARCH LOOP ])%
WHILE .MOVINGPTR LSS .ENDPTR
DO
BEGIN
RECDESC [ RDRECPTR ] = .MOVINGPTR; ! SET UP FINAL PTR NOW
IF .SEARCHRFA ISNT NULLID
THEN %(THIS IS AN ID SEARCH)%
BEGIN
%([ IS THIS THE RIGHT ID? ])%
IF .SEARCHID IS .MOVINGPTR [ DRRECORDID ]
THEN %(WE FOUND IT)%
BEGIN
LOOKAT ( ' ID MATCH FOUND AT: ',MOVINGPTR );
IF CHKFLAG ( MOVINGPTR [ DRFLAGS ],FLGDELETE) ISON
THEN SETFLAG (RECDESC[RDSTATUS],RDFLGDELETE);
GOODRETURN
END %(OF IF ID'S MATCH)%
END %(OF IF THIS WAS AN ID SEARCH)%
ELSE %(THIS IS A KEY SEARCH)%
BEGIN
%([ IGNORE RRV RECORDS ])%
IF CHKFLAG ( MOVINGPTR [ DRFLAGS ], FLGRRV ) ISON
THEN BEGIN %(EXIT BECAUSE WE DIDNT FIND THE KEY)%
SETPASTLASTFLAG ( RECDESC );
BADRETURN;
END;
DATAPTR = .MOVINGPTR + .HEADERSIZE;
%([ COMPARE THE KEY VALUES, DEPENDING ON
WHETHER THE DATA RECORD IS A USER
DATE RECORD OR A SECONDARY DATA RECORD ])%
SAVESTATUS = (IF .SIDRFLAG ISON
THEN %(IT IS A SIDR)%
CALLCKEYKK ( BPT ( RECDESC ),
LPT ( DATAPTR ) )
ELSE %(IT IS USER DATA RECORD)%
CALLCKEYKU ( BPT ( RECDESC ),
LPT ( DATAPTR ) ) );
%([ SHOULD WE GO ON? ])%
IF .SAVESTATUS IS TRUE
THEN %(WE WILL CHECK FOR DELETED RECORDS..THEN EXIT)%
BEGIN
IF CHKFLAG ( MOVINGPTR [ DRFLAGS ],FLGDELETE) ISON
THEN SETFLAG (RECDESC[RDSTATUS],RDFLGDELETE);
GOODRETURN
END; %(OF IF WE FOUND THE RECORD)%
%([ EXIT FOR FIRST-ONLY SEARCH ])%
IF .STOPATFIRSTFLAG ISNT FALSE THEN RETURN .SAVESTATUS
END; %(OF ELSE THIS WAS A KEY SEARCH)%
%([ WE DIDN'T FIND THE RECORD, SKIP OVER IT ])%
RECDESC [ RDLASTRECPTR ] = .MOVINGPTR; ! SAVE LAST RECORD
MOVINGPTR = .MOVINGPTR + SIZEOFANYRECORD ( MOVINGPTR )
END; %(OF WHILE MOVINGPTR LSS ENDPTR)%
%([ WE DID NOT FIND THE RECORD ])%
RECDESC [ RDRECPTR ] = .MOVINGPTR; ! RESTORE POINTER
SETPASTLASTFLAG ( RECDESC ); ! REMEMBER WE WENT TOO FAR
BADRETURN
END; %(OF SDATABKT)%
! INSRTUDR
! =========
! ROUTINE TO INSERT A USER DATA RECORD INTO A DATA BUCKET
! THIS ROUTINE DOES ALL MOVING OF RECORDS AND SPLITS.
! HOWEVER, NO INDEX MODIFICATION AT ALL IS DONE BY
! THIS ROUTINE OR ANY ROUTINE BELOW THIS ROUTINE.
! INPUT:
! RECDESC RECORD DESCRIPTOR PACKET
! RECPTR ADDRESS TO INSERT RECORD
! LENGTH LENGTH (IN WORDS) OR RECORD TO INSERT
!
! USERRECORDPTR ADDRESS OF USER DATA RECORD
! DATABD BUCKET DESC OF CURRENT BUCKET
! SPLITBD1 BUCKET DESC OF 1ST SPLIT BUCKET (RETURNED)
! SPLITBD2 BUCKET DESC OF 2ND SPLIT BUCKET (RETURNED)
!
! OTHER FIELDS USED:
! RSZW IN RST = SIZE IN WORDS OF USER RECORD
! OUTPUT:
! TRUE: RECORD INSERTED
! FALSE: ERROR
! NO MORE BUCKETS
! NO MORE CORE
!
! ON ERROR, USRSTS WILL BE SET TO THE APPROPRIATE ERROR CODE
! ARGUMENTS MODIFIED:
! RECORD DESCRIPTOR
! RECPTR ADDRESS OF INSERTED RECORD
! RFA RFA OF NEW RECORD
! LASTRECPTR ADDRESS OF NEW HIGH RECORD FOR
! ORIGINAL BUCKET, IF SPLIT
! RRV RFA OF RRV RECORD (SAME AS RFA)
! STATUS STATUS VALUE OF OPERATION
! IDXUPDATE INDEX UPDATE REQUIRED
! ROUTINES CALLED:
! ALCRFA
! MAKEUDR
! SPLIT
! COMPRESS
!
! NOTES:
!
! 1. ON OUTPUT, ALL BUCKETS (ORIGINAL AND SPLIT BUCKETS)
! HAVE BEEN UPDATED TO THE FILE. ALSO, THE NEW BUCKETS,
! IF ANY, ARE UNLOCKED. HOWEVER, THE ORIGINAL DATA
! BUCKET REMAINS LOCKED. IT IS THE CALLER'S RESPONSIBILITY
! TO EXPLICITLY UNLOCK THE DATA BUCKET.
!
! 2. IF A 3-BUCKET SPLIT OCCURS (ONLY POSSIBLE IF A VERY
! LARGE RECORD IS INSERTED), THEN 1ST SPLIT BUCKET
! DESCRIPTOR WILL REPRESENT THE BUCKET CONTAINING THE
! R(HIGH) RECORD SET AND R(NEW) WILL BE IN SPLITBD2.
GLOBAL ROUTINE INSRTUDR ( RECDESC, USERRECORDPTR, DATABD, SPLITBD1, SPLITBD2 ) =
BEGIN
ARGUMENT (RECDESC,BASEADD); ! RECORD DESC
ARGUMENT (USERRECORDPTR,BASEADD); ! USER RECORD ADDRESS
ARGUMENT (DATABD,BASEADD); ! PRIMARY BUCKET
ARGUMENT (SPLITBD1,BASEADD); ! NEW BKT-1
ARGUMENT (SPLITBD2,BASEADD);
MAP
RECDESC: POINTER,
USERRECORDPTR: POINTER,
DATABD: POINTER,
SPLITBD1: POINTER,
SPLITBD2: POINTER;
LOCAL
AREANUMBER, ! AREA NUMBER OF THE DATA AREA
FREESPACE, ! AMOUNT OF FREE SPACE LEFT IN DATA BUCKET
BUCKETSIZE, ! BUCKET SIZE OF DATA BUCKET
NEXTFREEWORD, ! NEXT WORD IN BUCKET WHICH IS AVAILABLE
NEWRECORDSIZE, ! SIZE OF RECORD TO BE INSERTED
AMOUNTTOMOVE, ! AMOUNT OF DATA TO BE MOVED FOR NEW RECORD
RETURNEDSPACE, ! AMOUNT OF NEW SPACE AFTER BUCKET COMPRESSION
MINFREESPACE, ! USED TO COMPUTE USER FREE SPACE
EXITFLAG, ! USED TO GET US OUT OF LOOP
LOOPCOUNT, ! BINARY SWITCH FOR # OF TIMES THRU LOOP
INSERTPTR, ! ADDRESS WHERE RECORD IS TO GO
LASTRECORDPTR: POINTER, ! PTR TO LAST RECORD IN ORIGINAL BKT
TEMPPTR: POINTER, ! TEMPORARY PTR VARIABLE
WINDOWPAGE;
REGISTER
BKTPTR: POINTER;
TRACE ('INSRTUDR');
%([ CHECK INPUT VALUES ])%
CHECKEXACTCOUNT;
EXITFLAG = FALSE; ! INIT VALUES
LOOPCOUNT = ZERO;
NEWRECORDSIZE = .RECDESC [ RDLENGTH ] + .KDB [ KDBHSZ ];
%([ THIS IS THE MAIN LOOP OF THIS ROUTINE. IF THE RECORD
WILL FIT INTO THE BUCKET, THEN A RETURN IS DONE FROM
THE MIDDLE OF THE LOOP. IF THE RECORD CANNOT FIT INTO
THE BUCKET, THEN THE BUCKET IS COMPRESSED. IF THE
RECORD WILL NOW FIT, THE LOOP IS EXECUTED AGAIN EXACTLY
AS IF THE RECORD WOULD INITIALLY FIT INTO THE BUCKET.
HOWEVER, IF THE RECORD STILL WONT FIT, CONTROL FALLS
THRU THE LOOP AND A BUCKET SPLIT OCCURS. ])%
WHILE .EXITFLAG IS FALSE
DO %(THIS LOOP)%
BEGIN
%([ GET THE ADDRESS WHERE WE WILL INSERT THE NEW RECORD.
NOTE THAT THIS ADDRESS MAY BE CHANGED BY "COMPRESS",
THUS, WE MUST RE-FETCH IT WITHIN THIS LOOP. ])%
INSERTPTR = .RECDESC [ RDRECPTR ]; ! GET FIRST POINTER
BKTPTR = .DATABD [ BKDBKTADR ];
BUCKETSIZE = .KDB [ KDBDBKZ ];
%([ COMPUTE FREE SPACE IN THIS BUCKET ])%
FREESPACE = ( .BUCKETSIZE ^ B2W ) - .BKTPTR [ BHNEXTBYTE ];
%([ CHECK TO SEE IF THE USER WANTED THE BUCKET TO
HAVE A LOAD-FILL PERCENTAGE ])%
IF CHKFLAG ( RAB [ RABROP ], ROPLOA ) ISON
THEN %(CHECK IF WE HAVE "FILLED" THE BUCKET )%
BEGIN
MINFREESPACE = ( .BUCKETSIZE ^ B2W ) - .KDB [ KDBDFLOFFSET ];
IF .MINFREESPACE GEQ .FREESPACE
THEN
FREESPACE = ZERO; ! FORCE A SPLIT
END; %(OF IF THE LOA BIT IS ON)%
%([ LET'S SEE THESE VALUES ])%
LOOKAT (' BKTPTR: ',BKTPTR);
LOOKAT (' FREESPACE: ',FREESPACE);
%([ CHECK TO SEE IF IT'LL FIT ])%
IF .NEWRECORDSIZE LEQ .FREESPACE
THEN
BEGIN %( TO INSERT THE RECORD )%
RTRACE ( %STRING(' RECORD CAN FIT... ',%CHAR(13),%CHAR(10)) );
NEXTFREEWORD = .BKTPTR + .BKTPTR [ BHNEXTBYTE ];
AMOUNTTOMOVE = .NEXTFREEWORD - .INSERTPTR; ! COMPUTE DATA TO MOVE DOWN
LOOKAT ( ' NEXTFREEWORD:', NEXTFREEWORD);
LOOKAT ( ' AMMTTOMOVE:', AMOUNTTOMOVE );
IF .AMOUNTTOMOVE LSS ZERO THEN RMSBUG ( MSGCOUNT );
%([ IF THIS ISNT THE LAST RECORD IN THE BUCKET
THEN WE NEED TO MOVE SOME RECORDS DOWN ])%
IF .AMOUNTTOMOVE ISNT ZERO
THEN
BEGIN %( TO MOVE RECORDS DOWN )%
RTRACE ( %STRING(' MOVING RECORDS DOWN ',%CHAR(13),%CHAR(10)) );
MOVEDOWN( %( START )% .INSERTPTR,
%( END )% .NEXTFREEWORD - 1,
%( SIZE )% .NEWRECORDSIZE );
END; %(OF IF AMOUNT TO MOVE ISNT ZERO)%
%([ ALLOCATE A NEW RFA FOR THIS RECORD ])%
RECDESC [ RDRFA ] =CALLALCRFA ( BPT ( DATABD ) );
%([ CREATE THE RECORD ])%
CALLMAKEUDR ( %(REC DESC)% BPT ( RECDESC ),
%(UDR PTR)% BPT ( USERRECORDPTR ) );
%([ RESET THE BUCKET HEADER DATA ])%
BKTPTR [ BHNEXTBYTE ] = .BKTPTR [ BHNEXTBYTE ] + .NEWRECORDSIZE;
%([ WRITE THE DATA PAGE OUT TO THE DISK ])%
IF ( NOT WRITEBEHIND )
THEN UPDATEBUCKET ( DATABD ) ! BUCKET UPDATE
ELSE SETBFDUPD(DATABD[BKDBFDADR]); !SET WRITE FLAG
GOODRETURN
END; %(OF IF NEWRECORDSIZE LEQ FREESPACE)%
%([ AT THIS POINT, THE RECORD WON'T FIT, WE MUST SPLIT ])%
RTRACE ( %STRING(' **RECORD WONT FIT**',%CHAR(13),%CHAR(10)) );
%([ INITIALIZE THE AMOUNT OF COMPRESSED SPACE TO ZERO ])%
RETURNEDSPACE = ZERO; ! CLEAR LOCAL
%([ IF THIS IS OUR FIRST TIME THRU THE LOOP, WE
MUST TRY TO COMPRESS TO BUCKET. IF THIS IS
OUR SECOND TIME THRU THE LOOP, THEN WE ALREADY
COMPRESSED THE BUCKET AND RECOVERED ENOUGH
SPACE FOR THE RECORD TO FIT. BUT, SOMEHOW
OUR INITIAL COMPUTATION CONCLUDED THAT THE
RECORD WOULD NOT FIT...THUS, WE HAVE A BUG SOMEWHERE ])%
IF .LOOPCOUNT IS ZERO
THEN
BEGIN
%([ COMPRESS THE BUCKET ])%
RETURNEDSPACE = CALLCOMPRESS ( BPT ( RECDESC ), BPT (DATABD) );
%([ BUMP OUR LOOP CONTROL FLAG ])%
LOOPCOUNT = .LOOPCOUNT+1;
%([ DID WE GET BACK ENOUGH SPACE TO INSERT THE RECORD?? ])%
LOOKAT ( ' SPACE RETURNED:', RETURNEDSPACE );
IF ( .RETURNEDSPACE + .FREESPACE ) LSS .NEWRECORDSIZE
THEN
EXITFLAG = TRUE;
END %( OF IF LOOPCOUNT IS ZERO )%
ELSE
RMSBUG ( MSGLOOP ); ! WE WENT THRU LOOP TWICE
END; %( OF DO WHILE EXITFLAG IS FALSE )%
%([ WE MUST NOW SPLIT THE BUCKET ])%
%([ SET UP ARGS FOR SPLIT ROUTINE: ])%
RECDESC [ RDLENGTH ] = .NEWRECORDSIZE; ! SIZE OF HOLE
IF CALLSPLIT ( %( REC-DESC )% BPT (RECDESC),
%( OLD DATABD )% BPT (DATABD),
%( USED FOR SPLIT )% BPT (SPLITBD1),
%( 3-BKT SPLIT )% BPT (SPLITBD2) ) IS FALSE
THEN %(SOMETHING VERY BAD HAS HAPPENED)%
RETURN FALSE;
%([ THE BUCKET HAS NOW BEEN SPLIT AND RECPTR
POINTS TO WHERE WE SHOULD WRITE THE NEW RECORD ])%
%([ WE WILL NOW CREATE THE USER DATA RECORD ])%
CALLMAKEUDR ( %( REC-DESC )% BPT (RECDESC),
%(USER RECORD)% BPT ( USERRECORDPTR ) );
%([ NOTE THAT LASTRECPTR NOW POINTS TO THE HIGHEST RECORD
IN THE OLD BUCKET ])%
%([ THERE IS ONE LAST THING WE MUST DO...WE MUST MOVE
THE NEW HIGH-KEY VALUE OF THE HIGHEST RECORD IN THE
OLD BUCKET INTO A TEMPORARY KEY BUFFER. WE MUST DO
THIS BECAUSE THE KEY VALUE IS UNAVAILABLE AFTER THE
BUCKET HAS BEEN RELEASED (BECAUSE IF THE BUFFER CAME
FROM FREE CORE, IT WILL BE DESTROYED AFTER USE).
THEREFORE, WE WILL MOVE THIS KEY VALUE INTO THE
BOTTOM HALF OF THE RST KEY BUFFER ])%
TEMPPTR = .RST [ RSTKEYBUFF ] + ( .FST [ FSTKBFSIZE ] / 2 );
LASTRECORDPTR = .RECDESC [ RDLASTRECPTR ] + .KDB [ KDBHSZ ];
CALLMOVEKEY ( %(FROM UDR)% LPT ( LASTRECORDPTR ),
%(TO BUFFER)% LPT ( TEMPPTR ) );
%([ WE WILL NOW UPDATE THE FILE BUCKETS WHICH WE PROCESSED.
NOTE THAT THESE BUCKETS MUST BE UPDATED IN REVERSE ORDER
OF THEIR LINKAGES IN THE BUCKET CHAIN IN ORDER TO AVOID
THE PROBLEM OF A BUCKET WHICH POINTS TO A NON-EXISTENT
BUCKET. ])%
%([ UPDATE THE THIRD BUCKET IF THERE WAS ONE ])%
IF .RECDESC [ RDCOUNT ] GTR 1 THEN UPDATEBUCKET ( SPLITBD2 );
%([ UPDATE THE RRV'S IN THE NEW BUCKET ])%
IF CALLUPDRRVS (%(OLD BKT)% BPT ( DATABD ),
%(NEW BKT)% BPT ( SPLITBD1 ) ) IS FALSE
%([ IF WE COULDN'T DO IT, TELL THE USER BUT GO ON ])%
THEN USRSTS = SU$RRV;
%([ UPDATE DATA BUCKET ])%
UPDATEBUCKET ( DATABD );
%([ *****NOTE THAT WE HAVE NOW UPDATED THE PRIMARY DATA
BUCKET TO DISK, BUT WE HAVE NOT RELEASED IT YET*** ])%
%([ FLUSH ALL SPLIT BUCKETS ])%
%([** FLUSH OLD BUCKET IF R-NEW GOES INTO NEW BUCKET AND RECORDS ARE **])%
%([** ACCESSED SEQUENTIALLY. ELSE FLUSH THE NEW BUCKET. THIS FIX HAS TO DO **])%
%([** WITH THE PROBLEM OF FLUSHING THE INCORRECT DATA BUCKET ON A SPLIT **])%
%([** WHEN RECORDS ARE INSERTED SEQUENTIALLY. SO THE FOLLOWING CHECK **])%
%([** MAKES SURE THE PROPER BUCKET IS FLUSHED. PREVIOUSLY THE SPLIT **])%
%([** BUCKET WAS BEING FLUSHED ALWAYS EVEN THOUGH THE OLD BUCKET WAS **])%
%([** NEVER REQUIRED. THIS RESULTED IN THE ER$SEQ ERROR CODE. **])%
TEMPPTR = ( IF FLUSHORIGBD ( RECDESC ) ISON
THEN
.DATABD
ELSE
.SPLITBD1 );
%([** END OF THE FIX. NOTE: ON THE CALL PUTBKT BELOW **])%
%([** 'LPT ( TEMPPTR )'IS USED INSTEAD OF 'BPT ( SPLITBD1 )' **])%
CALLPUTBKT ( %(NO UPDATE)% PCI ( FALSE),
%(BKT)% LPT ( TEMPPTR ) );
%([ WAS THIS A 3-BKT SPLIT? ])%
IF .RECDESC [ RDCOUNT ] GTR 1
THEN
BEGIN
CALLPUTBKT ( %(NO UPDATE)% PCI ( FALSE ),
%(BKT)% BPT ( SPLITBD2 ) );
%([ THIS 3-BUCKET SPLIT COULD HAVE BEEN CAUSED BY TWO
THINGS....A VERY BIG NEW RECORD OR A DUP WHICH COULDN'T
FIT IN THE ORIGINAL BUCKET SO A NEW ONE WAS ALLOCATED
JUST FOR THE DUP. IN THE LATTER CASE, WE DON'T WANT THE
DUP BUCKET TO BE ENTERED INTO THE INDEX, SO WE WILL FLAG
THAT ONLY A TWO-BUCKET SPLIT OCCURRED. ])%
IF ( DUPLICATEFLAG ( RECDESC ) ISON )
THEN %(RESET SPLIT COUNT TO 1)%
RECDESC [ RDCOUNT ] = 1
END; %(OF IF THIS WAS A 3-BKT SPLIT)%
GOODRETURN
END; %(OF INSRTUDR)%
! CHKDUP
! ======
! ROUTINE TO CHECK FOR DUPLICATE RECORDS. THIS ROUTINE
! WILL CHECK IF DUPLICATES ARE ALLOWED, AND IF SO,
! WHETHER ANY DUPLICATE RECORDS EXIST IN THE FILE.
!
! INPUT:
! RECDESC RECORD DESCRIPTOR PACKET
! RECPTR ADDRESS OF CURRENT DATA RECORD
! USERPTR ADDRESS OF SEARCH KEY STRING
! USERSIZE SIZE OF SEARCH KEY STRING
!
! BKTDESC BKT DESCRIPTOR OF DATA BUCKET
! OUTPUT:
! TRUE: DUPLICATE NOT SEEN, OR DUPLICATES ALLOWED.
! IF ENTRY WITH DUP KEY SEEN, RDFLGDUP SET.
! IF ENTRY REPRESENTS EXISTING REC, RDFLGSAME ALSO SET.
! FALSE: A DUPLICATE WAS SEEN AND IS NOT ALLOWED.
! INPUT ARGUMENTS MODIFIED:
!
! RECORD DESCRIPTOR
! RECPTR ADDRESS OF RECORD WHICH FOLLOWS
! LAST RECORD IN THE DUPLICATE SERIES
! ROUTINES CALLED:
! FNDREC
!
! NOTES:
!
! 1. THERE IS A MINOR OPTIMIZATION WHICH COULD BE DONE TO
! THIS ROUTINE*******************.
! THIS OCCURS WHEN WE ARE SKIPPING SECONDARY DATA RECORDS.
! BECAUSE SIDR'S ALWAYS EXPAND TO FILL THE BUCKET, UNLESS
! THE END OF THE SIDR IS THE LAST WORD IN THE BUCKET, THEN
! WE KNOW THAT THERE IS NOT A DUPLICATE OF THIS KEY.
! IN SUCH A CASE, THIS ROUTINE WILL READ IN THE NEXT BUCKET
! AND CHECK THE KEY OF THE NEXT SIDR, EVEN THOUGH WE KNOW
! THAT IT CAN'T BE THE SAME KEY. THUS, THE FOLLOWING
! OPTIMIZATION:
!
! WHEN WE GET READY TO SKIP A RECORD, CHECK TO SEE
! IF ITS A SECONDARY KEY AND IF THE CURRENT RECORD
! DOES NOT TOUCH THE END OF THE BUCKET. IF NOT,
! THEN SET LASTRECPTR AND EXIT.
GLOBAL ROUTINE CHKDUP ( RECDESC, BKTDESC ) =
BEGIN
ARGUMENT (RECDESC,BASEADD); ! RECORD DESC
ARGUMENT (BKTDESC,BASEADD); ! DATA BUCKET
LOCAL RRV; !SET FROM RDRRV, CALLER'S RRV
LOCAL I; !LOOP INDEX FOR SIDR ARRAY
MAP
RECDESC: POINTER,
BKTDESC: POINTER;
REGISTER
TEMPAC;
REGISTER
MOVINGPTR: POINTER; ! PTR TO SCAN DATA RECORDS
TRACE ('CHKDUP');
CHECKEXACTCOUNT;
RRV = .RECDESC[RDRRV]; !(SEE SIDR ARRAY LOOP)
%([ DID WE FIND AN EXACT MATCH? ])%
TEMPAC = .RECDESC [ RDSTATUS ]; ! GET STATUS FLAGS
IF ( CHKFLAG ( TEMPAC, (RDFLGLSS+RDFLGPST) ) ISON )
AND
( CHKFLAG ( TEMPAC, RDFLGDELETE ) IS OFF )
THEN %(WE DIDN'T EVEN SEE AN EXACT MATCH..SO EXIT)%
GOODRETURN;
%([ REMEMBER WE FOUND ANOTHER BKT ENTRY WITH SAME KEY ])%
SETDUPLICATFLAG ( RECDESC );
%([ WE MUST NOW POSITION OURSELVES TO THE END OF THE
LIST OF DUPS ])%
UNTIL ( CHKFLAG ( RECDESC [ RDSTATUS ], (RDFLGLSS + RDFLGPST ) ) ISON )
DO %(THIS LOOP)%
BEGIN
%([ IF THE "DELETE" FLAG IS OFF AT THIS POINT, IT
MEANS THAT WE HAVE SEEN A TRUE DUPLICATE WHICH
WAS NOT DELETED. IN SUCH A CASE, WE MUST REMEMBER
THAT WE HAVE SEEN IT, AND CHECK TO SEE IF DUPLICATES
ARE ALLOWED. ])%
IF CHKFLAG ( RECDESC [ RDSTATUS ], RDFLGDELETE ) IS OFF
THEN %(ARE THEY ALLOWED?)%
BEGIN
RTRACE (%STRING(' A DUP WAS FOUND',%CHAR(13),%CHAR(10)));
%([ REMEMBER WE FOUND A RECORD WITH SAME KEY ])%
SETSAMEKEYFLAG ( RECDESC );
%([ ARE THEY ALLOWED? ])%
IF CHKFLAG ( KDB [ KDBFLAGS ], FLGDUP ) IS OFF
THEN %(NO DUPS ARE ALLOWED)%
RETURNSTATUS ( ER$DUP )
END; %(OF IF NON-DELETED RECORD WAS FOUND)%
! SCAN SIDR ARRAY TO SEE IF RDRRV ALREADY THERE
MOVINGPTR = .RECDESC [ RDRECPTR ]; ! GET PTR TO CURR ENTRY
IF .KDB[KDBREF] NEQ 0
THEN BEGIN
INCR I FROM SIDRHDRSIZE+.KDB[KDBKSZW]
TO .MOVINGPTR[SIDRRECSIZE]+SIDRHDRSIZE-1
DO BEGIN
IF .MOVINGPTR[.I,WRD] EQL .RRV ! CURR UDR ALR THERE?
THEN IF CHKFLAG (RECDESC[RDSTATUS], RDFLGRETEX) ISON
THEN BEGIN ! RMSUTL CALL
RECDESC[RDSIDRELEMENT]=.I;
! TELL WHERE AT
BADRETURN; ! INDIC THAT IT HAPPENED
END
ELSE BEGIN ! ALW OPEN FOR WRITE
MOVINGPTR[.I,WRD] = 0;
!ZAP ENTRY THAT WASNT DELETED BECAUSE OF CRASH PROBABLY
SETUPD (BKTDESC); ! INSURE IT WRITTEN OUT
END
!END IF RRV MATCH
END; !END SIDR ARRAY LOOP
END; !END 2NDARY KEY
%([ SKIP A DATA RECORD ])%
RECDESC [ RDLASTRECPTR ] = .MOVINGPTR; ! STORE AS LAST PTR
RECDESC [ RDRECPTR ] = .MOVINGPTR + SIZEOFDATARECRD ( MOVINGPTR );
%([ WE HAVE NOW BUMPED TO THE NEXT DATA RECORD. WE
MUST COMPARE THE KEYS AND POSITION OURSELVES
CORRECTLY. ])%
IF CALLFNDREC (%(RD)% BPT ( RECDESC ),
%(START)% BPT ( BKTDESC ),
%(END)% BPT ( BKTDESC ) ) IS FALSE
THEN
RMSBUG ( MSGFAILURE )
END; %(OF UNTIL LSSFLAG OR PASTLASTFLAG IS ON)%
GOODRETURN
END; %(OF CHKDUP)%
END
ELUDOM