]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-bdb/search.c
add paged results support to back-ldap
[openldap] / servers / slapd / back-bdb / search.c
index e9ceab27deae2ac047671ab1fa2220cc16cb0261..fa80e81e01c8d4876bda7287df94f128196d32f3 100644 (file)
@@ -37,10 +37,12 @@ static int search_candidates(
        ID      *ids,
        ID      *scopes );
 
-static void send_pagerequest_response( 
+static int parse_paged_cookie( Operation *op, SlapReply *rs );
+
+static void send_paged_response( 
        Operation *op,
        SlapReply *rs,
-       ID  lastid,
+       ID  *lastid,
        int tentries );
 
 /* Dereference aliases for a single alias entry. Return the final
@@ -149,7 +151,7 @@ static int search_aliases(
        ID cursora, ida, cursoro, ido, *subscop2;
        Entry *matched, *a;
        EntryInfo *ei;
-       struct berval bv_alias = { sizeof("alias")-1, "alias" };
+       struct berval bv_alias = BER_BVC( "alias" );
        AttributeAssertion aa_alias;
        Filter  af;
        DB_LOCK locka, lockr;
@@ -215,9 +217,12 @@ static int search_aliases(
                        ida = bdb_idl_next(curscop, &cursora))
                {
                        ei = NULL;
+retry1:
                        rs->sr_err = bdb_cache_find_id(op, NULL,
                                ida, &ei, 0, locker, &lockr );
                        if (rs->sr_err != LDAP_SUCCESS) {
+                               if ( rs->sr_err == DB_LOCK_DEADLOCK ||
+                                       rs->sr_err == DB_LOCK_NOTGRANTED ) goto retry1;
                                continue;
                        }
                        a = ei->bei_e;
@@ -281,9 +286,15 @@ nextido:
                 * Set the name so that the scope's IDL can be retrieved.
                 */
                ei = NULL;
+sameido:
                rs->sr_err = bdb_cache_find_id(op, NULL, ido, &ei,
                        0, locker, &locka );
-               if ( rs->sr_err != LDAP_SUCCESS ) goto nextido;
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       if ( rs->sr_err == DB_LOCK_DEADLOCK ||
+                               rs->sr_err == DB_LOCK_NOTGRANTED )
+                               goto sameido;
+                       goto nextido;
+               }
                e = ei->bei_e;
        }
        return rs->sr_err;
@@ -330,7 +341,7 @@ bdb_abandon( Operation *op, SlapReply *rs )
        ps = bdb_drop_psearch( op, op->oq_abandon.rs_msgid );
        if ( ps ) {
                if ( ps->o_tmpmemctx ) {
-                       sl_mem_destroy( NULL, ps->o_tmpmemctx );
+                       slap_sl_mem_destroy( NULL, ps->o_tmpmemctx );
                }
                slap_op_free ( ps );
                return LDAP_SUCCESS;
@@ -348,7 +359,7 @@ bdb_cancel( Operation *op, SlapReply *rs )
                rs->sr_err = LDAP_CANCELLED;
                send_ldap_result( ps, rs );
                if ( ps->o_tmpmemctx ) {
-                       sl_mem_destroy( NULL, ps->o_tmpmemctx );
+                       slap_sl_mem_destroy( NULL, ps->o_tmpmemctx );
                }
                slap_op_free ( ps );
                return LDAP_SUCCESS;
@@ -361,6 +372,18 @@ int bdb_search( Operation *op, SlapReply *rs )
        return bdb_do_search( op, rs, op, NULL, 0 );
 }
 
+int bdb_psearch( Operation *op, SlapReply *rs, Operation *sop,
+       Entry *ps_e, int ps_type )
+{
+       int     rc;
+
+       sop->o_private = op->o_private;
+       rc = bdb_do_search( op, rs, sop, ps_e, ps_type );
+       sop->o_private = NULL;
+
+       return rc;
+}
+
 /* For persistent searches, op is the currently executing operation,
  * sop is the persistent search. For regular searches, sop = op.
  */
@@ -373,20 +396,19 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
        ID              id, cursor;
        ID              candidates[BDB_IDL_UM_SIZE];
        ID              scopes[BDB_IDL_DB_SIZE];
-       Entry           *e = NULL, base;
-       Entry   *matched = NULL;
-       EntryInfo       *ei;
-       struct berval   realbase = { 0, NULL };
+       Entry           *e = NULL, base, e_root = {0};
+       Entry           *matched = NULL;
+       EntryInfo       *ei, ei_root = {0};
+       struct berval   realbase = BER_BVNULL;
        int             manageDSAit;
        int             tentries = 0;
        ID              lastid = NOID;
        AttributeName   *attrs;
 
-       Filter contextcsnand, contextcsnle, cookief, csnfnot,
-               csnfeq, csnfand, csnfge;
+       Filter          contextcsnand, contextcsnle, cookief, csnfnot,
+                       csnfeq, csnfand, csnfge;
        AttributeAssertion aa_ge, aa_eq, aa_le;
