Browse Source

Reduce replication meta data stored in entry, cf ticket 569

Reviewed by richm, thanks
Ludwig Krispenz 12 years ago
parent
commit
c7f6f161f4

+ 10 - 0
ldap/servers/slapd/entry.c

@@ -1718,6 +1718,16 @@ entry2str_internal_put_attrlist( const Slapi_Attr *attrlist, int attr_state, int
 				else
 				{
 					/* There were no present values on which to place the ADCSN, so we put it on the first deleted value. */
+					if ( valueset_isempty(&a->a_deleted_values)) {
+						/* this means the entry is deleted and has no more attributes,
+						 * when writing the attr to disk we would loose the AD-csn.
+						 * Add an empty value to the set of deleted values. This will 
+						 * never be seen by any client. It will never be moved to the 
+						 * present values and is only used to preserve the AD-csn
+						 */
+						valueset_add_string (&a->a_deleted_values, "", CSN_TYPE_VALUE_DELETED, a->a_deletioncsn);
+					}				
+
 					entry2str_internal_put_valueset(a->a_type, a->a_deletioncsn, CSN_TYPE_ATTRIBUTE_DELETED, attr_state, &a->a_deleted_values, VALUE_DELETED, ecur, typebuf, typebuf_len, entry2str_ctrl);
 				}
 			}

+ 358 - 195
ldap/servers/slapd/entrywsi.c

@@ -419,25 +419,129 @@ entry_add_present_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a)
  * Preserves LDAP Information Model constraints,
  * returning an LDAP result code.
  */
+static void resolve_attribute_state_single_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state);
+static void resolve_attribute_state_deleted_to_present(Slapi_Entry *e, Slapi_Attr *a, Slapi_Value **valuestoupdate);
+static void resolve_attribute_state_present_to_deleted(Slapi_Entry *e, Slapi_Attr *a, Slapi_Value **valuestoupdate);
+static void resolve_attribute_state_to_present_or_deleted(Slapi_Entry *e, Slapi_Attr *a, Slapi_Value **valuestoupdate, int attribute_state);
+static int entry_add_present_values_wsi_single_valued(Slapi_Entry *e, const char *type, struct berval **bervals, const CSN *csn, int urp, long flags);
+static int entry_add_present_values_wsi_multi_valued(Slapi_Entry *e, const char *type, struct berval **bervals, const CSN *csn, int urp, long flags);
 static int
 entry_add_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **bervals, const CSN *csn, int urp, long flags)
