summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/back-ldbm/vlv_srch.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/back-ldbm/vlv_srch.c')
-rw-r--r--ldap/servers/slapd/back-ldbm/vlv_srch.c901
1 files changed, 901 insertions, 0 deletions
diff --git a/ldap/servers/slapd/back-ldbm/vlv_srch.c b/ldap/servers/slapd/back-ldbm/vlv_srch.c
new file mode 100644
index 00000000..0f72c418
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/vlv_srch.c
@@ -0,0 +1,901 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* vlv_srch.c */
+
+
+#include "back-ldbm.h"
+#include "vlv_srch.h"
+
+/* Attributes for vlvSearch */
+char* const type_vlvName = "cn";
+char* const type_vlvBase = "vlvBase";
+char* const type_vlvScope = "vlvScope";
+char* const type_vlvFilter = "vlvFilter";
+
+/* Attributes for vlvIndex */
+char* const type_vlvSort = "vlvSort";
+char* const type_vlvFilename = "vlvFilename";
+char* const type_vlvEnabled = "vlvEnabled";
+char* const type_vlvUses = "vlvUses";
+
+static const char *file_prefix= "vlv#"; /* '#' used to avoid collision with real attributes */
+static const char *file_suffix= LDBM_FILENAME_SUFFIX;
+
+static int vlvIndex_createfilename(struct vlvIndex* pIndex, char **ppc);
+
+static int vlvIndex_equal(const struct vlvIndex* p1, const sort_spec* sort_control);
+static void vlvIndex_checkforindex(struct vlvIndex* p, backend *be);
+
+/*
+ * Create a new vlvSearch object
+ */
+struct vlvSearch*
+vlvSearch_new()
+{
+ struct vlvSearch* p = (struct vlvSearch*)slapi_ch_calloc(1,sizeof(struct vlvSearch));
+ if(p!=NULL)
+ {
+ p->vlv_e= NULL;
+ p->vlv_dn= NULL;
+ p->vlv_name= NULL;
+ p->vlv_base= NULL;
+ p->vlv_scope= LDAP_SCOPE_BASE;
+ p->vlv_filter= NULL;
+ p->vlv_slapifilter= NULL;
+ p->vlv_index= NULL;
+ p->vlv_next= NULL;
+ }
+ return p;
+}
+
+/*
+ * Trim spaces off the end of the string
+ */
+static void
+trimspaces(char *s)
+{
+ PRUint32 i= strlen(s) - 1;
+ while(i > 0 && isascii(s[i]) && isspace(s[i]))
+ {
+ s[i]= '\0';
+ i--;
+ }
+}
+
+/*
+ * Re-Initialise a vlvSearch object
+ */
+void
+vlvSearch_reinit(struct vlvSearch* p, const struct backentry *base)
+{
+ if (p->vlv_initialized) {
+ return; /* no work to do */
+ }
+ if (LDAP_SCOPE_ONELEVEL != p->vlv_scope) {
+ /* Only kind we re-init is onelevel searches */
+ return;
+ }
+ /* Now down to work */
+ if (NULL != p->vlv_slapifilter) {
+ slapi_filter_free(p->vlv_slapifilter,1);
+ }
+ p->vlv_slapifilter= slapi_str2filter( p->vlv_filter );
+ filter_normalize(p->vlv_slapifilter);
+ /* make (&(parentid=idofbase)(|(originalfilter)(objectclass=referral))) */
+ {
+ Slapi_Filter *fid2kids= NULL;
+ Slapi_Filter *focref= NULL;
+ Slapi_Filter *fand= NULL;
+ Slapi_Filter *forr= NULL;
+ p->vlv_slapifilter= create_onelevel_filter(p->vlv_slapifilter, base, 0 /* managedsait */, &fid2kids, &focref, &fand, &forr);
+ }
+}
+
+/*
+ * Initialise a vlvSearch object
+ */
+void
+vlvSearch_init(struct vlvSearch* p, Slapi_PBlock *pb, const Slapi_Entry *e, ldbm_instance *inst)
+{
+ /* VLV specification */
+ /* Need to copy the entry here because this one is in the cache,
+ * not forever ! */
+ p->vlv_e= slapi_entry_dup( e );
+ p->vlv_dn= slapi_sdn_dup(slapi_entry_get_sdn_const(e));
+ p->vlv_name= slapi_entry_attr_get_charptr(e,type_vlvName);
+ p->vlv_base= slapi_sdn_new_dn_passin(slapi_entry_attr_get_charptr(e,type_vlvBase));
+ p->vlv_scope= slapi_entry_attr_get_int(e,type_vlvScope);
+ p->vlv_filter= slapi_entry_attr_get_charptr(e,type_vlvFilter);
+ p->vlv_initialized = 1;
+
+ /* JCM: Should perform some validation and report errors to the error log */
+ /* JCM: Add brackets around the filter if none are there... */
+ trimspaces(p->vlv_name);
+ trimspaces(p->vlv_filter);
+
+ if(strlen(p->vlv_filter)>0)
+ {
+ /* Convert the textual filter, into a Slapi_Filter structure */
+ p->vlv_slapifilter= slapi_str2filter( p->vlv_filter );
+ filter_normalize(p->vlv_slapifilter);
+ }
+
+ /* JCM: Really should convert the slapifilter into a string and use that. */
+
+ /* Convert the filter based on the scope of the search */
+ switch(p->vlv_scope)
+ {
+ case LDAP_SCOPE_BASE:
+ /* Don't need to alter the filter */
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ {
+ /*
+ * Get the base object for the search.
+ * The entry "" will never be contained in the database,
+ * so treat it as a special case.
+ */
+ struct backentry *e= NULL;
+ if ( !slapi_sdn_isempty(p->vlv_base)) {
+ Slapi_Backend *oldbe = NULL;
+ entry_address addr;
+
+ /* switch context to the target backend */
+ slapi_pblock_get(pb, SLAPI_BACKEND, &oldbe);
+ slapi_pblock_set(pb, SLAPI_BACKEND, inst->inst_be);
+ slapi_pblock_set(pb, SLAPI_PLUGIN, inst->inst_be->be_database);
+
+ addr.dn = (char*)slapi_sdn_get_ndn (p->vlv_base);
+ addr.uniqueid = NULL;
+ e = find_entry( pb, inst->inst_be, &addr, NULL );
+ /* Check to see if the entry is absent. If it is, mark this search
+ * as not initialized */
+ if (NULL == e) {
+ p->vlv_initialized = 0;
+ /* We crash on anyhow, and rely on the fact that the filter
+ * we create is bogus to prevent chaos */
+ }
+
+ /* switch context back to the DSE backend */
+ slapi_pblock_set(pb, SLAPI_BACKEND, oldbe);
+ slapi_pblock_set(pb, SLAPI_PLUGIN, oldbe->be_database);
+ }
+
+ /* make (&(parentid=idofbase)(|(originalfilter)(objectclass=referral))) */
+ {
+ Slapi_Filter *fid2kids= NULL;
+ Slapi_Filter *focref= NULL;
+ Slapi_Filter *fand= NULL;
+ Slapi_Filter *forr= NULL;
+ p->vlv_slapifilter= create_onelevel_filter(p->vlv_slapifilter, e, 0 /* managedsait */, &fid2kids, &focref, &fand, &forr);
+ /* jcm: fid2kids, focref, fand, and forr get freed when we free p->vlv_slapifilter */
+ cache_return(&inst->inst_cache,&e);
+ }
+ }
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ {
+ /* make (|(originalfilter)(objectclass=referral))) */
+ /* No need for scope-filter since we apply a scope test before the filter test */
+ Slapi_Filter *focref= NULL;
+ Slapi_Filter *forr= NULL;
+ p->vlv_slapifilter= create_subtree_filter(p->vlv_slapifilter, 0 /* managedsait */, &focref, &forr);
+ /* jcm: focref and forr get freed when we free p->vlv_slapifilter */
+ }
+ break;
+ }
+}
+
+/*
+ * Destroy an existing vlvSearch object
+ */
+void
+vlvSearch_delete(struct vlvSearch** ppvs)
+{
+ if(ppvs!=NULL && *ppvs!=NULL)
+ {
+ struct vlvIndex *pi, *ni;
+ slapi_sdn_free(&((*ppvs)->vlv_dn));
+ slapi_ch_free((void**)&((*ppvs)->vlv_name));
+ slapi_sdn_free(&((*ppvs)->vlv_base));
+ slapi_ch_free((void**)&((*ppvs)->vlv_filter));
+ slapi_filter_free((*ppvs)->vlv_slapifilter,1);
+ for(pi= (*ppvs)->vlv_index;pi!=NULL;)
+ {
+ ni= pi->vlv_next;
+ if(pi->vlv_be != NULL) {
+ vlvIndex_go_offline(pi,pi->vlv_be);
+ }
+ vlvIndex_delete(&pi);
+ pi= ni;
+ }
+ slapi_ch_free((void**)ppvs);
+ *ppvs= NULL;
+ }
+}
+
+/*
+ * Add a search to a list.
+ *
+ * We add it to the end of the list because there could
+ * be other threads traversing the list at this time.
+ */
+void
+vlvSearch_addtolist(struct vlvSearch* p, struct vlvSearch** pplist)
+{
+ if(pplist!=NULL && p!=NULL)
+ {
+ p->vlv_next= NULL;
+ if(*pplist==NULL)
+ {
+ *pplist= p;
+ }
+ else
+ {
+ struct vlvSearch* last= *pplist;
+ for(;last->vlv_next!=NULL;last=last->vlv_next);
+ last->vlv_next= p;
+ }
+ }
+}
+
+
+/*
+ * Compare two VLV Searches to see if they're the same, based on their VLV Search specification.
+ */
+static struct vlvIndex *
+vlvSearch_equal(const struct vlvSearch* p1, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control)
+{
+ struct vlvIndex *pi= NULL;
+ int r= (slapi_sdn_compare(p1->vlv_base,base)==0);
+ if(r) r= (p1->vlv_scope==scope);
+ if(r) r= (strcasecmp(p1->vlv_filter,filter)==0);
+ if(r)
+ {
+ pi= p1->vlv_index;
+ r= 0;
+ for(;!r && pi!=NULL;)
+ {
+ r= vlvIndex_equal(pi, sort_control);
+ if(!r)
+ {
+ pi= pi->vlv_next;
+ }
+ }
+ }
+ return pi;
+}
+
+/*
+ * Find an enabled VLV Search in a list which matches the
+ * description provided in "base, scope, filter, sort_control"
+ */
+struct vlvIndex*
+vlvSearch_findenabled(backend *be,struct vlvSearch* plist, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control)
+{
+ struct vlvSearch *t= plist;
+ struct vlvIndex *pi= NULL;
+ for(; (t!=NULL) && (pi == NULL); t= t->vlv_next)
+ {
+ pi= vlvSearch_equal(t,base,scope,filter,sort_control);
+ if(pi!=NULL)
+ {
+ if(!vlvIndex_enabled(pi))
+ {
+ /*
+ * A VLV Spec which matched the search criteria was found.
+ * But it hasn't been enabled yet. Check to see if the
+ * index is there. But, only check once every 60 seconds.
+ */
+ time_t curtime = current_time();
+ if(curtime>pi->vlv_lastchecked+60)
+ {
+ vlvIndex_checkforindex(pi, be);
+ pi->vlv_lastchecked= current_time();
+ }
+ }
+ if(!vlvIndex_enabled(pi))
+ {
+ pi= NULL;
+ }
+ }
+ }
+ return pi;
+}
+
+/*
+ * Find a VLV Search in a list which matches the name
+ */
+struct vlvIndex*
+vlvSearch_findname(const struct vlvSearch* plist, const char *name)
+{
+ const struct vlvSearch* t= plist;
+ for(; t!=NULL ; t= t->vlv_next)
+ {
+ struct vlvIndex *pi= t->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ if(strcasecmp(pi->vlv_name,name)==0)
+ {
+ return pi;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Find a VLV Search in a list which matches the index name
+ */
+struct vlvIndex*
+vlvSearch_findindexname(const struct vlvSearch* plist, const char *name)
+{
+ const struct vlvSearch* t= plist;
+ for(; t!=NULL ; t= t->vlv_next)
+ {
+ struct vlvIndex *pi= t->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ if(strcasecmp(pi->vlv_attrinfo->ai_type,name)==0)
+ {
+ return pi;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Get a list of VLV Index names.
+ * The returned pointer must be freed with slapi_ch_free
+ */
+char *
+vlvSearch_getnames(const struct vlvSearch* plist)
+{
+ /* Work out how long the string will be */
+ char *text;
+ int length= 5; /* enough to hold 'none' */
+ const struct vlvSearch* t= plist;
+ for(; t!=NULL ; t= t->vlv_next)
+ {
+ struct vlvIndex *pi= t->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ length+= strlen(pi->vlv_name) + 4;
+ }
+ }
+ /* Build a comma delimited list of Index names */
+ text= slapi_ch_malloc(length);
+ if(length==5)
+ {
+ strcpy(text,"none");
+ }
+ else
+ {
+ text[0]= '\0';
+ t= plist;
+ for(; t!=NULL ; t= t->vlv_next)
+ {
+ struct vlvIndex *pi= t->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ sprintf(text + strlen(text),"'%s', ",pi->vlv_name);
+ }
+ }
+ }
+ return text;
+}
+
+/*
+ * Find a VLV Search in a list, based on the DN.
+ */
+struct vlvSearch*
+vlvSearch_finddn(const struct vlvSearch* plist, const Slapi_DN *dn)
+{
+ const struct vlvSearch* curr= plist;
+ for(; curr!=NULL && slapi_sdn_compare(curr->vlv_dn,dn)!=0; curr= curr->vlv_next);
+ return (struct vlvSearch*)curr;
+}
+
+/*
+ * Remove a VLV Search from a list, based on the DN.
+ */
+void
+vlvSearch_removefromlist(struct vlvSearch** pplist, const Slapi_DN *dn)
+{
+ int done= 0;
+ struct vlvSearch* prev= NULL;
+ struct vlvSearch* curr= *pplist;
+ while(curr!=NULL && !done)
+ {
+ if(slapi_sdn_compare(curr->vlv_dn,dn)==0)
+ {
+ if(curr==*pplist)
+ {
+ *pplist= curr->vlv_next;
+ }
+ else
+ {
+ prev->vlv_next= curr->vlv_next;
+ }
+ done= 1;
+ }
+ else
+ {
+ prev= curr;
+ curr= curr->vlv_next;
+ }
+ }
+}
+
+/*
+ * Access Control Check to see if the client is allowed to use this VLV Search.
+ */
+int
+vlvSearch_accessallowed(struct vlvSearch *p, Slapi_PBlock *pb)
+{
+ char *attrs[2] = { NULL, NULL};
+
+ attrs[0] = type_vlvName;
+ return (plugin_call_acl_plugin ( pb, (Slapi_Entry*)p->vlv_e, attrs, NULL,
+ SLAPI_ACL_READ, ACLPLUGIN_ACCESS_READ_ON_VLV, NULL ) );
+}
+
+const Slapi_DN *vlvSearch_getBase(struct vlvSearch* p)
+{
+ return p->vlv_base;
+}
+
+int vlvSearch_getScope(struct vlvSearch* p)
+{
+ return p->vlv_scope;
+}
+
+Slapi_Filter *vlvSearch_getFilter(struct vlvSearch* p)
+{
+ return p->vlv_slapifilter;
+}
+
+int vlvSearch_isVlvSearchEntry(Slapi_Entry *e)
+{
+ return slapi_entry_attr_hasvalue(e, "objectclass", "vlvsearch");
+}
+
+void vlvSearch_addIndex(struct vlvSearch *pSearch, struct vlvIndex *pIndex)
+{
+ pIndex->vlv_next= NULL;
+ if(pSearch->vlv_index==NULL)
+ {
+ pSearch->vlv_index= pIndex;
+ }
+ else
+ {
+ struct vlvIndex* last= pSearch->vlv_index;
+ for(;last->vlv_next!=NULL;last=last->vlv_next);
+ last->vlv_next= pIndex;
+ }
+}
+
+/* ============================================================================================== */
+
+/*
+ * Create a new vlvIndex object
+ */
+struct vlvIndex*
+vlvIndex_new()
+{
+ struct vlvIndex* p = (struct vlvIndex*)slapi_ch_calloc(1,sizeof(struct vlvIndex));
+ if(p!=NULL)
+ {
+ p->vlv_sortspec= NULL;
+ p->vlv_attrinfo= attrinfo_new();
+ p->vlv_sortkey= NULL;
+ p->vlv_filename= NULL;
+ p->vlv_mrpb= NULL;
+ p->vlv_syntax_plugin= NULL;
+ p->vlv_indexlength_lock= PR_NewLock();
+ p->vlv_indexlength_cached= 0;
+ p->vlv_indexlength= 0;
+ p->vlv_online = 1;
+ p->vlv_enabled = 0;
+ p->vlv_lastchecked= 0;
+ p->vlv_uses= 0;
+ p->vlv_search= NULL;
+ p->vlv_next= NULL;
+ }
+ return p;
+}
+
+/*
+ * Destroy an existing vlvIndex object
+ */
+void
+vlvIndex_delete(struct vlvIndex** ppvs)
+{
+ if(ppvs!=NULL && *ppvs!=NULL)
+ {
+ slapi_ch_free((void**)&((*ppvs)->vlv_sortspec));
+ {
+ int n;
+ for(n=0;(*ppvs)->vlv_sortkey[n]!=NULL;n++)
+ {
+ if((*ppvs)->vlv_mrpb[n] != NULL) {
+ destroy_matchrule_indexer((*ppvs)->vlv_mrpb[n]);
+ slapi_pblock_destroy((*ppvs)->vlv_mrpb[n]);
+ }
+ }
+ }
+ ldap_free_sort_keylist((*ppvs)->vlv_sortkey);
+ attrinfo_delete(&((*ppvs)->vlv_attrinfo));
+ slapi_ch_free((void**)&((*ppvs)->vlv_mrpb));
+ slapi_ch_free((void**)&((*ppvs)->vlv_syntax_plugin));
+ PR_DestroyLock((*ppvs)->vlv_indexlength_lock);
+ slapi_ch_free((void**)ppvs);
+ *ppvs= NULL;
+ }
+}
+
+/*
+ * Initialise a vlvSearch object
+ */
+void
+vlvIndex_init(struct vlvIndex* p, backend *be, struct vlvSearch* pSearch, const Slapi_Entry *e)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ char *filename= NULL;
+
+ if (NULL == p)
+ return;
+
+ /* JCM: Should perform some validation and report errors to the error log */
+ /* JCM: Add brackets around the filter if none are there... */
+ p->vlv_sortspec= slapi_entry_attr_get_charptr(e,type_vlvSort);
+ trimspaces(p->vlv_sortspec);
+
+ p->vlv_name= slapi_entry_attr_get_charptr(e,type_vlvName);
+ trimspaces(p->vlv_name);
+
+ p->vlv_search= pSearch;
+
+ /* Convert the textual sort specification into a keylist structure */
+ ldap_create_sort_keylist(&(p->vlv_sortkey),p->vlv_sortspec);
+ {
+ /*
+ * For each sort attribute find the appropriate syntax plugin,
+ * and if it has a matching rule, create a matching rule indexer object.
+ */
+ int n;
+ for(n=0;p->vlv_sortkey[n]!=NULL;n++);
+ p->vlv_mrpb= (Slapi_PBlock**)slapi_ch_calloc(n+1,sizeof(Slapi_PBlock*));
+ p->vlv_syntax_plugin= (void **)(Slapi_PBlock**)slapi_ch_calloc(n+1,sizeof(Slapi_PBlock*));
+ for(n=0;p->vlv_sortkey[n]!=NULL;n++)
+ {
+ slapi_attr_type2plugin( p->vlv_sortkey[n]->sk_attrtype, &p->vlv_syntax_plugin[n] );
+ if(p->vlv_sortkey[n]->sk_matchruleoid!=NULL)
+ {
+ create_matchrule_indexer(&p->vlv_mrpb[n],p->vlv_sortkey[n]->sk_matchruleoid,p->vlv_sortkey[n]->sk_attrtype);
+ }
+
+ }
+
+ }
+
+ /* Create an index filename for the search */
+ if(vlvIndex_createfilename(p,&filename))
+ {
+ p->vlv_filename= slapi_ch_malloc(strlen(file_prefix) + strlen(filename) + strlen(file_suffix) + 1);
+ sprintf(p->vlv_filename,"%s%s%s",file_prefix,filename,file_suffix);
+
+ /* Create an attrinfo structure */
+ p->vlv_attrinfo->ai_type= slapi_ch_malloc(strlen(file_prefix) + strlen(filename) + 1);
+ sprintf(p->vlv_attrinfo->ai_type,"%s%s",file_prefix,filename);
+ p->vlv_attrinfo->ai_indexmask= INDEX_VLV;
+
+ /* Check if the index file actually exists */
+ if(li!=NULL)
+ {
+ vlvIndex_checkforindex(p, be);
+ }
+ p->vlv_lastchecked= current_time();
+ }
+ slapi_ch_free((void**)&filename);
+}
+
+/*
+ * Determine how many {key,data} pairs there are in the VLV Index.
+ * We only work out the length of the index once, then we cache
+ * it and maintain it.
+ */
+PRUint32
+vlvIndex_get_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
+{
+ if (NULL == p)
+ return 0;
+
+ if(!p->vlv_indexlength_cached)
+ {
+ DBC *dbc = NULL;
+ DB_TXN *db_txn = NULL;
+ int err= 0;
+ if (NULL != txn)
+ {
+ db_txn = txn->back_txn_txn;
+ }
+ err = db->cursor(db, db_txn, &dbc, 0);
+ if(err==0)
+ {
+ DBT key= {0};
+ DBT data= {0};
+ key.flags= DB_DBT_MALLOC;
+ data.flags= DB_DBT_MALLOC;
+ err= dbc->c_get(dbc,&key,&data,DB_LAST);
+ if(err==0)
+ {
+ free(key.data); key.data= NULL;
+ free(data.data); data.data= NULL;
+ err= dbc->c_get(dbc,&key,&data,DB_GET_RECNO);
+ if(err==0)
+ {
+ PR_Lock(p->vlv_indexlength_lock);
+ p->vlv_indexlength_cached= 1;
+ p->vlv_indexlength= *((db_recno_t*)data.data);
+ PR_Unlock(p->vlv_indexlength_lock);
+ free(data.data);
+ }
+ }
+ dbc->c_close(dbc);
+ }
+ else
+ {
+ /* couldn't get cursor??? */
+ }
+ }
+ return p->vlv_indexlength;
+}
+
+/*
+ * Increment the index length count.
+ * We keep track of the index length for efficiency.
+ */
+void
+vlvIndex_increment_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
+{
+ if (NULL == p)
+ return;
+
+ if(p->vlv_indexlength_cached)
+ {
+ PR_Lock(p->vlv_indexlength_lock);
+ p->vlv_indexlength++;
+ PR_Unlock(p->vlv_indexlength_lock);
+ }
+ else
+ {
+ p->vlv_indexlength= vlvIndex_get_indexlength(p, db, txn);
+ }
+}
+
+/*
+ * Decrement the index length count.
+ * We keep track of the index length for efficiency.
+ */
+void
+vlvIndex_decrement_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
+{
+ if (NULL == p)
+ return;
+
+ if(p->vlv_indexlength_cached)
+ {
+ /* jcm: Check for underflow? */
+ PR_Lock(p->vlv_indexlength_lock);
+ p->vlv_indexlength--;
+ PR_Unlock(p->vlv_indexlength_lock);
+ }
+ else
+ {
+ p->vlv_indexlength= vlvIndex_get_indexlength(p, db, txn);
+ }
+}
+
+/*
+ * Increment the usage counter
+ */
+void
+vlvIndex_incrementUsage(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return;
+ p->vlv_uses++;
+}
+
+/*
+ * Get the filename of the index.
+ */
+const char *
+vlvIndex_filename(const struct vlvIndex* p)
+{
+ if (NULL == p)
+ return NULL;
+ return p->vlv_filename;
+}
+
+/*
+ * Check if the index is available.
+ */
+int vlvIndex_enabled(const struct vlvIndex* p)
+{
+ if (NULL == p)
+ return 0;
+ return p->vlv_enabled;
+}
+
+int vlvIndex_online(const struct vlvIndex *p)
+{
+ if (NULL == p)
+ return 0;
+ return p->vlv_online;
+}
+
+void vlvIndex_go_offline(struct vlvIndex *p, backend *be)
+{
+ if (NULL == p)
+ return;
+ p->vlv_online = 0;
+ p->vlv_enabled = 0;
+ p->vlv_indexlength = 0;
+ p->vlv_attrinfo->ai_indexmask |= INDEX_OFFLINE;
+ dblayer_erase_index_file_nolock(be, p->vlv_attrinfo, 1 /* chkpt if not busy */);
+}
+
+void vlvIndex_go_online(struct vlvIndex *p, backend *be)
+{
+ if (NULL == p)
+ return;
+ p->vlv_attrinfo->ai_indexmask &= ~INDEX_OFFLINE;
+ p->vlv_online = 1;
+ vlvIndex_checkforindex(p, be);
+}
+
+
+/*
+ * Access Control Check to see if the client is allowed to use this VLV Index.
+ */
+int
+vlvIndex_accessallowed(struct vlvIndex *p, Slapi_PBlock *pb)
+{
+ if (NULL == p)
+ return 0;
+ return vlvSearch_accessallowed(p->vlv_search, pb);
+}
+
+const Slapi_DN *vlvIndex_getBase(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return NULL;
+ return vlvSearch_getBase(p->vlv_search);
+}
+
+int vlvIndex_getScope(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return 0;
+ return vlvSearch_getScope(p->vlv_search);
+}
+
+Slapi_Filter *vlvIndex_getFilter(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return NULL;
+ return vlvSearch_getFilter(p->vlv_search);
+}
+
+const char *vlvIndex_getName(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return NULL;
+ return p->vlv_name;
+}
+
+/*
+ * JCM: Could also match reverse sense of index and use in reverse.
+ */
+static int
+vlvIndex_equal(const struct vlvIndex* p1, const sort_spec* sort_control)
+{
+ int r= 1;
+ const sort_spec *t1= sort_control;
+ LDAPsortkey *t2= p1->vlv_sortkey[0];
+ int n= 1;
+ for(;t1!=NULL && t2!=NULL && r;t1= t1->next,t2=p1->vlv_sortkey[n],n++)
+ {
+ r= (t1->order && t2->sk_reverseorder) || (!t1->order && !t2->sk_reverseorder);
+ if(r) r= (strcasecmp(t1->type, t2->sk_attrtype)==0);
+ if(r)
+ {
+ if(t1->matchrule==NULL && t2->sk_matchruleoid==NULL)
+ {
+ r= 1;
+ }
+ else if(t1->matchrule!=NULL && t2->sk_matchruleoid!=NULL)
+ {
+ r= (strcasecmp(t1->matchrule, t2->sk_matchruleoid)==0);
+ }
+ else
+ {
+ r= 0;
+ }
+ }
+ }
+ if(r) r= (t1==NULL && t2==NULL);
+ return r;
+}
+
+/*
+ * Check if the index file actually exists,
+ * and set vlv_enabled appropriately
+ */
+static void
+vlvIndex_checkforindex(struct vlvIndex* p, backend *be)
+{
+ DB *db = NULL;
+
+ /* if the vlv index is offline (being generated), don't even look */
+ if (! p->vlv_online)
+ return;
+
+ if (dblayer_get_index_file(be, p->vlv_attrinfo, &db, 0) == 0) {
+ p->vlv_enabled = 1;
+ dblayer_release_index_file( be, p->vlv_attrinfo, db );
+ } else {
+ p->vlv_enabled = 0;
+ }
+}
+
+int vlvIndex_isVlvIndexEntry(Slapi_Entry *e)
+{
+ return slapi_entry_attr_hasvalue(e, "objectclass", "vlvindex");
+}
+
+/*
+ * Create the filename for the index.
+ * Extract all the alphanumeric characters from the descriptive name.
+ * Convert to all lower case.
+ */
+static int
+vlvIndex_createfilename(struct vlvIndex* pIndex, char **ppc)
+{
+ int filenameValid= 1;
+ unsigned int i;
+ char *p, *filename;
+ filename= slapi_ch_malloc(strlen(pIndex->vlv_name) + 1);
+ p= filename;
+ for(i=0;i<strlen(pIndex->vlv_name);i++)
+ {
+ if(isalnum(pIndex->vlv_name[i]))
+ {
+ *p= TOLOWER( pIndex->vlv_name[i] );
+ p++;
+ }
+ }
+ *p= '\0';
+ if(strlen(filename)==0)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Couldn't generate valid filename from Virtual List View Index Name (%s). Need some alphabetical characters.\n", pIndex->vlv_name, 0, 0);
+ filenameValid= 0;
+ }
+ /* JCM: Check if this file clashes with another VLV Index filename */
+ *ppc= filename;
+ return filenameValid;
+}
+
+int
+vlv_isvlv(char *filename)
+{
+ if (0 == strncmp(filename, file_prefix, 4))
+ return 1;
+ return 0;
+}