From 7b3f889ec16ea8d29b50221f21917864a7779b3c Mon Sep 17 00:00:00 2001
From: Pierangelo Masarati <ando@openldap.org>
Date: Sat, 26 Oct 2002 16:18:31 +0000
Subject: [PATCH] fix hasSubordinate filtering; now it can be safely turned on

---
 servers/slapd/back-bdb/external.h     |  4 ++
 servers/slapd/back-bdb/init.c         |  3 ++
 servers/slapd/back-bdb/lcup.c         | 53 ------------------------
 servers/slapd/back-bdb/operational.c  |  7 +++-
 servers/slapd/back-bdb/search.c       | 55 -------------------------
 servers/slapd/back-ldbm/external.h    |  4 ++
 servers/slapd/back-ldbm/init.c        |  3 ++
 servers/slapd/back-ldbm/operational.c | 23 ++++++++++-
 servers/slapd/back-ldbm/search.c      | 49 ----------------------
 servers/slapd/back-sql/search.c       |  3 +-
 servers/slapd/filterentry.c           | 59 ++++++++++++++++++++++++++-
 servers/slapd/proto-slap.h            |  2 +
 servers/slapd/slap.h                  | 12 ++++++
 13 files changed, 114 insertions(+), 163 deletions(-)

diff --git a/servers/slapd/back-bdb/external.h b/servers/slapd/back-bdb/external.h
index f7c285a808..9e595ef9ff 100644
--- a/servers/slapd/back-bdb/external.h
+++ b/servers/slapd/back-bdb/external.h
@@ -33,6 +33,10 @@ extern BI_chk_referrals	bdb_referrals;
 
 extern BI_operational	bdb_operational;
 
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+extern BI_has_subordinates bdb_hasSubordinates;
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
+
 /* tools.c */
 extern BI_tool_entry_open	bdb_tool_entry_open;
 extern BI_tool_entry_close	bdb_tool_entry_close;