-       int             entry_count = 0;
-       struct berval *search_context_csn = NULL;
+       struct berval   *search_context_csn = NULL;
        DB_LOCK         ctxcsn_lock;
        LDAPControl     *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
        int             num_ctrls = 0;
@@ -395,8 +417,6 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
        int             entry_sync_state = -1;
        AttributeName   null_attr;
        int             no_sync_state_change = 0;
-       struct slap_limits_set *limit = NULL;
-       int isroot = 0;
 
        u_int32_t       locker = 0;
        DB_LOCK         lock;
@@ -411,14 +431,18 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
        BerVarray       syncUUID_set = NULL;
        int                     syncUUID_set_cnt = 0;
 
+       struct  bdb_op_info     *opinfo = NULL;
+       DB_TXN                  *ltid = NULL;
+
 #ifdef NEW_LOGGING
        LDAP_LOG( OPERATION, ENTRY, "bdb_search\n", 0, 0, 0 );
 #else
-       Debug( LDAP_DEBUG_TRACE, "=> bdb_search\n",
-               0, 0, 0);
+       Debug( LDAP_DEBUG_TRACE, "=> bdb_search\n", 0, 0, 0);
 #endif
        attrs = sop->oq_search.rs_attrs;
 
+       opinfo = (struct bdb_op_info *) op->o_private;
+
        if ( !IS_PSEARCH && sop->o_sync_mode & SLAP_SYNC_REFRESH_AND_PERSIST ) {
                struct slap_session_entry *sent;
                if ( sop->o_sync_state.sid >= 0 ) {
@@ -482,8 +506,7 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
        null_attr.an_desc = NULL;
        null_attr.an_oc = NULL;
        null_attr.an_oc_exclude = 0;
-       null_attr.an_name.bv_len = 0;
-       null_attr.an_name.bv_val = NULL;
+       BER_BVZERO( &null_attr.an_name );
 
        for( num_ctrls = 0; num_ctrls < SLAP_MAX_RESPONSE_CONTROLS; num_ctrls++ ) {
                ctrls[num_ctrls] = NULL;
@@ -495,8 +518,7 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
                attrs[0].an_desc = NULL;
                attrs[0].an_oc = NULL;
                attrs[0].an_oc_exclude = 0;
-               attrs[0].an_name.bv_len = 0;
-               attrs[0].an_name.bv_val = NULL;
+               BER_BVZERO( &attrs[0].an_name );
        }
 
        manageDSAit = get_manageDSAit( sop );
@@ -512,24 +534,35 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
                }
        }
 