+{
+	int retVal = LDAP_SUCCESS;
+	Slapi_Attr *a= NULL;
+	int attr_state= entry_attr_find_wsi(e, type, &a);
+	if (ATTRIBUTE_NOTFOUND == attr_state)
+	{
+		/* Create a new attribute */
+		a = slapi_attr_new();
+		slapi_attr_init(a, type);
+		attrlist_add(&e->e_attrs, a);
+	}
+
+	if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
+	{
+		retVal = entry_add_present_values_wsi_single_valued( e, type, bervals, csn, urp, 0 );
+	}
+	else
+	{
+		retVal = entry_add_present_values_wsi_multi_valued( e, type, bervals, csn, urp, 0 );
+	}
+	return retVal;
+}
+static int
+entry_add_present_values_wsi_single_valued(Slapi_Entry *e, const char *type, struct berval **bervals, const CSN *csn, int urp, long flags)
 {
 	int retVal= LDAP_SUCCESS;
-    Slapi_Value **valuestoadd = NULL;
-    valuearray_init_bervalarray(bervals,&valuestoadd); /* JCM SLOW FUNCTION */
+	Slapi_Value **valuestoadd = NULL;
+	valuearray_init_bervalarray(bervals,&valuestoadd); /* JCM SLOW FUNCTION */
 	if(!valuearray_isempty(valuestoadd))
 	{
 		Slapi_Attr *a= NULL;
 		long a_flags_orig;
 		int attr_state= entry_attr_find_wsi(e, type, &a);
-		if (ATTRIBUTE_NOTFOUND == attr_state)
+        	a_flags_orig = a->a_flags;
+		a->a_flags |= flags;
+		/* Check if the type of the to-be-added values has DN syntax or not. */
+		if (slapi_attr_is_dn_syntax_attr(a)) {
+			valuearray_dn_normalize_value(valuestoadd);
+			a->a_flags |= SLAPI_ATTR_FLAG_NORMALIZED_CES;
+		}
+		if(urp)
 		{
-			/* Create a new attribute */
-			a = slapi_attr_new();
-			slapi_attr_init(a, type);
-			attrlist_add(&e->e_attrs, a);
+			valueset_remove_valuearray (&a->a_present_values, a, valuestoadd,
+						SLAPI_VALUE_FLAG_IGNOREERROR |
+						SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL);
+			valueset_remove_valuearray (&a->a_deleted_values, a, valuestoadd,
+						SLAPI_VALUE_FLAG_IGNOREERROR |
+						SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL);
+			valuearray_update_csn (valuestoadd,CSN_TYPE_VALUE_UPDATED,csn);
+			valueset_add_valuearray_ext(&a->a_present_values, valuestoadd, SLAPI_VALUE_FLAG_PASSIN);
+			slapi_ch_free ( (void **)&valuestoadd );
+
+			/*
+			 * Now delete non-RDN values from a->a_present_values; and
+			 * restore possible RDN values from a->a_deleted_values
+			 */
+			resolve_attribute_state_single_valued(e, a, attr_state);
+			retVal= LDAP_SUCCESS;
 		}
-        a_flags_orig = a->a_flags;
+		else
+		{
+			Slapi_Value **deletedvalues= NULL;
+			switch(attr_state)
+			{
+			case ATTRIBUTE_PRESENT:
+				/* The attribute is already on the present list */
+				break;
+			case ATTRIBUTE_DELETED:
+				/* Move the deleted attribute onto the present list */
+				entry_deleted_attribute_to_present_attribute(e, a);
+				break;
+			case ATTRIBUTE_NOTFOUND:
+				/* No-op - attribute was initialized & added to entry above */
+				break;
+			}
+			/* Check if any of the values to be added are on the deleted list */
+			valueset_remove_valuearray(&a->a_deleted_values,
+					a, valuestoadd,
+					SLAPI_VALUE_FLAG_IGNOREERROR|SLAPI_VALUE_FLAG_USENEWVALUE,
+					&deletedvalues); /* JCM Check return code */
+			if(deletedvalues!=NULL && deletedvalues[0]!=NULL)
+			{
+				/* Some of the values to be added were on the deleted list */
+				Slapi_Value **v= NULL;
+				Slapi_ValueSet vs;
+				/* Add each deleted value to the present list */
+				valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_UPDATED,csn);
+				valueset_add_valuearray_ext(&a->a_present_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN);
+				/* Remove the deleted values from the values to add */
+				valueset_set_valuearray_passin(&vs,valuestoadd); 
+				valueset_remove_valuearray(&vs, a, deletedvalues, SLAPI_VALUE_FLAG_IGNOREERROR, &v);
+				valuestoadd= valueset_get_valuearray(&vs);
+				valuearray_free(&v);
+				slapi_ch_free((void **)&deletedvalues);
+			}
+			valuearray_update_csn(valuestoadd,CSN_TYPE_VALUE_UPDATED,csn);
+			retVal= attr_add_valuearray(a, valuestoadd, slapi_entry_get_dn_const(e));
+			valuearray_free(&valuestoadd);
+		}
+		a->a_flags = a_flags_orig;
+	}
+	return(retVal);
+}
+static int
+entry_add_present_values_wsi_multi_valued(Slapi_Entry *e, const char *type, struct berval **bervals, const CSN *csn, int urp, long flags)
+{
+	int retVal= LDAP_SUCCESS;
+	Slapi_Value **valuestoadd = NULL;
+	valuearray_init_bervalarray(bervals,&valuestoadd); /* JCM SLOW FUNCTION */
+	if(!valuearray_isempty(valuestoadd))
+	{
+		Slapi_Attr *a= NULL;
+		long a_flags_orig;
+		int attr_state= entry_attr_find_wsi(e, type, &a);
+	        a_flags_orig = a->a_flags;
 		a->a_flags |= flags;
 		/* Check if the type of the to-be-added values has DN syntax or not. */
 		if (slapi_attr_is_dn_syntax_attr(a)) {
@@ -462,16 +566,14 @@ entry_add_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **b
 						SLAPI_VALUE_FLAG_IGNOREERROR |
 						SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL);
 
-			/* Append the pending values to a->a_present_values */
+			/* Now add the values in values to add to present or deleted 
+			 * values, depending on their csnset */
 			valuearray_update_csn (valuestoadd,CSN_TYPE_VALUE_UPDATED,csn);
-			valueset_add_valuearray_ext(&a->a_present_values, valuestoadd, SLAPI_VALUE_FLAG_PASSIN);
+
+			resolve_attribute_state_to_present_or_deleted(e, a, valuestoadd, attr_state);
+
 			slapi_ch_free ( (void **)&valuestoadd );
 
-			/*
-			 * Now delete non-RDN values from a->a_present_values; and
-			 * restore possible RDN values from a->a_deleted_values
-			 */
-			resolve_attribute_state(e, a, attr_state, 0);
 			retVal= LDAP_SUCCESS;
 		}
 		else
@@ -526,13 +628,155 @@ entry_add_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **b
  * returning an LDAP result code.
  */
 static int
+entry_delete_present_values_wsi_single_valued(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp, int mod_op, struct berval **replacevals);
+static int
+entry_delete_present_values_wsi_multi_valued(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp, int mod_op, struct berval **replacevals);
+static int
 entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp, int mod_op, struct berval **replacevals)
 {
 	int retVal= LDAP_SUCCESS;
 	Slapi_Attr *a= NULL;
 	int attr_state= entry_attr_find_wsi(e, type, &a);
+	
 	if(attr_state==ATTRIBUTE_PRESENT || (attr_state==ATTRIBUTE_DELETED && urp))
 	{
+		if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
+			retVal = entry_delete_present_values_wsi_single_valued(e, type, vals, csn, urp, mod_op, replacevals);
+		else
+			retVal = entry_delete_present_values_wsi_multi_valued(e, type, vals, csn, urp, mod_op, replacevals);
+	}
+	else if (attr_state==ATTRIBUTE_DELETED)
+	{
+#if defined(USE_OLD_UNHASHED)
+		if (is_type_forbidden(type)) {
+			retVal = LDAP_SUCCESS;
+		} else {
+			retVal= LDAP_NO_SUCH_ATTRIBUTE;
+		}
+#else
+		retVal= LDAP_NO_SUCH_ATTRIBUTE;
+#endif
+	}
+	else if (attr_state==ATTRIBUTE_NOTFOUND)
+	{
+#if defined(USE_OLD_UNHASHED)
+		if (is_type_protected(type) || is_type_forbidden(type))
+#else
+		if (is_type_protected(type))
+#endif
+		{
+			retVal = LDAP_SUCCESS;
+		} else {
+			if (!urp) {
+				LDAPDebug1Arg(LDAP_DEBUG_ARGS, "could not find attribute %s\n",
+				              type);
+			}
+			retVal = LDAP_NO_SUCH_ATTRIBUTE;
+		}
+		if ((LDAP_MOD_REPLACE == mod_op) && replacevals && replacevals[0])
+		{
+			/* Create a new attribute and set the adcsn */
+			Slapi_Attr *a = slapi_attr_new();
+			slapi_attr_init(a, type);
+			attr_set_deletion_csn(a,csn); 
+			entry_add_deleted_attribute_wsi(e, a);
+		}
+	}
+	return retVal;
+}
+static int
+entry_delete_present_values_wsi_single_valued(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp, int mod_op, struct berval **replacevals)
+{
+	int retVal= LDAP_SUCCESS;
+	Slapi_Attr *a= NULL;
+	int attr_state= entry_attr_find_wsi(e, type, &a);
+	/* The attribute is on the present list, or the deleted list and we're doing URP */
+	if ( vals == NULL || vals[0] == NULL )
+	{
+		/* delete the entire attribute */
+		LDAPDebug( LDAP_DEBUG_ARGS, "removing entire attribute %s\n", type, 0, 0 );
+		attr_set_deletion_csn(a,csn);
+		if(urp)
+		{
+			resolve_attribute_state_single_valued(e, a, attr_state);
+			/* resolve attr state single valued */
+			/* keep_present = check_attr_single_value_is_distingiuished(&a->a_present_values);
+			if ( !keep_present) {
+				slapi_valueset_done(&a->a_present_values);
+				entry_present_attribute_to_deleted_attribute(e, a);
+				}
+			 */
+		}
+		else
+		{
+			slapi_valueset_done(&a->a_present_values);
+			entry_present_attribute_to_deleted_attribute(e, a);
+		}
+		retVal= LDAP_SUCCESS; /* This Operation always succeeds when the attribute is Present */
+	}
+	else
+	{
+		/* delete some specific values */
+	    Slapi_Value **valuestodelete= NULL;
+	    valuearray_init_bervalarray(vals,&valuestodelete); /* JCM SLOW FUNCTION */
+		/* Check if the type of the to-be-deleted values has DN syntax 
+		 * or not. */
+		if (slapi_attr_is_dn_syntax_attr(a)) {
+			valuearray_dn_normalize_value(valuestodelete);
+			a->a_flags |= SLAPI_ATTR_FLAG_NORMALIZED_CES;
+		}
+		if(urp)
+		{
+			Slapi_Value **valuesupdated= NULL;
+			valueset_update_csn_for_valuearray(&a->a_present_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated);
+			if( valuesupdated && *valuesupdated)
+			{
+				attr_set_deletion_csn(a,csn);			
+			}
+			/* resolve attr state single valued */
+			valuearray_free(&valuesupdated);
+			valueset_update_csn_for_valuearray(&a->a_deleted_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated);
+			valuearray_free(&valuesupdated);
+			valuearray_update_csn(valuestodelete,CSN_TYPE_VALUE_DELETED,csn);
+			valueset_add_valuearray_ext(&a->a_deleted_values, valuestodelete, SLAPI_VALUE_FLAG_PASSIN);
+			slapi_ch_free((void **)&valuestodelete);
+			resolve_attribute_state_single_valued(e, a, attr_state);
+			retVal= LDAP_SUCCESS;
+		}
+		else
+		{
+			Slapi_Value **deletedvalues= NULL;
+			retVal= valueset_remove_valuearray(&a->a_present_values, a, valuestodelete, 0 /* Do Not Ignore Errors */,&deletedvalues);
+			if(retVal==LDAP_SUCCESS && deletedvalues != NULL)
+			{
+				valuearray_free(&deletedvalues);
+				/* The attribute is single valued and the value was successful deleted */
+				entry_present_attribute_to_deleted_attribute(e, a);
+			}
+			else if (retVal != LDAP_SUCCESS)
+			{
+				/* Failed 
+				 * - Value not found
+				 * - Operations error
+				 */
+				if ( retVal==LDAP_OPERATIONS_ERROR )
+				{
+					LDAPDebug( LDAP_DEBUG_ANY, "Possible existing duplicate "
+						"value for attribute type %s found in "
+						"entry %s\n", a->a_type, slapi_entry_get_dn_const(e), 0 );
+				}
+			}
+			valuearray_free(&valuestodelete);
+		}
+	}
+	return( retVal );
+}
+static int
+entry_delete_present_values_wsi_multi_valued(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp, int mod_op, struct berval **replacevals)
+{
+	int retVal= LDAP_SUCCESS;
+	Slapi_Attr *a= NULL;
+	int attr_state= entry_attr_find_wsi(e, type, &a);
 		/* The attribute is on the present list, or the deleted list and we're doing URP */
 		if ( vals == NULL || vals[0] == NULL )
 		{
@@ -541,16 +785,19 @@ entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval
 			attr_set_deletion_csn(a,csn);
 			if(urp)
 			{
-				resolve_attribute_state(e, a, attr_state, 1 /* set delete priority */); /* ABSOLVED */
+				/* there might be values added or specifically deleted later than 
+				 * the current attr delete operation. These values need to be 
+				 * preserved, all others can be removed, purging should o the job.
+				 */
+				valueset_purge(&a->a_present_values, csn);
+				valueset_purge(&a->a_deleted_values, csn);
+				if(valueset_isempty(&a->a_present_values))
+					entry_present_attribute_to_deleted_attribute(e, a);
 			}
 			else
 			{
-				if(!slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
-				{
-					/* We don't maintain a deleted value list for single valued attributes */
-					valueset_add_valueset(&a->a_deleted_values, &a->a_present_values); /* JCM Would be better to passin the valuestodelete */
-				}
 				slapi_valueset_done(&a->a_present_values);
+				slapi_valueset_done(&a->a_deleted_values);
 				entry_present_attribute_to_deleted_attribute(e, a);
 			}
 			retVal= LDAP_SUCCESS; /* This Operation always succeeds when the attribute is Present */
@@ -558,8 +805,8 @@ entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval
 		else
 		{
 			/* delete some specific values */
-		    Slapi_Value **valuestodelete= NULL;
-		    valuearray_init_bervalarray(vals,&valuestodelete); /* JCM SLOW FUNCTION */
+			Slapi_Value **valuestodelete= NULL;
+			valuearray_init_bervalarray(vals,&valuestodelete); /* JCM SLOW FUNCTION */
 			/* Check if the type of the to-be-deleted values has DN syntax 
 			 * or not. */
 			if (slapi_attr_is_dn_syntax_attr(a)) {
@@ -568,47 +815,43 @@ entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval
 			}
 			if(urp)
 			{
+				/* check if values to delete are in present values
+				 * if v in present values and VU-csn<csn and v not distinguished move to deleted values
+				 * if v not in present values, check deleted values and update csn, if distinguisehed at csn
+				 * 	move back to present values
+				 */ 
+
+
 				Slapi_Value **valuesupdated= NULL;
-				valueset_update_csn_for_valuearray(&a->a_present_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated);
-				/* if we removed the last value, we need to mark the attribute as deleted
-				   the resolve_attribute_state() code will "resurrect" the attribute if
-				   there are present values with a later CSN - otherwise, even though
-				   the value will be updated with a VDCSN which is later than the VUCSN,
-				   the attribute will not be deleted */
-				if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE) && valuesupdated &&
-				   *valuesupdated)
-				{
-					attr_set_deletion_csn(a,csn);			
-				}
+				valueset_update_csn_for_valuearray_ext(&a->a_present_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated, 1);
+				resolve_attribute_state_present_to_deleted(e, a, valuesupdated); 
 				valuearray_free(&valuesupdated);
-				valueset_update_csn_for_valuearray(&a->a_deleted_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated);
+
+				valueset_update_csn_for_valuearray_ext(&a->a_deleted_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated, 1);
+				resolve_attribute_state_deleted_to_present(e, a, valuesupdated);
 				valuearray_free(&valuesupdated);
+
 				valuearray_update_csn(valuestodelete,CSN_TYPE_VALUE_DELETED,csn);
 				valueset_add_valuearray_ext(&a->a_deleted_values, valuestodelete, SLAPI_VALUE_FLAG_PASSIN);
 				/* all the elements in valuestodelete are passed;
 				 * should free valuestodelete only (don't call valuearray_free)
 				 * [622023] */
 				slapi_ch_free((void **)&valuestodelete);
-				resolve_attribute_state(e, a, attr_state, 0);
 				retVal= LDAP_SUCCESS;
 			}
 			else
 			{
+				/* find the values to delete in present values, 
+				 * update the csns and add to the deleted values
+				 */  
 				Slapi_Value **deletedvalues= NULL;
 				retVal= valueset_remove_valuearray(&a->a_present_values, a, valuestodelete, 0 /* Do Not Ignore Errors */,&deletedvalues);
 				if(retVal==LDAP_SUCCESS && deletedvalues != NULL)
 				{
-					if(!slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
-					{
-						/* We don't maintain a deleted value list for single valued attributes */
-						/* Add each deleted value to the deleted set */
-						valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_DELETED,csn);
-						valueset_add_valuearray_ext(&a->a_deleted_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN);
-						slapi_ch_free((void **)&deletedvalues);
-					}
-					else {
-						valuearray_free(&deletedvalues);
-					}
+					/* Add each deleted value to the deleted set */
+					valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_DELETED,csn);
+					valueset_add_valuearray_ext(&a->a_deleted_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN);
+					slapi_ch_free((void **)&deletedvalues);
 					if(valueset_isempty(&a->a_present_values))
 					{
 						/* There are no present values, so move the
@@ -619,7 +862,6 @@ entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval
 				else if (retVal != LDAP_SUCCESS)
 				{
 					/* Failed 
-					 * - Duplicate value
 					 * - Value not found
 					 * - Operations error
 					 */
@@ -633,69 +875,6 @@ entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval
 				valuearray_free(&valuestodelete);
 			}
 		}
-	}
-	else if (attr_state==ATTRIBUTE_DELETED)
-	{
-		/* If the type is in the forbidden attr list (e.g., unhashed password),
-		 * we don't return the reason of the failure to the clients. */
-#if defined(USE_OLD_UNHASHED)
-		if (is_type_forbidden(type)) {
-			retVal = LDAP_SUCCESS;
-		} else {
-			retVal= LDAP_NO_SUCH_ATTRIBUTE;
-		}
-#else
-		retVal= LDAP_NO_SUCH_ATTRIBUTE;
-#endif
-	}
-	else if (attr_state==ATTRIBUTE_NOTFOUND)
-	{
-		/*
-		 * If type is in the protected_attrs_all list, we could ignore the
-		 * failure, as the attribute could only exist in the entry in the 
-		 * memory when the add/mod operation is done, while the retried entry 
-		 * from the db does not contain the attribute.
-		 * So is in the forbidden_attrs list.  We don't return the reason
-		 * of the failure.
-		 */
-#if defined(USE_OLD_UNHASHED)
-		if (is_type_protected(type) || is_type_forbidden(type))
-#else
-		if (is_type_protected(type))
-#endif
-		{
-			retVal = LDAP_SUCCESS;
-		} else {
-			if (!urp) {
-				/* Only warn if not urping */
-				LDAPDebug1Arg(LDAP_DEBUG_ARGS, "could not find attribute %s\n",
-				              type);
-			}
-			retVal = LDAP_NO_SUCH_ATTRIBUTE;
-		}
-		/* NOTE: LDAP says that a MOD REPLACE with no vals of a non-existent
-		   attribute is a no-op - MOD REPLACE with some vals will add the attribute */
-		/* if we are doing a replace with actual values, meaning the result
-		   of the mod is that the attribute will have some values, we need to create
-		   a dummy attribute for entry_add_present_values_wsi to use, and set
-		   the deletion csn to the csn of the current operation */
-		/* note that if LDAP_MOD_REPLACE == mod_op then vals is NULL - 
-		   see entry_replace_present_values_wsi */
-		if ((LDAP_MOD_REPLACE == mod_op) && replacevals && replacevals[0])
-		{
-			/* Create a new attribute and set the adcsn */
-			Slapi_Attr *a = slapi_attr_new();
-			slapi_attr_init(a, type);
-			attr_set_deletion_csn(a,csn); 
-			/* mark the attribute as deleted - it does not really
-			   exist yet - the code in entry_add_present_values_wsi
-			   will add it back to the present list in the non urp case,
-			   or determine if the attribute needs to be added
-			   or not in the urp case
-			*/
-			entry_add_deleted_attribute_wsi(e, a);
-		}
-	}
 	return( retVal );
 }
 
