!=========================================================================
! Copyright (C) VMware, Inc. 1986-2011.  All Rights Reserved.
!
! $Id: goodies.gs,v 1.12 2008-01-09 22:50:11 stever Exp $
!
! Description -
!   This file contains "goodies", GemStone Smalltalk commands that 
!   perform useful functions.  In general, goodies are grouped by their 
!   associated class.  At the end of this file, there are also
!   goodies for getting information about indexes, query execution, etc.
!
!   Each goodie is preceded by a statement of what it does and
!   whether any explicit privilege is required in your UserProfile.
!      
!   Many goodies require you to fill in one or more parameters
!   before execution.  Delete the dummy parameters (shown in ALL
!   CAPITAL LETTERS) and put in your own values.
!       
!   See the manual "Programming in GemStone" for more information.
!
!========================================================================

*****************************************************************
*              F U N   W I T H   C L A S S E S                  *
*****************************************************************


 TO VIEW ALL THE SELECTORS UNDERSTOOD BY INSTANCES OF A CLASS 
    | r lf |
    lf := Character lf.
    r := String new add: lf; yourself .
    CLASSNAME allSelectors do: [:each |
      r addAll: each; add: lf ].
    ^r


 TO VIEW ALL THE SELECTORS UNDERSTOOD BY A CLASS OBJECT 
    | r lf |
    lf := Character lf.
    r := String new add: lf; yourself .
    CLASSNAME class allSelectors do: [:each |
      r addAll: each; add: lf ].
    ^r


 TO VIEW THE SOURCE CODE FOR AN INSTANCE METHOD
    ^CLASSNAME sourceCodeAt: #SELECTOR


 TO VIEW THE SOURCE CODE FOR A CLASS METHOD
    ^CLASSNAME class sourceCodeAt: #SELECTOR


 TO VIEW THE SELECTORS USED IN AN INSTANCE METHOD
    | r lf sels |
    lf := Character lf.
    r := String new add: lf; yourself .
    sels := (CLASSNAME compiledMethodAt: #SELECTOR) _selectorPool.
    sels do: [ :aSel |
        r addAll: aSel; add: lf.
        ].
    ^r


 TO VIEW THE SELECTORS USED IN A CLASS METHOD
    | r lf sels |
    lf := Character lf.
    r := String new add: lf; yourself .
    sels := (CLASSNAME class compiledMethodAt: #SELECTOR) _selectorPool.
    sels do: [ :aSel |
        r addAll: aSel; add: lf.
        ].
    ^r



*****************************************************************
*          F U N   W I T H   R E P O S I T O R I E S            *
*****************************************************************


 TO CREATE A REPLICATE OF THE SYSTEM REPOSITORY
    >> this goodie requires Replicates privilege <<
    SystemRepository replicateWith: 'NAME_OF_REPLICATE'
    ^'done'


 TO VIEW THE FILENAMES OF THE SYSTEM REPOSITORY
    | r lf |
    lf := Character lf.
    r := String new add: lf; yourself .
    SystemRepository fileNames do: [ :fn |
            r addAll: fn;  add: lf.
            ].
    ^r


 TO DISPOSE OF A REPLICATE OF THE SYSTEM REPOSITORY
    >> this goodie requires Replicates privilege <<
    SystemRepository disposeOfReplicate: 'NAME_OF_REPLICATE'
    ^'done'


 TO MARK THE SYSTEM REPOSITORY FOR GARBAGE COLLECTION
    >> this goodie requires Garbage Collection privilege <<
    System currentSessions size == 1 
      ifTrue: [ SystemRepository markForCollection ]
      ifFalse: [ ^'there are other users on the system' ].
    ^'done'


 TO PERFORM GARBAGE COLLECTION ON A MARKED REPOSITORY
    >> this goodie requires Garbage Collection privilege <<
    System currentSessions size == 1 
      ifTrue: [ SystemRepository collect ]
      ifFalse: [ ^'there are other users on the system' ].
    ^'done'

*****************************************************************
*              F U N   W I T H   S Y S T E M                    *
*****************************************************************


 TO SEE HOW MANY SESSIONS ARE ACTIVE IN THE GEMSTONE 
    >> this goodie requires Session Access privilege <<
    System currentSessions size


 TO SEE THE USER IDs OF PEOPLE CURRENTLY USING THE GEMSTONE 
    >> this goodie requires Session Access privilege <<
    | r lf user |
    lf := Character lf.
    r := String new add: lf; yourself .
    System currentSessions do: [:each |
      user := System userProfileForSession: each.
      (user ~~ nil) ifTrue: [
        r addAll: user userId; add: lf
        ]
      ].
    ^r


 TO SUSPEND LOGINS
    >> this goodie requires System Control privilege <<
    System suspendLogins


 TO SHUT DOWN THE GEMSTONE SYSTEM
    >> this goodie requires System Control privilege <<
    System shutDown


 TO STOP ALL SESSIONS BUT THIS ONE
    >> this goodie requires System Control privilege <<
    System stopUserSessions 


 TO RESUME LOGINS
    >> this goodie requires System Control privilege <<
    System resumeLogins


 TO SEE ALL AUTHORIZED GEMSTONE USERS
    | r lf |
    lf := Character lf.
    r := String new add: lf; yourself .
    AllUsers do: [:each |
      r addAll: each userId; add: lf ].
    ^r


 TO CHANGE A USER'S PRIVILEGES
    >> you must be the Data Curator to execute this goodie <<
    Replace PRIV_STRINGS with one or more of the following 
    string literals
        'SystemControl'
        'Statistics'         (Not supported in GSS64)
        'SessionAccess'
        'UserPassword'
        'DefaultSegment'
        'OtherPassword'
        'SegmentCreation'
        'SegmentProtection'
        'FileControl'
        'GarbageCollection'
        'CodeModification'   (New in GSS64)
    ============================================================ "

    (AllUsers userWithId: 'USER_ID' ) 
       privileges:  #( PRIV_STRINGS  ).



 TO ADD A NEW USER TO THE GEMSTONE (short form - no privileges or groups)
    >> you must be the Data Curator to execute this goodie <<

    AllUsers addNewUserWithId: #USER_ID password: 'PASSWORD'


 TO ADD A NEW USER TO THE GEMSTONE (long form)
    >> you must be the Data Curator to execute this goodie <<
    Replace PRIV_STRINGS with one or more of the following 
    string literals
        'SystemControl'     'SessionAccess'
        'UserPassword'      'OtherPassword'
        'FileControl'       'GarbageCollection'                     
        'CodeModification'
    ============================================================ "

    AllUsers addNewUserWithId: 'USER_ID' password: 'PASSWORD'
        defaultSegment: aSegment
        privileges: #( PRIV_STRINGS )
        inGroups: #[ GROUPNAME, GROUPNAME, ... ]



*****************************************************************
*              F U N   W I T H   U S E R G L O B A L S          *
*****************************************************************


 TO VIEW ALL THE ENTRIES IN UserGlobals (or other SymbolDictionary)
    | r lf |
    lf := Character lf.
    r := String new add: lf; yourself .
    UserGlobals keys do: [ :each |
      r addAll: each; add: lf ].
    ^r


 TO REMOVE AN ENTRY FROM UserGlobals (or other SymbolDictionary)
    UserGlobals removeKey: #KEY_TO_REMOVE



*****************************************************************
*            F U N   W I T H   U S E R P R O F I L E S          *
*****************************************************************


 TO FIND WHICH DICTIONARY OF YOUR SYMBOL LIST HOLDS A NAMED OBJECT
    | das |
    das := System myUserProfile dictionaryAndSymbolOf: OBJECTNAME.
    " das now holds Array #(dict, symbol) for the object.
      Now find the symbol of the dict: "
    das := System myUserProfile dictionaryAndSymbolOf: (das at: 1).
    das == nil ifTrue: [^'dictionary holding object is not named'].
    ^das at: 2


 TO LIST THE NAMES OF THE DICTIONARIES IN YOUR SYMBOL LIST
    | r lf userPro das |
    lf := Character lf.
    r := String new add: lf; yourself .
    userPro := System myUserProfile.
    userPro symbolList do: [ :aDict |
        das := userPro dictionaryAndSymbolOf: aDict.
        das == nil ifTrue: [
            r addAll: 'unnamed dictionary'; add: lf. ]
        ifFalse: [
            r addAll: (das at: 2); add: lf. ].
        ].
    ^r


 TO LIST YOUR SYMBOL LIST AND ITS CONTENTS
    | r lf userPro das key |
    lf := Character lf.
    r := String new add: lf; yourself .
    userPro := System myUserProfile.
    " for each dictionary in the symbol list, list name and contents "
    userPro symbolList do: [ :aDict |
        das := userPro dictionaryAndSymbolOf: aDict.
        das == nil ifTrue: [
            r addAll: 'unnamed dictionary'; add: lf. ]
        ifFalse: [
            " for each entry in the dictionary, list name and class "
            r addAll: (das at: 2); add: lf.
            aDict doValues: [ :anEntry |
                r addAll: '    '; addAll: (key := anEntry key);
                  addAll: ' '.
                (30 - key size) timesRepeat: [ r addAll: ' ' ].
                r addAll: (anEntry value class name); add: lf.
                ].
            ].
        ].
    ^r


 TO LIST YOUR SYMBOL LIST AND ALL CLASS OBJECTS IT CONTAINS
    | r lf userPro das key |
    lf := Character lf.
    r := String new add: lf; yourself .
    userPro := System myUserProfile.
    " for each dictionary in the symbol list, list name and contents "
    userPro symbolList do: [ :aDict |
        das := userPro dictionaryAndSymbolOf: aDict.
        das == nil ifTrue: [
            r addAll: 'unnamed dictionary'; add: lf. ]
        ifFalse: [
            " for each entry in the dictionary, list name and class "
            r addAll: (das at: 2); add: lf.
            aDict doValues: [ :anEntry |
                (anEntry value isKindOf: Behavior) ifTrue: [
                    r addAll: '    '; addAll: (key := anEntry key);
                      addAll: ' '.
                    (30 - key size) timesRepeat: [ r addAll: ' ' ].
                    r addAll: (anEntry value class name); add: lf.
                    ].
                ].
            ].
        ].
    ^r


 TO CHANGE YOUR USER NAME (userid)
    >> you must be the Data Curator to execute this goodie <<
    System myUserProfile userId: #newIdSymbol


 TO CHANGE YOUR SYSTEM PASSWORD
    >> this goodie requires User Password privilege  <<
    System myUserProfile oldPassword: 'OLD PASSWORD STRING'
                         newPassword: 'NEW PASSWORD STRING'


 TO VIEW THE GROUPS YOU BELONG TO 
    | r lf |
    lf := Character lf.
    r := String new add: lf; yourself .
    System myUserProfile groups do: [:each |
      r addAll: each; add: lf ].
    ^r

 TO REMOVE A USER FROM THE SYSTEM
    >> you must be the Data Curator to execute this goodie <<

    RUN THIS PART THE FIRST TIME >>ONLY<< - THEN COMMIT IT
    UserGlobals at: #OldUsers put: (IdentitySet new)

    RUN THIS TO REMOVE THE USER - THEN COMMIT IT
    OldUsers add: (AllUsers userWithId: 'USERID').
    AllUsers remove: (AllUsers userWithId: 'USERID')


!------------------------------------------------------------------
! INDEXING/QUERY GOODIES
!------------------------------------------------------------------
! This is unsupported code available to aid customer support and consultants.
!
! You must be logged in as SystemUser to file in this code.
!
! ObsoleteIDX - This code will become Obsolete in the next release of GSS64.
!
!------------------------------------------------------------------
! goodie #1.
! Method to explain the order of query evaluation and usage of indexes
!------------------------------------------------------------------
!
! This code outputs a description of the order of predicate evaluation and
! usage of indexes when querying NSCs.
! This code is provided as a "goodie", but is not part of the
! supported class library.
!
! The following example illustrates how to use this code:
!
! myBagOfEmployees explainQuery: { :emp |
!     (emp.address.zip == 97223) &
!     (emp.address.state = 'OR') &
!     (emp.age > 30) }
!
! example output of above:
!
!  The query predicates will be executed in the following order:
!  1.   address.state = OR (utilizing an index on 'address.state')
!  2.   address.zip == 97223
!  3.   age > 30
!
!------------------------------------------------------------------


category: 'ObsoleteIDX - Goodies'
method: QueryExecuter
_explainQuery: anArray

" anArray is a four element Array of Arrays describing a SelectBlock
in the manner needed to process a query on the receiver :
    1. Array of the bound variables from the predicate
    2. Array of the predicate's terms
    3. Array of the predicate's paths
    4. Array of Strings of the path names used in the predicate "

| ordering offset str predicateType index1 index2 searchOp ops vals cr |
ops := #( #< #> #= #== #<= #>= #~= #~~ #unary #dual).

" get a description of the operations to invoke on indexes "
self _buildIndexOperationsList: anArray.

self optimize.

" get the order to invoke the operations "
ordering := self _getOperationsOrder.

ordering isEmpty
  ifTrue: [
    ^ 'It has been determined that no elements can satisfy the query as posed.'
  ].

str := String new.
cr := Character lf.
str add: 'The query predicates will be executed in the following order:';
  add: cr.

1 to: ordering size do: [ :i |
  str add: i asString; add: '.   '.

  offset := ordering at: i.
  predicateType := self at: offset.
  searchOp := ops at: (self at: offset + 2) + 1.

  " if it is path-constant or constant-path "
  ( predicateType == 2 _or: [ predicateType == 3 ] )
    ifTrue: [
      index1 := self at: offset + 1.
      searchOp = #dual
        ifTrue: [
          vals := self at: offset + 3.
          str add: (vals at: 2) asString; add: ' ';
            add: (ops at: (self _inverseOperatorFor: (vals at: 1)) + 1);
            add: ' '; add: index1 pathComponentsString; add: ' ';
            add: (ops at: (vals at: 3) + 1); add: ' ';
            add: (vals at: 4) asString.
          index1 isPathEvaluator
            ifFalse: [
              str add: ' (utilizing an index on '; add: $';
                add: index1 pathComponentsString; add: $'; add: ')'
            ].
          str add: cr
        ]
        ifFalse: [
          searchOp = #unary
            ifTrue: [
              str add: index1 pathComponentsString.
              index1 isPathEvaluator
                ifTrue: [ str add: ' ' ]
                ifFalse: [ str add: ' (utilizing an index)'; add: cr ].
            ]
            ifFalse: [
              str add: index1 pathComponentsString; add: ' ';
                add: searchOp;  add: ' ';
                add: (self at: offset + 3) asString.
              index1 isPathEvaluator 
                ifFalse: [
                  str add: ' (utilizing an index on '; add: $';
                    add: index1 pathComponentsString; add: $'; add: ')'
                ].
              str add: cr
            ]
        ]
    ].

  " if it is constant-constant "
  predicateType == 1
    ifTrue: [
      searchOp = #unary
        ifTrue: [
          str add: (self at: offset + 1) asString; add: cr
        ]
        ifFalse: [
          str add: (self at: offset + 1) asString; add: ' ';
            add: searchOp;  add: ' ';
            add: (self at: offset + 3) asString; add: cr
        ]
    ].

  " if it is path-path "
  predicateType == 4
    ifTrue: [
      index1 := self at: offset + 1.
      index2 := self at: offset + 3.
      str add: index1 pathComponentsString; add: ' ';
        add: searchOp; add: ' ';
        add: index2 pathComponentsString; add: cr
    ].
].

