Bug 48965

GemStone/S 64 Bit

3.5.2, 3.5.1, 3.5, 3.4.5, 3.4.4, 3.4.3, 3.4.2, 3.4.1, 3.4

3.5.3

Indexes with optionalPathTerm error on removing element without term

GemStone indexing allows you to specify the optionPathTerms option, which allows you to add objects to an indexed collection that do not have the indexed path term.

Attempting to remove an object that did not have the indexed path term, however, could encounter an error.

Workaround

File in the following as SystemUser, and commit.

run
	GsOptionalPathTerm removeSelector: #'_nextObjectFor:'.
	GsOptionalPathTerm removeSelector: #'_errorInvalidOffset:'.
%

method: GsOptionalPathTerm
_findAllValuesForIdenticalRootObject: rootObject
       " if index directly on NSC elements or anObject is nil "

       | ivOffset |
       ivOffset := self _ivOffsetFor: rootObject.
       ivOffset
               ifNil: [ 
                       "no values in index for rootObject"
                       ^ IdentityBag new ].
       ^ super _findAllValuesForIdenticalRootObject: rootObject
%
category: 'Accessing'
method: GsOptionalPathTerm
_nextObjectFor: anObject atInstVar: anIvOffset
       "Returns the object at the instance variable that corresponds to the receiver"
       | ivOffset |
       ivOffset := self _ivOffsetFor: anObject.
       ivOffset ifNil: [ ^ nil ].
       ^ super _nextObjectFor: anObject atInstVar: anIvOffset
%

category: 'Adding'
method: GsOptionalPathTerm
addMappingsForObject: anObject root: rootObject logging: aBoolean
       "Add dependency list entries for anObject."

       | ivOffset |
       (nil == anObject or: [ self size == 0 ])
               ifTrue: [ ^ self ].
       (ivOffset := self _ivOffsetFor: anObject) == nil
               ifTrue: [ ^ self recordNilOnPathForRoot: rootObject ].
       ^ super addMappingsForObject: anObject root: rootObject logging: aBoolean
%

category: 'Removing'
method: GsOptionalPathTerm
removeMappingsFor: anObject root: rootObject lastOne: aBoolean logging: doLogging
  "Remove entries in the btree and dependency lists for anObject."

       | ivOffset |
       (nil == anObject or: [ self size == 0 ])
               ifTrue: [ ^ self ].
       (ivOffset := self _ivOffsetFor: anObject) == nil
               ifTrue: [ ^ self removeNilOnPathForRoot: rootObject ].
       ^ super removeMappingsFor: anObject root: rootObject lastOne: aBoolean logging: doLogging
%
category: 'Private'
method: GsPathTerm
_findAllValuesForIdenticalRootObject: rootObject
       " if index directly on NSC elements or anObject is nil "

       | key tmpList |
       (self indicatesIndexOnNscElements or: [ nil == rootObject ])
               ifTrue: [ key := rootObject ]
               ifFalse: [ key := self _nextObjectFor: rootObject ].
       tmpList := IdentityBag new.
       self updateBtree btreeRoot _findAllValuesForIdenticalKey: key into: tmpList.
       ^ (tmpList occurrencesOf: rootObject) <= 1
%
category: 'private'
method: PathTerm
_findAllValuesForIdenticalRootObject: rootObject
       " if index directly on NSC elements or anObject is nil "

       | key tmpList |
       (self indicatesIndexOnNscElements or: [ nil == rootObject ])
               ifTrue: [ key := rootObject ]
               ifFalse: [ key := self _nextObjectFor: rootObject ].
       tmpList := IdentityBag new.
       self updateBtree btreeRoot _findAllValuesForIdenticalKey: key into: tmpList.
       ^ (tmpList occurrencesOf: rootObject) <= 1
%
category: 'Indexing Support'
method: UnorderedCollection
_isLastOccurrenceInIndexObjects: anObject
 "Returns true if the given object is maintained in the indexing objects for one
occurrence."

 | rootTerms pathTerm key val val2 num parentTerm |
 rootTerms := self _indexedPaths rootTerms.	" find a path term with a mapping in the index dictionary "
 1 to: rootTerms size do: [ :i | 
   pathTerm := rootTerms at: i.
   (pathTerm _isObsoletePathTerm)
     ifFalse: [ 
       pathTerm isRangeEqualityIndexLastPathTerm
         ifTrue: [ 
           pathTerm offset == 1
             ifTrue: [ ^ pathTerm _findAllValuesForIdenticalRootObject: anObject ]
             ifFalse: [ 
               pathTerm hasIndexDictionary
                 ifTrue: [
                   pathTerm := pathTerm getParentTerm.
                   val := pathTerm updateDict
                     at: anObject
                     term: pathTerm
                     otherwise: nil.
                   (BucketValueBag _hasInstance: val)
                     ifTrue: [     
                       " see if more than one mapping "
                       pathTerm indicatesNsc
                         ifTrue: [ 
                           num := val occurrencesOf: self.
                           num < val size
                             ifTrue: [ 
                               " if anObject is contained in other NSCs "
                               ^ false ].
                           pathTerm := pathTerm getParentTerm.	" get path term before this one "
                           val2 := pathTerm updateDict
                             at: self
                             term: pathTerm
                             otherwise: nil.
                           (BucketValueBag _hasInstance: val2)
                             ifTrue: [ ^ val2 size == num ]
                             ifFalse: [ ^ true ] ]
                         ifFalse: [ ^ (val occurrencesOf: self) <= 1 ] ]
                     ifFalse: [ ^ true ] ]
                 ifFalse: [ ^ (self occurrencesOf: anObject) <= 1] ] ]
         ifFalse: [ 
           pathTerm hasIndexDictionary
             ifTrue: [ 
               " get key to look up in index dictionary "
               (pathTerm indicatesIndexOnNscElements or: [ nil == anObject ])
                 ifTrue: [ key := anObject ]
                 ifFalse: [ 
                   " see if a path with '*.*' in it "
                   pathTerm indicatesNsc
                     ifTrue: [ 
                       val := pathTerm updateDict
                         at: anObject
                         term: pathTerm getParentTerm
                         otherwise: nil.
                       ^ (BucketValueBag _hasInstance: val) not ]
                     ifFalse: [ key := pathTerm _nextObjectFor: anObject ] ].
               val := pathTerm updateDict at: key term: pathTerm otherwise: nil.	" look up the mapping in the index dictionary "
               (BucketValueBag _hasInstance: val)
                 ifTrue: [ 
                   " see if more than one mapping "
                   (num := val occurrencesOf: anObject) <= 1
                     ifTrue: [ ^ true ]
                     ifFalse: [ 
                       " see if multiple occurrences are due to more than one object
                       referencing self "
                       " if there is a parent term, it is a SetValuedPathTerm "
                       parentTerm := pathTerm getParentTerm.
                       parentTerm == nil
                         ifTrue: [ ^ false ].
                       val2 := pathTerm updateDict
                         at: anObject
                         term: parentTerm
                         otherwise: nil.
                       (BucketValueBag _hasInstance: val2)
                         ifTrue: [ ^ (val2 occurrencesOf: self) == num ]
                         ifFalse: [ ^ false ] ] ]
                 ifFalse: [ ^ true ] ] 
             ifFalse: [ ^ (self occurrencesOf: anObject) <= 1] ] ] ].
 ^ true	" if we get this far, there are only incomplete indexes "
%

Last updated: 8/17/20