From b74af1b7c77c6a0fbdf8f24c7dd979d15db8772a Mon Sep 17 00:00:00 2001
From: Howard Chu <hyc@openldap.org>
Date: Wed, 19 Aug 2015 14:04:15 +0100
Subject: [PATCH] ITS#8220 restore refint performance

---
 servers/slapd/overlays/refint.c | 157 ++++++++++++++++++++++----------
 1 file changed, 111 insertions(+), 46 deletions(-)

diff --git a/servers/slapd/overlays/refint.c b/servers/slapd/overlays/refint.c
index 03a5e0e101..765054583a 100644
--- a/servers/slapd/overlays/refint.c
+++ b/servers/slapd/overlays/refint.c
@@ -74,6 +74,7 @@ typedef struct refint_q {
 	BerValue oldndn;
 	BerValue newdn;
 	BerValue newndn;
+	int do_sub;
 } refint_q;
 
 typedef struct refint_data_s {
@@ -86,9 +87,15 @@ typedef struct refint_data_s {
 	struct re_s *qtask;
 	refint_q *qhead;
 	refint_q *qtail;
+	BackendDB *db;
 	ldap_pvt_thread_mutex_t qmutex;
 } refint_data;
 
+typedef struct refint_pre_s {
+	slap_overinst *on;
+	int do_sub;
+} refint_pre;
+
 #define	RUNQ_INTERVAL	36000	/* a long time */
 
 static MatchingRule	*mr_dnSubtreeMatch;
@@ -357,6 +364,31 @@ refint_open(
 		ber_dupbv( &id->refint_dn, &refint_dn );
 		ber_dupbv( &id->refint_ndn, &refint_ndn );
 	}
+
+	/*
+	** find the backend that matches our configured basedn;
+	** make sure it exists and has search and modify methods;
+	**
+	*/
+
+	if ( on->on_info->oi_origdb != frontendDB ) {
+		BackendDB *db = select_backend(&id->dn, 1);
+
+		if ( db ) {
+			if ( !db->be_search || !db->be_modify ) {
+				Debug( LDAP_DEBUG_CONFIG,
+					"refint_response: backend missing search and/or modify\n",
+					0, 0, 0 );
+				return -1;
+			}
+			id->db = db;
+		} else {
+			Debug( LDAP_DEBUG_CONFIG,
+				"refint_response: no backend for our baseDN %s??\n",
+				id->dn.bv_val, 0, 0 );
+			return -1;
+		}
+	}
 	return(0);
 }
 