^ str
%
category: 'ObsoleteIDX - Goodies'
method: UnorderedCollection
explainQuery: aBlock

" Answer a string that shows the order of predicate evaluation for the query. "

<primitive: 901>
| result |
(aBlock _class == SelectBlock)
    ifTrue:[
        result := (QueryExecuter on: self)
            _explainQuery: (aBlock queryBlock value: nil).
    ]
    ifFalse:[
      result := 'This query will be executed by iterating through all elements,
executing the code in the block for each element.'
    ].
System _disableProtectedMode.
^ result
%

!--------------------------------------------------------------------------
!
!------------------------------------------------------------------
! goodie #2.
! Methods to get internal indexing objects for locking purposes.
!------------------------------------------------------------------
!
! This code is provided as a "goodie", but is not part of the
! supported class library.
!
! Public methods
!
! anObject _getIndexObjectsToLock
!   returns a set of B-tree nodes and other application objects along
!   index paths
!
! anObject _getIndexObjectsToLockOnPath: pathString
!   returns a set of B-tree nodes and other application objects along
!   the given index path
!
! anNsc _getIndexObjectsToLock
!   returns a set of all B-tree nodes for each equality index
!
! anNsc _getIndexObjectsToLockOnPath: pathString
!   returns a set of all B-tree nodes for the given equality index
!
!------------------------------------------------------------------
!

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: Object
_getIndexObjectsToLock

