]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/overlays/pcache.c
ITS#6831 additional filter cmp fixes
[openldap] / servers / slapd / overlays / pcache.c
index 716cd74cf4ef256426d069bc614f4b6751a5dd5e..4fc84cdaf5db5cce4219be6d3920993a09602b56 100644 (file)
@@ -1,7 +1,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2003-2009 The OpenLDAP Foundation.
+ * Copyright 2003-2011 The OpenLDAP Foundation.
  * Portions Copyright 2003 IBM Corporation.
  * Portions Copyright 2003-2009 Symas Corporation.
  * All rights reserved.
@@ -84,6 +84,7 @@ typedef struct cached_query_s {
        time_t                                          expiry_time;    /* time till the query is considered invalid */
        time_t                                          refresh_time;   /* time till the query is refreshed */
        time_t                                          bindref_time;   /* time till the bind is refreshed */
+       int                                             bind_refcnt;    /* number of bind operation referencing this query */
        unsigned long                   answerable_cnt; /* how many times it was answerable */
        int                                             refcnt; /* references since last refresh */
        ldap_pvt_thread_mutex_t         answerable_cnt_mutex;
@@ -141,8 +142,11 @@ typedef struct query_template_s {
        struct berval   querystr;       /* Filter string corresponding to the QT */
        struct berval   bindbase;       /* base DN for Bind request */
        struct berval   bindfilterstr;  /* Filter string for Bind request */
+       struct berval   bindftemp;      /* bind filter template */
        Filter          *bindfilter;
+       AttributeDescription **bindfattrs;      /* attrs to substitute in ftemp */
 
+       int                     bindnattrs;             /* number of bindfattrs */
        int                     bindscope;
        int             attr_set_index; /* determines the projected attributes */
        int             no_of_queries;  /* Total number of queries in the template */
@@ -248,6 +252,7 @@ static int privDB_cid;
 static AttributeDescription    *ad_queryId, *ad_cachedQueryURL;
 
 #ifdef PCACHE_MONITOR
+static AttributeDescription    *ad_numQueries, *ad_numEntries;
 static ObjectClass             *oc_olmPCache;
 #endif /* PCACHE_MONITOR */
 
@@ -282,6 +287,24 @@ static struct {
                "NO-USER-MODIFICATION "
                "USAGE directoryOperation )",
                &ad_cachedQueryURL },
+#ifdef PCACHE_MONITOR
+       { "( PCacheAttributes:3 "
+               "NAME 'pcacheNumQueries' "
+               "DESC 'Number of cached queries' "
+               "EQUALITY integerMatch "
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+               "NO-USER-MODIFICATION "
+               "USAGE directoryOperation )",
+               &ad_numQueries },
+       { "( PCacheAttributes:4 "
+               "NAME 'pcacheNumEntries' "
+               "DESC 'Number of cached entries' "
+               "EQUALITY integerMatch "
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+               "NO-USER-MODIFICATION "
+               "USAGE directoryOperation )",
+               &ad_numEntries },
+#endif /* PCACHE_MONITOR */
 
        { NULL }
 };
@@ -296,8 +319,10 @@ static struct {
                "NAME ( 'olmPCache' ) "
                "SUP top AUXILIARY "
                "MAY ( "
-                       "cachedQueryURL"
-                       ") )",
+                       "pcacheQueryURL "
+                       "$ pcacheNumQueries "
+                       "$ pcacheNumEntries "
+                       " ) )",
                &oc_olmPCache },
 #endif /* PCACHE_MONITOR */
 
@@ -322,7 +347,6 @@ add_query(
 static int
 remove_query_data(
        Operation       *op,
-       SlapReply       *rs,
        struct berval   *query_uuid );
 
 /*
@@ -408,6 +432,70 @@ query2url( Operation *op, CachedQuery *q, struct berval *urlbv, int dolock )
        return 0;
 }
 
+/* Find and record the empty filter clauses */
+
+static int
+ftemp_attrs( struct berval *ftemp, struct berval *template,
+       AttributeDescription ***ret, const char **text )
+{
+       int i;
+       int attr_cnt=0;
+       struct berval bv;
+       char *p1, *p2, *t1;
+       AttributeDescription *ad;
+       AttributeDescription **descs = NULL;
+       char *temp2;
+
+       temp2 = ch_malloc( ftemp->bv_len + 1 );
+       p1 = ftemp->bv_val;
+       t1 = temp2;
+
+       *ret = NULL;
+
+       for (;;) {
+               while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' )
+                       *t1++ = *p1++;
+
+               p2 = strchr( p1, '=' );
+               if ( !p2 )
+                       break;
+               i = p2 - p1;
+               AC_MEMCPY( t1, p1, i );
+               t1 += i;
+               *t1++ = '=';
+
+               if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
+               bv.bv_val = p1;
+               bv.bv_len = p2 - p1;
+               ad = NULL;
+               i = slap_bv2ad( &bv, &ad, text );
+               if ( i ) {
+                       ch_free( descs );
+                       return -1;
+               }
+               if ( *p2 == '<' || *p2 == '>' ) p2++;
+               if ( p2[1] != ')' ) {
+                       p2++;
+                       while ( *p2 != ')' ) p2++;
+                       p1 = p2;
+                       continue;
+               }
+
+               descs = (AttributeDescription **)ch_realloc(descs,
+                               (attr_cnt + 2)*sizeof(AttributeDescription *));
+
+               descs[attr_cnt++] = ad;
+
+               p1 = p2+1;
+       }
+       *t1 = '\0';
+       descs[attr_cnt] = NULL;
+       *ret = descs;
+       template->bv_val = temp2;
+       template->bv_len = t1 - temp2;
+       return attr_cnt;
+}
+
 static int
 template_attrs( char *template, struct attr_set *set, AttributeName **ret,
        const char **text )
@@ -427,7 +515,7 @@ template_attrs( char *template, struct attr_set *set, AttributeName **ret,
 
        *ret = NULL;
 
-       attrs = ch_malloc(( set->count+1 )*sizeof(AttributeName));
+       attrs = ch_calloc( set->count + 1, sizeof(AttributeName) );
        for ( i=0; i < set->count; i++ )
                attrs[i] = set->attrs[i];
        attr_cnt = i;
@@ -435,7 +523,7 @@ template_attrs( char *template, struct attr_set *set, AttributeName **ret,
        allop = an_find( attrs, slap_bv_all_operational_attrs );
 
        for (;;) {
-               while ( *p1 == '(' || *p1 == '&' || *p1 == '|' ) p1++;
+               while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' ) p1++;
                p2 = strchr( p1, '=' );
                if ( !p2 )
                        break;
@@ -511,7 +599,7 @@ url2query(
        LDAPURLDesc     *lud = NULL;
        struct berval   base,
                        tempstr = BER_BVNULL,
-                       uuid;
+                       uuid = BER_BVNULL;
        int             attrset;
        time_t          expiry_time;
        time_t          refresh_time;
@@ -587,9 +675,11 @@ url2query(
                        }
 
                        ber_str2bv( &lud->lud_exts[ i ][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID );
-                       rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL );
-                       if ( rc != LDAP_SUCCESS ) {
-                               goto error;
+                       if ( !BER_BVISEMPTY( &tmpUUID ) ) {
+                               rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL );
+                               if ( rc != LDAP_SUCCESS ) {
+                                       goto error;
+                               }
                        }
                        got |= GOT_UUID;
 
@@ -664,11 +754,10 @@ url2query(
        /* ignore expired queries */
        if ( expiry_time <= slap_get_time()) {
                Operation       op2 = *op;
-               SlapReply       rs2 = { 0 };
 
                memset( &op2.oq_search, 0, sizeof( op2.oq_search ) );
 
-               (void)remove_query_data( &op2, &rs2, &uuid );
+               (void)remove_query_data( &op2, &uuid );
 
                rc = 0;
 
@@ -680,6 +769,10 @@ url2query(
                }
                query.scope = lud->lud_scope;
                query.filter = str2filter( lud->lud_filter );
+               if ( query.filter == NULL ) {
+                       rc = -1;
+                       goto error;
+               }
 
                tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 );
                tempstr.bv_len = 0;
@@ -735,6 +828,7 @@ static int
 merge_entry(
        Operation               *op,
        Entry                   *e,
+       int                     dup,
        struct berval*          query_uuid )
 {
        int             rc;
@@ -748,6 +842,8 @@ merge_entry(
 
        slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
 
+       if ( dup )
+               e = entry_dup( e );
        attr = e->e_attrs;
        e->e_attrs = NULL;
 
@@ -770,10 +866,12 @@ merge_entry(
 
        if ( rc != LDAP_SUCCESS ) {
                if ( rc == LDAP_ALREADY_EXISTS ) {
+                       rs_reinit( &sreply, REP_RESULT );
                        slap_entry2mods( e, &modlist, &text, textbuf, textlen );
                        modlist->sml_op = LDAP_MOD_ADD;
                        op->o_tag = LDAP_REQ_MODIFY;
                        op->orm_modlist = modlist;
+                       op->o_managedsait = SLAP_CONTROL_CRITICAL;
                        op->o_bd->be_modify( op, &sreply );
                        slap_mods_free( modlist, 1 );
                } else if ( rc == LDAP_REFERRAL ||
@@ -788,7 +886,7 @@ merge_entry(
                }
        } else {
                if ( op->ora_e == e )
-                       be_entry_release_w( op, e );
+                       entry_free( e );
                rc = 1;
        }
 
@@ -824,38 +922,49 @@ static int pcache_filter_cmp( Filter *f1, Filter *f2 )
        int rc, weight1, weight2;
 
        switch( f1->f_choice ) {
-       case LDAP_FILTER_PRESENT:
+       case LDAP_FILTER_AND:
+       case LDAP_FILTER_OR:
                weight1 = 0;
                break;
+       case LDAP_FILTER_PRESENT:
+               weight1 = 1;
+               break;
        case LDAP_FILTER_EQUALITY:
        case LDAP_FILTER_GE:
        case LDAP_FILTER_LE:
-               weight1 = 1;
+               weight1 = 2;
                break;
        default:
-               weight1 = 2;
+               weight1 = 3;
        }
        switch( f2->f_choice ) {
-       case LDAP_FILTER_PRESENT:
+       case LDAP_FILTER_AND:
+       case LDAP_FILTER_OR:
                weight2 = 0;
                break;
+       case LDAP_FILTER_PRESENT:
+               weight2 = 1;
+               break;
        case LDAP_FILTER_EQUALITY:
        case LDAP_FILTER_GE:
        case LDAP_FILTER_LE:
-               weight2 = 1;
+               weight2 = 2;
                break;
        default:
-               weight2 = 2;
+               weight2 = 3;
        }
        rc = weight1 - weight2;
        if ( !rc ) {
                switch( weight1 ) {
                case 0:
+                       rc = pcache_filter_cmp( f1->f_and, f2->f_and );
                        break;
                case 1:
-                       rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value );
                        break;
                case 2:
+                       rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value );
+                       break;
+               case 3:
                        if ( f1->f_choice == LDAP_FILTER_SUBSTRINGS ) {
                                rc = 0;
                                if ( !BER_BVISNULL( &f1->f_sub_initial )) {
@@ -896,7 +1005,7 @@ static int pcache_filter_cmp( Filter *f1, Filter *f2 )
                        }
                        break;
                }
-               if ( !rc ) {
+               while ( !rc ) {
                        f1 = f1->f_next;
                        f2 = f2->f_next;
                        if ( f1 || f2 ) {
@@ -905,12 +1014,10 @@ static int pcache_filter_cmp( Filter *f1, Filter *f2 )
                                else if ( !f2 )
                                        rc = 1;
                                else {
-                                       while ( f1->f_choice == LDAP_FILTER_AND || f1->f_choice == LDAP_FILTER_OR )
-                                               f1 = f1->f_and;
-                                       while ( f2->f_choice == LDAP_FILTER_AND || f2->f_choice == LDAP_FILTER_OR )
-                                               f2 = f2->f_and;
                                        rc = pcache_filter_cmp( f1, f2 );
                                }
+                       } else {
+                               break;
                        }
                }
        }
@@ -921,7 +1028,7 @@ static int pcache_filter_cmp( Filter *f1, Filter *f2 )
 static int pcache_query_cmp( const void *v1, const void *v2 )
 {
        const CachedQuery *q1 = v1, *q2 =v2;
-       return pcache_filter_cmp( q1->first, q2->first );
+       return pcache_filter_cmp( q1->filter, q2->filter );
 }
 
 /* add query on top of LRU list */
@@ -1153,6 +1260,11 @@ filter_first( Filter *f )
        return f;
 }
 
+typedef struct fstack {
+       struct fstack *fs_next;
+       Filter *fs_fs;
+       Filter *fs_fi;
+} fstack;
 
 static CachedQuery *
 find_filter( Operation *op, Avlnode *root, Filter *inputf, Filter *first )
@@ -1164,6 +1276,7 @@ find_filter( Operation *op, Avlnode *root, Filter *inputf, Filter *first )
        int ret, rc, dir;
        Avlnode *ptr;
        CachedQuery cq, *qc;
+       fstack *stack = NULL, *fsp;
 
        cq.filter = inputf;
        cq.first = first;
@@ -1241,6 +1354,12 @@ nextpass:                        eqpass = 1;
                        case LDAP_FILTER_AND:
                                fs = fs->f_and;
                                fi = fi->f_and;
+                               /* save our stack position */
+                               fsp = op->o_tmpalloc(sizeof(fstack), op->o_tmpmemctx);
+                               fsp->fs_next = stack;
+                               fsp->fs_fs = fs->f_next;
+                               fsp->fs_fi = fi->f_next;
+                               stack = fsp;
                                res=1;
                                break;
                        case LDAP_FILTER_SUBSTRINGS:
@@ -1288,6 +1407,13 @@ nextpass:                        eqpass = 1;
                        default:
                                break;
                        }
+                       if (!fs && !fi && stack) {
+                               fsp = stack;
+                               stack = fsp->fs_next;
+                               fs = fsp->fs_fs;
+                               fi = fsp->fs_fi;
+                               op->o_tmpfree(fsp, op->o_tmpmemctx);
+                       }
                } while((res) && (fi != NULL) && (fs != NULL));
 
                if ( res )
@@ -1455,7 +1581,9 @@ add_query(
        }
        new_cached_query->expiry_time = now + ttl;
        new_cached_query->refresh_time = ttr;
+       new_cached_query->bindref_time = 0;
 
+       new_cached_query->bind_refcnt = 0;
        new_cached_query->answerable_cnt = 0;
        new_cached_query->refcnt = 1;
        ldap_pvt_thread_mutex_init(&new_cached_query->answerable_cnt_mutex);
@@ -1503,6 +1631,10 @@ add_query(
                templ->query = new_cached_query;
                templ->no_of_queries++;
        } else {
+               ldap_pvt_thread_mutex_destroy(&new_cached_query->answerable_cnt_mutex);
+               if (wlock)
+                       ldap_pvt_thread_rdwr_wunlock(&new_cached_query->rwlock);
+               ldap_pvt_thread_rdwr_destroy( &new_cached_query->rwlock );
                ch_free( new_cached_query );
                new_cached_query = find_filter( op, qbase->scopes[query->scope],
                                                        query->filter, first );
@@ -1512,16 +1644,16 @@ add_query(
        Debug( pcache_debug, "TEMPLATE %p QUERIES++ %d\n",
                        (void *) templ, templ->no_of_queries, 0 );
 
-       Debug( pcache_debug, "Unlock AQ index = %p \n",
-                       (void *) templ, 0, 0 );
-       ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
-
        /* Adding on top of LRU list  */
        if ( rc == 0 ) {
                ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
                add_query_on_top(qm, new_cached_query);
                ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
        }
+       Debug( pcache_debug, "Unlock AQ index = %p \n",
+                       (void *) templ, 0, 0 );
+       ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
+
        return rc == 0 ? new_cached_query : NULL;
 }
 
@@ -1650,19 +1782,16 @@ remove_func (
 static int
 remove_query_data(
        Operation       *op,
-       SlapReply       *rs,
        struct berval   *query_uuid )
 {
        struct query_info       *qi, *qnext;
-       char                    filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(queryId=)" ) ];
+       char                    filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
        AttributeAssertion      ava = ATTRIBUTEASSERTION_INIT;
        Filter                  filter = {LDAP_FILTER_EQUALITY};
        SlapReply               sreply = {REP_RESULT};
        slap_callback cb = { NULL, remove_func, NULL, NULL };
        int deleted = 0;
 
-       sreply.sr_entry = NULL;
-       sreply.sr_nentries = 0;
        op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
                "(%s=%s)", ad_queryId->ad_cname.bv_val, query_uuid->bv_val);
        filter.f_ava = &ava;
@@ -1695,6 +1824,7 @@ remove_query_data(
 
                op->o_req_dn = qi->xdn;
                op->o_req_ndn = qi->xdn;
+               rs_reinit( &sreply, REP_RESULT );
 
                if ( qi->del ) {
                        Debug( pcache_debug, "DELETING ENTRY TEMPLATE=%s\n",
@@ -1839,14 +1969,18 @@ filter2template(
        return 0;
 }
 
+#define        BI_HASHED       0x01
+#define        BI_DIDCB        0x02
+#define        BI_LOOKUP       0x04
+
+struct search_info;
+
 typedef struct bindinfo {
-       CachedQuery *bi_query;
-       struct berval *bi_querystr;
-       struct berval *bi_base;
-       Filter *bi_filter;
-       int bi_scope;
-       int bi_hashed;
-       int bi_didcb;
+       cache_manager *bi_cm;
+       CachedQuery *bi_cq;
+       QueryTemplate *bi_templ;
+       struct search_info *bi_si;
+       int bi_flags;
        slap_callback bi_cb;
 } bindinfo;
 
@@ -1869,7 +2003,6 @@ struct search_info {
 static void
 remove_query_and_data(
        Operation       *op,
-       SlapReply       *rs,
        cache_manager   *cm,
        struct berval   *uuid )
 {
@@ -1882,7 +2015,7 @@ remove_query_and_data(
                Debug( pcache_debug,
                        "Removing query UUID %s\n",
                        uuid->bv_val, 0, 0 );
-               return_val = remove_query_data( op, rs, uuid );
+               return_val = remove_query_data( op, uuid );
                Debug( pcache_debug,
                        "QUERY REMOVED, SIZE=%d\n",
                        return_val, 0, 0);
@@ -1932,11 +2065,7 @@ fetch_queryId_cb( Operation *op, SlapReply *rs )
        }
 
        /* clear entry if required */
-       if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
-               entry_free( rs->sr_entry );
-               rs->sr_entry = NULL;
-               rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
-       }
+       rs_flush_entry( op, rs, (slap_overinst *) op->o_bd->bd_info );
 
        return rc;
 }
@@ -1955,7 +2084,6 @@ pcache_remove_entries_from_cache(
        OperationBuffer opbuf;
        Operation       op2;
        slap_callback   sc = { 0 };
-       SlapReply       rs = { REP_RESULT };
        Filter          f = { 0 };
        char            filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ];
        AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
@@ -2002,6 +2130,7 @@ pcache_remove_entries_from_cache(
 
        for ( s = 0; !BER_BVISNULL( &entryUUIDs[ s ] ); s++ ) {
                BerVarray       vals = NULL;
+               SlapReply       rs = { REP_RESULT };
 
                op->ors_filterstr.bv_len = snprintf( filtbuf, sizeof( filtbuf ),
                        "(entryUUID=%s)", entryUUIDs[ s ].bv_val );
@@ -2020,7 +2149,7 @@ pcache_remove_entries_from_cache(
                        for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
                                struct berval   val = vals[ i ];
 
-                               remove_query_and_data( op, &rs, cm, &val );
+                               remove_query_and_data( op, cm, &val );
 
                                if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
                                        ch_free( val.bv_val );
@@ -2045,12 +2174,11 @@ pcache_remove_query_from_cache(
        struct berval   *queryid )
 {
        Operation       op2 = *op;
-       SlapReply       rs2 = { 0 };
 
        op2.o_bd = &cm->db;
 
        /* remove the selected query */
-       remove_query_and_data( &op2, &rs2, cm, queryid );
+       remove_query_and_data( &op2, cm, queryid );
 
        return LDAP_SUCCESS;
 }
@@ -2073,7 +2201,7 @@ pcache_remove_entry_queries_from_cache(
        slap_callback           sc = { 0 };
        SlapReply               rs = { REP_RESULT };
        Filter                  f = { 0 };
-       char                    filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(queryId=)" ) ];
+       char                    filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
        AttributeAssertion      ava = ATTRIBUTEASSERTION_INIT;
        AttributeName           attrs[ 2 ] = {{{ 0 }}};
        int                     rc;
@@ -2141,7 +2269,7 @@ pcache_remove_entry_queries_from_cache(
                for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
                        struct berval   val = vals[ i ];
 
-                       remove_query_and_data( op, &rs, cm, &val );
+                       remove_query_and_data( op, cm, &val );
 
                        if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
                                ch_free( val.bv_val );
@@ -2157,7 +2285,6 @@ pcache_remove_entry_queries_from_cache(
 static int
 cache_entries(
        Operation       *op,
-       SlapReply       *rs,
        struct berval *query_uuid )
 {
        struct search_info *si = op->o_callback->sc_private;
@@ -2189,10 +2316,10 @@ cache_entries(
                e->e_private = NULL;
                while ( cm->cur_entries > (cm->max_entries) ) {
                        BER_BVZERO( &crp_uuid );
-                       remove_query_and_data( op_tmp, rs, cm, &crp_uuid );
+                       remove_query_and_data( op_tmp, cm, &crp_uuid );
                }
 
-               return_val = merge_entry(op_tmp, e, query_uuid);
+               return_val = merge_entry(op_tmp, e, 0, query_uuid);
                ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
                cm->cur_entries += return_val;
                Debug( pcache_debug,
@@ -2289,8 +2416,7 @@ over:;
                                        entry_free(si->head);
                                }
                        }
-                       op->o_callback = op->o_callback->sc_next;
-                       op->o_tmpfree( cb, op->o_tmpmemctx );
+
                } else if ( si->caching_reason != PC_IGNORE ) {
                        CachedQuery *qc = qm->addfunc(op, qm, &si->query,
                                si->qtemp, si->caching_reason, 1 );
@@ -2298,9 +2424,11 @@ over:;
                        if ( qc != NULL ) {
                                switch ( si->caching_reason ) {
                                case PC_POSITIVE:
-                                       cache_entries( op, rs, &qc->q_uuid );
-                                       if ( si->pbi )
-                                               si->pbi->bi_query = qc;
+                                       cache_entries( op, &qc->q_uuid );
+                                       if ( si->pbi ) {
+                                               qc->bind_refcnt++;
+                                               si->pbi->bi_cq = qc;
+                                       }
                                        break;
 
                                case PC_SIZELIMIT:
@@ -2346,6 +2474,9 @@ over:;
                } else {
                        filter_free( si->query.filter );
                }
+
+               op->o_callback = op->o_callback->sc_next;
+               op->o_tmpfree( cb, op->o_tmpmemctx );
        }
 
        return SLAP_CB_CONTINUE;
@@ -2378,7 +2509,14 @@ pcache_response(
                        } else if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED
                                && si->qtemp->limitttl )
                        {
+                               Entry *e;
+
                                si->caching_reason = PC_SIZELIMIT;
+                               for (;si->head; si->head=e) {
+                                       e = si->head->e_private;
+                                       si->head->e_private = NULL;
+                                       entry_free(si->head);
+                               }
                        }
 
                } else if ( si->qtemp->negttl && !si->count && !si->over &&
@@ -2432,11 +2570,58 @@ pcache_chk_controls(
        return rs->sr_err;
 }
 
+static int
+pc_setpw( Operation *op, struct berval *pwd, cache_manager *cm )
+{
+       struct berval vals[2];
+
+       {
+               const char *text = NULL;
+               BER_BVZERO( &vals[0] );
+               slap_passwd_hash( pwd, &vals[0], &text );
+               if ( BER_BVISEMPTY( &vals[0] )) {
+                       Debug( pcache_debug, "pc_setpw: hash failed %s\n",
+                               text, 0, 0 );
+                       return LDAP_OTHER;
+               }
+       }
+
+       BER_BVZERO( &vals[1] );
+
+       {
+               Modifications mod;
+               SlapReply sr = { REP_RESULT };
+               slap_callback cb = { 0, slap_null_cb, 0, 0 };
+               int rc;
+
+               mod.sml_op = LDAP_MOD_REPLACE;
+               mod.sml_flags = 0;
+               mod.sml_desc = slap_schema.si_ad_userPassword;
+               mod.sml_type = mod.sml_desc->ad_cname;
+               mod.sml_values = vals;
+               mod.sml_nvalues = NULL;
+               mod.sml_numvals = 1;
+               mod.sml_next = NULL;
+
+               op->o_tag = LDAP_REQ_MODIFY;
+               op->orm_modlist = &mod;
+               op->o_bd = &cm->db;
+               op->o_dn = op->o_bd->be_rootdn;
+               op->o_ndn = op->o_bd->be_rootndn;
+               op->o_callback = &cb;
+               Debug( pcache_debug, "pc_setpw: CACHING BIND for %s\n",
+                       op->o_req_dn.bv_val, 0, 0 );
+               rc = op->o_bd->be_modify( op, &sr );
+               ch_free( vals[0].bv_val );
+               return rc;
+       }
+}
+
 typedef struct bindcacheinfo {
        slap_overinst *on;
        CachedQuery *qc;
 } bindcacheinfo;
-       
+
 static int
 pc_bind_save( Operation *op, SlapReply *rs )
 {
@@ -2444,43 +2629,72 @@ pc_bind_save( Operation *op, SlapReply *rs )
                bindcacheinfo *bci = op->o_callback->sc_private;
                slap_overinst *on = bci->on;
                cache_manager *cm = on->on_bi.bi_private;
-               struct berval vals[2];
-               const char *text = NULL;
+               CachedQuery *qc = bci->qc;
+               int delete = 0;
 
-               BER_BVZERO( &vals[0] );
-               BER_BVZERO( &vals[1] );
-               slap_passwd_hash( &op->orb_cred, &vals[0], &text );
-               if ( BER_BVISEMPTY( &vals[0] )) {
-                       Debug( pcache_debug, "pcache_bind_save: hash failed %s\n",
-                               text, 0, 0 );
-               } else {
+               ldap_pvt_thread_rdwr_wlock( &qc->rwlock );
+               if ( qc->bind_refcnt-- ) {
                        Operation op2 = *op;
-                       Modifications mod;
-                       SlapReply sr = { REP_RESULT };
-                       slap_callback cb = { 0, slap_null_cb, 0, 0 };
-                       
-                       mod.sml_op = LDAP_MOD_REPLACE;
-                       mod.sml_flags = 0;
-                       mod.sml_desc = slap_schema.si_ad_userPassword;
-                       mod.sml_type = mod.sml_desc->ad_cname;
-                       mod.sml_values = vals;
-                       mod.sml_nvalues = NULL;
-                       mod.sml_numvals = 1;
-                       mod.sml_next = NULL;
-
-                       op2.o_tag = LDAP_REQ_MODIFY;
-                       op2.orm_modlist = &mod;
-                       op2.o_bd = &cm->db;
-                       op2.o_dn = op2.o_bd->be_rootdn;
-                       op2.o_ndn = op2.o_bd->be_rootndn;
-                       op2.o_callback = &cb;
-                       if ( op2.o_bd->be_modify( &op2, &sr ) == LDAP_SUCCESS )
+                       if ( pc_setpw( &op2, &op->orb_cred, cm ) == LDAP_SUCCESS )
                                bci->qc->bindref_time = op->o_time + bci->qc->qtemp->bindttr;
+               } else {
+                       bci->qc = NULL;
+                       delete = 1;
                }
+               ldap_pvt_thread_rdwr_wunlock( &qc->rwlock );
+               if ( delete ) free_query(qc);
        }
        return SLAP_CB_CONTINUE;
 }
 
+static Filter *
+pc_bind_attrs( Operation *op, Entry *e, QueryTemplate *temp,
+       struct berval *fbv )
+{
+       int i, len = 0;
+       struct berval *vals, pres = BER_BVC("*");
+       char *p1, *p2;
+       Attribute *a;
+
+       vals = op->o_tmpalloc( temp->bindnattrs * sizeof( struct berval ),
+               op->o_tmpmemctx );
+
+       for ( i=0; i<temp->bindnattrs; i++ ) {
+               a = attr_find( e->e_attrs, temp->bindfattrs[i] );
+               if ( a && a->a_vals ) {
+                       vals[i] = a->a_vals[0];
+                       len += a->a_vals[0].bv_len;
+               } else {
+                       vals[i] = pres;
+               }
+       }
+       fbv->bv_len = len + temp->bindftemp.bv_len;
+       fbv->bv_val = op->o_tmpalloc( fbv->bv_len + 1, op->o_tmpmemctx );
+
+       p1 = temp->bindftemp.bv_val;
+       p2 = fbv->bv_val;
+       i = 0;
+       while ( *p1 ) {
+               *p2++ = *p1;
+               if ( p1[0] == '=' && p1[1] == ')' ) {
+                       AC_MEMCPY( p2, vals[i].bv_val, vals[i].bv_len );
+                       p2 += vals[i].bv_len;
+                       i++;
+               }
+               p1++;
+       }
+       *p2 = '\0';
+       op->o_tmpfree( vals, op->o_tmpmemctx );
+
+       /* FIXME: are we sure str2filter_x can't fail?
+        * caller needs to check */
+       {
+               Filter *f = str2filter_x( op, fbv->bv_val );
+               assert( f != NULL );
+               return f;
+       }
+}
+
 /* Check if the requested entry is from the cache and has a valid
  * ttr and password hash
  */
@@ -2491,17 +2705,34 @@ pc_bind_search( Operation *op, SlapReply *rs )
                bindinfo *pbi = op->o_callback->sc_private;
 
                /* We only care if this is an already cached result and we're
-                * below the refresh time.
+                * below the refresh time, or we're offline.
                 */
-               if ( pbi->bi_query && op->o_time < pbi->bi_query->bindref_time ) {
-                       Attribute *a;
+               if ( pbi->bi_cq ) {
+                       if (( pbi->bi_cm->cc_paused & PCACHE_CC_OFFLINE ) ||
+                               op->o_time < pbi->bi_cq->bindref_time ) {
+                               Attribute *a;
+
+                               /* See if a recognized password is hashed here */
+                               a = attr_find( rs->sr_entry->e_attrs,
+                                       slap_schema.si_ad_userPassword );
+                               if ( a && a->a_vals[0].bv_val[0] == '{' &&
+                                       lutil_passwd_scheme( a->a_vals[0].bv_val ))
+                                       pbi->bi_flags |= BI_HASHED;
+                       } else {
+                               Debug( pcache_debug, "pc_bind_search: cache is stale, "
+                                       "reftime: %ld, current time: %ld\n",
+                                       pbi->bi_cq->bindref_time, op->o_time, 0 );
+                       }
+               } else if ( pbi->bi_si ) {
+                       /* This search result is going into the cache */
+                       struct berval fbv;
+                       Filter *f;
 
-                       /* See if a recognized password is hashed here */
-                       a = attr_find( rs->sr_entry->e_attrs,
-                               slap_schema.si_ad_userPassword );
-                       if ( a && a->a_vals[0].bv_val[0] == '{' &&
-                               lutil_passwd_scheme( a->a_vals[0].bv_val ))
-                               pbi->bi_hashed = 1;
+                       filter_free( pbi->bi_si->query.filter );
+                       f = pc_bind_attrs( op, rs->sr_entry, pbi->bi_templ, &fbv );
+                       op->o_tmpfree( fbv.bv_val, op->o_tmpmemctx );
+                       pbi->bi_si->query.filter = filter_dup( f, NULL );
+                       filter_free_x( op, f, 1 );
                }
        }
        return 0;
@@ -2512,7 +2743,7 @@ static int
 pc_bind_resp( Operation *op, SlapReply *rs )
 {
        bindinfo *pbi = op->o_callback->sc_private;
-       if ( !pbi->bi_didcb ) {
+       if ( !( pbi->bi_flags & BI_DIDCB )) {
                slap_callback *sc = op->o_callback;
                while ( sc && sc->sc_response != pcache_response )
                        sc = sc->sc_next;
@@ -2520,86 +2751,103 @@ pc_bind_resp( Operation *op, SlapReply *rs )
                        sc = op->o_callback;
                pbi->bi_cb.sc_next = sc->sc_next;
                sc->sc_next = &pbi->bi_cb;
-               pbi->bi_didcb = 1;
+               pbi->bi_flags |= BI_DIDCB;
        }
        return SLAP_CB_CONTINUE;
 }
 
+#ifdef PCACHE_CONTROL_PRIVDB
 static int
-pcache_op_bind(
+pcache_op_privdb(
        Operation               *op,
        SlapReply               *rs )
 {
        slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
        cache_manager   *cm = on->on_bi.bi_private;
-       QueryTemplate *temp;
-       slap_callback   cb = { 0 }, *sc;
-       bindinfo bi;
-       bindcacheinfo *bci;
-       Operation op2;
-       int is_priv = 0;
-
-#ifdef PCACHE_CONTROL_PRIVDB
-       if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL )
-               is_priv = 1;
-#endif
+       slap_callback   *save_cb;
+       slap_op_t       type;
 
-       /* Skip if we're not configured for Binds, and no priv control */
-       if ( !cm->cache_binds && !is_priv )
+       /* skip if control is unset */
+       if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) {
                return SLAP_CB_CONTINUE;
+       }
 
        /* The cache DB isn't open yet */
        if ( cm->defer_db_open ) {
-               if ( !is_priv ) {
-                       /* Just passthru for now */
-                       return SLAP_CB_CONTINUE;
-               } else {
-                       send_ldap_error( op, rs, LDAP_UNAVAILABLE,
-                               "pcachePrivDB: cacheDB not available" );
-                       return rs->sr_err;
-               }
+               send_ldap_error( op, rs, LDAP_UNAVAILABLE,
+                       "pcachePrivDB: cacheDB not available" );
+               return rs->sr_err;
        }
 
-       if ( is_priv ) {
-               int             rc;
-               slap_callback   *save_cb;
+       /* FIXME: might be a little bit exaggerated... */
+       if ( !be_isroot( op ) ) {
+               save_cb = op->o_callback;
+               op->o_callback = NULL;
+               send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+                       "pcachePrivDB: operation not allowed" );
+               op->o_callback = save_cb;
 
-               /* FIXME: might be a little bit exaggerated... */
-               if ( !be_isroot( op ) ) {
-                       save_cb = op->o_callback;
-                       op->o_callback = NULL;
-                       send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
-                               "pcachePrivDB: operation not allowed" );
-                       op->o_callback = save_cb;
+               return rs->sr_err;
+       }
 
-                       return rs->sr_err;
-               }
+       /* map tag to operation */
+       type = slap_req2op( op->o_tag );
+       if ( type != SLAP_OP_LAST ) {
+               BI_op_func      **func;
+               int             rc;
 
                /* execute, if possible */
-               if ( cm->db.be_bind != NULL ) {
-                       op2 = *op;
+               func = &cm->db.be_bind;
+               if ( func[ type ] != NULL ) {
+                       Operation       op2 = *op;
+       
                        op2.o_bd = &cm->db;
 
-                       rc = op2.o_bd->be_bind( &op2, rs );
-                       if ( rc == LDAP_SUCCESS ) {
+                       rc = func[ type ]( &op2, rs );
+                       if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) {
                                op->o_conn->c_authz_cookie = cm->db.be_private;
                        }
-               } else {
-                       /* otherwise fall back to error */
-                       save_cb = op->o_callback;
-                       op->o_callback = NULL;
-                       send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
-                               "operation not supported with pcachePrivDB control" );
-                       op->o_callback = save_cb;
-               }
 
-               return rs->sr_err;
+                       return rs->sr_err;
+               }
        }
 
-       /* First search for the target entry and userPassword. This will
-        * give us a cached_query to work with. Find a matching template
-        * to use for the search.
-        */
+       /* otherwise fall back to error */
+       save_cb = op->o_callback;
+       op->o_callback = NULL;
+       send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+               "operation not supported with pcachePrivDB control" );
+       op->o_callback = save_cb;
+
+       return rs->sr_err;
+}
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+static int
+pcache_op_bind(
+       Operation               *op,
+       SlapReply               *rs )
+{
+       slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
+       cache_manager   *cm = on->on_bi.bi_private;
+       QueryTemplate *temp;
+       Entry *e;
+       slap_callback   cb = { 0 }, *sc;
+       bindinfo bi;
+       bindcacheinfo *bci;
+       Operation op2;
+       int rc;
+
+#ifdef PCACHE_CONTROL_PRIVDB
+       if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL )
+               return pcache_op_privdb( op, rs );
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+       /* Skip if we're not configured for Binds, or cache DB isn't open yet */
+       if ( !cm->cache_binds || cm->defer_db_open )
+               return SLAP_CB_CONTINUE;
+
+       /* First find a matching template with Bind info */
        for ( temp=cm->qm->templates; temp; temp=temp->qmnext ) {
                if ( temp->bindttr && dnIsSuffix( &op->o_req_ndn, &temp->bindbase ))
                        break;
@@ -2608,28 +2856,45 @@ pcache_op_bind(
        if ( !temp )
                return SLAP_CB_CONTINUE;
 
+       /* See if the entry is already locally cached. If so, we can
+        * populate the query filter to retrieve the cached query. We
+        * need to check the bindrefresh time in the query.
+        */
        op2 = *op;
        op2.o_dn = op->o_bd->be_rootdn;
        op2.o_ndn = op->o_bd->be_rootndn;
+       bi.bi_flags = 0;
+
+       op2.o_bd = &cm->db;
+       e = NULL;
+       rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, NULL, 0, &e );
+       if ( rc == LDAP_SUCCESS && e ) {
+               bi.bi_flags |= BI_LOOKUP;
+               op2.ors_filter = pc_bind_attrs( op, e, temp, &op2.ors_filterstr );
+               be_entry_release_r( &op2, e );
+       } else {
+               op2.ors_filter = temp->bindfilter;
+               op2.ors_filterstr = temp->bindfilterstr;
+       }
 
+       op2.o_bd = op->o_bd;
        op2.o_tag = LDAP_REQ_SEARCH;
        op2.ors_scope = LDAP_SCOPE_BASE;
        op2.ors_deref = LDAP_DEREF_NEVER;
        op2.ors_slimit = 1;
        op2.ors_tlimit = SLAP_NO_LIMIT;
        op2.ors_limit = NULL;
-       op2.ors_filter = temp->bindfilter;
-       op2.ors_filterstr = temp->bindfilterstr;
        op2.ors_attrs = cm->qm->attr_sets[temp->attr_set_index].attrs;
        op2.ors_attrsonly = 0;
 
        /* We want to invoke search at the same level of the stack
         * as we're already at...
         */
-       bi.bi_query = NULL;
-       bi.bi_querystr = &temp->querystr;
-       bi.bi_hashed = 0;
-       bi.bi_didcb = 0;
+       bi.bi_cm = cm;
+       bi.bi_templ = temp;
+       bi.bi_cq = NULL;
+       bi.bi_si = NULL;
+
        bi.bi_cb.sc_response = pc_bind_search;
        bi.bi_cb.sc_cleanup = NULL;
        bi.bi_cb.sc_private = &bi;
@@ -2639,19 +2904,29 @@ pcache_op_bind(
        overlay_op_walk( &op2, rs, op_search, on->on_info, on );
 
        /* OK, just bind locally */
-       if ( bi.bi_hashed ) {
+       if ( bi.bi_flags & BI_HASHED ) {
                BackendDB *be = op->o_bd;
                op->o_bd = &cm->db;
+               int delete = 0;
+
+               Debug( pcache_debug, "pcache_op_bind: CACHED BIND for %s\n",
+                       op->o_req_dn.bv_val, 0, 0 );
 
                if ( op->o_bd->be_bind( op, rs ) == LDAP_SUCCESS ) {
                        op->o_conn->c_authz_cookie = cm->db.be_private;
                }
                op->o_bd = be;
+               ldap_pvt_thread_rdwr_wlock( &bi.bi_cq->rwlock );
+               if ( !bi.bi_cq->bind_refcnt-- ) {
+                       delete = 1;
+               }
+               ldap_pvt_thread_rdwr_wunlock( &bi.bi_cq->rwlock );
+               if ( delete ) free_query( bi.bi_cq );
                return rs->sr_err;
        }
 
        /* We have a cached query to work with */
-       if ( bi.bi_query ) {
+       if ( bi.bi_cq ) {
                sc = op->o_tmpalloc( sizeof(slap_callback) + sizeof(bindcacheinfo),
                        op->o_tmpmemctx );
                sc->sc_response = pc_bind_save;
@@ -2661,77 +2936,12 @@ pcache_op_bind(
                sc->sc_next = op->o_callback;
                op->o_callback = sc;
                bci->on = on;
-               bci->qc = bi.bi_query;
+               bci->qc = bi.bi_cq;
        }
        return SLAP_CB_CONTINUE;
 }
 
-#ifdef PCACHE_CONTROL_PRIVDB
-static int
-pcache_op_privdb(
-       Operation               *op,
-       SlapReply               *rs )
-{
-       slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
-       cache_manager   *cm = on->on_bi.bi_private;
-       slap_callback   *save_cb;
-       slap_op_t       type;
-
-       /* skip if control is unset */
-       if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) {
-               return SLAP_CB_CONTINUE;
-       }
-
-       /* The cache DB isn't open yet */
-       if ( cm->defer_db_open ) {
-               send_ldap_error( op, rs, LDAP_UNAVAILABLE,
-                       "pcachePrivDB: cacheDB not available" );
-               return rs->sr_err;
-       }
-
-       /* FIXME: might be a little bit exaggerated... */
-       if ( !be_isroot( op ) ) {
-               save_cb = op->o_callback;
-               op->o_callback = NULL;
-               send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
-                       "pcachePrivDB: operation not allowed" );
-               op->o_callback = save_cb;
-
-               return rs->sr_err;
-       }
-
-       /* map tag to operation */
-       type = slap_req2op( op->o_tag );
-       if ( type != SLAP_OP_LAST ) {
-               BI_op_func      **func;
-               int             rc;
-
-               /* execute, if possible */
-               func = &cm->db.be_bind;
-               if ( func[ type ] != NULL ) {
-                       Operation       op2 = *op;
-       
-                       op2.o_bd = &cm->db;
-
-                       rc = func[ type ]( &op2, rs );
-                       if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) {
-                               op->o_conn->c_authz_cookie = cm->db.be_private;
-                       }
-
-                       return rs->sr_err;
-               }
-       }
-
-       /* otherwise fall back to error */
-       save_cb = op->o_callback;
-       op->o_callback = NULL;
-       send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
-               "operation not supported with pcachePrivDB control" );
-       op->o_callback = save_cb;
-
-       return rs->sr_err;
-}
-#endif /* PCACHE_CONTROL_PRIVDB */
+static slap_response refresh_merge;
 
 static int
 pcache_op_search(
@@ -2771,7 +2981,8 @@ pcache_op_search(
        cm->db.be_acl = op->o_bd->be_acl;
 
        {
-               /* See if we're processing a Bind request */
+               /* See if we're processing a Bind request
+                * or a cache refresh */
                slap_callback *cb = op->o_callback;
 
                for ( ; cb; cb=cb->sc_next ) {
@@ -2779,11 +2990,26 @@ pcache_op_search(
                                pbi = cb->sc_private;
                                break;
                        }
+                       if ( cb->sc_response == refresh_merge ) {
+                               /* This is a refresh, do not search the cache */
+                               return SLAP_CB_CONTINUE;
+                       }
                }
        }
 
+       /* FIXME: cannot cache/answer requests with pagedResults control */
+
+       query.filter = op->ors_filter;
+
        if ( pbi ) {
-               tempstr = *pbi->bi_querystr;
+               query.base = pbi->bi_templ->bindbase;
+               query.scope = pbi->bi_templ->bindscope;
+               attr_set = pbi->bi_templ->attr_set_index;
+               cacheable = 1;
+               qtemp = pbi->bi_templ;
+               if ( pbi->bi_flags & BI_LOOKUP )
+                       answerable = qm->qcfunc(op, qm, &query, qtemp);
+
        } else {
                tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1,
                        op->o_tmpmemctx );
@@ -2793,41 +3019,36 @@ pcache_op_search(
                        op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
                        return SLAP_CB_CONTINUE;
                }
-       }
 
-       Debug( pcache_debug, "query template of incoming query = %s\n",
-                                       tempstr.bv_val, 0, 0 );
+               Debug( pcache_debug, "query template of incoming query = %s\n",
+                                               tempstr.bv_val, 0, 0 );
 
-       /* FIXME: cannot cache/answer requests with pagedResults control */
+               /* find attr set */
+               attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets);
 
-       /* find attr set */
-       attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets);
+               query.base = op->o_req_ndn;
+               query.scope = op->ors_scope;
 
-       query.filter = op->ors_filter;
-       query.base = op->o_req_ndn;
-       query.scope = op->ors_scope;
-
-       /* check for query containment */
-       if (attr_set > -1) {
-               QueryTemplate *qt = qm->attr_sets[attr_set].templates;
-               for (; qt; qt = qt->qtnext ) {
-                       /* find if template i can potentially answer tempstr */
-                       if ( ber_bvstrcasecmp( &qt->querystr, &tempstr ) != 0 )
-                               continue;
-                       cacheable = 1;
-                       qtemp = qt;
-                       Debug( pcache_debug, "Entering QC, querystr = %s\n",
-                                       op->ors_filterstr.bv_val, 0, 0 );
-                       answerable = qm->qcfunc(op, qm, &query, qt);
-
-                       /* if != NULL, rlocks qtemp->t_rwlock */
-                       if (answerable)
-                               break;
+               /* check for query containment */
+               if (attr_set > -1) {
+                       QueryTemplate *qt = qm->attr_sets[attr_set].templates;
+                       for (; qt; qt = qt->qtnext ) {
+                               /* find if template i can potentially answer tempstr */
+                               if ( ber_bvstrcasecmp( &qt->querystr, &tempstr ) != 0 )
+                                       continue;
+                               cacheable = 1;
+                               qtemp = qt;
+                               Debug( pcache_debug, "Entering QC, querystr = %s\n",
+                                               op->ors_filterstr.bv_val, 0, 0 );
+                               answerable = qm->qcfunc(op, qm, &query, qt);
+
+                               /* if != NULL, rlocks qtemp->t_rwlock */
+                               if (answerable)
+                                       break;
+                       }
                }
-       }
-       if ( !pbi )
                op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
-
+       }
 
        if (answerable) {
                BackendDB       *save_bd = op->o_bd;
@@ -2841,28 +3062,40 @@ pcache_op_search(
                        answerable->answerable_cnt, 0, 0 );
                ldap_pvt_thread_mutex_unlock( &answerable->answerable_cnt_mutex );
 
-               ldap_pvt_thread_rdwr_rlock(&answerable->rwlock);
+               ldap_pvt_thread_rdwr_wlock(&answerable->rwlock);
                if ( BER_BVISNULL( &answerable->q_uuid )) {
                        /* No entries cached, just an empty result set */
                        i = rs->sr_err = 0;
                        send_ldap_result( op, rs );
                } else {
                        /* Let Bind know we used a cached query */
-                       if ( pbi )
-                               pbi->bi_query = answerable;
+                       if ( pbi ) {
+                               answerable->bind_refcnt++;
+                               pbi->bi_cq = answerable;
+                       }
 
                        op->o_bd = &cm->db;
-#if 0
                        if ( cm->response_cb == PCACHE_RESPONSE_CB_TAIL ) {
+                               slap_callback cb;
                                /* The cached entry was already processed by any
                                 * other overlays, so don't let it get processed again.
+                                *
+                                * This loop removes over_back_response from the stack.
                                 */
-                               op->o_callback = NULL;
+                               if ( overlay_callback_after_backover( op, &cb, 0) == 0 ) {
+                                       slap_callback **scp;
+                                       for ( scp = &op->o_callback; *scp != NULL;
+                                               scp = &(*scp)->sc_next ) {
+                                               if ( (*scp)->sc_next == &cb ) {
+                                                       *scp = cb.sc_next;
+                                                       break;
+                                               }
+                                       }
+                               }
                        }
-#endif
                        i = cm->db.bd_info->bi_op_search( op, rs );
                }
-               ldap_pvt_thread_rdwr_runlock(&answerable->rwlock);
+               ldap_pvt_thread_rdwr_wunlock(&answerable->rwlock);
                /* locked by qtemp->qcfunc (query_containment) */
                ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock);
                op->o_bd = save_bd;
@@ -2910,6 +3143,8 @@ pcache_op_search(
                si->swap_saved_attrs = 1;
                si->save_attrs = op->ors_attrs;
                si->pbi = pbi;
+               if ( pbi )
+                       pbi->bi_si = si;
 
                op->ors_attrs = qtemp->t_attrs.attrs;
 
@@ -2942,19 +3177,30 @@ get_attr_set(
        query_manager* qm,
        int num )
 {
-       int i;
+       int i = 0;
        int count = 0;
 
        if ( attrs ) {
-               for ( ; attrs[count].an_name.bv_val; count++ );
+               for ( ; attrs[i].an_name.bv_val; i++ ) {
+                       /* only count valid attribute names
+                        * (searches ignore others, this overlay does the same) */
+                       if ( attrs[i].an_desc ) {
+                               count++;
+                       }
+               }
        }
 
-       /* recognize a single "*" or a "1.1" */
-       if ( count == 0 ) {
+       /* recognize default or explicit single "*" */
+       if ( ! attrs ||
+               ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_all_user_attrs ) ) )
+       {
                count = 1;
                attrs = slap_anlist_all_user_attributes;
 
-       } else if ( count == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) {
+       /* recognize implicit (no valid attributes) or explicit single "1.1" */
+       } else if ( count == 0 ||
+               ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) )
+       {
                count = 0;
                attrs = NULL;
        }
@@ -2975,6 +3221,8 @@ get_attr_set(
                }
 
                for ( a2 = attrs; a2->an_name.bv_val; a2++ ) {
+                       if ( !a2->an_desc && !bvmatch( &a2->an_name, slap_bv_all_user_attrs ) ) continue;
+
                        if ( !an_find( qm->attr_sets[i].attrs, &a2->an_name ) ) {
                                found = 0;
                                break;
@@ -3029,7 +3277,6 @@ refresh_merge( Operation *op, SlapReply *rs )
 {
        if ( rs->sr_type == REP_SEARCH ) {
                refresh_info *ri = op->o_callback->sc_private;
-               BackendDB *be = op->o_bd;
                Entry *e;
                dnlist *dnl;
                slap_callback *ocb;
@@ -3043,7 +3290,7 @@ refresh_merge( Operation *op, SlapReply *rs )
                        /* No local entry, just add it. FIXME: we are not checking
                         * the cache entry limit here
                         */
-                        merge_entry( op, rs->sr_entry, &ri->ri_q->q_uuid );
+                        merge_entry( op, rs->sr_entry, 1, &ri->ri_q->q_uuid );
                } else {
                        /* Entry exists, update it */
                        Entry ne;
@@ -3076,6 +3323,7 @@ refresh_merge( Operation *op, SlapReply *rs )
                        slap_mods_free( modlist, 1 );
                        /* mods is NULL if there are no changes */
                        if ( mods ) {
+                               SlapReply rs2 = { REP_RESULT };
                                struct berval dn = op->o_req_dn;
                                struct berval ndn = op->o_req_ndn;
                                op->o_tag = LDAP_REQ_MODIFY;
@@ -3083,7 +3331,9 @@ refresh_merge( Operation *op, SlapReply *rs )
                                op->o_req_dn = rs->sr_entry->e_name;
                                op->o_req_ndn = rs->sr_entry->e_nname;
                                op->o_callback = &cb;
-                               op->o_bd->be_modify( op, rs );
+                               op->o_bd->be_modify( op, &rs2 );
+                               rs->sr_err = rs2.sr_err;
+                               rs_assert_done( &rs2 );
                                slap_mods_free( mods, 1 );
                                op->o_req_dn = dn;
                                op->o_req_ndn = ndn;
@@ -3137,12 +3387,12 @@ refresh_purge( Operation *op, SlapReply *rs )
 }
 
 static int
-refresh_query( Operation *op, SlapReply *rs, CachedQuery *query,
-       slap_overinst *on )
+refresh_query( Operation *op, CachedQuery *query, slap_overinst *on )
 {
+       SlapReply rs = {REP_RESULT};
        slap_callback cb = { 0 };
        refresh_info ri = { 0 };
-       char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(queryId=)" ) ];
+       char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
        AttributeAssertion      ava = ATTRIBUTEASSERTION_INIT;
        Filter filter = {LDAP_FILTER_EQUALITY};
        AttributeName attrs[ 2 ] = {{{ 0 }}};
@@ -3177,7 +3427,7 @@ refresh_query( Operation *op, SlapReply *rs, CachedQuery *query,
        op->ors_attrsonly = 0;
 
        op->o_bd = on->on_info->oi_origdb;
-       rc = op->o_bd->be_search( op, rs );
+       rc = op->o_bd->be_search( op, &rs );
        if ( rc ) {
                op->o_bd = ri.ri_be;
                goto leave;
@@ -3200,17 +3450,17 @@ refresh_query( Operation *op, SlapReply *rs, CachedQuery *query,
        attrs[ 0 ].an_name = ad_queryId->ad_cname;
        op->ors_attrs = attrs;
        op->ors_attrsonly = 0;
-       rs->sr_entry = NULL;
-       rs->sr_nentries = 0;
-       rc = op->o_bd->be_search( op, rs );
+       rs_reinit( &rs, REP_RESULT );
+       rc = op->o_bd->be_search( op, &rs );
        if ( rc ) goto leave;
 
        while (( dn = ri.ri_dels )) {
                op->o_req_dn = dn->dn;
                op->o_req_ndn = dn->dn;
+               rs_reinit( &rs, REP_RESULT );
                if ( dn->delete ) {
                        op->o_tag = LDAP_REQ_DELETE;
-                       op->o_bd->be_delete( op, rs );
+                       op->o_bd->be_delete( op, &rs );
                } else {
                        Modifications mod;
                        struct berval vals[2];
@@ -3228,7 +3478,7 @@ refresh_query( Operation *op, SlapReply *rs, CachedQuery *query,
 
                        op->o_tag = LDAP_REQ_MODIFY;
                        op->orm_modlist = &mod;
-                       op->o_bd->be_modify( op, rs );
+                       op->o_bd->be_modify( op, &rs );
                }
                ri.ri_dels = dn->next;
                op->o_tmpfree( dn, op->o_tmpmemctx );
@@ -3253,7 +3503,6 @@ consistency_check(
        OperationBuffer opbuf;
        Operation *op;
 
-       SlapReply rs = {REP_RESULT};
        CachedQuery *query, *qprev;
        int return_val, pause = PCACHE_CC_PAUSED;
        QueryTemplate *templ;
@@ -3299,7 +3548,7 @@ consistency_check(
                                if ( query->refcnt )
                                        query->expiry_time = op->o_time + templ->ttl;
                                if ( query->expiry_time > op->o_time ) {
-                                       refresh_query( op, &rs, query, on );
+                                       refresh_query( op, query, on );
                                        continue;
                                }
                        }
@@ -3317,8 +3566,8 @@ consistency_check(
                                        Debug( pcache_debug, "Unlock CR index = %p\n",
                                                        (void *) templ, 0, 0 );
                                }
-                               ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
                                if ( !rem ) {
+                                       ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
                                        continue;
                                }
                                ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
@@ -3327,7 +3576,7 @@ consistency_check(
                                if ( BER_BVISNULL( &query->q_uuid ))
                                        return_val = 0;
                                else
-                                       return_val = remove_query_data(op, &rs, &query->q_uuid);
+                                       return_val = remove_query_data(op, &query->q_uuid);
                                Debug( pcache_debug, "STALE QUERY REMOVED, SIZE=%d\n",
                                                        return_val, 0, 0 );
                                ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
@@ -3340,7 +3589,15 @@ consistency_check(
                                        "STALE QUERY REMOVED, CACHE ="
                                        "%d entries\n",
                                        cm->cur_entries, 0, 0 );
-                               free_query(query);
+                               ldap_pvt_thread_rdwr_wlock( &query->rwlock );
+                               if ( query->bind_refcnt-- ) {
+                                       rem = 0;
+                               } else {
+                                       rem = 1;
+                               }
+                               ldap_pvt_thread_rdwr_wunlock( &query->rwlock );
+                               if ( rem ) free_query(query);
+                               ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
                        } else if ( !templ->ttr && query->expiry_time > ttl ) {
                                /* We don't need to check for refreshes, and this
                                 * query's expiry is too new, and all subsequent queries
@@ -3575,13 +3832,23 @@ pc_cf_gen( ConfigArgs *c )
                                /* count the attr length */
                                for ( attr_name = qm->attr_sets[i].attrs;
                                        attr_name->an_name.bv_val; attr_name++ )
+                               {
                                        bv.bv_len += attr_name->an_name.bv_len + 1;
+                                       if ( attr_name->an_desc &&
+                                                       ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
+                                               bv.bv_len += STRLENOF("undef:");
+                                       }
+                               }
 
                                bv.bv_val = ch_malloc( bv.bv_len+1 );
                                ptr = lutil_strcopy( bv.bv_val, c->cr_msg );
                                for ( attr_name = qm->attr_sets[i].attrs;
                                        attr_name->an_name.bv_val; attr_name++ ) {
                                        *ptr++ = ' ';
+                                       if ( attr_name->an_desc &&
+                                                       ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
+                                               ptr = lutil_strcopy( ptr, "undef:" );
+                                       }
                                        ptr = lutil_strcopy( ptr, attr_name->an_name.bv_val );
                                }
                                ber_bvarray_add( &c->rvalue_vals, &bv );
@@ -3620,11 +3887,11 @@ pc_cf_gen( ConfigArgs *c )
                                        temp->attr_set_index,
                                        temp->bindttr,
                                        ldap_pvt_scope2str( temp->bindscope ));
-                               bv.bv_len += temp->bindbase.bv_len + temp->querystr.bv_len + 3;
-                               bv.bv_val = ch_malloc( bv.bv_len+1 );
+                               bv.bv_len += temp->bindbase.bv_len + temp->bindftemp.bv_len + 4;
+                               bv.bv_val = ch_malloc( bv.bv_len + 1 );
                                ptr = bv.bv_val;
                                *ptr++ = '"';
-                               ptr = lutil_strcopy( ptr, temp->querystr.bv_val );
+                               ptr = lutil_strcopy( ptr, temp->bindftemp.bv_val );
                                *ptr++ = '"';
                                ptr = lutil_strcopy( ptr, c->cr_msg );
                                *ptr++ = '"';
@@ -3837,7 +4104,12 @@ pc_cf_gen( ConfigArgs *c )
                                        all_op = 1;
                                        BER_BVSTR( &attr_name->an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
                                } else {
-                                       if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) {
+                                       if ( strncasecmp( c->argv[i], "undef:", STRLENOF("undef:") ) == 0 ) {
+                                               struct berval bv;
+                                               ber_str2bv( c->argv[i] + STRLENOF("undef:"), 0, 0, &bv );
+                                               attr_name->an_desc = slap_bv2tmp_ad( &bv, NULL );
+
+                                       } else if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) {
                                                strcpy( c->cr_msg, text );
                                                Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                                ch_free( qm->attr_sets[num].attrs );
@@ -3987,34 +4259,53 @@ pc_temp_fail:
                        Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
                        return 1;
                }
-               { struct berval bv;
+               {       struct berval bv, tempbv;
+                       AttributeDescription **descs;
+                       int ndescs;
                        ber_str2bv( c->argv[1], 0, 0, &bv );
+                       ndescs = ftemp_attrs( &bv, &tempbv, &descs, &text );
+                       if ( ndescs < 0 ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
+                                       text );
+                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                               return 1;
+                       }
                        for ( temp = qm->templates; temp; temp=temp->qmnext ) {
-                               if ( temp->attr_set_index == i && bvmatch( &bv,
+                               if ( temp->attr_set_index == i && bvmatch( &tempbv,
                                        &temp->querystr ))
                                        break;
                        }
+                       ch_free( tempbv.bv_val );
                        if ( !temp ) {
+                               ch_free( descs );
                                snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind template %s %d invalid",
                                        c->argv[1], i );
                                Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                return 1;
                        }
+                       ber_dupbv( &temp->bindftemp, &bv );
+                       temp->bindfattrs = descs;
+                       temp->bindnattrs = ndescs;
                }
                if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                "unable to parse bind ttr=\"%s\"",
                                c->argv[3] );
                        Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                               return( 1 );
+pc_bind_fail:
+                       ch_free( temp->bindfattrs );
+                       temp->bindfattrs = NULL;
+                       ch_free( temp->bindftemp.bv_val );
+                       BER_BVZERO( &temp->bindftemp );
+                       return( 1 );
                }
-               i = ldap_pvt_str2scope( c->argv[4] );
-               if ( i < 0 ) {
+               num = ldap_pvt_str2scope( c->argv[4] );
+               if ( num < 0 ) {
                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                "unable to parse bind scope=\"%s\"",
                                c->argv[4] );
                        Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                       return( 1 );
+                       goto pc_bind_fail;
                }
                {
                        struct berval dn, ndn;
@@ -4025,29 +4316,31 @@ pc_temp_fail:
                                        "invalid bind baseDN=\"%s\"",
                                        c->argv[5] );
                                Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                                       return( 1 );
+                               goto pc_bind_fail;
                        }
                        if ( temp->bindbase.bv_val )
                                ch_free( temp->bindbase.bv_val );
                        temp->bindbase = ndn;
                }
                {
-                       /* convert the template into all presence filters */
+                       /* convert the template into dummy filter */
                        struct berval bv;
-                       char *eq = temp->querystr.bv_val, *e2;
+                       char *eq = temp->bindftemp.bv_val, *e2;
                        Filter *f;
                        i = 0;
                        while ((eq = strchr(eq, '=' ))) {
                                eq++;
-                               i++;
+                               if ( eq[0] == ')' )
+                                       i++;
                        }
-                       bv.bv_len = temp->querystr.bv_len + i;
+                       bv.bv_len = temp->bindftemp.bv_len + i;
                        bv.bv_val = ch_malloc( bv.bv_len + 1 );
-                       for ( e2 = bv.bv_val, eq = temp->querystr.bv_val;
+                       for ( e2 = bv.bv_val, eq = temp->bindftemp.bv_val;
                                *eq; eq++ ) {
                                if ( *eq == '=' ) {
                                        *e2++ = '=';
-                                       *e2++ = '*';
+                                       if ( eq[1] == ')' )
+                                               *e2++ = '*';
                                } else {
                                        *e2++ = *eq;
                                }
@@ -4059,7 +4352,9 @@ pc_temp_fail:
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                        "unable to parse bindfilter=\"%s\"", bv.bv_val );
                                Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                                       return( 1 );
+                               ch_free( temp->bindbase.bv_val );
+                               BER_BVZERO( &temp->bindbase );
+                               goto pc_bind_fail;
                        }
                        if ( temp->bindfilter )
                                filter_free( temp->bindfilter );
@@ -4069,7 +4364,7 @@ pc_temp_fail:
                        temp->bindfilter = f;
                }
                temp->bindttr = (time_t)t;
-               temp->bindscope = i;
+               temp->bindscope = num;
                cm->cache_binds = 1;
                break;
 
@@ -4207,9 +4502,13 @@ pcache_db_init(
        cm->check_cacheability = 0;
        cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
        cm->defer_db_open = 1;
+       cm->cache_binds = 0;
        cm->cc_period = 1000;
        cm->cc_paused = 0;
        cm->cc_arg = NULL;
+#ifdef PCACHE_MONITOR
+       cm->monitor_cb = NULL;
+#endif /* PCACHE_MONITOR */
 
        qm->attr_sets = NULL;
        qm->templates = NULL;
@@ -4309,13 +4608,13 @@ pcache_db_open2(
                        OperationBuffer opbuf;
                        Operation       *op;
                        slap_callback   cb = { 0 };
-                       SlapReply       rs = { 0 };
+                       SlapReply       rs = { REP_RESULT };
                        BerVarray       vals = NULL;
                        Filter          f = { 0 }, f2 = { 0 };
                        AttributeAssertion      ava = ATTRIBUTEASSERTION_INIT;
                        AttributeName   attrs[ 2 ] = {{{ 0 }}};
 
-                       connection_fake_init( &conn, &opbuf, thrctx );
+                       connection_fake_init2( &conn, &opbuf, thrctx, 0 );
                        op = &opbuf.ob_op;
 
                        op->o_bd = &cm->db;
@@ -4339,7 +4638,7 @@ pcache_db_open2(
                        op->ors_slimit = 1;
                        op->ors_tlimit = SLAP_NO_LIMIT;
                        op->ors_limit = NULL;
-                       ber_str2bv( "(cachedQueryURL=*)", 0, 0, &op->ors_filterstr );
+                       ber_str2bv( "(pcacheQueryURL=*)", 0, 0, &op->ors_filterstr );
                        f.f_choice = LDAP_FILTER_PRESENT;
                        f.f_desc = ad_cachedQueryURL;
                        op->ors_filter = &f;
@@ -4374,8 +4673,8 @@ pcache_db_open2(
                        op->ors_scope = LDAP_SCOPE_SUBTREE;
                        op->ors_attrs = slap_anlist_no_attrs;
 
+                       rs_reinit( &rs, REP_RESULT );
                        op->o_callback->sc_response = pcache_cachedquery_count_cb;
-                       rs.sr_nentries = 0;
                        op->o_callback->sc_private = &rs.sr_nentries;
 
                        rc = op->o_bd->be_search( op, &rs );
@@ -4476,6 +4775,16 @@ pcache_db_close(
        QueryTemplate *tm;
        int i, rc = 0;
 
+       /* stop the thread ... */
+       if ( cm->cc_arg ) {
+               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+               if ( ldap_pvt_runqueue_isrunning( &slapd_rq, cm->cc_arg ) ) {
+                       ldap_pvt_runqueue_stoptask( &slapd_rq, cm->cc_arg );
+               }
+               ldap_pvt_runqueue_remove( &slapd_rq, cm->cc_arg );
+               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+       }
+
        if ( cm->save_queries ) {
                CachedQuery     *qc;
                BerVarray       vals = NULL;
@@ -4491,9 +4800,10 @@ pcache_db_close(
 
                thrctx = ldap_pvt_thread_pool_context();
 
-               connection_fake_init( &conn, &opbuf, thrctx );
+               connection_fake_init2( &conn, &opbuf, thrctx, 0 );
                op = &opbuf.ob_op;
 
+                mod.sml_numvals = 0;
                if ( qm->templates != NULL ) {
                        for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
                                for ( qc = tm->query; qc; qc = qc->next ) {
@@ -4501,6 +4811,7 @@ pcache_db_close(
 
                                        if ( query2url( op, qc, &bv, 0 ) == 0 ) {
                                                ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
+                                               mod.sml_numvals++;
                                        }
                                }
                        }
@@ -4527,7 +4838,6 @@ pcache_db_close(
                mod.sml_type = ad_cachedQueryURL->ad_cname;
                mod.sml_values = vals;
                mod.sml_nvalues = NULL;
-                mod.sml_numvals = 1;
                mod.sml_next = NULL;
                Debug( pcache_debug,
                        "%sSETTING CACHED QUERY URLS\n",
@@ -4544,15 +4854,6 @@ pcache_db_close(
        cm->db.be_limits = NULL;
        cm->db.be_acl = NULL;
 
-       /* stop the thread ... */
-       if ( cm->cc_arg ) {
-               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
-               if ( ldap_pvt_runqueue_isrunning( &slapd_rq, cm->cc_arg ) ) {
-                       ldap_pvt_runqueue_stoptask( &slapd_rq, cm->cc_arg );
-               }
-               ldap_pvt_runqueue_remove( &slapd_rq, cm->cc_arg );
-               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
-       }
 
        if ( cm->db.bd_info->bi_db_close ) {
                rc = cm->db.bd_info->bi_db_close( &cm->db, NULL );
@@ -4566,12 +4867,29 @@ pcache_db_close(
                }
                avl_free( tm->qbase, pcache_free_qbase );
                free( tm->querystr.bv_val );
+               free( tm->bindfattrs );
+               free( tm->bindftemp.bv_val );
+               free( tm->bindfilterstr.bv_val );
+               free( tm->bindbase.bv_val );
+               filter_free( tm->bindfilter );
                ldap_pvt_thread_rdwr_destroy( &tm->t_rwlock );
                free( tm->t_attrs.attrs );
                free( tm );
        }
 
-       for ( i=0; i<cm->numattrsets; i++ ) {
+       for ( i = 0; i < cm->numattrsets; i++ ) {
+               int j;
+
+               /* Account of LDAP_NO_ATTRS */
+               if ( !qm->attr_sets[i].count ) continue;
+
+               for ( j = 0; !BER_BVISNULL( &qm->attr_sets[i].attrs[j].an_name ); j++ ) {
+                       if ( qm->attr_sets[i].attrs[j].an_desc &&
+                                       ( qm->attr_sets[i].attrs[j].an_desc->ad_flags &
+                                         SLAP_DESC_TEMPORARY ) ) {
+                               slap_sl_mfuncs.bmf_free( qm->attr_sets[i].attrs[j].an_desc, NULL );
+                       }
+               }
                free( qm->attr_sets[i].attrs );
        }
        free( qm->attr_sets );
@@ -4655,6 +4973,7 @@ static char *extops[] = {
 };
 #endif /* PCACHE_CONTROL_PRIVDB */
 
+static struct berval pcache_exop_MODIFY_PASSWD = BER_BVC( LDAP_EXOP_MODIFY_PASSWD );
 #ifdef PCACHE_EXOP_QUERY_DELETE
 static struct berval pcache_exop_QUERY_DELETE = BER_BVC( PCACHE_EXOP_QUERY_DELETE );
 
@@ -4890,7 +5209,7 @@ pcache_exop_query_delete(
                len = snprintf( buf, sizeof( buf ), " dn=\"%s\"", op->o_req_ndn.bv_val );
 
                if ( !BER_BVISNULL( &uuid ) && len < sizeof( buf ) ) {
-                       snprintf( &buf[ len ], sizeof( buf ) - len, " queryId=\"%s\"", uuid.bv_val );
+                       snprintf( &buf[ len ], sizeof( buf ) - len, " pcacheQueryId=\"%s\"", uuid.bv_val );
                }
 
                Debug( LDAP_DEBUG_STATS, "%s QUERY DELETE%s\n",
@@ -4899,6 +5218,10 @@ pcache_exop_query_delete(
        op->o_req_dn = op->o_req_ndn;
 
        op->o_bd = select_backend( &op->o_req_ndn, 0 );
+       if ( op->o_bd == NULL ) {
+               send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
+                       "no global superior knowledge" );
+       }
        rs->sr_err = backend_check_restrictions( op, rs,
                (struct berval *)&pcache_exop_QUERY_DELETE );
        if ( rs->sr_err != LDAP_SUCCESS ) {
@@ -4928,6 +5251,7 @@ done:;
 
         return rs->sr_err;
 }
+#endif /* PCACHE_EXOP_QUERY_DELETE */
 
 static int
 pcache_op_extended( Operation *op, SlapReply *rs )
@@ -4941,6 +5265,7 @@ pcache_op_extended( Operation *op, SlapReply *rs )
        }
 #endif /* PCACHE_CONTROL_PRIVDB */
 
+#ifdef PCACHE_EXOP_QUERY_DELETE
        if ( bvmatch( &op->ore_reqoid, &pcache_exop_QUERY_DELETE ) ) {
                struct berval   uuid = BER_BVNULL;
                ber_tag_t       tag = LBER_DEFAULT;
@@ -4968,11 +5293,66 @@ pcache_op_extended( Operation *op, SlapReply *rs )
                }
 
                op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
+               return rs->sr_err;
        }
+#endif /* PCACHE_EXOP_QUERY_DELETE */
 
-       return rs->sr_err;
+       /* We only care if we're configured for Bind caching */
+       if ( bvmatch( &op->ore_reqoid, &pcache_exop_MODIFY_PASSWD ) &&
+               cm->cache_binds ) {
+               /* See if the local entry exists and has a password.
+                * It's too much work to find the matching query, so
+                * we just see if there's a hashed password to update.
+                */
+               Operation op2 = *op;
+               Entry *e = NULL;
+               int rc;
+               int doit = 0;
+
+               op2.o_bd = &cm->db;
+               op2.o_dn = op->o_bd->be_rootdn;
+               op2.o_ndn = op->o_bd->be_rootndn;
+               rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL,
+                       slap_schema.si_ad_userPassword, 0, &e );
+               if ( rc == LDAP_SUCCESS && e ) {
+                       /* See if a recognized password is hashed here */
+                       Attribute *a = attr_find( e->e_attrs,
+                               slap_schema.si_ad_userPassword );
+                       if ( a && a->a_vals[0].bv_val[0] == '{' &&
+                               lutil_passwd_scheme( a->a_vals[0].bv_val )) {
+                               doit = 1;
+                       }
+                       be_entry_release_r( &op2, e );
+               }
+
+               if ( doit ) {
+                       rc = overlay_op_walk( op, rs, op_extended, on->on_info,
+                               on->on_next );
+                       if ( rc == LDAP_SUCCESS ) {
+                               req_pwdexop_s *qpw = &op->oq_pwdexop;
+
+                               /* We don't care if it succeeds or not */
+                               pc_setpw( &op2, &qpw->rs_new, cm );
+                       }
+                       return rc;
+               }
+       }
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+pcache_entry_release( Operation  *op, Entry *e, int rw )
+{
+       slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
+       cache_manager   *cm = on->on_bi.bi_private;
+       BackendDB *db = op->o_bd;
+       int rc;
+
+       op->o_bd = &cm->db;
+       rc = be_entry_release_rw( op, e, rw );
+       op->o_bd = db;
+       return rc;
 }
-#endif /* PCACHE_EXOP_QUERY_DELETE */
 
 #ifdef PCACHE_MONITOR
 
@@ -4989,8 +5369,6 @@ pcache_monitor_update(
        CachedQuery     *qc;
        BerVarray       vals = NULL;
 
-       assert( ad_cachedQueryURL != NULL );
-
        attr_delete( &e->e_attrs, ad_cachedQueryURL );
        if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( ad_cachedQueryURL, rs->sr_attrs ) )
                && qm->templates != NULL )
@@ -5014,6 +5392,36 @@ pcache_monitor_update(
                }
        }
 
+       {
+               Attribute       *a;
+               char            buf[ SLAP_TEXT_BUFLEN ];
+               struct berval   bv;
+
+               /* number of cached queries */
+               a = attr_find( e->e_attrs, ad_numQueries );
+               assert( a != NULL );
+
+               bv.bv_val = buf;
+               bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", cm->num_cached_queries );
+
+               if ( a->a_nvals != a->a_vals ) {
+                       ber_bvreplace( &a->a_nvals[ 0 ], &bv );
+               }
+               ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
+               /* number of cached entries */
+               a = attr_find( e->e_attrs, ad_numEntries );
+               assert( a != NULL );
+
+               bv.bv_val = buf;
+               bv.bv_len = snprintf( buf, sizeof( buf ), "%d", cm->cur_entries );
+
+               if ( a->a_nvals != a->a_vals ) {
+                       ber_bvreplace( &a->a_nvals[ 0 ], &bv );
+               }
+               ber_bvreplace( &a->a_vals[ 0 ], &bv );
+       }
+
        return SLAP_CB_CONTINUE;
 }
 
@@ -5053,6 +5461,22 @@ pcache_monitor_free(
                textbuf, sizeof( textbuf ) );
        /* don't care too much about return code... */
 
+       /* remove attrs */
+       mod.sm_values = NULL;
+       mod.sm_desc = ad_numQueries;
+       mod.sm_numvals = 0;
+       rc = modify_delete_values( e, &mod, 1, &text,
+               textbuf, sizeof( textbuf ) );
+       /* don't care too much about return code... */
+
+       /* remove attrs */
+       mod.sm_values = NULL;
+       mod.sm_desc = ad_numEntries;
+       mod.sm_numvals = 0;
+       rc = modify_delete_values( e, &mod, 1, &text,
+               textbuf, sizeof( textbuf ) );
+       /* don't care too much about return code... */
+
        return SLAP_CB_CONTINUE;
 }
 
@@ -5123,7 +5547,7 @@ pcache_monitor_db_open( BackendDB *be )
        }
 
        /* alloc as many as required (plus 1 for objectClass) */
-       a = attrs_alloc( 1 + 0 );
+       a = attrs_alloc( 1 + 2 );
        if ( a == NULL ) {
                rc = 1;
                goto cleanup;
@@ -5133,6 +5557,18 @@ pcache_monitor_db_open( BackendDB *be )
        attr_valadd( a, &oc_olmPCache->soc_cname, NULL, 1 );
        next = a->a_next;
 
+       {
+               struct berval   bv = BER_BVC( "0" );
+
+               next->a_desc = ad_numQueries;
+               attr_valadd( next, &bv, NULL, 1 );
+               next = next->a_next;
+
+               next->a_desc = ad_numEntries;
+               attr_valadd( next, &bv, NULL, 1 );
+               next = next->a_next;
+       }
+
        cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
        cb->mc_update = pcache_monitor_update;
        cb->mc_free = pcache_monitor_free;
@@ -5183,7 +5619,7 @@ pcache_monitor_db_close( BackendDB *be )
 
                if ( mi && &mi->bi_extra ) {
                        mbe = mi->bi_extra;
-                       mbe->unregister_entry_callback( NULL,
+                       mbe->unregister_entry_callback( &cm->monitor_ndn,
                                (monitor_callback_t *)cm->monitor_cb,
                                NULL, 0, NULL );
                }
@@ -5302,12 +5738,9 @@ pcache_initialize()
        pcache.on_bi.bi_op_add = pcache_op_privdb;
        pcache.on_bi.bi_op_delete = pcache_op_privdb;
 #endif /* PCACHE_CONTROL_PRIVDB */
-#ifdef PCACHE_EXOP_QUERY_DELETE
        pcache.on_bi.bi_extended = pcache_op_extended;
-#elif defined( PCACHE_CONTROL_PRIVDB )
-       pcache.on_bi.bi_extended = pcache_op_privdb;
-#endif
 
+       pcache.on_bi.bi_entry_release_rw = pcache_entry_release;
        pcache.on_bi.bi_chk_controls = pcache_chk_controls;
 
        pcache.on_bi.bi_cf_ocs = pcocs;