diff --git a/servers/slapd/back-bdb/init.c b/servers/slapd/back-bdb/init.c
index 0e3cab45de..aa956d2b1e 100644
--- a/servers/slapd/back-bdb/init.c
+++ b/servers/slapd/back-bdb/init.c
@@ -615,6 +615,9 @@ bdb_initialize(
 
 	bi->bi_chk_referrals = bdb_referrals;
 	bi->bi_operational = bdb_operational;
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+	bi->bi_has_subordinates = bdb_hasSubordinates;
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
 	bi->bi_entry_release_rw = bdb_entry_release;
 
 	/*
diff --git a/servers/slapd/back-bdb/lcup.c b/servers/slapd/back-bdb/lcup.c
index 9cf1a1479e..633d8111af 100644
--- a/servers/slapd/back-bdb/lcup.c
+++ b/servers/slapd/back-bdb/lcup.c
@@ -110,11 +110,6 @@ bdb_psearch(
 	int isroot = 0;
 	int scopeok = 0;
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-	int		filter_hasSubordinates = 0;
-	Attribute	*hasSubordinates = NULL;
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 	u_int32_t	locker;
 	DB_LOCK		lock;
 
@@ -386,14 +381,6 @@ dn2entry_retry:
 		}
 	}
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-	/*
-	 * is hasSubordinates used in the filter ?
-	 * FIXME: we may compute this directly when parsing the filter
-	 */
-	filter_hasSubordinates = filter_has_subordinates( filter );
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 	lcupf.f_choice = LDAP_FILTER_AND;
 	lcupf.f_and = &csnfnot;
 	lcupf.f_next = NULL;
@@ -568,52 +555,12 @@ dn2entry_retry:
 		goto test_done;
 	}
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-	/*
-	 * if hasSubordinates is used in the filter,
-	 * append it to the entry's attributes
-	 */
-	if ( filter_hasSubordinates ) {
-		int	hs;
-
-		rc = bdb_hasSubordinates( be, ps_conn, ps_op, e, &hs);
-		if ( rc != LDAP_SUCCESS ) {
-			goto test_done;
-		}
-
-		hasSubordinates = slap_operational_hasSubordinate(
-			hs == LDAP_COMPARE_TRUE );
-
-		if ( hasSubordinates == NULL ) {
-			goto test_done;
-		}
-
-		hasSubordinates->a_next = e->e_attrs;
-		e->e_attrs = hasSubordinates;
-	}
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 	if ( psearch_type != LCUP_PSEARCH_BY_SCOPEOUT ) {
 		rc = test_filter( be, ps_conn, ps_op, e, &lcupf );
 	} else {
 		rc = LDAP_COMPARE_TRUE;
 	}
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-	if ( hasSubordinates ) {
-		/*
-		 * FIXME: this is fairly inefficient, because 
-		 * if hasSubordinates is among the required
-		 * attrs, it will be added again later;
-		 * maybe we should leave it and check
-		 * check later if it's already present,
-		 * if required
-		 */
-		e->e_attrs = e->e_attrs->a_next;
-		attr_free( hasSubordinates );
-	}
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 	if ( rc == LDAP_COMPARE_TRUE ) {
 		struct berval	dn;
 
diff --git a/servers/slapd/back-bdb/operational.c b/servers/slapd/back-bdb/operational.c
index 1c5d4b14d6..7cead61e7c 100644
--- a/servers/slapd/back-bdb/operational.c
+++ b/servers/slapd/back-bdb/operational.c
@@ -16,9 +16,9 @@
 #include "proto-bdb.h"
 
 /*
- * sets the supported operational attributes (if required)
+ * sets *hasSubordinates to LDAP_COMPARE_TRUE/LDAP_COMPARE_FALSE
+ * if the entry has children or not.
  */
-
 int
 bdb_hasSubordinates(
 	BackendDB	*be,
@@ -124,6 +124,9 @@ return_results:
 	return rc;
 }
 
+/*
+ * sets the supported operational attributes (if required)
+ */
 int
 bdb_operational(
 	BackendDB	*be,
diff --git a/servers/slapd/back-bdb/search.c b/servers/slapd/back-bdb/search.c
index 03a0a540bc..5d2bdcec14 100644
--- a/servers/slapd/back-bdb/search.c
+++ b/servers/slapd/back-bdb/search.c
@@ -67,10 +67,6 @@ bdb_search(
 	struct slap_limits_set *limit = NULL;
 	int isroot = 0;
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-	int		filter_hasSubordinates = 0;
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 	u_int32_t	locker;
 	DB_LOCK		lock;
 
@@ -342,14 +338,6 @@ dn2entry_retry:
 		}
 	}
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-	/*
-	 * is hasSubordinates used in the filter ?
-	 * FIXME: we may compute this directly when parsing the filter
-	 */
-	filter_hasSubordinates = filter_has_subordinates( filter );
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 #ifdef LDAP_CLIENT_UPDATE
 	if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
 		lcupf.f_choice = LDAP_FILTER_AND;
@@ -382,9 +370,6 @@ dn2entry_retry:
 		id = bdb_idl_next( candidates, &cursor ) )
 	{
 		int		scopeok = 0;
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-		Attribute	*hasSubordinates = NULL;
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
 
 		/* check for abandon */
 		if ( op->o_abandon ) {
@@ -548,31 +533,6 @@ id2entry_retry:
 			goto loop_continue;
 		}
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-		/*
-		 * if hasSubordinates is used in the filter,
-		 * append it to the entry's attributes
-		 */
-		if ( filter_hasSubordinates ) {
-			int	hs;
-
-			rc = bdb_hasSubordinates( be, conn, op, e, &hs);
-			if ( rc != LDAP_SUCCESS ) {
-				goto loop_continue;
-			}
-
-			hasSubordinates = slap_operational_hasSubordinate(
-				hs == LDAP_COMPARE_TRUE );
-
-			if ( hasSubordinates == NULL ) {
-				goto loop_continue;
-			}
-
-			hasSubordinates->a_next = e->e_attrs;
-			e->e_attrs = hasSubordinates;
-		}
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 		/* if it matches the filter and scope, send it */
 #ifdef LDAP_CLIENT_UPDATE
 		if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) {
@@ -583,21 +543,6 @@ id2entry_retry:
 			rc = test_filter( be, conn, op, e, filter );
 		}
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-		if ( hasSubordinates ) {
-			/*
-			 * FIXME: this is fairly inefficient, because 
-			 * if hasSubordinates is among the required
-			 * attrs, it will be added again later;
-			 * maybe we should leave it and check
-			 * check later if it's already present,
-			 * if required
-			 */
-			e->e_attrs = e->e_attrs->a_next;
-			attr_free( hasSubordinates );
-		}
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 		if ( rc == LDAP_COMPARE_TRUE ) {
 			struct berval	dn;
 
diff --git a/servers/slapd/back-ldbm/external.h b/servers/slapd/back-ldbm/external.h
index 7c3faad166..93b40b1dd4 100644
--- a/servers/slapd/back-ldbm/external.h
+++ b/servers/slapd/back-ldbm/external.h
@@ -43,6 +43,10 @@ extern BI_acl_attribute	ldbm_back_attribute;
 
 extern BI_operational	ldbm_back_operational;
 
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+extern BI_has_subordinates	ldbm_back_hasSubordinates;
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
+
 /* hooks for slap tools */
 extern BI_tool_entry_open	ldbm_tool_entry_open;
 extern BI_tool_entry_close	ldbm_tool_entry_close;
diff --git a/servers/slapd/back-ldbm/init.c b/servers/slapd/back-ldbm/init.c
index be55fac73c..327b0b79a9 100644
--- a/servers/slapd/back-ldbm/init.c
+++ b/servers/slapd/back-ldbm/init.c
@@ -71,6 +71,9 @@ ldbm_back_initialize(
 	bi->bi_acl_attribute = ldbm_back_attribute;
 	bi->bi_chk_referrals = ldbm_back_referrals;
 	bi->bi_operational = ldbm_back_operational;
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+	bi->bi_has_subordinates = ldbm_back_hasSubordinates;
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
 
 	/*
 	 * hooks for slap tools
diff --git a/servers/slapd/back-ldbm/operational.c b/servers/slapd/back-ldbm/operational.c
index 51c0fb710f..bc68116c28 100644
--- a/servers/slapd/back-ldbm/operational.c
+++ b/servers/slapd/back-ldbm/operational.c
@@ -16,9 +16,30 @@
 #include "proto-back-ldbm.h"
 
 /*
- * sets the supported operational attributes (if required)
+ * sets *hasSubordinates to LDAP_COMPARE_TRUE/LDAP_COMPARE_FALSE
+ * if the entry has children or not.
  */
+int
+ldbm_back_hasSubordinates(
+	BackendDB	*be,
+	Connection	*conn, 
+	Operation	*op,
+	Entry		*e,
+	int		*hasSubordinates )
+{
+	if ( has_children( be, e ) ) {
+		*hasSubordinates = LDAP_COMPARE_TRUE;
 
+	} else {
+		*hasSubordinates = LDAP_COMPARE_FALSE;
+	}
+
+	return 0;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
 int
 ldbm_back_operational(
 	BackendDB	*be,
diff --git a/servers/slapd/back-ldbm/search.c b/servers/slapd/back-ldbm/search.c
index eac5f831e9..1d41140078 100644
--- a/servers/slapd/back-ldbm/search.c
+++ b/servers/slapd/back-ldbm/search.c
@@ -57,10 +57,6 @@ ldbm_back_search(
 	struct slap_limits_set *limit = NULL;
 	int isroot = 0;
 		
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-	int		filter_hasSubordinates = 0;
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 #ifdef NEW_LOGGING
 	LDAP_LOG( BACK_LDBM, ENTRY, "ldbm_back_search: enter\n", 0, 0, 0 );
 #else
@@ -292,22 +288,11 @@ searchit:
 	/* compute it anyway; root does not use it */
 	stoptime = op->o_time + tlimit;
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-	/*
-	 * is hasSubordinates used in the filter ?
-	 * FIXME: we may compute this directly when parsing the filter
-	 */
-	filter_hasSubordinates = filter_has_subordinates( filter );
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 	for ( id = idl_firstid( candidates, &cursor ); id != NOID;
 	    id = idl_nextid( candidates, &cursor ) )
 	{
 		int scopeok = 0;
 		int result = 0;
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-		Attribute	*hasSubordinates = NULL;
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
 
 		/* check for abandon */
 		if ( op->o_abandon ) {
@@ -436,43 +421,9 @@ searchit:
 			goto loop_continue;
 		}
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-		/*
-		 * if hasSubordinates is used in the filter,
-		 * append it to the entry's attributes
-		 */
-		if ( filter_hasSubordinates ) {
-			int	hs;
-
-			hs = has_children( be, e );
-			hasSubordinates = slap_operational_hasSubordinate( hs );
-			if ( hasSubordinates == NULL ) {
-				goto loop_continue;
-			}
-
-			hasSubordinates->a_next = e->e_attrs;
-			e->e_attrs = hasSubordinates;
-		}
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 		/* if it matches the filter and scope, send it */
 		result = test_filter( be, conn, op, e, filter );
 
-#ifdef SLAP_X_FILTER_HASSUBORDINATES
-		if ( hasSubordinates ) {
-			/*
-			 * FIXME: this is fairly inefficient, because 
-			 * if hasSubordinates is among the required
-			 * attrs, it will be added again later;
-			 * maybe we should leave it and check
-			 * check later if it's already present,
-			 * if required
-			 */
-			e->e_attrs = e->e_attrs->a_next;
-			attr_free( hasSubordinates );
-		}
-#endif /* SLAP_X_FILTER_HASSUBORDINATES */
-
 		if ( result == LDAP_COMPARE_TRUE ) {
 			struct berval	dn;
 
diff --git a/servers/slapd/back-sql/search.c b/servers/slapd/back-sql/search.c
index 0032f2ffdf..77428ea18c 100644
--- a/servers/slapd/back-sql/search.c
+++ b/servers/slapd/back-sql/search.c
@@ -399,7 +399,7 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f )
 		 */
 		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
 				(ber_len_t)sizeof( "1=1" ) - 1, "1=1" );
-		if ( ad != NULL ) {
+		if ( ad == slap_schema.si_ad_hasSubordinates ) {
 			/*
 			 * We use this flag since we need to parse
 			 * the filter anyway; we should have used
@@ -407,6 +407,7 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f )
 			 * filter_has_subordinates()
 			 */
 			bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
+
 		} else {
 			/*
 			 * clear attributes to fetch, to require ALL
diff --git a/servers/slapd/filterentry.c b/servers/slapd/filterentry.c
index 82cd8cd327..eee672a9b9 100644
--- a/servers/slapd/filterentry.c
+++ b/servers/slapd/filterentry.c
@@ -460,6 +460,41 @@ test_ava_filter(
 		}
 	}
 
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+	if ( ava->aa_desc == slap_schema.si_ad_hasSubordinates 
+			&& be && be->be_has_subordinates ) {
+		int		hasSubordinates;
+		struct berval	hs;
+
+		/*
+		 * No other match should be allowed ...
+		 */
+		assert( type == LDAP_FILTER_EQUALITY );
+		
+		if ( (*be->be_has_subordinates)( be, conn, op, e, &hasSubordinates ) ) {
+			return LDAP_OTHER;
+		}
+
+		if ( hasSubordinates == LDAP_COMPARE_TRUE ) {
+			hs.bv_val = "TRUE";
+			hs.bv_len = sizeof( "TRUE" ) - 1;
+
+		} else if ( hasSubordinates == LDAP_COMPARE_FALSE ) {
+			hs.bv_val = "FALSE";
+			hs.bv_len = sizeof( "FALSE" ) - 1;
+
+		} else {
+			return LDAP_OTHER;
+		}
+
+		if ( bvmatch( &ava->aa_value, &hs ) ) {
+			return LDAP_COMPARE_TRUE;
+		}
+
+		return LDAP_COMPARE_FALSE;
+	}
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
+
 	return( LDAP_COMPARE_FALSE );
 }
 
@@ -473,13 +508,33 @@ test_presence_filter(
 	AttributeDescription *desc
 )
 {
+	Attribute	*a;
+
 	if ( !access_allowed( be, conn, op, e, desc, NULL, ACL_SEARCH, NULL ) )
 	{
 		return LDAP_INSUFFICIENT_ACCESS;
 	}
 
-	return attrs_find( e->e_attrs, desc ) != NULL
-		? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
+	a = attrs_find( e->e_attrs, desc );
+
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+	if ( a == NULL && desc == slap_schema.si_ad_hasSubordinates ) {
+
+		/*
+		 * XXX: fairly optimistic: if the function is defined,
+		 * then PRESENCE must succeed, because hasSubordinate
+		 * is boolean-valued; I think we may live with this 
+		 * simplification by now
+		 */
+		if ( be && be->be_has_subordinates ) {
+			return LDAP_COMPARE_TRUE;
+		}
+
+		return LDAP_COMPARE_FALSE;
+	}
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
+
+	return a != NULL ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
 }
 
 
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index 51dec17aea..3d2bd8065f 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -485,6 +485,8 @@ LDAP_SLAPD_F (void) vrFilter2bv LDAP_P(( ValuesReturnFilter *f, struct berval *f
 
 /*
  * define to honor hasSubordinates operational attribute in search filters
+ * (in previous use there was a flaw with back-bdb and back-ldbm; now it 
+ * is fixed).
  */
 #undef SLAP_X_FILTER_HASSUBORDINATES
 
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index bb611b8fc8..e3cb7d55e6 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -1206,6 +1206,9 @@ struct slap_backend_db {
 #define		be_group	bd_info->bi_acl_group
 #define		be_attribute	bd_info->bi_acl_attribute
 #define		be_operational	bd_info->bi_operational
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+#define		be_has_subordinates bd_info->bi_has_subordinates
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
 
 #define		be_controls	bd_info->bi_controls
 
@@ -1406,6 +1409,12 @@ typedef int (BI_operational)  LDAP_P((Backend *bd,
 		struct slap_conn *c, struct slap_op *o,
 		Entry *e, AttributeName *attrs, int opattrs, Attribute **a ));
 
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+typedef int (BI_has_subordinates) LDAP_P((Backend *bd,
+		struct slap_conn *c, struct slap_op *o,
+	        Entry *e, int *has_subordinates ));
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
+
 typedef int (BI_connection_init) LDAP_P((BackendDB *bd,
 		struct slap_conn *c));
 typedef int (BI_connection_destroy) LDAP_P((BackendDB *bd,
@@ -1499,6 +1508,9 @@ struct slap_backend_info {
 	BI_acl_attribute	*bi_acl_attribute;
 
 	BI_operational	*bi_operational;
+#ifdef SLAP_X_FILTER_HASSUBORDINATES
+	BI_has_subordinates	*bi_has_subordinates;
+#endif /* SLAP_X_FILTER_HASSUBORDINATES */
 
 	BI_connection_init	*bi_connection_init;
 	BI_connection_destroy	*bi_connection_destroy;
-- 
GitLab