]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-bdb/idl.c
Set lock detector to DEFAULT, not NORUN.
[openldap] / servers / slapd / back-bdb / idl.c
index 5d534a853385f3b6072cb9a38af167248a4fbde6..a4b010b5f923cd056be0433991739d6431cc8d6d 100644 (file)
@@ -230,32 +230,61 @@ bdb_idl_fetch_key(
 
 #ifdef BDB_IDL_MULTI
        {
+               DBC *cursor;
                ID buf[BDB_IDL_UM_SIZE];
                ID *i, *j;
                void *ptr;
                size_t len;
+               int rc2;
+               int flags = bdb->bi_db_opflags | DB_MULTIPLE;
                data.data = buf;
                data.ulen = BDB_IDL_UM_SIZEOF;
                data.flags = DB_DBT_USERMEM;
-               rc = db->get( db, tid, key, &data, bdb->bi_db_opflags |
-                       DB_MULTIPLE );
+
+               if ( tid )
+                       flags |= DB_RMW;
+
+               rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
+               if( rc != 0 ) {
+                       Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
+                               "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
+                       return rc;
+               }
+               rc = cursor->c_get( cursor, key, &data, flags | DB_SET );
                if (rc == 0) {
-                       DB_MULTIPLE_INIT( ptr, &data );
                        i = ids;
-                       while (ptr) {
-                               DB_MULTIPLE_NEXT(ptr, &data, j, len);
-                               if (j) {
-                                       ++i;
-                                       AC_MEMCPY( i, j, sizeof(ID) );
+                       while (rc == 0) {
+                               DB_MULTIPLE_INIT( ptr, &data );
+                               while (ptr) {
+                                       DB_MULTIPLE_NEXT(ptr, &data, j, len);
+                                       if (j) {
+                                               ++i;
+                                               AC_MEMCPY( i, j, sizeof(ID) );
+                                       }
                                }
+                               rc = cursor->c_get( cursor, key, &data, flags | DB_NEXT_DUP );
                        }
+                       if ( rc == DB_NOTFOUND ) rc = 0;
+                       ids[0] = i - ids;
+                       /* On disk, a range is denoted by 0 in the first element */
                        if (ids[1] == 0) {
+                               if (ids[0] != BDB_IDL_RANGE_SIZE) {
+                                       Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
+                                               "range size mismatch: expected %ld, got %ld\n",
+                                               BDB_IDL_RANGE_SIZE, ids[0], 0 );
+                                       cursor->c_close( cursor );
+                                       return -1;
+                               }
                                BDB_IDL_RANGE( ids, ids[2], ids[3] );
-                       } else {
-                               ids[0] = (i - ids);
                        }
                        data.size = BDB_IDL_SIZEOF(ids);
                }
+               rc2 = cursor->c_close( cursor );
+               if (rc2) {
+                       Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
+                               "close failed: %s (%d)\n", db_strerror(rc2), rc2, 0 );
+                       return rc2;
+               }
        }
 #else
        data.data = ids;
@@ -316,22 +345,114 @@ bdb_idl_insert_key(
 
        DBTzero( &data );
 #ifdef BDB_IDL_MULTI
-       /* FIXME: We really ought to count how many data items currently exist
-        * for this key, and cap off with a range when we hit the max. We need
-        * to use a 0 in the first slot to denote a range - since the data are
-        * sorted in ascending order, the only way to get a flag into the
-        * first slot is to use the smallest possible ID value. The fetch
-        * function above can turn it into a "memory-format" range. We also
-        * have to delete all of the existing data items when converting from
-        * a list to a range. And of course, if it's already a range, we need
-        * to read it in and process it instead of just doing the blind put
-        * that we do right now...
-        */
-       data.data = &id;
-       data.size = sizeof(id);
-       data.flags = DB_DBT_USERMEM;
+       {
+               ID buf[BDB_IDL_DB_MAX];
+               DBC *cursor;
+               ID lo, hi;
+               char *err;
+
+               data.size = sizeof( ID );
+               data.ulen = data.size;
+               data.flags = DB_DBT_USERMEM;
+
+               rc = bdb_idl_fetch_key( be, db, tid, key, buf );
+               if ( rc && rc != DB_NOTFOUND )
+                       return rc;
 
-       rc = db->put( db, tid, key, &data, DB_NODUPDATA );
+               /* If it never existed, or there's room in the current key,
+                * just store it.
+                */
+               if ( rc == DB_NOTFOUND || ( !BDB_IDL_IS_RANGE(buf) &&
+                       BDB_IDL_N(buf) < BDB_IDL_DB_MAX ) ) {
+                       data.data = &id;
+                       rc = db->put( db, tid, key, &data, DB_NODUPDATA );
+               } else if ( BDB_IDL_IS_RANGE(buf) ) {
+                       /* If it's a range and we're outside the boundaries,
+                        * rewrite the range boundaries.
+                        */
+                       if ( id < BDB_IDL_RANGE_FIRST(buf) ||
+                               id > BDB_IDL_RANGE_LAST(buf) ) {
+                               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;
+                               }
+                               if ( id < BDB_IDL_RANGE_FIRST(buf) ) {
+                                       data.data = buf+1;
+                               } else {
+                                       data.data = buf+2;
+                               }
+                               rc = cursor->c_get( cursor, key, &data, DB_GET_BOTH | DB_RMW );
+                               if ( rc != 0 ) {
+                                       err = "c_get";
+fail:                          Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
+                                               "%s failed: %s (%d)\n", err, db_strerror(rc), rc );
+                                       if ( cursor ) cursor->c_close( cursor );
+                                       return rc;
+                               }
+                               data.data = &id;
+                               /* We should have been able to just overwrite the old
+                                * value with the new, but apparently we have to delete
+                                * it first.
+                                */
+                               rc = cursor->c_del( cursor, 0 );
+                               if ( rc ) {
+                                       err = "c_del";
+                                       goto fail;
+                               }
+                               rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
+                               if ( rc ) {
+                                       err = "c_put";
+                                       goto fail;
+                               }
+                               rc = cursor->c_close( cursor );
+                               if ( rc ) {
+                                       cursor = NULL;
+                                       err = "c_close";
+                                       goto fail;
+                               }
+                       }
+               } else {                /* convert to a range */
+                       lo = BDB_IDL_FIRST(buf);
+                       hi = BDB_IDL_LAST(buf);
+
+                       if (id < lo)
+                               lo = id;
+                       else if (id > hi)
+                               hi = id;
+
+                       cursor = NULL;
+
+                       /* Delete all of the old IDL so we can replace with a range */
+                       rc = db->del( db, tid, key, 0 );
+                       if ( rc ) {
+                               err = "del";
+                               goto fail;
+                       }
+
+                       /* Write the range */
+                       data.data = &id;
+                       id = 0;
+                       rc = db->put( db, tid, key, &data, 0 );
+                       if ( rc ) {
+                               err = "put #1";
+                               goto fail;
+                       }
+                       id = lo;
+                       rc = db->put( db, tid, key, &data, 0 );
+                       if ( rc ) {
+                               err = "put #2";
+                               goto fail;
+                       }
+                       id = hi;
+                       rc = db->put( db, tid, key, &data, 0 );
+                       if ( rc ) {
+                               err = "put #3";
+                               goto fail;
+                       }
+               }
+       }
 #else
        data.data = ids;
        data.ulen = sizeof ids;