+
+ bdb_cache_entryinfo_lock( cx->ei );
+
+ /* If number of kids in the cache differs from on-disk, load
+ * up all the kids from the database
+ */
+ if ( cx->ei->bei_ckids+1 != cx->ei->bei_dkids ) {
+ EntryInfo ei;
+ db_recno_t dkids = cx->ei->bei_dkids;
+ ei.bei_parent = cx->ei;
+
+ bdb_cache_entryinfo_unlock( cx->ei );
+
+ cx->rc = cx->db->cursor( cx->db, NULL, &cx->dbc,
+ cx->bdb->bi_db_opflags );
+ if ( cx->rc ) return cx->rc;
+
+ cx->data.data = &cx->dbuf;
+ cx->data.ulen = sizeof(ID);
+ cx->data.dlen = sizeof(ID);
+ cx->data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
+
+ /* The first item holds the parent ID. Ignore it. */
+ cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data, DB_SET );
+ if ( cx->rc ) {
+ cx->dbc->c_close( cx->dbc );
+ if ( cx->rc == DB_NOTFOUND ) goto saveit;
+ return cx->rc;
+ }
+
+ /* If the on-disk count is zero we've never checked it.
+ * Count it now.
+ */
+ if ( !dkids ) {
+ cx->dbc->c_count( cx->dbc, &dkids, 0 );
+ cx->ei->bei_dkids = dkids;
+ }
+
+ cx->data.data = cx->buf;
+ cx->data.ulen = BDB_IDL_UM_SIZE * sizeof(ID);
+ cx->data.flags = DB_DBT_USERMEM;
+
+ /* Fetch the rest of the IDs in a loop... */
+ while ( (cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data,
+ DB_MULTIPLE | DB_NEXT_DUP )) == 0 ) {
+ u_int8_t *j;
+ size_t len;
+ DB_MULTIPLE_INIT( cx->ptr, &cx->data );
+ while (cx->ptr) {
+ DB_MULTIPLE_NEXT( cx->ptr, &cx->data, j, len );
+ if (j) {
+ EntryInfo *ei2;
+ diskNode *d = (diskNode *)j;
+ short nrlen;
+
+ AC_MEMCPY( &ei.bei_id, &d->entryID, sizeof(ID) );
+ AC_MEMCPY( &nrlen, &d->nrdnlen, sizeof(d->nrdnlen) );
+ ei.bei_nrdn.bv_len = nrlen;
+ /* nrdn/rdn are set in-place.
+ * hdb_cache_load will copy them as needed
+ */
+ ei.bei_nrdn.bv_val = d->nrdn;
+ ei.bei_rdn.bv_len = len - sizeof(diskNode) - ei.bei_nrdn.bv_len;
+ ei.bei_rdn.bv_val = d->nrdn + ei.bei_nrdn.bv_len + 1;
+ bdb_idl_insert( cx->tmp, ei.bei_id );
+ hdb_cache_load( cx->bdb, &ei, &ei2 );
+ }
+ }
+ }
+ cx->rc = cx->dbc->c_close( cx->dbc );
+ } else {
+ /* The in-memory cache is in sync with the on-disk data.
+ * do we have any kids?
+ */
+ cx->rc = 0;
+ if ( cx->ei->bei_ckids > 0 ) {
+
+ /* Walk the kids tree; order is irrelevant since bdb_idl_insert
+ * will insert in sorted order.
+ */
+ avl_apply( cx->ei->bei_kids, apply_func, cx->tmp, -1, AVL_POSTORDER );
+ }
+ bdb_cache_entryinfo_unlock( cx->ei );
+ }
+
+saveit:
+#ifdef SLAP_IDL_CACHE
+ if ( cx->bdb->bi_idl_cache_max_size ) {
+ bdb_idl_cache_put( cx->bdb, cx->db, &cx->key, cx->tmp, cx->rc );
+ }
+#endif
+ ;
+gotit:
+ if ( !BDB_IDL_IS_ZERO( cx->tmp )) {
+ if ( cx->prefix == DN_SUBTREE_PREFIX ) {
+ if (cx->ei->bei_state & CACHE_ENTRY_NO_GRANDKIDS) {
+ bdb_idl_union( cx->ids, cx->tmp );
+ } else {
+ ID *save, idcurs;
+ EntryInfo *ei = cx->ei;
+ int nokids = 1;
+ save = cx->op->o_tmpalloc( BDB_IDL_SIZEOF( cx->tmp ),
+ cx->op->o_tmpmemctx );
+ BDB_IDL_CPY( save, cx->tmp );
+ bdb_idl_union( cx->ids, cx->tmp );
+
+ idcurs = 0;
+ for ( cx->id = bdb_idl_first( save, &idcurs );
+ cx->id != NOID;
+ cx->id = bdb_idl_next( save, &idcurs )) {
+ cx->ei = NULL;
+ hdb_dn2idl_internal( cx );
+ if ( !BDB_IDL_IS_ZERO( cx->tmp ))
+ nokids = 0;
+ }
+ cx->op->o_tmpfree( save, cx->op->o_tmpmemctx );
+ if ( nokids ) ei->bei_state |= CACHE_ENTRY_NO_GRANDKIDS;
+ }
+ /* Make sure caller knows it had kids! */
+ cx->tmp[0]=1;
+
+ cx->rc = 0;
+ } else {
+ BDB_IDL_CPY( cx->ids, cx->tmp );
+ }
+ }
+ return cx->rc;