]> git.sur5r.net Git - openldap/commitdiff
handle sizelimit in caching (in partial fulfilment of ITS#5114)
authorPierangelo Masarati <ando@openldap.org>
Wed, 12 Sep 2007 21:09:04 +0000 (21:09 +0000)
committerPierangelo Masarati <ando@openldap.org>
Wed, 12 Sep 2007 21:09:04 +0000 (21:09 +0000)
servers/slapd/overlays/pcache.c
tests/data/proxycache.out
tests/data/slapd-proxycache.conf
tests/scripts/conf.sh
tests/scripts/test020-proxycache

index 58c5e1e81e49dba813b056f372227a7926e8bcb3..0e9b80df3025f0b5aa4b7c02778c19321000c4ea 100644 (file)
@@ -72,6 +72,7 @@ typedef struct cached_query_s {
        Qbase                                   *qbase;
        int                                             scope;
        struct berval                   q_uuid;         /* query identifier */
+       int                             q_sizelimit;
        struct query_template_s         *qtemp; /* template of the query */
        time_t                          expiry_time;    /* time till the query is considered valid */
        struct cached_query_s           *next;          /* next query in the template */
@@ -127,17 +128,36 @@ typedef struct query_template_s {
        int             no_of_queries;  /* Total number of queries in the template */
        time_t          ttl;            /* TTL for the queries of this template */
        time_t          negttl;         /* TTL for negative results */
+       time_t          limitttl;       /* TTL for sizelimit exceeding results */
        struct attr_set t_attrs;        /* filter attrs + attr_set */
 } QueryTemplate;
 
+typedef enum {
+       PC_IGNORE = 0,
+       PC_POSITIVE,
+       PC_NEGATIVE,
+       PC_SIZELIMIT
+} pc_caching_reason_t;
+
+static const char *pc_caching_reason_str[] = {
+       "IGNORE",
+       "POSITIVE",
+       "NEGATIVE",
+       "SIZELIMIT",
+
+       NULL
+};
+
 struct query_manager_s;
 
 /* prototypes for functions for 1) query containment
  * 2) query addition, 3) cache replacement
  */
-typedef CachedQuery *  (QCfunc)(Operation *op, struct query_manager_s*, Query*, QueryTemplate*);
-typedef CachedQuery *  (AddQueryfunc)(Operation *op, struct query_manager_s*, Query*, QueryTemplate*, int positive);
-typedef void   (CRfunc)(struct query_manager_s*, struct berval * );
+typedef CachedQuery *(QCfunc)(Operation *op, struct query_manager_s*,
+       Query*, QueryTemplate*);
+typedef CachedQuery *(AddQueryfunc)(Operation *op, struct query_manager_s*,
+       Query*, QueryTemplate*, pc_caching_reason_t);
+typedef void (CRfunc)(struct query_manager_s*, struct berval*);
 
 /* LDAP query cache */
 typedef struct query_manager_s {
@@ -225,7 +245,7 @@ add_query(
        query_manager* qm,
        Query* query,
        QueryTemplate *templ,
-       int positive);
+       pc_caching_reason_t why );
 
 static int
 remove_query_data(
@@ -452,7 +472,7 @@ url2query(
                        goto error;
                }
 
-               cq = add_query( op, qm, &query, qt, 1 );
+               cq = add_query( op, qm, &query, qt, PC_POSITIVE );
                if ( cq != NULL ) {
                        cq->expiry_time = expiry_time;
                        cq->q_uuid = uuid;
@@ -1093,6 +1113,10 @@ query_containment(Operation *op, query_manager *qm,
                                        qc = find_filter( op, qbptr->scopes[tscope],
                                                        query->filter, first );
                                        if ( qc ) {
+                                               if ( qc->q_sizelimit ) {
+                                                       ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
+                                                       return NULL;
+                                               }
                                                ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
                                                if (qm->lru_top != qc) {
                                                        remove_query(qm, qc);
@@ -1135,24 +1159,41 @@ add_query(
        query_manager* qm,
        Query* query,
        QueryTemplate *templ,
-       int positive)
+       pc_caching_reason_t why )
 {
        CachedQuery* new_cached_query = (CachedQuery*) ch_malloc(sizeof(CachedQuery));
        Qbase *qbase, qb;
        Filter *first;
        int rc;
+       time_t ttl = 0;;
 
        new_cached_query->qtemp = templ;
        BER_BVZERO( &new_cached_query->q_uuid );
-       if ( positive ) {
-               new_cached_query->expiry_time = slap_get_time() + templ->ttl;
-       } else {
-               new_cached_query->expiry_time = slap_get_time() + templ->negttl;
+       new_cached_query->q_sizelimit = 0;
+
+       switch ( why ) {
+       case PC_POSITIVE:
+               ttl = templ->ttl;
+               break;
+
+       case PC_NEGATIVE:
+               ttl = templ->negttl;
+               break;
+
+       case PC_SIZELIMIT:
+               ttl = templ->limitttl;
+               break;
+
+       default:
+               assert( 0 );
+               break;
        }
+       new_cached_query->expiry_time = slap_get_time() + ttl;
        new_cached_query->lru_up = NULL;
        new_cached_query->lru_down = NULL;
-       Debug( pcache_debug, "Added query expires at %ld\n",
-                       (long) new_cached_query->expiry_time, 0, 0 );
+       Debug( pcache_debug, "Added query expires at %ld (%s)\n",
+                       (long) new_cached_query->expiry_time,
+                       pc_caching_reason_str[ why ], 0 );
 
        new_cached_query->scope = query->scope;
        new_cached_query->filter = query->filter;
@@ -1286,7 +1327,7 @@ cache_replacement(query_manager* qm, struct berval *result)
        ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
 
        *result = bottom->q_uuid;
-       bottom->q_uuid.bv_val = NULL;
+       BER_BVZERO( &bottom->q_uuid );
 
        Debug( pcache_debug, "Lock CR index = %p\n", (void *) temp, 0, 0 );
        ldap_pvt_thread_rdwr_wlock(&temp->t_rwlock);
@@ -1527,6 +1568,8 @@ struct search_info {
        int max;
        int over;
        int count;
+       int slimit;
+       int slimit_exceeded;
        Entry *head, *tail;
 };
 
@@ -1874,8 +1917,18 @@ static int
 pcache_op_cleanup( Operation *op, SlapReply *rs ) {
        slap_callback   *cb = op->o_callback;
        struct search_info *si = cb->sc_private;
-       if ( rs->sr_type == REP_RESULT || op->o_abandon || 
-                       rs->sr_err == SLAPD_ABANDON ) {
+
+       if ( rs->sr_type == REP_SEARCH ) {
+               /* don't return more entries than requested by the client */
+               if ( si->slimit && rs->sr_nentries >= si->slimit ) {
+                       si->slimit_exceeded = 1;
+               }
+       }
+
+       if ( rs->sr_type == REP_RESULT || 
+               op->o_abandon || 
+               rs->sr_err == SLAPD_ABANDON )
+       {
                if ( si->save_attrs != NULL ) {
                        rs->sr_attrs = si->save_attrs;
                        op->ors_attrs = si->save_attrs;
@@ -1883,6 +1936,7 @@ pcache_op_cleanup( Operation *op, SlapReply *rs ) {
                op->o_callback = op->o_callback->sc_next;
                op->o_tmpfree( cb, op->o_tmpmemctx );
        }
+
        return SLAP_CB_CONTINUE;
 }
 
@@ -1914,6 +1968,7 @@ pcache_response(
                                if ( !si->head ) si->head = e;
                                if ( si->tail ) si->tail->e_private = e;
                                si->tail = e;
+
                        } else {
                                si->over = 1;
                                si->count = 0;
@@ -1926,16 +1981,44 @@ pcache_response(
                        }
                }
 
+               /* don't return more entries than requested by the client */
+               if ( si->slimit_exceeded ) {
+                       return 0;
+               }
+
        } else if ( rs->sr_type == REP_RESULT ) {
-               if ( si->count ||
-                       ( si->qtemp->negttl && !si->count && !si->over &&
-                               rs->sr_err == LDAP_SUCCESS )) {
-                       CachedQuery *qc = qm->addfunc(op, qm, &si->query, si->qtemp,
-                               si->count);
+               pc_caching_reason_t why = PC_IGNORE;
+
+               if ( si->count ) {
+                       if ( rs->sr_err == LDAP_SUCCESS ) {
+                               why = PC_POSITIVE;
+
+                       } else if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED
+                               && si->qtemp->limitttl )
+                       {
+                               why = PC_SIZELIMIT;
+                       }
+
+               } else if ( si->qtemp->negttl && !si->count && !si->over &&
+                               rs->sr_err == LDAP_SUCCESS )
+               {
+                       why = PC_NEGATIVE;
+               }
+
+               if ( why != PC_IGNORE ) {
+                       CachedQuery *qc = qm->addfunc(op, qm, &si->query,
+                               si->qtemp, why );
 
                        if ( qc != NULL ) {
-                               if ( si->count )
+                               switch ( why ) {
+                               case PC_POSITIVE:
                                        cache_entries( op, rs, &qc->q_uuid );
+                                       break;
+
+                               case PC_SIZELIMIT:
+                                       qc->q_sizelimit = rs->sr_nentries;
+                                       break;
+                               }
                                ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
                                cm->num_cached_queries++;
                                Debug( pcache_debug, "STORED QUERIES = %lu\n",
@@ -1953,6 +2036,7 @@ pcache_response(
                                        }
                                        ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
                                }
+
                        } else if ( si->count ) {
                                /* duplicate query, free it */
                                Entry *e;
@@ -1962,10 +2046,16 @@ pcache_response(
                                        entry_free(si->head);
                                }
                        }
+
                } else {
                        filter_free( si->query.filter );
                }
+
+               if ( si->slimit_exceeded ) {
+                       rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+               }
        }
+
        return SLAP_CB_CONTINUE;
 }
 
@@ -2255,6 +2345,12 @@ pcache_op_search(
                si->max = cm->num_entries_limit ;
                si->over = 0;
                si->count = 0;
+               si->slimit = 0;
+               si->slimit_exceeded = 0;
+               if ( op->ors_slimit && op->ors_slimit < cm->num_entries_limit ) {
+                       si->slimit = op->ors_slimit;
+                       op->ors_slimit = cm->num_entries_limit;
+               }
                si->head = NULL;
                si->tail = NULL;
                si->save_attrs = op->ors_attrs;
@@ -2455,9 +2551,10 @@ static ConfigTable pccfg[] = {
                        "DESC 'A set of attributes to cache' "
                        "SYNTAX OMsDirectoryString )", NULL, NULL },
        { "proxytemplate", "filter> <attrset-index> <TTL> <negTTL",
-               4, 5, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
+               4, 6, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
                "( OLcfgOvAt:2.3 NAME 'olcProxyTemplate' "
-                       "DESC 'Filter template, attrset, cache TTL, optional negative TTL' "
+                       "DESC 'Filter template, attrset, cache TTL, "
+                               "optional negative TTL, optional sizelimit TTL' "
                        "SYNTAX OMsDirectoryString )", NULL, NULL },
        { "response-callback", "head|tail(default)",
                2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
@@ -2580,17 +2677,14 @@ pc_cf_gen( ConfigArgs *c )
                        break;
                case PC_TEMP:
                        for (temp=qm->templates; temp; temp=temp->qmnext) {
-                               if ( temp->negttl ) {
-                                       bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               " %d %ld %ld",
-                                               temp->attr_set_index,
-                                               temp->ttl,
-                                               temp->negttl );
-                               } else {
-                                       bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), " %d %ld",
-                                               temp->attr_set_index,
-                                               temp->ttl );
-                               }
+                               /* HEADS-UP: always print all;
+                                * if optional == 0, ignore */
+                               bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       " %d %ld %ld %ld",
+                                       temp->attr_set_index,
+                                       temp->ttl,
+                                       temp->negttl,
+                                       temp->limitttl );
                                bv.bv_len += temp->querystr.bv_len + 2;
                                bv.bv_val = ch_malloc( bv.bv_len+1 );
                                ptr = bv.bv_val;
@@ -2839,23 +2933,37 @@ pc_cf_gen( ConfigArgs *c )
                ldap_pvt_thread_rdwr_init( &temp->t_rwlock );
                temp->query = temp->query_last = NULL;
                if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
-                       snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template ttl=\"%s\"",
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                               "unable to parse template ttl=\"%s\"",
                                c->argv[3] );
                        Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
                        return( 1 );
                }
                temp->ttl = (time_t)t;
-               if ( c->argc == 5 ) {
+               temp->negttl = (time_t)0;
+               temp->limitttl = (time_t)0;
+               switch ( c->argc ) {
+               case 6:
+                       if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       "unable to parse template sizelimit ttl=\"%s\"",
+                                       c->argv[5] );
+                               Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                                       return( 1 );
+                       }
+                       temp->limitttl = (time_t)t;
+                       /* fallthru */
+
+               case 5:
                        if ( lutil_parse_time( c->argv[4], &t ) != 0 ) {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                       "unable to parse template negttl=\"%s\"",
+                                       "unable to parse template negative ttl=\"%s\"",
                                        c->argv[4] );
                                Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                        return( 1 );
                        }
                        temp->negttl = (time_t)t;
-               } else {
-                       temp->negttl = 0;
+                       break;
                }
 
                temp->no_of_queries = 0;
index 886f2549f1260597cf957eb471e7a8fa1dadd283..96167929e482443a338a3b5fd80be67de56d5d8e 100644 (file)
@@ -1,4 +1,4 @@
-# Query 1: filter:(sn=Jon) attrs: all
+# Query 1: filter:(sn=Jon) attrs:all (expect nothing)
 # Query 2: filter:(|(cn=*Jon*)(sn=Jon*)) attrs:cn sn title uid
 dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
 cn: James A Jones 1
@@ -24,7 +24,7 @@ sn: Doe
 uid: johnd
 title: System Administrator, Information Technology Division
 
-# Query 3: filter:(sn=Smith*) attrs:cn sn title uid
+# Query 3: filter:(sn=Smith*) attrs:cn sn uid
 dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
 cn: Jennifer Smith
 cn: Jen Smith
@@ -65,7 +65,7 @@ postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
 mail: bjorn@mailgw.example.com
 telephoneNumber: +1 313 555 0355
 
-# Query 6: filter:(mail=*@mail.alumni.example.com) cn sn title uid
+# Query 6: filter:(mail=*@mail.alumni.example.com) attrs:cn sn title uid
 dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
 cn: Dorothy Stevens
 cn: Dot Stevens
@@ -101,7 +101,7 @@ sn: Hampster
 uid: uham
 title: Secretary, UM Alumni Association
 
-# Query 7: filter:(mail=*) cn sn title uid
+# Query 7: filter:(mail=*) attrs:cn sn title uid
 dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
  dc=com
 cn: Barbara Jensen
@@ -176,7 +176,21 @@ sn: Hampster
 uid: uham
 title: Secretary, UM Alumni Association
 
-# Query 8: filter:(|(cn=*Jones)(sn=Jones)) attrs:cn sn title uid
+# Query 8: filter:(mail=*example.com) attrs:cn sn title uid
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+
+# Query 9: filter:(uid=b*) attrs:mail
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+mail: bjensen@mailgw.example.com
+
+# Query 10: filter:(|(cn=*Jones)(sn=Jones)) attrs:cn sn title uid
 dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
 cn: James A Jones 1
 cn: James Jones
@@ -194,7 +208,7 @@ sn: Doe
 uid: jjones
 title: Senior Manager, Information Technology Division
 
-# Query 9: filter:(sn=Smith) attrs:cn sn title uid
+# Query 11: filter:(sn=Smith) attrs:cn sn title uid
 dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
 cn: Jennifer Smith
 cn: Jen Smith
@@ -202,7 +216,7 @@ sn: Smith
 uid: jen
 title: Telemarketer, UM Alumni Association
 
-# Query 10: filter:(uid=bjorn) attrs:mail postaladdress telephonenumber cn uid
+# Query 12: filter:(uid=bjorn) attrs:mail postaladdress telephonenumber cn uid
 dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
  =com
 cn: Bjorn Jensen
@@ -212,7 +226,7 @@ postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
 mail: bjorn@mailgw.example.com
 telephoneNumber: +1 313 555 0355
 
-# Query 11: filter:(mail=jaj@mail.alumni.example.com) cn sn title uid
+# Query 13: filter:(mail=jaj@mail.alumni.example.com) attrs:cn sn title uid
 dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
 cn: James A Jones 1
 cn: James Jones
@@ -221,3 +235,17 @@ sn: Jones
 uid: jaj
 title: Mad Cow Researcher, UM Alumni Association
 
+# Query 14: filter:(mail=*example.com) attrs:cn sn title uid
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+
+# Query 15: filter:(uid=b*) attrs:mail
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+mail: bjorn@mailgw.example.com
+
index b9257c8ff3895a639b7611a130b689dfeac44815..87bffc6700e5b6b3257b2ad21be91672b45e780a 100644 (file)
@@ -39,17 +39,20 @@ argsfile    @TESTDIR@/slapd.2.args
 database       ldap
 suffix          "dc=example,dc=com"
 rootdn          "dc=example,dc=com"
+rootpw         "secret"
 uri            "@URI1@"
 
+limits         dn="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" size=1
+
 overlay                pcache
 proxycache     @BACKEND@ 100 2 @ENTRY_LIMIT@ @CACHETTL@
 proxyattrset 0         sn cn title uid
 proxyattrset 1         mail postaladdress telephonenumber cn uid
-proxytemplate          (|(cn=)(sn=)) 0 @CACHETTL@
-proxytemplate          (sn=) 0 @CACHETTL@
-proxytemplate          (uid=) 1 @CACHETTL@
-proxytemplate          (mail=) 0 @CACHETTL@
-       
+proxytemplate          (|(cn=)(sn=)) 0 @CACHETTL@ @NCACHETTL@ @SCACHETTL@
+proxytemplate          (sn=) 0 @CACHETTL@ @NCACHETTL@ @SCACHETTL@
+proxytemplate          (uid=) 1 @CACHETTL@ @NCACHETTL@ @SCACHETTL@
+proxytemplate          (mail=) 0 @CACHETTL@ @NCACHETTL@ @SCACHETTL@
+
 #bdb#cachesize 20
 #hdb#cachesize 20
 
index e4a73a3b41f4c6716bdeb6a35e6fcbe5ff596cb8..25dd524f0176c20a2e5ff0c4c12da47652611054 100755 (executable)
@@ -71,6 +71,8 @@ sed -e "s/@BACKEND@/${BACKEND}/"                      \
        -e "s;@PORT6@;${PORT6};"                        \
        -e "s/@SASL_MECH@/${SASL_MECH}/"                \
        -e "s/@CACHETTL@/${CACHETTL}/"                  \
+       -e "s/@NCACHETTL@/${NCACHETTL}/"                \
+       -e "s/@SCACHETTL@/${SCACHETTL}/"                \
        -e "s/@ENTRY_LIMIT@/${CACHE_ENTRY_LIMIT}/"      \
        -e "s;@TESTDIR@;${TESTDIR};"                    \
        -e "s;@DATADIR@;${DATADIR};"                    \
index 070970af97b6f51ca929afc3ce3a0d6bb2a95849..44e55b6bd9e363244f8661f6265b0de380c44ff3 100755 (executable)
@@ -14,7 +14,9 @@
 ## <http://www.OpenLDAP.org/license.html>.
 
 CACHETTL="1m"
-CACHE_ENTRY_LIMIT=10
+NCACHETTL="1m"
+SCACHETTL="1m"
+CACHE_ENTRY_LIMIT=6
 
 . $SRCDIR/scripts/defines.sh
 
@@ -109,13 +111,17 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-cat /dev/null > $SLAVEOUT
+cat /dev/null > $SEARCHOUT
 
 echo "Making queries on the proxy cache..." 
-echo "Query 1: filter:(sn=Jon) attrs: all" 
-echo "# Query 1: filter:(sn=Jon) attrs: all" >> $SLAVEOUT
+CNT=0
+
+CNT=`expr $CNT + 1`
+FILTER="(sn=Jon)"
+echo "Query $CNT: filter:$FILTER attrs:all (expect nothing)" 
+echo "# Query $CNT: filter:$FILTER attrs:all (expect nothing)" >> $SEARCHOUT
 $LDAPSEARCH -x -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'sn=Jon' >> $SLAVEOUT 2>&1
+       "$FILTER" >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -132,10 +138,13 @@ if test $RC != 0 ; then
        exit 0
 fi
 
-echo "Query 2: filter:(|(cn=*Jon*)(sn=Jon*)) attrs:cn sn title uid"  
-echo "# Query 2: filter:(|(cn=*Jon*)(sn=Jon*)) attrs:cn sn title uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(|(cn=*Jon*)(sn=Jon*))"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -x -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       '(|(cn=*Jon*)(sn=Jon*))' cn sn title uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -143,10 +152,13 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-echo "Query 3: filter:(sn=Smith*) attrs:cn sn uid"  
-echo "# Query 3: filter:(sn=Smith*) attrs:cn sn uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(sn=Smith*)"
+ATTRS="cn sn uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'sn=Smith*' cn sn uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -154,21 +166,27 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-echo "Query 4: filter:(sn=Doe*) attrs:cn sn title uid"  
-echo "# Query 4: filter:(sn=Doe*) attrs:cn sn title uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(sn=Doe*)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'sn=Doe' cn sn title uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
        test $KILLSERVERS != no && kill -HUP $KILLPIDS
        exit $RC
 fi
-echo "Query 5: filter:(uid=bjorn) attrs:mail postaladdress telephonenumber cn uid"  
-echo "# Query 5: filter:(uid=bjorn) attrs:mail postaladdress telephonenumber cn uid" >> $SLAVEOUT
+
+CNT=`expr $CNT + 1`
+FILTER="(uid=bjorn)"
+ATTRS="mail postaladdress telephonenumber cn uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'uid=bjorn' mail postaladdress telephonenumber cn uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -176,10 +194,13 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-echo "Query 6: filter:(mail=*@mail.alumni.example.com) cn sn title uid"  
-echo "# Query 6: filter:(mail=*@mail.alumni.example.com) cn sn title uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(mail=*@mail.alumni.example.com)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'mail=*@mail.alumni.example.com' cn sn title uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -187,10 +208,13 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-echo "Query 7: filter:(mail=*) cn sn title uid"  
-echo "# Query 7: filter:(mail=*) cn sn title uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(mail=*)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'mail=*' cn sn title uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -198,8 +222,64 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-# queries 2-6 are cacheable
-CACHEABILITY=0111110
+CNT=`expr $CNT + 1`
+FILTER="(mail=*example.com)"
+ATTRS="cn sn title uid"
+USERDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+PASSWD="bjorn"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
+       -D "$USERDN" -w "$PASSWD" \
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+case $RC in
+0)
+       echo "ldapsearch should have failed!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+4)
+       echo "ldapsearch failed ($RC)"
+       ;;
+*)
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+CNT=`expr $CNT + 1`
+FILTER="(uid=b*)"
+ATTRS="mail"
+USERDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+PASSWD="bjorn"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
+       -D "$USERDN" -w "$PASSWD" \
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+case $RC in
+0)
+       echo "ldapsearch should have failed!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+4)
+       echo "ldapsearch failed ($RC)"
+       ;;
+*)
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+FIRST=$CNT
+
+# queries 2-6,8-9 are cacheable
+CACHEABILITY=011111011
 grep CACHEABLE $LOG2 | awk '{ 
                if ($2 == "NOT") 
                        printf "Query %d not cacheable\n",NR