"Return an set of internal indexing objects to lock to ensure that
modifications to the receiver will be able to be committed."

<primitive: 901>
| result depList |
depList := DependencyList for: self.
depList == nil
  ifTrue: [
    System _disableProtectedMode.
    ^ IdentitySet new
  ].

result := IdentitySet new.
" for each path term ... "
1 to: depList size by: 2 do: [ :i |
  (depList at: i) _putIndexObjectsToLockFor: self into: result.
].
System _disableProtectedMode.
^ result
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: Object
_getIndexObjectsToLockOnPath: pathString

"Return an set of internal indexing objects to lock to ensure that
modifications to the receiver will be able to be committed.  Only
indexes with the given path are considered."

<primitive: 901>
| result depList pathArray |
depList := DependencyList for: self.
depList == nil
  ifTrue: [
    System _disableProtectedMode.
    ^ IdentitySet new
  ].

pathArray := pathString asArrayOfPathTerms.
result := IdentitySet new.
" for each path term ... "
1 to: depList size by: 2 do: [ :i |
  (depList at: i) _putIndexObjectsToLockFor: self
    into: result
    path: pathArray
].
System _disableProtectedMode.
^ result
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: UnorderedCollection
_getIndexObjectsToLock

"Return an set of internal indexing objects to lock to ensure that
modifications to the receiver will be able to be committed.  This
returns a set of all B-tree nodes for each equality index."

