int hdb_dn2id_children( Operation *op, DB_TXN *txn, Entry *e ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; DB *db = bdb->bi_dn2id->bdi_db; DBT key, data; DBC *cursor; int rc; ID id; diskNode d; DBTzero(&key); key.size = sizeof(ID); key.data = &e->e_id; key.flags = DB_DBT_USERMEM; BDB_ID2DISK( e->e_id, &id ); /* IDL cache is in host byte order */ if ( bdb->bi_idl_cache_size ) { rc = bdb_idl_cache_get( bdb, db, &key, NULL ); if ( rc != LDAP_NO_SUCH_OBJECT ) { return rc; } } key.data = &id; DBTzero(&data); data.data = &d; data.ulen = sizeof(d); data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL; data.dlen = sizeof(d); rc = db->cursor( db, txn, &cursor, bdb->bi_db_opflags ); if ( rc ) return rc; rc = cursor->c_get( cursor, &key, &data, DB_SET ); if ( rc == 0 ) { db_recno_t dkids; rc = cursor->c_count( cursor, &dkids, 0 ); if ( rc == 0 ) { BEI(e)->bei_dkids = dkids; if ( dkids < 2 ) rc = DB_NOTFOUND; } } cursor->c_close( cursor ); return rc; }
int bdb_idl_insert_key( BackendDB *be, DB *db, DB_TXN *tid, DBT *key, ID id ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; int rc; DBT data; DBC *cursor; ID lo, hi, nlo, nhi, nid; char *err; { char buf[16]; Debug( LDAP_DEBUG_ARGS, "bdb_idl_insert_key: %lx %s\n", (long) id, bdb_show_key( key, buf ), 0 ); } assert( id != NOID ); DBTzero( &data ); data.size = sizeof( ID ); data.ulen = data.size; data.flags = DB_DBT_USERMEM; BDB_ID2DISK( id, &nid ); rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags ); if ( rc != 0 ) { Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: " "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 ); return rc; } data.data = &nlo; /* Fetch the first data item for this key, to see if it * exists and if it's a range. */ rc = cursor->c_get( cursor, key, &data, DB_SET ); err = "c_get"; if ( rc == 0 ) { if ( nlo != 0 ) { /* not a range, count the number of items */ db_recno_t count; rc = cursor->c_count( cursor, &count, 0 ); if ( rc != 0 ) { err = "c_count"; goto fail; } if ( count >= BDB_IDL_DB_MAX ) { /* No room, convert to a range */ DBT key2 = *key; db_recno_t i; key2.dlen = key2.ulen; key2.flags |= DB_DBT_PARTIAL; BDB_DISK2ID( &nlo, &lo ); data.data = &nhi; rc = cursor->c_get( cursor, &key2, &data, DB_NEXT_NODUP ); if ( rc != 0 && rc != DB_NOTFOUND ) { err = "c_get next_nodup"; goto fail; } if ( rc == DB_NOTFOUND ) { rc = cursor->c_get( cursor, key, &data, DB_LAST ); if ( rc != 0 ) { err = "c_get last"; goto fail; } } else { rc = cursor->c_get( cursor, key, &data, DB_PREV ); if ( rc != 0 ) { err = "c_get prev"; goto fail; } } BDB_DISK2ID( &nhi, &hi ); /* Update hi/lo if needed, then delete all the items * between lo and hi */ if ( id < lo ) { lo = id; nlo = nid; } else if ( id > hi ) { hi = id; nhi = nid; } data.data = &nid; /* Don't fetch anything, just position cursor */ data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL; data.dlen = data.ulen = 0; rc = cursor->c_get( cursor, key, &data, DB_SET ); if ( rc != 0 ) { err = "c_get 2"; goto fail; } rc = cursor->c_del( cursor, 0 ); if ( rc != 0 ) { err = "c_del range1"; goto fail; } /* Delete all the records */ for ( i=1; i<count; i++ ) { rc = cursor->c_get( cursor, &key2, &data, DB_NEXT_DUP ); if ( rc != 0 ) { err = "c_get next_dup"; goto fail; } rc = cursor->c_del( cursor, 0 ); if ( rc != 0 ) { err = "c_del range"; goto fail; } } /* Store the range marker */ data.size = data.ulen = sizeof(ID); data.flags = DB_DBT_USERMEM; nid = 0; rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST ); if ( rc != 0 ) { err = "c_put range"; goto fail; } nid = nlo; rc = cursor->c_put( cursor, key, &data, DB_KEYLAST ); if ( rc != 0 ) { err = "c_put lo"; goto fail; } nid = nhi; rc = cursor->c_put( cursor, key, &data, DB_KEYLAST ); if ( rc != 0 ) { err = "c_put hi"; goto fail; } } else { /* There's room, just store it */ goto put1; } } else { /* It's a range, see if we need to rewrite * the boundaries */ hi = id; data.data = &nlo; rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP ); if ( rc != 0 ) { err = "c_get lo"; goto fail; } BDB_DISK2ID( &nlo, &lo ); if ( id > lo ) { data.data = &nhi; rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP ); if ( rc != 0 ) { err = "c_get hi"; goto fail; } BDB_DISK2ID( &nhi, &hi ); } if ( id < lo || id > hi ) { /* Delete the current lo/hi */ rc = cursor->c_del( cursor, 0 ); if ( rc != 0 ) { err = "c_del"; goto fail; } data.data = &nid; rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST ); if ( rc != 0 ) { err = "c_put lo/hi"; goto fail; } } } } else if ( rc == DB_NOTFOUND ) { put1: data.data = &nid; rc = cursor->c_put( cursor, key, &data, DB_NODUPDATA ); /* Don't worry if it's already there */ if ( rc != 0 && rc != DB_KEYEXIST ) { err = "c_put id"; goto fail; } } else { /* initial c_get failed, nothing was done */ fail: Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: " "%s failed: %s (%d)\n", err, db_strerror(rc), rc ); cursor->c_close( cursor ); return rc; } /* If key was added (didn't already exist) and using IDL cache, * update key in IDL cache. */ if ( !rc && bdb->bi_idl_cache_max_size ) { bdb_idl_cache_add_id( bdb, db, key, id ); } rc = cursor->c_close( cursor ); if( rc != 0 ) { Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: " "c_close failed: %s (%d)\n", db_strerror(rc), rc, 0 ); } return rc; }
int bdb_tool_idl_add( BackendDB *be, DB *db, DB_TXN *txn, DBT *key, ID id ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; bdb_tool_idl_cache *ic, itmp; bdb_tool_idl_cache_entry *ice; int rc; if ( !bdb->bi_idl_cache_max_size ) return bdb_idl_insert_key( be, db, txn, key, id ); DBT2bv( key, &itmp.kstr ); ic = avl_find( (Avlnode *)db->app_private, &itmp, bdb_tool_idl_cmp ); /* No entry yet, create one */ if ( !ic ) { DBC *curs; DBT data; ID nid; int rc; ic = ch_malloc( sizeof( bdb_tool_idl_cache ) + itmp.kstr.bv_len ); ic->kstr.bv_len = itmp.kstr.bv_len; ic->kstr.bv_val = (char *)(ic+1); AC_MEMCPY( ic->kstr.bv_val, itmp.kstr.bv_val, ic->kstr.bv_len ); ic->head = ic->tail = NULL; ic->last = 0; ic->count = 0; avl_insert( (Avlnode **)&db->app_private, ic, bdb_tool_idl_cmp, avl_dup_error ); /* load existing key count here */ rc = db->cursor( db, NULL, &curs, 0 ); if ( rc ) return rc; data.ulen = sizeof( ID ); data.flags = DB_DBT_USERMEM; data.data = &nid; rc = curs->c_get( curs, key, &data, DB_SET ); if ( rc == 0 ) { if ( nid == 0 ) { ic->count = BDB_IDL_DB_SIZE+1; } else { db_recno_t count; curs->c_count( curs, &count, 0 ); ic->count = count; BDB_DISK2ID( &nid, &ic->first ); } } curs->c_close( curs ); } /* are we a range already? */ if ( ic->count > BDB_IDL_DB_SIZE ) { ic->last = id; return 0; /* Are we at the limit, and converting to a range? */ } else if ( ic->count == BDB_IDL_DB_SIZE ) { int n; for ( ice = ic->head, n=0; ice; ice = ice->next, n++ ) /* counting */ ; if ( n ) { ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock ); ic->tail->next = bdb_tool_idl_free_list; bdb_tool_idl_free_list = ic->head; bdb->bi_idl_cache_size -= n; ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock ); } ic->head = ic->tail = NULL; ic->last = id; ic->count++; return 0; } /* No free block, create that too */ if ( !ic->tail || ( ic->count & (IDBLOCK-1)) == 0) { ice = NULL; ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock ); if ( bdb->bi_idl_cache_size >= bdb->bi_idl_cache_max_size ) { ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock ); rc = bdb_tool_idl_flush_db( db, ic ); if ( rc ) return rc; avl_insert( (Avlnode **)&db->app_private, ic, bdb_tool_idl_cmp, avl_dup_error ); ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock ); } bdb->bi_idl_cache_size++; if ( bdb_tool_idl_free_list ) { ice = bdb_tool_idl_free_list; bdb_tool_idl_free_list = ice->next; } ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock ); if ( !ice ) { ice = ch_malloc( sizeof( bdb_tool_idl_cache_entry )); } memset( ice, 0, sizeof( *ice )); if ( !ic->head ) { ic->head = ice; } else { ic->tail->next = ice; } ic->tail = ice; if ( !ic->count ) ic->first = id; } ice = ic->tail; ice->ids[ ic->count & (IDBLOCK-1) ] = id; ic->count++; return 0; }
int bdb_idl_insert_key( BackendDB *be, DB *db, DB_TXN *tid, DBT *key, ID id ) { struct bdb_info *bdb = (struct bdb_info *) be->be_private; int rc; DBT data; DBC *cursor; ID lo, hi, tmp; char *err; { char buf[16]; #ifdef NEW_LOGGING LDAP_LOG( INDEX, ARGS, "bdb_idl_insert_key: %lx %s\n", (long) id, bdb_show_key( key, buf ), 0 ); #else Debug( LDAP_DEBUG_ARGS, "bdb_idl_insert_key: %lx %s\n", (long) id, bdb_show_key( key, buf ), 0 ); #endif } assert( id != NOID ); #ifdef SLAP_IDL_CACHE if ( bdb->bi_idl_cache_size ) { bdb_idl_cache_entry_t *matched_idl_entry, idl_tmp; DBT2bv( key, &idl_tmp.kstr ); idl_tmp.db = db; ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_mutex ); matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp, bdb_idl_entry_cmp ); if ( matched_idl_entry != NULL ) { if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry, bdb_idl_entry_cmp ) == NULL ) { #ifdef NEW_LOGGING LDAP_LOG( INDEX, ERR, "bdb_idl_fetch_key: AVL delete failed\n", 0, 0, 0 ); #else Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: " "AVL delete failed\n", 0, 0, 0 ); #endif } --bdb->bi_idl_cache_size; IDL_LRU_DELETE( bdb, matched_idl_entry ); free( matched_idl_entry->kstr.bv_val ); free( matched_idl_entry->idl ); free( matched_idl_entry ); } ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex ); } #endif DBTzero( &data ); data.size = sizeof( ID ); data.ulen = data.size; data.flags = DB_DBT_USERMEM; rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags ); if ( rc != 0 ) { #ifdef NEW_LOGGING LDAP_LOG( INDEX, ERR, "bdb_idl_insert_key: cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 ); #else Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: " "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 ); #endif return rc; } data.data = &tmp; /* Fetch the first data item for this key, to see if it * exists and if it's a range. */ rc = cursor->c_get( cursor, key, &data, DB_SET | DB_RMW ); err = "c_get"; if ( rc == 0 ) { if ( tmp != 0 ) { /* not a range, count the number of items */ db_recno_t count; rc = cursor->c_count( cursor, &count, 0 ); if ( rc != 0 ) { err = "c_count"; goto fail; } if ( count >= BDB_IDL_DB_MAX ) { /* No room, convert to a range */ DBT key2 = *key; key2.dlen = key2.ulen; key2.flags |= DB_DBT_PARTIAL; lo = tmp; data.data = &hi; rc = cursor->c_get( cursor, &key2, &data, DB_NEXT_NODUP ); if ( rc != 0 && rc != DB_NOTFOUND ) { err = "c_get next_nodup"; goto fail; } if ( rc == DB_NOTFOUND ) { rc = cursor->c_get( cursor, key, &data, DB_LAST ); if ( rc != 0 ) { err = "c_get last"; goto fail; } } else { rc = cursor->c_get( cursor, key, &data, DB_PREV ); if ( rc != 0 ) { err = "c_get prev"; goto fail; } } if ( id < lo ) lo = id; else if ( id > hi ) hi = id; rc = db->del( db, tid, key, 0 ); if ( rc != 0 ) { err = "del"; goto fail; } data.data = &id; id = 0; rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST ); if ( rc != 0 ) { err = "c_put 0"; goto fail; } id = lo; rc = cursor->c_put( cursor, key, &data, DB_KEYLAST ); if ( rc != 0 ) { err = "c_put lo"; goto fail; } id = hi; rc = cursor->c_put( cursor, key, &data, DB_KEYLAST ); if ( rc != 0 ) { err = "c_put hi"; goto fail; } } else { /* There's room, just store it */ goto put1; } } else { /* It's a range, see if we need to rewrite * the boundaries */ hi = id; data.data = &lo; rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP ); if ( rc != 0 ) { err = "c_get lo"; goto fail; } if ( id > lo ) { data.data = &hi; rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP ); if ( rc != 0 ) { err = "c_get hi"; goto fail; } } if ( id < lo || id > hi ) { /* Delete the current lo/hi */ rc = cursor->c_del( cursor, 0 ); if ( rc != 0 ) { err = "c_del"; goto fail; } data.data = &id; rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST ); if ( rc != 0 ) { err = "c_put lo/hi"; goto fail; } } } } else if ( rc == DB_NOTFOUND ) { put1: data.data = &id; rc = cursor->c_put( cursor, key, &data, DB_NODUPDATA ); /* Don't worry if it's already there */ if ( rc != 0 && rc != DB_KEYEXIST ) { err = "c_put id"; goto fail; } } else { /* initial c_get failed, nothing was done */ fail: #ifdef NEW_LOGGING LDAP_LOG( INDEX, ERR, "bdb_idl_insert_key: %s failed: %s (%d)\n", err, db_strerror(rc), rc ); #else Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: " "%s failed: %s (%d)\n", err, db_strerror(rc), rc ); #endif cursor->c_close( cursor ); return rc; } rc = cursor->c_close( cursor ); if( rc != 0 ) { #ifdef NEW_LOGGING LDAP_LOG( INDEX, ERR, "bdb_idl_insert_key: c_close failed: %s (%d)\n", db_strerror(rc), rc, 0 ); #else Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: " "c_close failed: %s (%d)\n", db_strerror(rc), rc, 0 ); #endif } return rc; }
int hdb_dn2id( Operation *op, struct berval *in, EntryInfo *ei, DB_TXN *txn, DB_LOCK *lock ) { struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; DB *db = bdb->bi_dn2id->bdi_db; DBT key, data; DBC *cursor; int rc = 0, nrlen; diskNode *d; char *ptr; unsigned char dlen[2]; ID idp, parentID; Debug( LDAP_DEBUG_TRACE, "=> hdb_dn2id(\"%s\")\n", in->bv_val, 0, 0 ); nrlen = dn_rdnlen( op->o_bd, in ); if (!nrlen) nrlen = in->bv_len; DBTzero(&key); key.size = sizeof(ID); key.data = &idp; key.ulen = sizeof(ID); key.flags = DB_DBT_USERMEM; parentID = ( ei->bei_parent != NULL ) ? ei->bei_parent->bei_id : 0; BDB_ID2DISK( parentID, &idp ); DBTzero(&data); data.size = sizeof(diskNode) + nrlen - sizeof(ID) - 1; data.ulen = data.size * 3; data.dlen = data.ulen; data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL; rc = db->cursor( db, txn, &cursor, bdb->bi_db_opflags ); if ( rc ) return rc; d = op->o_tmpalloc( data.size * 3, op->o_tmpmemctx ); d->nrdnlen[1] = nrlen & 0xff; d->nrdnlen[0] = (nrlen >> 8) | 0x80; dlen[0] = d->nrdnlen[0]; dlen[1] = d->nrdnlen[1]; ptr = lutil_strncopy( d->nrdn, in->bv_val, nrlen ); *ptr = '\0'; data.data = d; rc = bdb_dn2id_lock( bdb, in, 0, txn, lock ); if ( rc ) goto func_leave; rc = cursor->c_get( cursor, &key, &data, DB_GET_BOTH_RANGE ); if ( rc == 0 && (dlen[1] != d->nrdnlen[1] || dlen[0] != d->nrdnlen[0] || strncmp( d->nrdn, in->bv_val, nrlen ))) { rc = DB_NOTFOUND; } if ( rc == 0 ) { ptr = (char *) data.data + data.size - sizeof(ID); BDB_DISK2ID( ptr, &ei->bei_id ); ei->bei_rdn.bv_len = data.size - sizeof(diskNode) - nrlen; ptr = d->nrdn + nrlen + 1; ber_str2bv( ptr, ei->bei_rdn.bv_len, 1, &ei->bei_rdn ); if ( ei->bei_parent != NULL && !ei->bei_parent->bei_dkids ) { db_recno_t dkids; /* How many children does the parent have? */ /* FIXME: do we need to lock the parent * entryinfo? Seems safe... */ cursor->c_count( cursor, &dkids, 0 ); ei->bei_parent->bei_dkids = dkids; } } func_leave: cursor->c_close( cursor ); op->o_tmpfree( d, op->o_tmpmemctx ); if( rc != 0 ) { Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id: get failed: %s (%d)\n", db_strerror( rc ), rc, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id: got id=0x%lx\n", ei->bei_id, 0, 0 ); } return rc; }