@@ -433,6 +465,8 @@ refint_search_cb(
 
 			na = NULL;
 
+			/* Are we doing subtree matching or simple equality? */
+			if ( rq->do_sub ) {
 			for(i = 0, b = a->a_nvals; b[i].bv_val; i++) {
 				if(dnIsSuffix(&b[i], &rq->oldndn)) {
 					is_exact = b[i].bv_len == rq->oldndn.bv_len;
@@ -504,9 +538,24 @@ refint_search_cb(
 					ber_bvarray_add_x( &na->new_nvals, &dn, op->o_tmpmemctx );
 				}
 			}
+			} else {
+				/* entry has no children, just equality matching */
+				is_exact = attr_valfind( a,
+					SLAP_MR_EQUALITY|SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH|
+					SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, &rq->oldndn, &i, NULL );
+				if ( is_exact == LDAP_SUCCESS ) {
+					na = op->o_tmpcalloc( 1,
+						sizeof( refint_attrs ),
+						op->o_tmpmemctx );
+					na->next = ip->attrs;
+					ip->attrs = na;
+					na->attr = ia->attr;
+					na->ra_numvals = 1;
+				}
+			}
 
 			/* Deleting/replacing all values and a nothing DN is configured? */
-			if ( na && na->ra_numvals == i && !BER_BVISNULL(&dd->nothing) )
+			if ( na && na->ra_numvals == a->a_numvals && !BER_BVISNULL(&dd->nothing) )
 				na->dont_empty = 1;
 
 			Debug( LDAP_DEBUG_TRACE, "refint_search_cb: %s: %s (#%d)\n",
@@ -729,11 +778,9 @@ refint_qtask( void *ctx, void *arg )
 	ftop.f_or = NULL;
 	op->ors_filter = &ftop;
 	for(ip = id->attrs; ip; ip = ip->next) {
+		/* this filter can be either EQUALITY or EXT */
 		fptr = op->o_tmpcalloc( sizeof(Filter) + sizeof(MatchingRuleAssertion),
 			1, op->o_tmpmemctx );
-		/* Use (attr:dnSubtreeMatch:=value) to catch subtree rename
-		 * and subtree delete where supported */
-		fptr->f_choice = LDAP_FILTER_EXT;
 		fptr->f_mra = (MatchingRuleAssertion *)(fptr+1);
 		fptr->f_mr_rule = mr_dnSubtreeMatch;
 		fptr->f_mr_rule_text = mr_dnSubtreeMatch->smr_bvoid;
@@ -764,8 +811,15 @@ refint_qtask( void *ctx, void *arg )
 		if ( !rq )
 			break;
 
-		for (fptr = ftop.f_or; fptr; fptr = fptr->f_next )
+		for (fptr = ftop.f_or; fptr; fptr = fptr->f_next ) {
 			fptr->f_mr_value = rq->oldndn;
+			/* Use (attr:dnSubtreeMatch:=value) to catch subtree rename
+			 * and subtree delete where supported */
+			if (rq->do_sub)
+				fptr->f_choice = LDAP_FILTER_EXT;
+			else
+				fptr->f_choice = LDAP_FILTER_EQUALITY;
+		}
 
 		filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
 
@@ -875,60 +929,29 @@ refint_response(
 	SlapReply *rs
 )
 {
-	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
-	refint_data *id = on->on_bi.bi_private;
+	refint_pre *rp;
+	slap_overinst *on;
+	refint_data *id;
 	BerValue pdn;
-	int ac;
 	refint_q *rq;
-	BackendDB *db = NULL;
 	refint_attrs *ip;
+	int ac;
 
 	/* If the main op failed or is not a Delete or ModRdn, ignore it */
 	if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
 		rs->sr_err != LDAP_SUCCESS )
 		return SLAP_CB_CONTINUE;
 
-	/*
-	** validate (and count) the list of attrs;
-	**
-	*/
-
-	for(ip = id->attrs, ac = 0; ip; ip = ip->next, ac++);
-	if(!ac) {
-		Debug( LDAP_DEBUG_TRACE,
-			"refint_response called without any attributes\n", 0, 0, 0 );
-		return SLAP_CB_CONTINUE;
-	}
-
-	/*
-	** find the backend that matches our configured basedn;
-	** make sure it exists and has search and modify methods;
-	**
-	*/
-
-	if ( on->on_info->oi_origdb != frontendDB ) {
-		db = select_backend(&id->dn, 1);
-
-		if ( db ) {
-			if ( !db->be_search || !db->be_modify ) {
-				Debug( LDAP_DEBUG_TRACE,
-					"refint_response: backend missing search and/or modify\n",
-					0, 0, 0 );
-				return SLAP_CB_CONTINUE;
-			}
-		} else {
-			Debug( LDAP_DEBUG_TRACE,
-				"refint_response: no backend for our baseDN %s??\n",
-				id->dn.bv_val, 0, 0 );
-			return SLAP_CB_CONTINUE;
-		}
-	}
+	rp = op->o_callback->sc_private;
+	on = rp->on;
+	id = on->on_bi.bi_private;
 
 	rq = ch_calloc( 1, sizeof( refint_q ));
 	ber_dupbv( &rq->olddn, &op->o_req_dn );
 	ber_dupbv( &rq->oldndn, &op->o_req_ndn );
-	rq->db = db;
+	rq->db = id->db;
 	rq->rdata = id;
+	rq->do_sub = rp->do_sub;
 
 	if ( op->o_tag == LDAP_REQ_MODRDN ) {
 		if ( op->oq_modrdn.rs_newSup ) {
@@ -977,6 +1000,47 @@ refint_response(
 	return SLAP_CB_CONTINUE;
 }
 
+/* Check if the target entry exists and has children.
+ * Do nothing if target doesn't exist.
+ */
+static int
+refint_preop(
+	Operation *op,
+	SlapReply *rs
+)
+{
+	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+	refint_data *id = on->on_bi.bi_private;
+	Entry *e;
+	int rc;
+
+	/* are any attrs configured? */
+	if ( !id->attrs )
+		return SLAP_CB_CONTINUE;
+
+	rc = overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on );
+	if ( rc == LDAP_SUCCESS ) {
+		slap_callback *sc = op->o_tmpcalloc( 1,
+			sizeof(slap_callback)+sizeof(refint_pre), op->o_tmpmemctx );
+		refint_pre *rp = (refint_pre *)(sc+1);
+		rp->on = on;
+		rp->do_sub = 1;	/* assume there are children */
+		if ( op->o_bd->be_has_subordinates ) {
+			int has = 0;
+			rc = op->o_bd->be_has_subordinates( op, e, &has );
+			/* there definitely are not children */
+			if ( rc == LDAP_SUCCESS && has == LDAP_COMPARE_FALSE )
+				rp->do_sub = 0;
+		}
+		overlay_entry_release_ov( op, e, 0, on );
+		sc->sc_response = refint_response;
+		sc->sc_private = rp;
+		sc->sc_next = op->o_callback;
+		op->o_callback = sc;
+	}
+	return SLAP_CB_CONTINUE;
+}
+
 /*
 ** init_module is last so the symbols resolve "for free" --
 ** it expects to be called automagically during dynamic module initialization
@@ -999,7 +1063,8 @@ int refint_initialize() {
 	refint.on_bi.bi_db_destroy = refint_db_destroy;
 	refint.on_bi.bi_db_open = refint_open;
 	refint.on_bi.bi_db_close = refint_close;
-	refint.on_response = refint_response;
+	refint.on_bi.bi_op_delete = refint_preop;
+	refint.on_bi.bi_op_modrdn = refint_preop;
 
 	refint.on_bi.bi_cf_ocs = refintocs;
 	rc = config_register_schema ( refintcfg, refintocs );
-- 
GitLab