<primitive: 901>
| result iList indexObj |
result := super _getIndexObjectsToLock.

iList := self _indexedPaths.
iList == nil
  ifTrue: [
    System _disableProtectedMode.
    ^ IdentitySet new
  ].

1 to: iList size by: 2 do: [ :j |
  indexObj := iList at: j.
  " lock all B-tree nodes of equality index "
  indexObj isRangeEqualityIndex
    ifTrue: [
      indexObj btreeRoot _preOrderDo: [ :node | result add: node ]
    ]
].
System _disableProtectedMode.
^ result
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: UnorderedCollection
_getIndexObjectsToLockOnPath: pathString

"Returns a set of internal indexing objects to lock to ensure that
 modifications to the receiver will be able to be committed.  This returns
 a set of all B-tree nodes for the equality index with the given path."

<primitive: 901>
| result indexObj |

self _indexedPaths == nil
  ifTrue: [
    System _disableProtectedMode.
    ^ IdentitySet new
  ].

indexObj := self _findRangeIndexWithPath: pathString asArrayOfPathTerms.
result := IdentitySet new.

" lock all B-tree nodes of equality index "
indexObj ~~ nil
  ifTrue: [
    indexObj btreeRoot _preOrderDo: [ :node | result add: node ]
  ].

System _disableProtectedMode.
^ result
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: PathTerm
_putIndexObjectsToLockFor: anObject into: set