@@ -221,10 +301,13 @@ else
        exit 1
 fi
 
-echo "Query 8: filter:(|(cn=*Jones)(sn=Jones)) attrs:cn sn title uid"  
-echo "# Query 8: filter:(|(cn=*Jones)(sn=Jones)) attrs:cn sn title uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(|(cn=*Jones)(sn=Jones))"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -x -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       '(|(cn=*Jones)(sn=Jones))' cn sn title uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -232,10 +315,13 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-echo "Query 9: filter:(sn=Smith) attrs:cn sn title uid"  
-echo "# Query 9: filter:(sn=Smith) attrs:cn sn title uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(sn=Smith)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'sn=Smith' cn sn title uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -243,10 +329,13 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-echo "Query 10: filter:(uid=bjorn) attrs:mail postaladdress telephonenumber cn uid"  
-echo "# Query 10: filter:(uid=bjorn) attrs:mail postaladdress telephonenumber cn uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(uid=bjorn)"
+ATTRS="mail postaladdress telephonenumber cn uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'uid=bjorn' mail postaladdress telephonenumber cn uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 if test $RC != 0 ; then
        echo "ldapsearch failed ($RC)!"
@@ -254,10 +343,13 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-echo "Query 11: filter:(mail=jaj@mail.alumni.example.com) cn sn title uid"  
-echo "# Query 11: filter:(mail=jaj@mail.alumni.example.com) cn sn title uid" >> $SLAVEOUT
+CNT=`expr $CNT + 1`
+FILTER="(mail=jaj@mail.alumni.example.com)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
 $LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
