#define IDL_CMP(x,y) ( x < y ? -1 : ( x > y ? 1 : 0 ) )
+#ifdef SLAP_IDL_CACHE
+#define IDL_LRU_DELETE( bdb, e ) do { \
+ if ( e->idl_lru_prev != NULL ) { \
+ e->idl_lru_prev->idl_lru_next = e->idl_lru_next; \
+ } else { \
+ bdb->bi_idl_lru_head = e->idl_lru_next; \
+ } \
+ if ( e->idl_lru_next != NULL ) { \
+ e->idl_lru_next->idl_lru_prev = e->idl_lru_prev; \
+ } else { \
+ bdb->bi_idl_lru_tail = e->idl_lru_prev; \
+ } \
+} while ( 0 )
+
+#define IDL_LRU_ADD( bdb, e ) do { \
+ e->idl_lru_next = bdb->bi_idl_lru_head; \
+ if ( e->idl_lru_next != NULL ) { \
+ e->idl_lru_next->idl_lru_prev = (e); \
+ } \
+ (bdb)->bi_idl_lru_head = (e); \
+ e->idl_lru_prev = NULL; \
+ if ( (bdb)->bi_idl_lru_tail == NULL ) { \
+ (bdb)->bi_idl_lru_tail = (e); \
+ } \
+} while ( 0 )
+
+static int
+bdb_idl_entry_cmp( const void *v_idl1, const void *v_idl2 )
+{
+ const bdb_idl_cache_entry_t *idl1 = v_idl1, *idl2 = v_idl2;
+ int rc;
+
+ if ((rc = idl1->db - idl2->db )) return rc;
+ if ((rc = idl1->kstr.bv_len - idl2->kstr.bv_len )) return rc;
+ return ( memcmp ( idl1->kstr.bv_val, idl2->kstr.bv_val , idl1->kstr.bv_len ) );
+}
+#endif
+
#if IDL_DEBUG > 0
static void idl_check( ID *ids )
{
return 0;
}
+#if 0 /* unused */
static int idl_delete( ID *ids, ID id )
{
unsigned x = bdb_idl_search( ids, id );
return 0;
}
+#endif /* unused */
static char *
bdb_show_key(
struct bdb_info *bdb = (struct bdb_info *) be->be_private;
int rc;
DBT data;
-#ifdef BDB_IDL_MULTI
- /* buf must be large enough to grab the entire IDL in one
- * get(), otherwise BDB 4 will leak resources on subsequent
- * get's. We can safely call get() twice - once for the data,
- * and once to get the DB_NOTFOUND result meaning there's
- * no more data. See ITS#2040 for details.
- */
- ID buf[BDB_IDL_DB_SIZE*5];
DBC *cursor;
ID *i;
void *ptr;
size_t len;
int rc2;
int flags = bdb->bi_db_opflags | DB_MULTIPLE;
+#ifdef SLAP_IDL_CACHE
+ bdb_idl_cache_entry_t idl_tmp;
#endif
+ /* buf must be large enough to grab the entire IDL in one
+ * get(), otherwise BDB 4 will leak resources on subsequent
+ * get's. We can safely call get() twice - once for the data,
+ * and once to get the DB_NOTFOUND result meaning there's
+ * no more data. See ITS#2040 for details. This bug is fixed
+ * in BDB 4.1 so a smaller buffer will work if stack space is
+ * too limited.
+ */
+ ID buf[BDB_IDL_DB_SIZE*5];
+
{
char buf[16];
#ifdef NEW_LOGGING
}
assert( ids != NULL );
+#ifdef SLAP_IDL_CACHE
+ if ( bdb->bi_idl_cache_max_size ) {
+ bdb_idl_cache_entry_t *matched_idl_entry;
+ 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 ) {
+ BDB_IDL_CPY( ids, matched_idl_entry->idl );
+ IDL_LRU_DELETE( bdb, matched_idl_entry );
+ IDL_LRU_ADD( bdb, matched_idl_entry );
+ ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
+ return LDAP_SUCCESS;
+ }
+ ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
+ }
+#endif
+
DBTzero( &data );
-#ifdef BDB_IDL_MULTI
data.data = buf;
data.ulen = sizeof(buf);
data.flags = DB_DBT_USERMEM;
#endif
return rc2;
}
-#else /* BDB_IDL_MULTI */
- data.data = ids;
- data.ulen = BDB_IDL_UM_SIZEOF;
- data.flags = DB_DBT_USERMEM;
- /* fetch it */
- rc = db->get( db, tid, key, &data, bdb->bi_db_opflags );
-#endif /* BDB_IDL_MULTI */
-
if( rc == DB_NOTFOUND ) {
return rc;
return -1;
}
+#ifdef SLAP_IDL_CACHE
+ if ( bdb->bi_idl_cache_max_size ) {
+ bdb_idl_cache_entry_t *ee;
+ ee = (bdb_idl_cache_entry_t *) malloc( sizeof( bdb_idl_cache_entry_t ) );
+ ee->db = db;
+ ee->idl = (ID*) malloc ( BDB_IDL_SIZEOF ( ids ) );
+ ee->idl_lru_prev = NULL;
+ ee->idl_lru_next = NULL;
+ BDB_IDL_CPY( ee->idl, ids );
+ ber_dupbv( &ee->kstr, &idl_tmp.kstr );
+ ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_mutex );
+ if ( avl_insert( &bdb->bi_idl_tree, (caddr_t) ee,
+ bdb_idl_entry_cmp, avl_dup_error )) {
+ free( ee->kstr.bv_val );
+ free( ee->idl );
+ free( ee );
+ } else {
+ IDL_LRU_ADD( bdb, ee );
+ if ( ++bdb->bi_idl_cache_size > bdb->bi_idl_cache_max_size ) {
+ int i = 0;
+ while ( bdb->bi_idl_lru_tail != NULL && i < 10 ) {
+ ee = bdb->bi_idl_lru_tail;
+ avl_delete( &bdb->bi_idl_tree, (caddr_t) ee,
+ bdb_idl_entry_cmp );
+ IDL_LRU_DELETE( bdb, ee );
+ i++;
+ --bdb->bi_idl_cache_size;
+ free( ee->kstr.bv_val );
+ free( ee->idl );
+ free( ee );
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
+ }
+#endif
+
return rc;
}
struct bdb_info *bdb = (struct bdb_info *) be->be_private;
int rc;
DBT data;
-#ifdef BDB_IDL_MULTI
DBC *cursor;
ID lo, hi, tmp;
char *err;
-#else
- ID ids[BDB_IDL_DB_SIZE];
-#endif
{
char buf[16];
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 ) {
+ avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry,
+ bdb_idl_entry_cmp );
+ --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 );
-#ifdef BDB_IDL_MULTI
data.size = sizeof( ID );
data.ulen = data.size;
data.flags = DB_DBT_USERMEM;
return rc;
}
rc = cursor->c_close( cursor );
-#else /* !BDB_IDL_MULTI */
- data.data = ids;
- data.ulen = sizeof ids;
- data.flags = DB_DBT_USERMEM;
-
- /* fetch the key for read/modify/write */
- rc = db->get( db, tid, key, &data, DB_RMW | bdb->bi_db_opflags );
-
- if( rc == DB_NOTFOUND ) {
- ids[0] = 1;
- ids[1] = id;
- data.size = 2 * sizeof( ID );
-
- } else if ( rc != 0 ) {
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR, "bdb_idl_insert_key: get failed: %s (%d)\n",
- db_strerror(rc), rc, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
- "get failed: %s (%d)\n",
- db_strerror(rc), rc, 0 );
-#endif
- return rc;
-
- } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
- /* size not multiple of ID size */
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR,
- "bdb_idl_insert_key: odd size: expected %ld multiple, got %ld\n",
- (long) sizeof( ID ), (long) data.size, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
- "odd size: expected %ld multiple, got %ld\n",
- (long) sizeof( ID ), (long) data.size, 0 );
-#endif
- return -1;
-
- } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
- /* size mismatch */
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR,
- "bdb_idl_insert_key: odd size: expected %ld multiple, got %ld\n",
- (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
- "get size mismatch: expected %ld, got %ld\n",
- (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
-#endif
- return -1;
-
- } else if ( BDB_IDL_IS_RANGE(ids) ) {
- if( id < ids[1] ) {
- ids[1] = id;
- } else if ( ids[2] > id ) {
- ids[2] = id;
- } else {
- return 0;
- }
-
- } else {
- rc = bdb_idl_insert( ids, id );
-
- if( rc == -1 ) {
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, DETAIL1, "bdb_idl_insert_key: dup\n", 0, 0, 0 );
-#else
- Debug( LDAP_DEBUG_TRACE, "=> bdb_idl_insert_key: dup\n",
- 0, 0, 0 );
-#endif
- return 0;
- }
- if( rc != 0 ) {
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR,
- "bdb_idl_insert_key: insert failed: (%d)\n", rc, 0, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
- "bdb_idl_insert failed (%d)\n",
- rc, 0, 0 );
-#endif
-
- return rc;
- }
-
- data.size = BDB_IDL_SIZEOF( ids );
- }
-
- /* store the key */
- rc = db->put( db, tid, key, &data, 0 );
-#endif
- if( rc != 0 && rc != DB_KEYEXIST ) {
+ if( rc != 0 ) {
#ifdef NEW_LOGGING
LDAP_LOG( INDEX, ERR,
- "bdb_idl_insert_key: put failed: %s (%d)\n",
+ "bdb_idl_insert_key: c_close failed: %s (%d)\n",
db_strerror(rc), rc, 0 );
#else
Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
- "put failed: %s (%d)\n",
+ "c_close failed: %s (%d)\n",
db_strerror(rc), rc, 0 );
#endif
}
struct bdb_info *bdb = (struct bdb_info *) be->be_private;
int rc;
DBT data;
-#ifdef BDB_IDL_MULTI
DBC *cursor;
ID lo, hi, tmp;
char *err;
-#else
- ID ids[BDB_IDL_DB_SIZE];
-#endif
{
char buf[16];
}
assert( id != NOID );
- DBTzero( &data );
+#ifdef SLAP_IDL_CACHE
+ if ( bdb->bi_idl_cache_max_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 ) {
+ avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry,
+ bdb_idl_entry_cmp );
+ --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
-#ifdef BDB_IDL_MULTI
+ DBTzero( &data );
data.data = &tmp;
data.size = sizeof( id );
data.ulen = data.size;
return rc;
}
rc = cursor->c_close( cursor );
-
-#else /* BDB_IDL_MULTI */
-
- data.data = ids;
- data.ulen = sizeof( ids );
- data.flags = DB_DBT_USERMEM;
-
- /* fetch the key for read/modify/write */
- rc = db->get( db, tid, key, &data, DB_RMW | bdb->bi_db_opflags );
-
- if ( rc != 0 ) {
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR, "bdb_idl_delete_key: get failed: %s (%d)\n",
- db_strerror(rc), rc, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
- "get failed: %s (%d)\n",
- db_strerror(rc), rc, 0 );
-#endif
- return rc;
-
- } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
- /* size not multiple of ID size */
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR,
- "bdb_idl_delete_key: odd size: expected: %ld multiple, got %ld\n",
- (long) sizeof( ID ), (long) data.size, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
- "odd size: expected %ld multiple, got %ld\n",
- (long) sizeof( ID ), (long) data.size, 0 );
-#endif
- return -1;
-
- } else if ( BDB_IDL_IS_RANGE(ids) ) {
- return 0;
-
- } else if ( data.size != (1 + ids[0]) * sizeof( ID ) ) {
- /* size mismatch */
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR,
- "bdb_idl_delete_key: get size mismatch: expected: %ld, got %ld\n",
- (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
- "get size mismatch: expected %ld, got %ld\n",
- (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
-#endif
- return -1;
-
- } else {
- rc = idl_delete( ids, id );
-
- if( rc != 0 ) {
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR,
- "bdb_idl_delete_key: delete failed: (%d)\n", rc, 0, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
- "idl_delete failed (%d)\n",
- rc, 0, 0 );
-#endif
- return rc;
- }
-
- if( ids[0] == 0 ) {
- /* delete the key */
- rc = db->del( db, tid, key, 0 );
- if( rc != 0 ) {
-#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR,
- "bdb_idl_delete_key: delete failed: %s (%d)\n",
- db_strerror(rc), rc, 0 );
-#else
- Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
- "delete failed: %s (%d)\n",
- db_strerror(rc), rc, 0 );
-#endif
- }
- return rc;
- }
-
- data.size = (ids[0]+1) * sizeof( ID );
- }
-
- /* store the key */
- rc = db->put( db, tid, key, &data, 0 );
-
-#endif /* BDB_IDL_MULTI */
-
if( rc != 0 ) {
#ifdef NEW_LOGGING
- LDAP_LOG( INDEX, ERR, "bdb_idl_delete_key: put failed: %s (%d)\n",
+ LDAP_LOG( INDEX, ERR, "bdb_idl_delete_key: c_close failed: %s (%d)\n",
db_strerror(rc), rc, 0 );
#else
Debug( LDAP_DEBUG_ANY,
- "=> bdb_idl_delete_key: put failed: %s (%d)\n",
+ "=> bdb_idl_delete_key: c_close failed: %s (%d)\n",
db_strerror(rc), rc, 0 );
#endif
}
return 0;
}
- if ( BDB_IDL_IS_RANGE( a ) && BDB_IDL_IS_RANGE(b) ) {
- a[1] = idmin;
- a[2] = idmax;
- return 0;
- }
-
if ( BDB_IDL_IS_RANGE( a ) ) {
- ID *tmp = a;
- a = b;
- b = tmp;
- swap = 1;
+ if ( BDB_IDL_IS_RANGE(b) ) {
+ /* If both are ranges, just shrink the boundaries */
+ a[1] = idmin;
+ a[2] = idmax;
+ return 0;
+ } else {
+ /* Else swap so that b is the range, a is a list */
+ ID *tmp = a;
+ a = b;
+ b = tmp;
+ swap = 1;
+ }
}
- if ( BDB_IDL_IS_RANGE( b ) && BDB_IDL_FIRST( b ) <= idmin &&
- BDB_IDL_LAST( b ) >= idmax) {
+ /* If a range completely covers the list, the result is
+ * just the list. If idmin to idmax is contiguous, just
+ * turn it into a range.
+ */
+ if ( BDB_IDL_IS_RANGE( b )
+ && BDB_IDL_FIRST( b ) <= BDB_IDL_FIRST( a )
+ && BDB_IDL_LAST( b ) >= BDB_IDL_LAST( a ) ) {
if (idmax - idmin + 1 == a[0])
{
a[0] = NOID;
goto done;
}
+ /* Fine, do the intersection one element at a time.
+ * First advance to idmin in both IDLs.
+ */
+ cursora = cursorb = idmin;
ida = bdb_idl_first( a, &cursora );
idb = bdb_idl_first( b, &cursorb );
cursorc = 0;
- while( ida < idmin )
- ida = bdb_idl_next( a, &cursora );
- while( idb < idmin )
- idb = bdb_idl_next( b, &cursorb );
-
while( ida <= idmax || idb <= idmax ) {
if( ida == idb ) {
a[++cursorc] = ida;
return NOID;
}
+