"Places into a set any internal indexing objects to lock to ensure that
 modifications to the anObject can be committed."

| ivOffset nextObj |

( nil == anObject _or: [ self size == 0 ] )
  ifTrue: [ ^ set ].

(ivOffset := self _ivOffsetFor: anObject) == nil
  ifTrue: [
    anObject _errorInvalidOffset: name .
    self _uncontinuableError
  ].

" get the next object along the path "
nextObj := self _nextObjectFor: anObject atInstVar: ivOffset.

updateBtree ~~ nil
  ifTrue: [ " need to lock B-tree nodes "
    updateBtree btreeRoot _putNodesToLockForKey: nextObj value: anObject into: set
  ].

" no need to add index dictionary because it is RC
updateDict ~~ nil
  ifTrue: [ set add: updateDict ].
"

set add: anObject.

nil == nextObj
  ifTrue: [ ^ set ].

1 to: children size do: [ :i |
  " make recursive call to add mappings "
  (children at: i) _putIndexObjectsToLockFor: nextObj into: set
].
^ set
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: PathTerm
_putIndexObjectsToLockFor: anObject into: set path: pathArray

"Place into a set any internal indexing objects to lock to ensure that
modifications to the anObject can be committed."

| ivOffset nextObj |

(pathArray at: offset) = name
  ifFalse: [ ^ set ].