-       'mail=jaj@mail.alumni.example.com' cn sn title uid >> $SLAVEOUT 2>&1
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
 RC=$?
 
 if test $RC != 0 ; then
@@ -266,18 +358,75 @@ if test $RC != 0 ; then
        exit $RC
 fi
 
-#queries 8-11 are answerable
-ANSWERABILITY=1111
-grep ANSWERABLE $LOG2 | awk '{ 
-               if (NR > 7) { 
+CNT=`expr $CNT + 1`
+FILTER="(mail=*example.com)"
+ATTRS="cn sn title uid"
+USERDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+PASSWD="bjorn"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
+       -D "$USERDN" -w "$PASSWD" \
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+case $RC in
+0)
+       echo "ldapsearch should have failed!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+4)
+       echo "ldapsearch failed ($RC)"
+       ;;
+*)
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+CNT=`expr $CNT + 1`
+FILTER="(uid=b*)"
+ATTRS="mail"
+USERDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+PASSWD="bjorn"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"  
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT2 \
+       -D "$USERDN" -w "$PASSWD" \
+       "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+case $RC in
+0)
+       echo "ldapsearch should have failed!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+4)
+       echo "ldapsearch failed ($RC)"
+       ;;
+*)
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+#queries 10-12,15 are answerable, 13-14 are not
+#actually, 12 would be answerable, but since 8 made mail=*example.com
+#not answerable because of sizelimit, queries contained in it are no longer
+#answerable as well
+ANSWERABILITY=111001
+grep ANSWERABLE $LOG2 | awk -vFIRST=$FIRST '{ 
+               if (NR > FIRST) { 
                        if ($2 == "NOT") 
                                printf "Query %d not answerable\n",NR
                        else 
                                printf "Query %d answerable\n",NR 
                }
        }' 
-ANSWERED=`grep ANSWERABLE $LOG2 | awk '{ 
-               if (NR > 7) { 
+ANSWERED=`grep ANSWERABLE $LOG2 | awk -vFIRST=$FIRST '{ 
+               if (NR > FIRST) { 
                        if ($2 == "NOT") 
                                printf "0" 
                        else 
@@ -295,7 +444,7 @@ else
 fi
 
 echo "Filtering ldapsearch results..."
-. $LDIFFILTER < $SLAVEOUT > $SEARCHFLT
+. $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
 echo "Filtering original ldif..."
 . $LDIFFILTER < $PROXYCACHEOUT > $LDIFFLT
 echo "Comparing filter output..."