@@ -825,7 +1004,7 @@ entry_apply_mods_wsi(Slapi_Entry *e, Slapi_Mods *smods, const CSN *csn, int urp)
 			   and the csn doesn't already have a subsequence
 			   if the csn already has a subsequence, assume it was generated
 			   on another replica in the correct order */
-			if (urp && (csn_get_subseqnum(csn) == 0)) {
+			if (csn_get_subseqnum(csn) == 0) {
 				csn_increment_subsequence(&localcsn);
 			}
 		}
@@ -1043,87 +1222,54 @@ value_distinguished_at_csn(const Slapi_Entry *e, const Slapi_Attr *original_attr
 	return r;
 }
 
+/* This call ensures that the value does not contain a deletion_csn which is before the presence_csn or distinguished_csn of the value. */ 
 static void
-resolve_attribute_state_multi_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state, int delete_priority)
+resolve_attribute_state_deleted_to_present(Slapi_Entry *e, Slapi_Attr *a, Slapi_Value **valuestoupdate)
 {
-	int i;
+	const CSN *vdcsn;
+	const CSN *vucsn;
+	const CSN *deletedcsn;
 	const CSN *adcsn= attr_get_deletion_csn(a);
-	Slapi_ValueSet *vs= valueset_dup(&a->a_present_values); /* JCM This is slow... but otherwise we end up iterating through a changing array */
-	Slapi_Value *v;
-
-	/* Loop over the present attribute values */
-	i= slapi_valueset_first_value( vs, &v );
-	while(v!=NULL)
-	{
-		const CSN *vdcsn;
-		const CSN *vucsn;
-		const CSN *deletedcsn;
-	    /* This call ensures that the value does not contain a deletion_csn
-		 * which is before the presence_csn or distinguished_csn of the value.
-		 */ 
-	    purge_attribute_state_multi_valued(a, v);
-		vdcsn= value_get_csn(v, CSN_TYPE_VALUE_DELETED);
-		vucsn= value_get_csn(v, CSN_TYPE_VALUE_UPDATED);
-		deletedcsn= csn_max(vdcsn, adcsn);
-
-		/* Check if the attribute or value was deleted after the value was
-		 * last updated.  If the value update CSN and the deleted CSN are
-		 * the same (meaning they are separate mods from the same exact
-		 * operation), we should only delete the value if delete priority
-		 * is set.  Delete priority should only be set when we are deleting
-		 * all value of an attribute.  This prevents us from leaving a value
-		 * that was added as a previous mod in the same exact modify
-		 * operation as the subsequent delete.*/
-		if((csn_compare(vucsn,deletedcsn)<0) ||
-			(delete_priority && (csn_compare(vucsn,deletedcsn) == 0)))
-		{
-	        if(!value_distinguished_at_csn(e, a, v, deletedcsn))
+	int i;
+	if ( valuestoupdate != NULL && valuestoupdate[0] != NULL ) {
+		for (i=0;valuestoupdate[i]!=NULL;++i) {
+			purge_attribute_state_multi_valued(a, valuestoupdate[i]);
+			vdcsn= value_get_csn(valuestoupdate[i], CSN_TYPE_VALUE_DELETED);
+			vucsn= value_get_csn(valuestoupdate[i], CSN_TYPE_VALUE_UPDATED);
+			deletedcsn= csn_max(vdcsn, adcsn);
+			if((csn_compare(vucsn,deletedcsn)>=0) ||
+	        		value_distinguished_at_csn(e, a, valuestoupdate[i], deletedcsn))
 			{
-				entry_present_value_to_deleted_value(a,v);
+				entry_deleted_value_to_present_value(a,valuestoupdate[i]);
 			}
+			valuestoupdate[i]->v_csnset = NULL;
 		}
-		i= slapi_valueset_next_value( vs, i, &v );
 	}
-	slapi_valueset_free(vs);
-
-	/* Loop over the deleted attribute values */
-	vs= valueset_dup(&a->a_deleted_values); /* JCM This is slow... but otherwise we end up iterating through a changing array */
-	i= slapi_valueset_first_value( vs, &v );
-	while(v!=NULL)
-	{
-		const CSN *vdcsn;
-		const CSN *vucsn;
-		const CSN *deletedcsn;
-	    /* This call ensures that the value does not contain a deletion_csn which is before the presence_csn or distinguished_csn of the value. */ 
-	    purge_attribute_state_multi_valued(a, v);
-		vdcsn= value_get_csn(v, CSN_TYPE_VALUE_DELETED);
-		vucsn= value_get_csn(v, CSN_TYPE_VALUE_UPDATED);
-		deletedcsn= csn_max(vdcsn, adcsn);
+}
 
-		/* check if the attribute or value was deleted after the value was last updated */
-		/* When a replace operation happens, the entry_replace_present_values_wsi() function
-		 * first calls entry_delete_present_values_wsi with vals == NULL to essentially delete
-		 * the attribute and set the deletion csn.  If the urp flag is set (urp in this case
-		 * meaning the operation is a replicated op), entry_delete_present_values_wsi will
-		 * call this function which will move the present values to the deleted values
-		 * (see above - delete_priority will be 1) then the below code will move the
-		 * attribute to the deleted list.
-		 * next, entry_replace_present_values_wsi will call entry_add_present_values_wsi
-		 * to add the values provided in the replace operation.  We need to be able to
-		 * "resurrect" these deleted values and resurrect the deleted attribute.  In the
-		 * replace case, the deletedcsn will be the same as the vucsn of the values that
-		 * should be present values.
-		 */
-		if((csn_compare(vucsn,deletedcsn)>0) ||
-		   ((delete_priority == 0) && (csn_compare(vucsn,deletedcsn)==0)) ||
-	        value_distinguished_at_csn(e, a, v, deletedcsn))
-		{
-			entry_deleted_value_to_present_value(a,v);
+static void
+resolve_attribute_state_to_present_or_deleted(Slapi_Entry *e, Slapi_Attr *a, Slapi_Value **valuestoupdate, int attribute_state)
+{
+	const CSN *vdcsn;
+	const CSN *vucsn;
+	const CSN *deletedcsn;
+	const CSN *adcsn= attr_get_deletion_csn(a);
+	int i;
+	if ( valuestoupdate != NULL && valuestoupdate[0] != NULL ) {
+		for (i=0;valuestoupdate[i]!=NULL;++i) {
+	    		purge_attribute_state_multi_valued(a, valuestoupdate[i]);
+			vdcsn= value_get_csn(valuestoupdate[i], CSN_TYPE_VALUE_DELETED);
+			vucsn= value_get_csn(valuestoupdate[i], CSN_TYPE_VALUE_UPDATED);
+			deletedcsn= csn_max(vdcsn, adcsn);
+			if((csn_compare(vucsn,deletedcsn)>=0) ||
+	        		value_distinguished_at_csn(e, a, valuestoupdate[i], deletedcsn))
+			{
+				slapi_valueset_add_value_ext(&a->a_present_values, valuestoupdate[i], SLAPI_VALUE_FLAG_PASSIN);
+			} else {
+				slapi_valueset_add_value_ext(&a->a_deleted_values, valuestoupdate[i], SLAPI_VALUE_FLAG_PASSIN);
+			}
 		}
-		i= slapi_valueset_next_value( vs, i, &v );
 	}
-	slapi_valueset_free(vs);
-
 	if(valueset_isempty(&a->a_present_values))
 	{
 		if(attribute_state==ATTRIBUTE_PRESENT)
@@ -1140,6 +1286,35 @@ resolve_attribute_state_multi_valued(Slapi_Entry *e, Slapi_Attr *a, int attribut
 	}
 }
 
+static void
+resolve_attribute_state_present_to_deleted(Slapi_Entry *e, Slapi_Attr *a, Slapi_Value **valuestoupdate)
+{
+	const CSN *vdcsn;
+	const CSN *vucsn;
+	const CSN *deletedcsn;
+	const CSN *adcsn= attr_get_deletion_csn(a);
+	int i;
+	if ( valuestoupdate != NULL && valuestoupdate[0] != NULL ) {
+	for (i=0;valuestoupdate[i]!=NULL;++i) {
+	/* This call ensures that the value does not contain a deletion_csn
+	 * which is before the presence_csn or distinguished_csn of the value.
+	 */ 
+	    purge_attribute_state_multi_valued(a, valuestoupdate[i]);
+		vdcsn= value_get_csn(valuestoupdate[i], CSN_TYPE_VALUE_DELETED);
+		vucsn= value_get_csn(valuestoupdate[i], CSN_TYPE_VALUE_UPDATED);
+		deletedcsn= csn_max(vdcsn, adcsn);
+			if(csn_compare(vucsn,deletedcsn)<0) 
+			{
+	        		if(!value_distinguished_at_csn(e, a, valuestoupdate[i], deletedcsn))
+				{
+					entry_present_value_to_deleted_value(a,valuestoupdate[i]);
+				}
+			}
+		valuestoupdate[i]->v_csnset = NULL;
+	}
+	}
+}
+
 static void
 resolve_attribute_state_single_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state)
 {
@@ -1292,15 +1467,3 @@ resolve_attribute_state_single_valued(Slapi_Entry *e, Slapi_Attr *a, int attribu
     }
 }
 
-static void
-resolve_attribute_state(Slapi_Entry *e, Slapi_Attr *a, int attribute_state, int delete_priority)
-{
-	if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
-	{
-		resolve_attribute_state_single_valued(e,a,attribute_state);
-	}
-	else
-	{
-		resolve_attribute_state_multi_valued(e,a,attribute_state, delete_priority);
-	}
-}

+ 1 - 0
ldap/servers/slapd/proto-slap.h

@@ -195,6 +195,7 @@ Slapi_ValueSet *valueset_dup(const Slapi_ValueSet *dupee);
 void valueset_remove_string(const Slapi_Attr *a, Slapi_ValueSet *vs, const char *s);
 int valueset_replace(Slapi_Attr *a, Slapi_ValueSet *vs, Slapi_Value **vals);
 void valueset_update_csn_for_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **valuestoupdate, CSNType t, const CSN *csn, Slapi_Value ***valuesupdated);
+void valueset_update_csn_for_valuearray_ext(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **valuestoupdate, CSNType t, const CSN *csn, Slapi_Value ***valuesupdated, int csnref_updated);
 void valueset_set_valuearray_byval(Slapi_ValueSet *vs, Slapi_Value **addvals);
 void valueset_set_valuearray_passin(Slapi_ValueSet *vs, Slapi_Value **addvals);
 int valuearray_subtract_bvalues(Slapi_Value **va, struct berval **bvals);

+ 9 - 0
ldap/servers/slapd/valueset.c

@@ -1427,6 +1427,11 @@ valueset_replace(Slapi_Attr *a, Slapi_ValueSet *vs, Slapi_Value **valstoreplace)
  */
 void
 valueset_update_csn_for_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **valuestoupdate, CSNType t, const CSN *csn, Slapi_Value ***valuesupdated)
+{
+	valueset_update_csn_for_valuearray_ext(vs, a, valuestoupdate, t, csn, valuesupdated, 0);
+}
+void
+valueset_update_csn_for_valuearray_ext(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **valuestoupdate, CSNType t, const CSN *csn, Slapi_Value ***valuesupdated, int csnref_updated)
 {
 	if(!valuearray_isempty(valuestoupdate) &&
 		!valuearray_isempty(vs->va))
@@ -1450,6 +1455,8 @@ valueset_update_csn_for_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slap
 				if(rc==LDAP_SUCCESS)
 				{
 					value_update_csn(vs->va[index],t,csn);
+					if (csnref_updated)
+						valuestoupdate[i]->v_csnset = value_get_csnset(vs->va[index]);
 					valuearrayfast_add_value_passin(&vaf_valuesupdated,valuestoupdate[i]);
 					valuestoupdate[i] = NULL;
 				}
@@ -1465,6 +1472,8 @@ valueset_update_csn_for_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slap
 				if(index!=-1)
 				{
 					value_update_csn(vs->va[index],t,csn);
+					if (csnref_updated)
+						valuestoupdate[i]->v_csnset = value_get_csnset(vs->va[index]);
 					valuearrayfast_add_value_passin(&vaf_valuesupdated,valuestoupdate[i]);
 					valuestoupdate[i]= NULL;
 				}