( nil == anObject _or: [ self size == 0 ] )
  ifTrue: [ ^ set ].

(ivOffset := self _ivOffsetFor: anObject) == nil
  ifTrue: [
    anObject _errorInvalidOffset: name .
    self _uncontinuableError
  ].

" get the next object along the path "
nextObj := self _nextObjectFor: anObject atInstVar: ivOffset.

updateBtree ~~ nil
  ifTrue: [ " need to lock B-tree nodes "
    updateBtree btreeRoot _putNodesToLockForKey: nextObj value: anObject into: set
  ].

" no need to add index dictionary because it is RC
updateDict ~~ nil
  ifTrue: [ set add: updateDict ].
"

set add: anObject.

nil == nextObj
  ifTrue: [ ^ set ].

1 to: children size do: [ :i |
  " make recursive call to add mappings "
  (children at: i) _putIndexObjectsToLockFor: nextObj into: set path: pathArray
].
^ set
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: SetValuedPathTerm
_putIndexObjectsToLockFor: anNsc into: set

"Place into a set any internal indexing objects to lock to ensure that
modifications to the anObject can be committed."

| setElement sz |

( nil == anNsc _or: [ self size == 0 ] )
    ifTrue: [ ^ set ].

anNsc class isNsc
    ifFalse: [  ^ self _errorPathObjectNotAnNsc: anNsc ].

set add: anNsc.

" no need to add index dictionary because it is RC
updateDict ~~ nil
  ifTrue: [ set add: updateDict ].
"

sz := children size.
1 to: anNsc size do: [ :i |
  setElement := anNsc _at: i.

  updateBtree ~~ nil
    ifTrue: [
      updateBtree btreeRoot _putNodesToLockForKey: setElement value: setElement into: set
    ].

  1 to: sz do: [ :j |
    (children at: j) _putIndexObjectsToLockFor: setElement into: set
  ]
].
^ set
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: SetValuedPathTerm
_putIndexObjectsToLockFor: anNsc into: set path: pathArray

"Place into a set any internal indexing objects to lock to ensure that
modifications to the anObject can be committed."

| setElement sz |

(pathArray at: offset) = name
  ifFalse: [ ^ set ].

( nil == anNsc _or: [ self size == 0 ] )
    ifTrue: [ ^ set ].

anNsc class isNsc
    ifFalse: [  ^ self _errorPathObjectNotAnNsc: anNsc ].

set add: anNsc.

" no need to add index dictionary because it is RC
updateDict ~~ nil
  ifTrue: [ set add: updateDict ].
"

sz := children size.
1 to: anNsc size do: [ :i |
  setElement := anNsc _at: i.

  updateBtree ~~ nil
    ifTrue: [
      updateBtree btreeRoot _putNodesToLockForKey: setElement value: setElement into: set
    ].

  1 to: sz do: [ :j |
    (children at: j) _putIndexObjectsToLockFor: setElement into: set path: pathArray
  ]
].
^ set
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: BtreeInteriorNode
_putNodesToLockForKey: aKey value: aValue into: array

"An entry for the given key is located in the receiver.  Place the receiver 
 in the Array and recurse through the appropriate children."

| index eSize maxIndex childNode |
array add: self.

" find first child node that contains aKey "
index := self _binarySearchCoveringKey: aKey value: aValue.
(super at: index) _putNodesToLockForKey: aKey value: aValue into: array.

eSize := self entrySize.
maxIndex := self _lastIndex.
index := index + eSize.

" now scan child nodes until none contain the key/value "
[ index < maxIndex ] whileTrue: [
  childNode := super at: index.
  " see if first entry of child is for the key/value "
  (childNode _compareKey: aKey value: aValue equalToEntryAt: 2)
    ifTrue: [ childNode _putNodesToLockForKey: aKey value: aValue into: array ]
    ifFalse: [ ^ self ].
 index := index + eSize
]
%

category: 'ObsoleteIDX - Indexing Support (Locking)'
method: BtreeLeafNode
_putNodesToLockForKey: aKey value: aValue into: array

"An entry for the given key/value is located in the receiver.
 Place the receiver in the Array."

array add: self
%