-       rs->sr_err = LOCK_ID( bdb->bi_dbenv, &locker );
+       if ( opinfo ) {
+               ltid = opinfo->boi_txn;
+               locker = TXN_ID( ltid );
+       } else {
+               rs->sr_err = LOCK_ID( bdb->bi_dbenv, &locker );
 
-       switch(rs->sr_err) {
-       case 0:
-               break;
-       default:
-               send_ldap_error( sop, rs, LDAP_OTHER, "internal error" );
-               return rs->sr_err;
+               switch(rs->sr_err) {
+               case 0:
+                       break;
+               default:
+                       send_ldap_error( sop, rs, LDAP_OTHER, "internal error" );
+                       return rs->sr_err;
+               }
        }
 
        if ( sop->o_req_ndn.bv_len == 0 ) {
                /* DIT root special case */
-               e = (Entry *) &slap_entry_root;
+               ei_root.bei_e = &e_root;
+               ei_root.bei_parent = &ei_root;
+               e_root.e_private = &ei_root;
+               e_root.e_id = 0;
+               BER_BVSTR( &e_root.e_nname, "" );
+               BER_BVSTR( &e_root.e_name, "" );
+               ei = &ei_root;
                rs->sr_err = LDAP_SUCCESS;
        } else {
 dn2entry_retry:
                /* get entry with reader lock */
-               rs->sr_err = bdb_dn2entry( op, NULL, &sop->o_req_ndn, &ei,
+               rs->sr_err = bdb_dn2entry( op, ltid, &sop->o_req_ndn, &ei,
                        1, locker, &lock );
        }
 
@@ -542,14 +575,16 @@ dn2entry_retry:
                break;
        case LDAP_BUSY:
                send_ldap_error( sop, rs, LDAP_BUSY, "ldap server busy" );
-               LOCK_ID_FREE (bdb->bi_dbenv, locker );
+               if ( !opinfo )
+                       LOCK_ID_FREE (bdb->bi_dbenv, locker );
                return LDAP_BUSY;
        case DB_LOCK_DEADLOCK:
        case DB_LOCK_NOTGRANTED:
                goto dn2entry_retry;
        default:
                send_ldap_error( sop, rs, LDAP_OTHER, "internal error" );
-               LOCK_ID_FREE (bdb->bi_dbenv, locker );
+               if ( !opinfo )
+                       LOCK_ID_FREE (bdb->bi_dbenv, locker );
                return rs->sr_err;
        }
 
@@ -560,7 +595,7 @@ dn2entry_retry:
        }
 
        if ( e == NULL ) {
-               struct berval matched_dn = { 0, NULL };
+               struct berval matched_dn = BER_BVNULL;
 
                if ( matched != NULL ) {
                        BerVarray erefs;
@@ -589,7 +624,8 @@ dn2entry_retry:
                rs->sr_matched = matched_dn.bv_val;
                send_ldap_result( sop, rs );
 
-               LOCK_ID_FREE (bdb->bi_dbenv, locker );
+               if ( !opinfo )
+                       LOCK_ID_FREE (bdb->bi_dbenv, locker );
                if ( rs->sr_ref ) {
                        ber_bvarray_free( rs->sr_ref );
                        rs->sr_ref = NULL;
@@ -601,7 +637,7 @@ dn2entry_retry:
                return rs->sr_err;
        }
 
-       if ( !manageDSAit && e != &slap_entry_root && is_entry_referral( e ) ) {
+       if ( !manageDSAit && e != &e_root && is_entry_referral( e ) ) {
                /* entry is a referral, don't allow add */
                struct berval matched_dn;
                BerVarray erefs;
@@ -631,7 +667,8 @@ dn2entry_retry:
                rs->sr_matched = matched_dn.bv_val;
                send_ldap_result( sop, rs );
 
-               LOCK_ID_FREE (bdb->bi_dbenv, locker );
+               if ( !opinfo )
+                       LOCK_ID_FREE (bdb->bi_dbenv, locker );
                ber_bvarray_free( rs->sr_ref );
                rs->sr_ref = NULL;
                ber_memfree( matched_dn.bv_val );
@@ -647,82 +684,8 @@ dn2entry_retry:
                return 1;
        }
 
-       /* if not root, get appropriate limits */
-       if ( be_isroot( op->o_bd, &sop->o_ndn ) ) {
-               isroot = 1;
-       } else {
-               ( void ) get_limits( op, &sop->o_ndn, &limit );
-       }
-
-       /* The time/size limits come first because they require very little
-        * effort, so there's no chance the candidates are selected and then 
-        * the request is not honored only because of time/size constraints
-        */
-
-       /* if no time limit requested, use soft limit (unless root!) */
-       if ( isroot ) {
-               if ( sop->oq_search.rs_tlimit == 0 ) {
-                       sop->oq_search.rs_tlimit = -1;  /* allow root to set no limit */
-               }
-
-               if ( sop->oq_search.rs_slimit == 0 ) {
-                       sop->oq_search.rs_slimit = -1;
-               }
-
-       } else {
-               /* if no limit is required, use soft limit */
-               if ( sop->oq_search.rs_tlimit <= 0 ) {
-                       sop->oq_search.rs_tlimit = limit->lms_t_soft;
-
-               /* if requested limit higher than hard limit, abort */
-               } else if ( sop->oq_search.rs_tlimit > limit->lms_t_hard ) {
-                       /* no hard limit means use soft instead */
-                       if ( limit->lms_t_hard == 0
-                                       && limit->lms_t_soft > -1
-                                       && sop->oq_search.rs_tlimit > limit->lms_t_soft ) {
-                               sop->oq_search.rs_tlimit = limit->lms_t_soft;
-
-                       /* positive hard limit means abort */
-                       } else if ( limit->lms_t_hard > 0 ) {
-                               rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
-                               send_ldap_result( sop, rs );
-                               rs->sr_err = LDAP_SUCCESS;
-                               goto done;
-                       }
-               
-                       /* negative hard limit means no limit */
-               }
-               
-               /* if no limit is required, use soft limit */
-               if ( sop->oq_search.rs_slimit <= 0 ) {
-                       if ( get_pagedresults(sop) && limit->lms_s_pr != 0 ) {
-                               sop->oq_search.rs_slimit = limit->lms_s_pr;
-                       } else {
-                               sop->oq_search.rs_slimit = limit->lms_s_soft;
-                       }
-
-               /* if requested limit higher than hard limit, abort */
-               } else if ( sop->oq_search.rs_slimit > limit->lms_s_hard ) {
-                       /* no hard limit means use soft instead */
-                       if ( limit->lms_s_hard == 0
-                                       && limit->lms_s_soft > -1
-                                       && sop->oq_search.rs_slimit > limit->lms_s_soft ) {
-                               sop->oq_search.rs_slimit = limit->lms_s_soft;
-
-                       /* positive hard limit means abort */
-                       } else if ( limit->lms_s_hard > 0 ) {
-                               rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
-                               send_ldap_result( sop, rs );
-                               rs->sr_err = LDAP_SUCCESS;      
-                               goto done;
-                       }
-                       
-                       /* negative hard limit means no limit */
-               }
-       }
-
        /* compute it anyway; root does not use it */
-       stoptime = op->o_time + sop->oq_search.rs_tlimit;
+       stoptime = op->o_time + sop->ors_tlimit;
 
        /* need normalized dn below */
        ber_dupbv( &realbase, &e->e_nname );
@@ -734,7 +697,7 @@ dn2entry_retry:
        base.e_nname = realbase;
        base.e_id = e->e_id;
 
-       if ( e != &slap_entry_root ) {
+       if ( e != &e_root ) {
                bdb_cache_return_entry_r(bdb->bi_dbenv, &bdb->bi_cache, e, &lock);
        }
        e = NULL;
@@ -809,22 +772,33 @@ dn2entry_retry:
        }
 
        /* if not root and candidates exceed to-be-checked entries, abort */
-       if ( !isroot && limit->lms_s_unchecked != -1 ) {
-               if ( BDB_IDL_N(candidates) > (unsigned) limit->lms_s_unchecked ) {
-                       rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
-                       send_ldap_result( sop, rs );
-                       rs->sr_err = LDAP_SUCCESS;
-                       goto done;
-               }
+       if ( sop->ors_limit     /* isroot == FALSE */ &&
+               sop->ors_limit->lms_s_unchecked != -1 &&
+               BDB_IDL_N(candidates) > (unsigned) sop->ors_limit->lms_s_unchecked )
+       {
+               rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+               send_ldap_result( sop, rs );
+               rs->sr_err = LDAP_SUCCESS;
+               goto done;
        }
 
-       if ( isroot || !limit->lms_s_pr_hide ) {
+       if ( sop->ors_limit == NULL     /* isroot == TRUE */ ||
+               !sop->ors_limit->lms_s_pr_hide )
+       {
                tentries = BDB_IDL_N(candidates);
        }
 
-       if ( get_pagedresults(sop) ) {
-               if ( sop->o_pagedresults_state.ps_cookie == 0 ) {
-                       id = 0;
+       if ( get_pagedresults( sop ) > SLAP_NO_CONTROL ) {
+               /* deferred cookie parsing */
+               rs->sr_err = parse_paged_cookie( sop, rs );
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       send_ldap_result( sop, rs );
+                       goto done;
+               }
+
+               if ( (ID)( sop->o_pagedresults_state.ps_cookie ) == 0 ) {
+                       id = bdb_idl_first( candidates, &cursor );
+
                } else {
                        if ( sop->o_pagedresults_size == 0 ) {
                                rs->sr_err = LDAP_SUCCESS;
@@ -833,20 +807,25 @@ dn2entry_retry:
                                goto done;
                        }
                        for ( id = bdb_idl_first( candidates, &cursor );
-                               id != NOID && id <= (ID)( sop->o_pagedresults_state.ps_cookie );
-                               id = bdb_idl_next( candidates, &cursor ) ) /* empty */;
+                               id != NOID &&
+                                       id <= (ID)( sop->o_pagedresults_state.ps_cookie );
+                               id = bdb_idl_next( candidates, &cursor ) )
+                       {
+                               /* empty */;
+                       }
                }
+
                if ( cursor == NOID ) {
 #ifdef NEW_LOGGING
                        LDAP_LOG ( OPERATION, RESULTS, 
                                "bdb_search: no paged results candidates\n", 
-                       0, 0, 0 );
+                               0, 0, 0 );
 #else
                        Debug( LDAP_DEBUG_TRACE, 
                                "bdb_search: no paged results candidates\n",
                                0, 0, 0 );
 #endif
-                       send_pagerequest_response( sop, rs, lastid, 0 );
+                       send_paged_response( sop, rs, &lastid, 0 );
 
                        rs->sr_err = LDAP_OTHER;
                        goto done;
@@ -943,7 +922,9 @@ loop_begin:
                }
 
                /* check time limit */
-               if ( sop->oq_search.rs_tlimit != -1 && slap_get_time() > stoptime ) {
+               if ( sop->ors_tlimit != SLAP_NO_LIMIT
+                               && slap_get_time() > stoptime )
+               {
                        rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
                        rs->sr_ref = rs->sr_v2ref;
                        send_ldap_result( sop, rs );
@@ -955,7 +936,7 @@ loop_begin:
 id2entry_retry:
                        /* get the entry with reader lock */
                        ei = NULL;
-                       rs->sr_err = bdb_cache_find_id( op, NULL,
+                       rs->sr_err = bdb_cache_find_id( op, ltid,
                                id, &ei, 0, locker, &lock );
 
                        if (rs->sr_err == LDAP_BUSY) {
@@ -1043,12 +1024,14 @@ id2entry_retry:
                        if ( ei->bei_parent->bei_id == base.e_id ) scopeok = 1;
                        break;
 
+#ifdef LDAP_SCOPE_CHILDREN
                case LDAP_SCOPE_CHILDREN:
                        if ( id == base.e_id ) break;
                        /* Fall-thru */
+#endif
                case LDAP_SCOPE_SUBTREE: {
                        EntryInfo *tmp;
-                       for (tmp = BEI(e); tmp->bei_parent;
+                       for (tmp = BEI(e); tmp;
                                 tmp = tmp->bei_parent ) {
                                if ( tmp->bei_id == base.e_id ) {
                                        scopeok = 1;
@@ -1139,6 +1122,7 @@ id2entry_retry:
                        } else {
                                rs->sr_err = LDAP_COMPARE_TRUE;
                        }
+
                } else {
                        if ( sop->o_sync_mode & SLAP_SYNC_REFRESH ) {
                                rc_sync = test_filter( sop, rs->sr_entry, &cookief );
@@ -1157,6 +1141,7 @@ id2entry_retry:
 #endif
                                                }
                                                entry_sync_state = LDAP_SYNC_ADD;
+
                                        } else {
                                                if ( no_sync_state_change ) {
                                                        goto loop_continue;
@@ -1164,6 +1149,7 @@ id2entry_retry:
                                                entry_sync_state = LDAP_SYNC_PRESENT;
                                        }
                                }
+
                        } else {
                                rs->sr_err = test_filter( sop,
                                        rs->sr_entry, sop->oq_search.rs_filter );
@@ -1172,7 +1158,7 @@ id2entry_retry:
 
                if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
                        /* check size limit */
-            if ( --sop->oq_search.rs_slimit == -1 &&
+                       if ( --sop->ors_slimit == -1 &&
                                sop->o_sync_slog_size == -1 )
                        {
                                if (!IS_PSEARCH) {
@@ -1188,10 +1174,9 @@ id2entry_retry:
                                goto done;
                        }
 
-                       if ( get_pagedresults(sop) ) {
+                       if ( get_pagedresults(sop) > SLAP_NO_CONTROL ) {
                                if ( rs->sr_nentries >= sop->o_pagedresults_size ) {
-                                       send_pagerequest_response( sop, rs,
-                                               lastid, tentries );
+                                       send_paged_response( sop, rs, &lastid, tentries );
                                        goto done;
                                }
                                lastid = id;
@@ -1223,6 +1208,7 @@ id2entry_retry:
                                                        }
                                                        if (psid_e != NULL) free (psid_e);
                                                }
+
                                                if ( ps_type == LDAP_PSEARCH_BY_ADD ) {
                                                        entry_sync_state = LDAP_SYNC_ADD;
                                                } else if ( ps_type == LDAP_PSEARCH_BY_DELETE ) {
@@ -1239,6 +1225,7 @@ id2entry_retry:
                                                        rs->sr_err = LDAP_OTHER;
                                                        goto done;
                                                }
+
                                                if ( sop->o_sync_slog_size != -1 ) {
                                                        if ( entry_sync_state == LDAP_SYNC_DELETE ) {
                                                                result = slap_add_session_log( op, sop, e );
@@ -1256,15 +1243,20 @@ id2entry_retry:
                                                                num_ctrls++, 1, &cookie );
                                                        if ( rs->sr_err != LDAP_SUCCESS ) goto done;
                                                        rs->sr_attrs = attrs;
+                                                       rs->sr_operational_attrs = NULL;
                                                        rs->sr_ctrls = ctrls;
+                                                       rs->sr_flags = 0;
                                                        result = send_search_entry( sop, rs );
                                                        if ( cookie.bv_val ) ch_free( cookie.bv_val );  
-                                                       sl_free( ctrls[num_ctrls-1]->ldctl_value.bv_val,
-                                                                sop->o_tmpmemctx );
-                                                       sl_free( ctrls[--num_ctrls], sop->o_tmpmemctx );
+                                                       slap_sl_free(
+                                                               ctrls[num_ctrls-1]->ldctl_value.bv_val,
+                                                               sop->o_tmpmemctx );
+                                                       slap_sl_free( ctrls[--num_ctrls],
+                                                               sop->o_tmpmemctx );
                                                        ctrls[num_ctrls] = NULL;
                                                        rs->sr_ctrls = NULL;
                                                }
+
                                        } else if ( ps_type == LDAP_PSEARCH_BY_PREMODIFY ) {
                                                struct psid_entry* psid_e;
                                                psid_e = (struct psid_entry *) ch_calloc(1,
@@ -1284,6 +1276,7 @@ id2entry_retry:
                                                        ps_type, 0, 0);
 #endif
                                        }
+
                                } else {
                                        if ( sop->o_sync_mode & SLAP_SYNC_REFRESH ) {
                                                if ( rc_sync == LDAP_COMPARE_TRUE ) { /* ADD */
@@ -1293,12 +1286,17 @@ id2entry_retry:
                                                        if ( rs->sr_err != LDAP_SUCCESS ) goto done;
                                                        rs->sr_ctrls = ctrls;
                                                        rs->sr_attrs = sop->oq_search.rs_attrs;
+                                                       rs->sr_operational_attrs = NULL;
+                                                       rs->sr_flags = 0;
                                                        result = send_search_entry( sop, rs );
-                                                       sl_free( ctrls[num_ctrls-1]->ldctl_value.bv_val,
-                                                                sop->o_tmpmemctx );
-                                                       sl_free( ctrls[--num_ctrls], sop->o_tmpmemctx );
+                                                       slap_sl_free(
+                                                               ctrls[num_ctrls-1]->ldctl_value.bv_val,
+                                                               sop->o_tmpmemctx );
+                                                       slap_sl_free( ctrls[--num_ctrls],
+                                                               sop->o_tmpmemctx );
                                                        ctrls[num_ctrls] = NULL;
                                                        rs->sr_ctrls = NULL;
+
                                                } else { /* PRESENT */
                                                        if ( sync_send_present_mode ) {
                                                                result = slap_build_syncUUID_set( sop,
@@ -1325,13 +1323,18 @@ id2entry_retry:
                                                                                syncUUID_set_cnt = 0;
                                                                        }
                                                                }
+
                                                        } else {
                                                                result = 1;
                                                        }
                                                }
+
                                        } else {
                                                rs->sr_attrs = sop->oq_search.rs_attrs;
+                                               rs->sr_operational_attrs = NULL;
                                                rs->sr_ctrls = NULL;
+                                               rs->sr_flags = 0;
+                                               rs->sr_err = LDAP_SUCCESS;
                                                result = send_search_entry( sop, rs );
                                        }
                                }
@@ -1342,15 +1345,17 @@ id2entry_retry:
                                case 1:         /* entry not sent */
                                        break;
                                case -1:        /* connection closed */
-                                       if (!IS_PSEARCH)
-                                       bdb_cache_return_entry_r(bdb->bi_dbenv,
-                                               &bdb->bi_cache, e, &lock);
+                                       if (!IS_PSEARCH) {
+                                               bdb_cache_return_entry_r(bdb->bi_dbenv,
+                                                       &bdb->bi_cache, e, &lock);
+                                       }
                                        e = NULL;
                                        rs->sr_entry = NULL;
                                        rs->sr_err = LDAP_OTHER;
                                        goto done;
                                }
                        }
+
                } else {
 #ifdef NEW_LOGGING
                        LDAP_LOG ( OPERATION, RESULTS,
@@ -1403,6 +1408,7 @@ nochange:
                                        rs->sr_ctrls = NULL;
                                        slap_send_syncinfo( sop, rs,
                                                LDAP_TAG_SYNC_REFRESH_PRESENT, &cookie, 1, NULL, 0 );
+
                                } else {
                                        if ( !no_sync_state_change ) {
                                                int slog_found = 0;
@@ -1412,7 +1418,8 @@ nochange:
                                                {
                                                        if ( ps_list->o_sync_slog_size > 0 ) {
                                                                if ( ps_list->o_sync_state.sid ==
-                                                                       sop->o_sync_state.sid ) {
+                                                                       sop->o_sync_state.sid )
+                                                               {
                                                                        slog_found = 1;
                                                                        break;
                                                                }
@@ -1427,6 +1434,7 @@ nochange:
                                                }
                                                ldap_pvt_thread_rdwr_runlock( &bdb->bi_pslist_rwlock );
                                        }
+
                                        rs->sr_err = LDAP_SUCCESS;
                                        rs->sr_rspoid = LDAP_SYNC_INFO;
                                        rs->sr_ctrls = NULL;
@@ -1434,9 +1442,8 @@ nochange:
                                                LDAP_TAG_SYNC_REFRESH_DELETE, &cookie, 1, NULL, 0 );
                                }
 
-                               if ( cookie.bv_val ) {
-                                       ch_free( cookie.bv_val );
-                               }
+                               if ( cookie.bv_val ) ch_free( cookie.bv_val );
+
                        } else {
                                /* refreshOnly mode */
                                struct berval cookie;
@@ -1446,6 +1453,7 @@ nochange:
                                if ( sync_send_present_mode ) {
                                        slap_build_sync_done_ctrl( sop, rs, ctrls,
                                                num_ctrls++, 1, &cookie, LDAP_SYNC_REFRESH_PRESENTS );
+
                                } else {
                                        if ( !no_sync_state_change ) {
                                                int slog_found = 0;
@@ -1467,6 +1475,7 @@ nochange:
                                                }
                                                ldap_pvt_thread_rdwr_runlock( &bdb->bi_pslist_rwlock );
                                        }
+
                                        slap_build_sync_done_ctrl( sop, rs, ctrls,
                                                num_ctrls++, 1, &cookie, LDAP_SYNC_REFRESH_DELETES );
                                }
@@ -1478,19 +1487,24 @@ nochange:
                                rs->sr_rspoid = NULL;
                                send_ldap_result( sop, rs );
                                if ( ctrls[num_ctrls-1]->ldctl_value.bv_val != NULL ) {
-                                       sl_free( ctrls[num_ctrls-1]->ldctl_value.bv_val,
+                                       slap_sl_free( ctrls[num_ctrls-1]->ldctl_value.bv_val,
                                                sop->o_tmpmemctx );
                                }
-                               sl_free( ctrls[--num_ctrls], sop->o_tmpmemctx );
+                               slap_sl_free( ctrls[--num_ctrls], sop->o_tmpmemctx );
                                ctrls[num_ctrls] = NULL;
                                if ( cookie.bv_val ) ch_free( cookie.bv_val );  
                        }
+
                } else {
                        rs->sr_ctrls = NULL;
                        rs->sr_ref = rs->sr_v2ref;
                        rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
                        rs->sr_rspoid = NULL;
-                       send_ldap_result( sop, rs );
+                       if ( get_pagedresults(sop) > SLAP_NO_CONTROL ) {
+                               send_paged_response( sop, rs, NULL, 0 );
+                       } else {
+                               send_ldap_result( sop, rs );
+                       }
                }
        }
 
@@ -1502,7 +1516,8 @@ done:
                bdb_cache_return_entry_r( bdb->bi_dbenv, &bdb->bi_cache, e, &lock );
        }
 
-       LOCK_ID_FREE( bdb->bi_dbenv, locker );
+       if ( !opinfo )
+               LOCK_ID_FREE( bdb->bi_dbenv, locker );
 
        ber_bvfree( search_context_csn );
 
@@ -1649,7 +1664,7 @@ static int search_candidates(
        {
                if( !get_manageDSAit(op) && !get_domainScope(op) ) {
                        /* match referral objects */
-                       struct berval bv_ref = { sizeof("referral")-1, "referral" };
+                       struct berval bv_ref = BER_BVC( "referral" );
                        rf.f_choice = LDAP_FILTER_EQUALITY;
                        rf.f_ava = &aa_ref;
                        rf.f_av_desc = slap_schema.si_ad_objectClass;
@@ -1673,7 +1688,7 @@ static int search_candidates(
 
 #ifdef BDB_SUBENTRIES
        if( get_subentries_visibility( op ) ) {
-               struct berval bv_subentry = { sizeof("SUBENTRY")-1, "SUBENTRY" };
+               struct berval bv_subentry = BER_BVC( "SUBENTRY" );
                sf.f_choice = LDAP_FILTER_EQUALITY;
                sf.f_ava = &aa_subentry;
                sf.f_av_desc = slap_schema.si_ad_objectClass;
@@ -1733,45 +1748,170 @@ static int search_candidates(
        return rc;
 }
 
+static int
+parse_paged_cookie( Operation *op, SlapReply *rs )
+{
+       LDAPControl     **c;
+       int             rc = LDAP_SUCCESS;
+       ber_tag_t       tag;
+       ber_int_t       size;
+       BerElement      *ber;
+       struct berval   cookie = BER_BVNULL;
+
+       /* this function must be invoked only if the pagedResults
+        * control has been detected, parsed and partially checked
+        * by the frontend */
+       assert( get_pagedresults( op ) > SLAP_NO_CONTROL );
+
+       /* look for the appropriate ctrl structure */
+       for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
+               if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
+               {
+                       break;
+               }
+       }
+
+       if ( c[0] == NULL ) {
+               rs->sr_text = "missing pagedResults control";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       /* Already tested by frontend */
+       assert( c[0]->ldctl_value.bv_len > 0 );
+#if 0
+       if ( c[0]->ldctl_value.bv_len == 0 ) {
+               rs->sr_text = "paged results control value is empty (or absent)";
+               return LDAP_PROTOCOL_ERROR;
+       }
+#endif
+
+       /* Parse the control value
+        *      realSearchControlValue ::= SEQUENCE {
+        *              size    INTEGER (0..maxInt),
+        *                              -- requested page size from client
+        *                              -- result set size estimate from server
+        *              cookie  OCTET STRING
+        * }
+        */
+       ber = ber_init( &c[0]->ldctl_value );
+       if ( ber == NULL ) {
+               rs->sr_text = "internal error";
+               return LDAP_OTHER;
+       }
+
+       tag = ber_scanf( ber, "{im}", &size, &cookie );
+
+       /* Already tested by frontend */
+       assert( tag != LBER_ERROR );
+#if 0
+       if ( tag == LBER_ERROR ) {
+               rs->sr_text = "paged results control could not be decoded";
+               rc = LDAP_PROTOCOL_ERROR;
+               goto done;
+       }
+#endif
+
+       /* Already tested by frontend */
+       assert( size >= 0 );
+#if 0
+       if ( size < 0 ) {
+               rs->sr_text = "paged results control size invalid";
+               rc = LDAP_PROTOCOL_ERROR;
+               goto done;
+       }
+#endif
+
+       /* cookie decoding/checks deferred to backend... */
+       if ( cookie.bv_len ) {
+               PagedResultsCookie reqcookie;
+               if( cookie.bv_len != sizeof( reqcookie ) ) {
+                       /* bad cookie */
+                       rs->sr_text = "paged results cookie is invalid";
+                       rc = LDAP_PROTOCOL_ERROR;
+                       goto done;
+               }
+
+               AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
+
+               if ( reqcookie > op->o_pagedresults_state.ps_cookie ) {
+                       /* bad cookie */
+                       rs->sr_text = "paged results cookie is invalid";
+                       rc = LDAP_PROTOCOL_ERROR;
+                       goto done;
+
+               } else if ( reqcookie < op->o_pagedresults_state.ps_cookie ) {
+                       rs->sr_text = "paged results cookie is invalid or old";
+                       rc = LDAP_UNWILLING_TO_PERFORM;
+                       goto done;
+               }
+
+       } else {
+               /* Initial request.  Initialize state. */
+#if 0
+               if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
+                       /* There's another pagedResults control on the
+                        * same connection; reject new pagedResults controls 
+                        * (allowed by RFC2696) */
+                       rs->sr_text = "paged results cookie unavailable; try later";
+                       rc = LDAP_UNWILLING_TO_PERFORM;
+                       goto done;
+               }
+#endif
+               op->o_pagedresults_state.ps_cookie = 0;
+               op->o_pagedresults_state.ps_count = 0;
+       }
+
+done:;
+       (void)ber_free( ber, 1 );
+
+       return rc;
+}
+
 static void
-send_pagerequest_response( 
+send_paged_response( 
        Operation       *op,
        SlapReply       *rs,
-       ID              lastid,
+       ID              *lastid,
        int             tentries )
 {
        LDAPControl     ctrl, *ctrls[2];
        BerElementBuffer berbuf;
        BerElement      *ber = (BerElement *)&berbuf;
-       struct berval   cookie = { 0, NULL };
        PagedResultsCookie respcookie;
+       struct berval cookie;
 
 #ifdef NEW_LOGGING
        LDAP_LOG ( OPERATION, ENTRY,
-               "send_pagerequest_response: lastid: (0x%08lx) "
-               "nentries: (0x%081x)\n", 
-               lastid, rs->sr_nentries, NULL );
+               "send_paged_response: lastid=0x%08lx nentries=%d\n", 
+               lastid ? *lastid : 0, rs->sr_nentries, NULL );
 #else
-       Debug(LDAP_DEBUG_ARGS, "send_pagerequest_response: lastid: (0x%08lx) "
-               "nentries: (0x%081x)\n", lastid, rs->sr_nentries, NULL );
+       Debug(LDAP_DEBUG_ARGS,
+               "send_paged_response: lastid=0x%08lx nentries=%d\n", 
+               lastid ? *lastid : 0, rs->sr_nentries, NULL );
 #endif
 
-       ctrl.ldctl_value.bv_val = NULL;
+       BER_BVZERO( &ctrl.ldctl_value );
        ctrls[0] = &ctrl;
        ctrls[1] = NULL;
 
        ber_init2( ber, NULL, LBER_USE_DER );
 
-       respcookie = ( PagedResultsCookie )lastid;
+       if ( lastid ) {
+               respcookie = ( PagedResultsCookie )(*lastid);
+               cookie.bv_len = sizeof( respcookie );
+               cookie.bv_val = (char *)&respcookie;
+
+       } else {
+               respcookie = ( PagedResultsCookie )0;
+               BER_BVSTR( &cookie, "" );
+       }
+
        op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
-       cookie.bv_len = sizeof( respcookie );
-       cookie.bv_val = (char *)&respcookie;
+       op->o_conn->c_pagedresults_state.ps_count =
+               op->o_pagedresults_state.ps_count + rs->sr_nentries;
 
-       /*
-        * FIXME: we should consider sending an estimate of the entries
-        * left, after appropriate security check is done
-        */
-       ber_printf( ber, "{iO}", tentries, &cookie ); 
+       /* return size of 0 -- no estimate */
+       ber_printf( ber, "{iO}", 0, &cookie ); 
 
        if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
                goto done;
@@ -1783,7 +1923,9 @@ send_pagerequest_response(
        rs->sr_ctrls = ctrls;
        rs->sr_err = LDAP_SUCCESS;
        send_ldap_result( op, rs );
+       rs->sr_ctrls = NULL;
 
 done:
        (void) ber_free_buf( ber );
-}                      
+}
+