summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/index_subsystem.c
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/slapd/index_subsystem.c
downloadds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.gz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.xz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/servers/slapd/index_subsystem.c')
-rw-r--r--ldap/servers/slapd/index_subsystem.c1286
1 files changed, 1286 insertions, 0 deletions
diff --git a/ldap/servers/slapd/index_subsystem.c b/ldap/servers/slapd/index_subsystem.c
new file mode 100644
index 00000000..e696860e
--- /dev/null
+++ b/ldap/servers/slapd/index_subsystem.c
@@ -0,0 +1,1286 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2002 Netscape Communications Corporation. All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* The indexing subsystem
+ * ----------------------
+ *
+ * This provides support for indexing plugins and assigning
+ * those plugins to sub-filters of a search filter. Currently
+ * the old indexing code still exists and operates on those
+ * indexes which do not have a plugin assigned. This indexing
+ * abstraction is intended to eventually decouple the index mechanics
+ * from the back-end where that is possible. Hopefully, while
+ * supporting the needs of virtual attribute indexes, it will allow
+ * easier integration of other back ends.
+ *
+ */
+
+/* includes */
+#include "slap.h"
+#include "./back-ldbm/back-ldbm.h"
+#include "./back-ldbm/idlapi.h"
+#include "index_subsys.h"
+
+#define INDEX_IDLIST_INITIAL_SIZE 128 /* make this a good size to avoid constant reallocs */
+
+/* data */
+static void **idl_api;
+
+struct _indexLinkedList
+{
+ void *pNext;
+ void *pPrev;
+};
+typedef struct _indexLinkedList indexLinkedList;
+
+struct _indexEntry
+{
+ indexLinkedList list;
+ char *indexedAttribute;
+ Slapi_Filter *indexfilter;
+ char *indexfilterstr;
+ char **associated_attrs;
+ void *user_data;
+ Slapi_DN *namespace_dn;
+ index_search_callback lookup_func; /* search call back */
+};
+typedef struct _indexEntry indexEntry;
+
+struct _indexPlugin
+{
+ indexLinkedList list;
+ char *id;
+ indexEntry *indexes;
+ index_validate_callback validate_op;
+};
+typedef struct _indexPlugin indexPlugin;
+
+struct _globalIndexCache
+{
+ indexPlugin *pPlugins;
+ indexEntry **ppIndexIndex; /* sorted list with key: indexEntry.indexedAttribute */
+ int index_count;
+ PRRWLock *cache_lock;
+};
+typedef struct _globalIndexCache globalIndexCache;
+
+static globalIndexCache *theCache = 0;
+
+/* prototypes */
+static int index_subsys_decoder_done(Slapi_Filter *f);
+static int index_subsys_assign_decoders(Slapi_Filter *f);
+static int index_subsys_assign_decoder(Slapi_Filter *f);
+static int index_subsys_group_decoders(Slapi_Filter *f);
+static indexEntry *index_subsys_find_decoder(Slapi_Filter *f);
+static int index_subsys_unlink_subfilter(Slapi_Filter *fcomplex, Slapi_Filter *fsub);
+static int index_subsys_index_matches_associated(indexEntry *index, Slapi_Filter *f);
+
+/* matching alg - note : values 0/1/2/3 supported right now*/
+#define INDEX_MATCH_NONE 0
+#define INDEX_MATCH_EQUALITY 1
+#define INDEX_MATCH_PRESENCE 2
+#define INDEX_MATCH_SUBSTRING 3
+#define INDEX_MATCH_COMPLEX 4
+static int index_subsys_index_matches_filter(indexEntry *index, Slapi_Filter *f);
+
+static void index_subsys_read_lock()
+{
+ PR_RWLock_Rlock(theCache->cache_lock);
+}
+
+static void index_subsys_write_lock()
+{
+ PR_RWLock_Wlock(theCache->cache_lock);
+}
+
+static void index_subsys_unlock()
+{
+ PR_RWLock_Unlock(theCache->cache_lock);
+}
+
+int slapi_index_entry_list_create(IndexEntryList **list)
+{
+ if(idl_api)
+ *list = (IndexEntryList*)IDList_alloc(idl_api, INDEX_IDLIST_INITIAL_SIZE);
+ else
+ *list = 0;
+
+ return !(*list);
+}
+
+int slapi_index_entry_list_add(IndexEntryList **list, IndexEntryID id)
+{
+ if(idl_api)
+ IDList_insert(idl_api, (IDList **)list, (ID)id);
+
+ return 0; /* no way to tell failure */
+}
+
+
+static int index_subsys_index_matches_filter(indexEntry *index, Slapi_Filter *f)
+{
+ int ret = INDEX_MATCH_NONE;
+
+ /* simple filters only right now */
+ if(slapi_attr_types_equivalent(index->indexedAttribute, f->f_type))
+ {
+ /* ok we have some type of match, lets find out which */
+
+ switch(index->indexfilter->f_choice)
+ {
+ case LDAP_FILTER_PRESENT:
+ /* present means "x=*" */
+ if(f->f_choice == LDAP_FILTER_PRESENT)
+ ret = INDEX_MATCH_PRESENCE;
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ /* our equality filters look like this "x=**"
+ * that means the filter will be assigned
+ * a substring f_choice by the filter code
+ * in str2filter.c
+ * but we need to differentiate so we take
+ * advantage of the fact that this creates a
+ * special substring filter with no substring
+ * to look for...
+ */
+ if( index->indexfilter->f_sub_initial == 0 &&
+ index->indexfilter->f_sub_any == 0 &&
+ index->indexfilter->f_sub_final == 0
+ )
+ {
+ /* this is an index equality filter */
+ if(f->f_choice == LDAP_FILTER_EQUALITY)
+ ret = INDEX_MATCH_EQUALITY;
+ }
+ else
+ {
+ /* this is a regualar substring filter */
+ if(f->f_choice == LDAP_FILTER_SUBSTRINGS)
+ ret = INDEX_MATCH_SUBSTRING;
+ }
+
+ break;
+
+ default:
+ /* we don't know about any other type yet */
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/* index_subsys_assign_filter_decoders
+ * -----------------------------------
+ * assigns index plugins to sub-filters
+ */
+int index_subsys_assign_filter_decoders(Slapi_PBlock *pb)
+{
+ int rc;
+ Slapi_Filter *f;
+ char *subsystem = "index_subsys_assign_filter_decoders";
+ char logbuf[ 1024 ];
+
+ /* extract the filter */
+ slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &f);
+
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_FILTER ) && NULL != f ) {
+ logbuf[0] = '\0';
+ slapi_log_error( SLAPI_LOG_FATAL, subsystem, "before: %s\n",
+ slapi_filter_to_string(f, logbuf, sizeof(logbuf)));
+ }
+
+ /* find decoders */
+ rc = index_subsys_assign_decoders(f);
+
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_FILTER ) && NULL != f ) {
+ logbuf[0] = '\0';
+ slapi_log_error( SLAPI_LOG_FATAL, subsystem, " after: %s\n",
+ slapi_filter_to_string(f, logbuf, sizeof(logbuf)));
+ }
+
+ return rc;
+}
+
+/* index_subsys_filter_decoders_done
+ * ---------------------------------
+ * removes assigned index plugins in
+ * sub-filters
+ */
+int index_subsys_filter_decoders_done(Slapi_PBlock *pb)
+{
+ Slapi_Filter *f;
+
+ /* extract the filter */
+ slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &f);
+
+ /* remove decoders */
+ return index_subsys_decoder_done(f);
+}
+
+
+/* index_subsys_unlink_subfilter
+ * -----------------------------
+ * removes the sub-filter from
+ * the complex filter list
+ * does NOT deallocate the sub-filter
+ */
+static int index_subsys_unlink_subfilter(Slapi_Filter *fcomplex, Slapi_Filter *fsub)
+{
+ int ret = -1;
+ Slapi_Filter *f;
+ Slapi_Filter *f_prev = 0;
+
+ for(f=fcomplex->f_list; f != NULL; f = f->f_next)
+ {
+ if(f == fsub)
+ {
+ if(f_prev)
+ {
+ f_prev->f_next = f->f_next;
+ f->f_next = 0;
+ ret = 0;
+ break;
+ }
+ else
+ {
+ /* was at the beginning of the list */
+ fcomplex->f_list = f->f_next;
+ f->f_next = 0;
+ ret = 0;
+ break;
+ }
+ }
+
+ f_prev = f;
+ }
+
+ return ret;
+}
+
+/* index_subsys_index_matches_associated
+ * -------------------------------------
+ * determines if there is any kind of match
+ * between the specified type and the index.
+ *
+ * matches could be on the indexed type or
+ * on any associated attribute
+ * returns:
+ * 0 when false
+ * non-zero when true
+ */
+static int index_subsys_index_matches_associated(indexEntry *index, Slapi_Filter *f)
+{
+ int ret = 0;
+ char **associated_attrs = index->associated_attrs;
+
+ if(INDEX_MATCH_NONE != index_subsys_index_matches_filter(index, f))
+ {
+ /* matched on indexed attribute */
+ ret = -1;
+ goto bail;
+ }
+
+ /* check associated attributes */
+ if(associated_attrs)
+ {
+ int i;
+ char *type = f->f_type;
+
+ for(i=0; associated_attrs[i]; i++)
+ {
+ if(slapi_attr_types_equivalent(associated_attrs[i], type))
+ {
+ /* matched on associated attribute */
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+bail:
+ return ret;
+}
+
+
+/* index_subsys_flatten_filter
+ * ---------------------------
+ * takes a complex filter as argument (assumed)
+ * and merges all compatible complex sub-filters
+ * such that their list of sub-filters are moved
+ * to the main list of sub-filters in f.
+ *
+ * This "flattens" the filter so that there are
+ * the minimum number of nested complex filters
+ * possible.
+ *
+ * What is a "compatible complex sub-filter?"
+ * Answer: a complex sub-filter which is of the
+ * same type (AND or OR) as the containing complex
+ * filter and which is either assigned the same
+ * index decoder or no index decoder is assigned to
+ * either complex filter.
+ *
+ * This function assumes that it has already
+ * been called for every complex sub-filter of f
+ * i.e. it only looks one layer deep.
+ *
+ * Note that once a filter has been processed in
+ * this fashion, rearranging the filter based
+ * on the optimal evaluation order becomes very
+ * much simpler. It should also have benefits for
+ * performance when a filter is evaluated many
+ * times since a linear list traversal is faster than a
+ * context switch to recurse down into a complex
+ * filter structure.
+ *
+ */
+static void index_subsys_flatten_filter(Slapi_Filter *flist)
+{
+ struct slapi_filter *f = flist->f_list;
+ struct slapi_filter *fprev = 0;
+ struct slapi_filter *flast = 0;
+
+ while(f)
+ {
+ if(f->assigned_decoder == flist->assigned_decoder)
+ {
+ /* mmmk, but is the filter complex? */
+ if(f->f_choice == LDAP_FILTER_AND || f->f_choice == LDAP_FILTER_OR)
+ {
+ if(f->f_choice == flist->f_choice)
+ {
+ /* flatten this, and remember
+ * we expect that any complex sub-filters
+ * have already been flattened, so we
+ * simply transfer the contents of this
+ * sub-filter to the main sub-filter and
+ * remove this complex sub-filter
+ *
+ * take care not to change the filter
+ * ordering in any way (it may have been
+ * optimized)
+ */
+ struct slapi_filter *fnext = f->f_next;
+ struct slapi_filter *fsub = 0;
+
+ while(f->f_list)
+ {
+ fsub = f->f_list;
+ index_subsys_unlink_subfilter(f, f->f_list);
+ fsub->f_next = fnext;
+
+ if(flast)
+ {
+ /* we inserted something before - insert after */
+ flast->f_next = fsub;
+ }
+ else
+ {
+ /* first insertion */
+ if(fprev)
+ {
+ fprev->f_next = fsub;
+ }
+ else
+ {
+ /* insert at list start */
+ flist->f_list = fsub;
+ }
+
+ fprev = fsub;
+ }
+
+ flast = fsub;
+ }
+
+ /* zero for dont recurse - recursing would
+ * be bad since we have created a mutant
+ * complex filter with no children
+ */
+ slapi_filter_free(f, 0);
+ f = fnext;
+ }
+ else
+ {
+ fprev = f;
+ f = f->f_next;
+ }
+ }
+ else
+ {
+ fprev = f;
+ f = f->f_next;
+ }
+ }
+ else
+ {
+ fprev = f;
+ f = f->f_next;
+ }
+ }
+}
+
+/* index_subsys_group_decoders
+ * ---------------------------
+ * looks for grouping opportunities
+ * such that a complex filter may
+ * be assigned to a single index.
+ *
+ * it is assumed that any complex sub-filters
+ * have already been assigned decoders
+ * using this function if it
+ * was possible to do so
+ */
+static int index_subsys_group_decoders(Slapi_Filter *flist)
+{
+ int ret = 0;
+ struct slapi_filter *f = 0;
+ struct slapi_filter *f_indexed = 0;
+ struct slapi_filter *fhead = 0;
+ int index_count = 0;
+ int matched = 1;
+
+ switch(flist->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ break;
+
+ default:
+ /* any other result not handled by this code */
+ goto bail;
+ }
+
+ /* make sure we start with an unassigned filter */
+ flist->assigned_decoder = 0;
+
+ /* Since this function is about optimal grouping of complex filters,
+ * lets explain what is happening here:
+ *
+ * Beyond detecting that what was passed in is already optimal,
+ * there are 4 basic problems we need to solve here:
+ *
+ * Input this function Output
+ * 1) (&(indexed)(other)(associated)) -> X -> (&(&(indexed)(associated))(other))
+ * 2) (&(&(indexed)(other))(associated)) -> X -> (&(&(indexed)(associated))(other))
+ * 3) (&(&(associated)(other))(indexed)) -> X -> (&(&(indexed)(associated))(other))
+ * 4) (&(&(indexed)(associated))(associated)) -> X -> (&(indexed)(associated)(associated))
+ *
+ * To avoid having special code for 2) and 3) we make them look like
+ * 1) by flattening the filter - note this will only flatten subfilters
+ * which have no decoder assigned since the filter we flatten has no
+ * decoder assigned - and this behaviour is exactly what we want.
+ * 4) is a special case of 1) and since that is the case, we can allow
+ * the code for 1) to process it but make sure we flatten the filter
+ * before exit. If 4) is exactly as stated without any other non-indexed
+ * or associated references then in fact it will be detected as a completely
+ * matching filter prior to reaching the code for 1).
+ */
+
+ index_subsys_flatten_filter(flist);
+ fhead = flist->f_list;
+
+ /* find the first index assigned */
+ for ( f_indexed = fhead; f_indexed != NULL; f_indexed = f_indexed->f_next )
+ {
+ if( f_indexed->assigned_decoder )
+ {
+ /* non-null decoder means assigned */
+ break;
+ }
+ }
+
+ if(NULL == f_indexed)
+ /* nothing to process */
+ goto bail;
+
+ /* determine if whole filter matches
+ * to avoid allocations where it is
+ * not necessary
+ */
+ for ( f = fhead; f != NULL; f = f->f_next )
+ {
+ if(f->assigned_decoder != f_indexed->assigned_decoder)
+ {
+ switch(f->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ /*
+ * All complex subfilters are guaranteed to have the correct
+ * decoder assigned already, so this is a mismatch.
+ */
+
+ matched = 0;
+ break;
+
+ default:
+ if(!index_subsys_index_matches_associated(f_indexed->assigned_decoder, f))
+ {
+ matched = 0;
+ }
+ break;
+ }
+
+ if(!matched)
+ break;
+ }
+ }
+
+ if(matched)
+ {
+ /* whole filter matches - assign to this decoder */
+ flist->assigned_decoder = f_indexed->assigned_decoder;
+ /* finally lets flatten this filter if possible
+ * Didn't we do that already? No, we flattened the
+ * filter *prior* to assigning a decoder
+ */
+ index_subsys_flatten_filter(flist);
+ goto bail;
+ }
+
+ /* whole filter does not match so,
+ * if the sub-filter count is > 2
+ * for each assigned sub-filter,
+ * match other groupable filters
+ * and extract them into another sub-filter
+ */
+
+ /* count */
+ for ( f = fhead; f != NULL && index_count < 3; f = f->f_next )
+ {
+ index_count++;
+ }
+
+ if(index_count > 2)
+ {
+ /* this is where we start re-arranging the filter assertions
+ * into groups which can be serviced by a single plugin
+ * at this point we know that:
+ * a) the filter has at least 2 assertions that cannot be grouped
+ * b) there are more than 2 assertions and so grouping is still possible
+ */
+
+ struct slapi_filter *f_listposition=f_indexed; /* flist subfilter list iterator */
+ int main_iterate; /* controls whether to iterate to the next sub-filter of flist */
+
+ while(f_listposition)
+ {
+ /* the target grouping container - we move sub-filters here */
+ struct slapi_filter *targetf=0;
+
+ /* indicates we found an existing targetf */
+ int assigned = 0;
+
+ struct slapi_filter *f_last = 0; /* previos filter in list */
+
+ /* something to join with next compatible
+ * subfilter we find - this will be the
+ * first occurence of a filter assigned
+ * to a particular decoder
+ */
+ struct slapi_filter *saved_filter = 0;
+
+ struct slapi_filter *f_tmp = 0; /* save filter for list fixups */
+
+ /* controls whether to iterate to the
+ * next sub-filter of flist
+ * inner loop
+ */
+ int iterate = 1;
+
+ f_indexed = f_listposition;
+ main_iterate = 1;
+
+ /* finding a convenient existing sub-filter of the same
+ * type as the containing filter avoids allocation
+ * so lets look for one
+ */
+
+ for ( f = fhead; f != NULL; f = f->f_next)
+ {
+ switch(f->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ if( f->f_choice == flist->f_choice &&
+ f->assigned_decoder == f_indexed->assigned_decoder)
+ {
+ targetf = f;
+ assigned = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if(assigned)
+ break;
+ }
+
+ /* now look for grouping opportunities */
+ for ( f = fhead; f != NULL; (iterate && f) ? f = f->f_next : f )
+ {
+ iterate = 1;
+
+ if(f != targetf)
+ {
+ switch(f->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ if( (targetf && f->f_choice == targetf->f_choice)
+ && f->assigned_decoder == f_indexed->assigned_decoder)
+ {
+ /* ok we have a complex filter we can group - group it
+ * it is worth noting that if we got here, then we must
+ * have found a complex filter suitable for for putting
+ * sub-filters in, so there is no need to add the newly
+ * merged complex filter to the main complex filter,
+ * since it is already there
+ */
+
+ /* main filter list fix ups */
+ f_tmp = f;
+ f = f->f_next;
+ iterate = 0;
+
+ if(f_tmp == f_listposition)
+ {
+ f_listposition = f;
+ main_iterate = 0;
+ }
+
+ /* remove f from the main complex filter */
+ index_subsys_unlink_subfilter(flist, f_tmp);
+
+
+ /* merge - note, not true merge since f gets
+ * added to targetf as a sub-filter
+ */
+ slapi_filter_join_ex(targetf->f_choice, targetf, f_tmp, 0);
+
+ /* since it was not a true merge, lets do the true merge now */
+ index_subsys_flatten_filter(targetf);
+ }
+ break;
+
+ default:
+ if(index_subsys_index_matches_associated(f_indexed->assigned_decoder, f))
+ {
+ if(targetf)
+ {
+ /* main filter list fix ups */
+ f_tmp = f;
+ f = f->f_next;
+ iterate = 0;
+
+ if(f_tmp == f_listposition)
+ {
+ f_listposition = f;
+ main_iterate = 0;
+ }
+
+ index_subsys_unlink_subfilter(flist, f_tmp);
+ targetf = slapi_filter_join_ex( targetf->f_choice, targetf, f_tmp, 0 );
+ }
+ else
+ {
+ if(saved_filter)
+ {
+ /* main filter list fix ups */
+ f_tmp = f;
+ f = f->f_next;
+ iterate = 0;
+
+ if(f_tmp == f_listposition)
+ {
+ f_listposition = f;
+ main_iterate = 0;
+ }
+
+ index_subsys_unlink_subfilter(flist, f_tmp);
+ index_subsys_unlink_subfilter(flist, saved_filter);
+ targetf = slapi_filter_join_ex( flist->f_choice, saved_filter, f_tmp, 0 );
+ targetf->assigned_decoder = f_indexed->assigned_decoder;
+ }
+ else
+ {
+ /* nothing to join so save this for
+ * when we find another compatible
+ * filter
+ */
+ saved_filter = f;
+ }
+ }
+
+ if(!assigned && targetf)
+ {
+ /* targetf has just been created, so
+ * we must add it to the main complex filter
+ */
+ targetf->f_next = flist->f_list;
+ flist->f_list = targetf;
+ assigned = 1;
+ }
+ }
+
+ break;
+ }
+ }
+
+ f_last = f;
+ }
+
+ /* iterate through the main list
+ * to the next indexed sub-filter
+ */
+ if( f_listposition &&
+ (main_iterate ||
+ (!main_iterate &&
+ !f_listposition->assigned_decoder)))
+ {
+ if(!f_listposition->f_next)
+ {
+ f_listposition = 0;
+ break;
+ }
+
+ for ( f_listposition = f_listposition->f_next; f_listposition != NULL; f_listposition = f_listposition->f_next )
+ {
+ if( f_listposition->assigned_decoder )
+ {
+ /* non-null decoder means assigned */
+ break;
+ }
+ }
+ }
+ }
+ }
+
+bail:
+
+ return ret;
+}
+
+
+/* index_subsys_assign_decoders
+ * ----------------------------
+ * recurses through complex filters
+ * assigning decoders
+ */
+static int index_subsys_assign_decoders(Slapi_Filter *f)
+{
+ int ret = 0;
+ Slapi_Filter *subf;
+
+ switch ( slapi_filter_get_choice( f ) )
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /* assign simple filters first */
+ f->assigned_decoder = 0;
+ for(subf=f->f_list; subf != NULL; subf = subf->f_next )
+ ret = index_subsys_assign_decoders(subf);
+
+ /* now check for filter grouping opportunities... */
+ if(slapi_filter_get_choice( f ) != LDAP_FILTER_NOT)
+ index_subsys_group_decoders(f);
+ else
+ {
+ /* LDAP_FILTER_NOT is a special case
+ * the contained sub-filter determines
+ * the assigned index - the index plugin has
+ * the option to refuse to service the
+ * NOT filter when it is presented
+ */
+ f->assigned_decoder = f->f_list->assigned_decoder;
+ }
+
+ break;
+
+ default:
+ /* find a decoder that fits this simple filter */
+ ret = index_subsys_assign_decoder(f);
+ }
+
+ return ret;
+}
+
+/* index_subsys_decoder_done
+ * -------------------------
+ * recurses through complex filters
+ * removing decoders
+ */
+static int index_subsys_decoder_done(Slapi_Filter *f)
+{
+ int ret = 0;
+ Slapi_Filter *subf;
+ indexEntry *index;
+ indexEntry *next_index;
+
+ switch ( slapi_filter_get_choice( f ) )
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /* remove simple filters first */
+ for(subf=f->f_list; subf != NULL; subf = subf->f_next )
+ ret = index_subsys_decoder_done(subf);
+
+ break;
+
+ default:
+ /* free the decoders - shallow free */
+ index = f->assigned_decoder;
+
+ while(index)
+ {
+ next_index = index->list.pNext;
+ slapi_ch_free((void**)index);
+ index = next_index;
+ }
+
+ f->assigned_decoder = 0;
+ }
+
+ return ret;
+}
+
+/* index_subsys_evaluate_filter
+ * ----------------------------
+ * passes the filter to the correct plugin
+ * index_subsys_assign_filter_decoders() must
+ * have been called previously on this filter
+ * this function can be safely called on all
+ * filters post index_subsys_assign_filter_decoders()
+ * whether they are assigned to a plugin or not
+ *
+ * returns:
+ * INDEX_FILTER_EVALUTED: a candidate list is produced
+ * INDEX_FILTER_UNEVALUATED: filter not considered
+ */
+int index_subsys_evaluate_filter(Slapi_Filter *f, Slapi_DN *namespace_dn, IndexEntryList **out)
+{
+ int ret = INDEX_FILTER_UNEVALUATED;
+ indexEntry *index = (indexEntry*)f->assigned_decoder;
+
+ if(index && theCache)
+ {
+ index_subsys_read_lock();
+
+ /* there is a list of indexes which may
+ * provide an answer for this filter, we
+ * need to invoke the first one that matches
+ * the namespace requested
+ */
+ for(; index; index = index->list.pNext)
+ {
+ /* check namespace */
+ if(slapi_sdn_compare(index->namespace_dn, namespace_dn))
+ {
+ /* wrong namespace */
+ continue;
+ }
+
+ /* execute the search */
+ if(index->lookup_func)
+ {
+ ret = (index->lookup_func)(f, out, index->user_data);
+ break;
+ }
+ }
+
+ index_subsys_unlock();
+ }
+
+ return ret;
+}
+
+
+/* slapi_index_register_decoder
+ * ----------------------------
+ * This allows a decoder to register itself,
+ * it also builds the initial cache when first
+ * called. Note, there is no way to deregister
+ * once registered - this allows a lock free cache
+ * at the expense of a restart to clear old
+ * indexes, this shouldnt be a problem since it is
+ * not expected that indexes will be removed
+ * often.
+ */
+int slapi_index_register_decoder(char *plugin_id, index_validate_callback validate_op)
+{
+ static int firstTime = 1;
+ static int gotIDLapi = 0;
+ int ret = 0;
+ indexPlugin *plg;
+
+ if(firstTime)
+ {
+ /* create the cache */
+ theCache = (globalIndexCache*)slapi_ch_malloc(sizeof(globalIndexCache));
+ if(theCache)
+ {
+ theCache->pPlugins = 0;
+ theCache->ppIndexIndex = 0;
+ theCache->index_count = 0;
+ theCache->cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "Index Plugins");;
+ firstTime = 0;
+
+ if(!gotIDLapi)
+ {
+ if(slapi_apib_get_interface(IDL_v1_0_GUID, &idl_api))
+ {
+ gotIDLapi = 1;
+ }
+ }
+ }
+ else
+ {
+ ret = -1;
+ goto bail;
+ }
+ }
+
+ index_subsys_write_lock();
+
+ /* add the index decoder to the cache - no checks, better register once only*/
+ plg = (indexPlugin*)slapi_ch_malloc(sizeof(indexPlugin));
+ if(plg)
+ {
+ plg->id = slapi_ch_strdup(plugin_id);
+ plg->indexes = 0;
+ plg->validate_op = validate_op;
+
+ /* we always add to the start of the linked list */
+ plg->list.pPrev = 0;
+
+ if(theCache->pPlugins)
+ {
+ plg->list.pNext = theCache->pPlugins;
+ theCache->pPlugins->list.pPrev = plg;
+ }
+ else
+ plg->list.pNext = 0;
+
+
+ theCache->pPlugins = plg;
+ }
+ else
+ ret = -1;
+
+ index_subsys_unlock();
+
+bail:
+ return ret;
+}
+
+
+/* slapi_index_register_index
+ * --------------------------
+ * a plugin that has registered itself may
+ * then proceed to register individual indexes
+ * that it looks after. This function adds
+ * the indexes to the plugin cache.
+ */
+int slapi_index_register_index(char *plugin_id, indexed_item *registration_item, void *user_data)
+{
+ int ret = 0;
+ indexPlugin *plg;
+ indexEntry *index;
+ int a_matched_index = 0;
+ Slapi_Filter *tmp_f = slapi_str2filter(registration_item->index_filter);
+ int i = 0;
+ Slapi_Backend *be;
+
+ if(!theCache)
+ return -1;
+
+ index_subsys_write_lock();
+
+ /* first lets find the plugin */
+ plg = theCache->pPlugins;
+
+ while(plg)
+ {
+ if(!slapi_UTF8CASECMP(plugin_id, plg->id))
+ {
+ /* found plugin */
+ break;
+ }
+
+ plg = plg->list.pNext;
+ }
+
+ if(0 == plg)
+ {
+ /* not found */
+ ret = -1;
+ goto bail;
+ }
+
+ /* now add the new index - we shall assume indexes
+ * will not be registered twice by different plugins,
+ * in that event, the last one added wins
+ * the first matching index in the list is always
+ * the current one, other matching indexes are ignored
+ * therefore reregistering an index with NULL
+ * callbacks disables the index for that plugin
+ */
+
+ /* find the index if already registered */
+
+ index = plg->indexes;
+
+ while(index)
+ {
+ if(index_subsys_index_matches_filter(index, tmp_f))
+ {
+ /* found it - free what is currently there, it will be replaced */
+ slapi_ch_free((void**)&index->indexfilterstr);
+ slapi_filter_free(index->indexfilter, 1);
+ slapi_ch_free((void**)&index->indexedAttribute);
+
+ charray_free( index->associated_attrs );
+ index->associated_attrs = 0;
+
+ a_matched_index = 1;
+ break;
+ }
+
+ index = index->list.pNext;
+ }
+
+ if(!index)
+ index = (indexEntry*)slapi_ch_calloc(1,sizeof(indexEntry));
+
+ index->indexfilterstr = slapi_ch_strdup(registration_item->index_filter);
+ index->indexfilter = tmp_f;
+ index->lookup_func = registration_item->search_op;
+ index->user_data = user_data;
+
+ /* map the namespace dn to a backend dn */
+ be = slapi_be_select( registration_item->namespace_dn );
+
+ if(be == defbackend_get_backend())
+ {
+ ret = -1;
+ goto bail;
+ }
+
+ index->namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
+
+ /* for now, we support simple filters only */
+ index->indexedAttribute = slapi_ch_strdup(index->indexfilter->f_type);
+
+ /* add associated attributes */
+ if(registration_item->associated_attrs)
+ {
+ index->associated_attrs =
+ cool_charray_dup( registration_item->associated_attrs );
+ }
+
+ if(!a_matched_index)
+ {
+ if(plg->indexes)
+ {
+ index->list.pNext = plg->indexes;
+ plg->indexes->list.pPrev = plg;
+ }
+ else
+ index->list.pNext = 0;
+
+ index->list.pPrev = 0;
+
+ plg->indexes = index;
+
+ theCache->index_count++;
+ }
+
+ /* now we need to rebuild the index (onto the indexed items)
+ * this is a bit inefficient since
+ * every new index that is added triggers
+ * an index rebuild - but this is countered
+ * by the fact this will probably happen once
+ * at start up most of the time, and very rarely
+ * otherwise, so normal server performance should
+ * not be unduly effected effected
+ * we take care to build the index and only then swap it in
+ * for the old one
+ * PARPAR: we need to RW (or maybe a ref count) lock here
+ * only alternative would be to not have an index :(
+ * for few plugins with few indexes thats a possibility
+ * traditionally many indexes have not been a good idea
+ * anyway so...
+ */
+
+/* indexIndex = (indexEntry**)slapi_ch_malloc(sizeof(indexEntry*) * (theCache->index_count+1));
+*/
+ /* for now, lets see how fast things are without an index
+ * that should not be a problem at all to begin with since
+ * presence will be the only index decoder. Additionally,
+ * adding an index means we need locks - um, no.
+ * so no more to do
+ */
+
+bail:
+ index_subsys_unlock();
+
+ return ret;
+}
+
+/* index_subsys_index_matches_index
+ * --------------------------------
+ * criteria for a match is that the types
+ * are the same and that all the associated
+ * attributes that are configured for cmp_to
+ * are also in cmp_with.
+ */
+int index_subsys_index_matches_index(indexEntry *cmp_to, indexEntry *cmp_with)
+{
+ int ret = 0;
+
+ if(slapi_attr_types_equivalent(cmp_to->indexedAttribute, cmp_with->indexedAttribute))
+ {
+ /* now check associated */
+ if(cmp_to->associated_attrs)
+ {
+ if(cmp_with->associated_attrs)
+ {
+ int x,y;
+
+ ret = 1;
+
+ for(x=0; cmp_to->associated_attrs[x] && ret == 1; x++)
+ {
+ ret = 0;
+
+ for(y=0; cmp_with->associated_attrs[y]; y++)
+ {
+ if(slapi_attr_types_equivalent(
+ cmp_to->associated_attrs[x],
+ cmp_with->associated_attrs[y]
+ ))
+ {
+ /* matched on associated attribute */
+ ret = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* no associated is an auto match */
+ ret = 1;
+ }
+
+ }
+
+ return ret;
+}
+
+indexEntry *index_subsys_index_shallow_dup(indexEntry *dup_this)
+{
+ indexEntry *ret = (indexEntry *)slapi_ch_calloc(1,sizeof(indexEntry));
+
+ /* shallow copy - dont copy linked list pointers */
+ ret->indexedAttribute = dup_this->indexedAttribute;
+ ret->indexfilter = dup_this->indexfilter;
+ ret->indexfilterstr = dup_this->indexfilterstr;
+ ret->user_data = dup_this->user_data;
+ ret->namespace_dn = dup_this->namespace_dn;
+ ret->lookup_func = dup_this->lookup_func;
+ ret->associated_attrs = dup_this->associated_attrs;
+
+ return ret;
+}
+
+/* index_subsys_assign_decoder
+ * ---------------------------
+ * finds a decoder which will service
+ * the filter if one is available and assigns
+ * the decoder to the filter. Currently this
+ * function supports only simple filters, but
+ * may in the future support complex filter
+ * assignment (possibly including filter rewriting
+ * to make more matches possible)
+ *
+ * populates f->alternate_decoders if more than one
+ * index could deal with a filter - only filters that
+ * have a match with all associated attributes of the
+ * first found filter are said to match - their
+ * namespaces may be different
+ */
+static int index_subsys_assign_decoder(Slapi_Filter *f)
+{
+ int ret = 0; /* always succeed */
+ indexPlugin *plg;
+ indexEntry *index = 0;
+ indexEntry *last = 0;
+
+ f->assigned_decoder = 0;
+
+ if(!theCache)
+ return 0;
+
+ index_subsys_read_lock();
+
+ plg = theCache->pPlugins;
+
+ while(plg)
+ {
+ index = plg->indexes;
+
+ while(index)
+ {
+ if(INDEX_MATCH_NONE != index_subsys_index_matches_filter(index, f))
+ {
+ /* we have a match, assign this decoder if not disabled */
+ if(index->lookup_func)
+ {
+ if(!f->assigned_decoder)
+ {
+ f->assigned_decoder = index_subsys_index_shallow_dup(index);
+ last = f->assigned_decoder;
+ }
+ else
+ {
+ /* add to alternate list - we require that they all
+ * have the same associated attributes configuration for now
+ * though they may have different namespaces
+ */
+ if(index_subsys_index_matches_index(f->assigned_decoder, index))
+ {
+ /* add to end */
+ last->list.pNext = index_subsys_index_shallow_dup(index);
+ last = last->list.pNext;
+ }
+ }
+ }
+ else
+ {
+ /* index disabled, so we must allow another plugin to
+ * get a crack at servicing the index
+ */
+ break;
+ }
+ }
+
+ index = index->list.pNext;
+ }
+
+ plg = plg->list.pNext;
+ }
+
+ index_subsys_unlock();
+
+ return ret;
+}
+