Blame SOURCES/0059-Ticket-47988-Schema-learning-mechanism-in-replicatio.patch

f92ce9
From e68a455c1191bfc597d95ce1f6326a9f6397c39d Mon Sep 17 00:00:00 2001
f92ce9
From: "Thierry bordaz (tbordaz)" <tbordaz@redhat.com>
f92ce9
Date: Thu, 22 Jan 2015 14:29:52 +0100
f92ce9
Subject: [PATCH 59/59] Ticket 47988: Schema learning mechanism, in
f92ce9
 replication, unable to extend an existing definition
f92ce9
f92ce9
Bug Description:
f92ce9
	At the beginning of a replication session, a supplier checks the status of remote schema vs
f92ce9
	its own schema. If the remote schema contains new/extended definitions, the supplier learns
f92ce9
	those definitions.
f92ce9
	It learns through internal MOD_ADD operation on cn=schema.
f92ce9
	For extending definition, this fails because the definition already exists.
f92ce9
f92ce9
Fix Description:
f92ce9
	It needs to MOD_DEL and MOD_ADD those extended definitions while it needs to do MOD_ADD for new definitions.
f92ce9
	It uses the field 'old_value' in 'struct schema_mods_indexes' to determine if it needs to del some definitions.
f92ce9
	Some definitions can not be deleted
f92ce9
		- if an objectclass is standard or is a superior of others oc
f92ce9
		- if an attribute is a standard definition or is used in objectclass
f92ce9
	This was problematic for updating the schema, so the fix is relaxing those controls for
f92ce9
	internal operations
f92ce9
f92ce9
https://fedorahosted.org/389/ticket/47988
f92ce9
f92ce9
Reviewed by: ?
f92ce9
f92ce9
Platforms tested: F17
f92ce9
f92ce9
Flag Day: no
f92ce9
f92ce9
Doc impact: no
f92ce9
f92ce9
(cherry picked from commit 51e05df9c37c66206041f026c9a67ec17bc9ea4a)
f92ce9
(cherry picked from commit 107316806d291e584028e278c9ce1e0e99ff5617)
f92ce9
---
f92ce9
 .../servers/plugins/replication/repl5_connection.c |  17 +
f92ce9
 ldap/servers/slapd/schema.c                        | 365 ++++++++++++++++-----
f92ce9
 2 files changed, 309 insertions(+), 73 deletions(-)
f92ce9
f92ce9
diff --git a/ldap/servers/plugins/replication/repl5_connection.c b/ldap/servers/plugins/replication/repl5_connection.c
f92ce9
index 2971025..eddcae8 100644
f92ce9
--- a/ldap/servers/plugins/replication/repl5_connection.c
f92ce9
+++ b/ldap/servers/plugins/replication/repl5_connection.c
f92ce9
@@ -1779,6 +1779,7 @@ conn_push_schema(Repl_Connection *conn, CSN **remotecsn)
f92ce9
 	CSN *localcsn = NULL;
f92ce9
 	Slapi_PBlock *spb = NULL;
f92ce9
 	char localcsnstr[CSN_STRSIZE + 1] = {0};
f92ce9
+	char remotecnsstr[CSN_STRSIZE+1] = {0};
f92ce9
 
f92ce9
 	if (!remotecsn)
f92ce9
 	{
f92ce9
@@ -1807,6 +1808,16 @@ conn_push_schema(Repl_Connection *conn, CSN **remotecsn)
f92ce9
 		}
f92ce9
 		else
f92ce9
 		{			
f92ce9
+                        if (*remotecsn) {
f92ce9
+                            csn_as_string (*remotecsn, PR_FALSE, remotecnsstr);
f92ce9
+                            csn_as_string (localcsn, PR_FALSE, localcsnstr);
f92ce9
+                            slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
f92ce9
+                                                        "[S] Checking consumer schema localcsn:%s / remotecsn:%s\n", localcsnstr, remotecnsstr);
f92ce9
+                        } else {
f92ce9
+                            csn_as_string (localcsn, PR_FALSE, localcsnstr);
f92ce9
+                            slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
f92ce9
+                                                        "[S] Checking consumer schema localcsn:%s / remotecsn:NULL\n", localcsnstr);
f92ce9
+                        }
f92ce9
                         if (!update_consumer_schema(conn)) {
f92ce9
                                 /* At least one schema definition (attributetypes/objectclasses) of the consumer
f92ce9
                                  * is a superset of the supplier.
f92ce9
@@ -1815,7 +1826,11 @@ conn_push_schema(Repl_Connection *conn, CSN **remotecsn)
f92ce9
                                  * So it could be possible that a second attempt (right now) of update_consumer_schema
f92ce9
                                  * would be successful
f92ce9
                                  */
f92ce9
+                                slapi_log_error(SLAPI_LOG_REPL, "schema",
f92ce9
+                                                        "[S] schema definitions may have been learned\n");
f92ce9
                                 if (!update_consumer_schema(conn)) {
f92ce9
+                                        slapi_log_error(SLAPI_LOG_REPL, "schema",
f92ce9
+                                                        "[S] learned definitions are not suffisant to try to push the schema \n");
f92ce9
                                         return_value = CONN_OPERATION_FAILED;
f92ce9
                                 }
f92ce9
                         } 
f92ce9
@@ -1831,6 +1846,8 @@ conn_push_schema(Repl_Connection *conn, CSN **remotecsn)
f92ce9
                                                 memcpy(remotecsnstr, remote_schema_csn_bervals[0]->bv_val,
f92ce9
                                                         remote_schema_csn_bervals[0]->bv_len);
f92ce9
                                                 remotecsnstr[remote_schema_csn_bervals[0]->bv_len] = '\0';
f92ce9
+                                                slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
f92ce9
+                                                        "[S] Reread remotecsn:%s\n", remotecsnstr);
f92ce9
                                                 *remotecsn = csn_new_by_string(remotecsnstr);
f92ce9
                                                 if (*remotecsn && (csn_compare(localcsn, *remotecsn) <= 0)) {
f92ce9
                                                         return_value = CONN_SCHEMA_NO_UPDATE_NEEDED;
f92ce9
diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c
f92ce9
index 8744a6d..05329a6 100644
f92ce9
--- a/ldap/servers/slapd/schema.c
f92ce9
+++ b/ldap/servers/slapd/schema.c
f92ce9
@@ -139,6 +139,7 @@ typedef struct repl_schema_policy {
f92ce9
 struct schema_mods_indexes {
f92ce9
         int index;
f92ce9
         char *new_value;
f92ce9
+        char *old_value;
f92ce9
         struct schema_mods_indexes *next;
f92ce9
 };
f92ce9
 
f92ce9
@@ -155,9 +156,9 @@ static int oc_check_required(Slapi_PBlock *, Slapi_Entry *,struct objclass *);
f92ce9
 static int oc_check_allowed_sv(Slapi_PBlock *, Slapi_Entry *e, const char *type, struct objclass **oclist );
f92ce9
 static int schema_delete_objectclasses ( Slapi_Entry *entryBefore,
f92ce9
 		LDAPMod *mod, char *errorbuf, size_t errorbufsize,
f92ce9
-		int schema_ds4x_compat );
f92ce9
+		int schema_ds4x_compat, int is_internal_operation);
f92ce9
 static int schema_delete_attributes ( Slapi_Entry *entryBefore,
f92ce9
-		LDAPMod *mod, char *errorbuf, size_t errorbufsize);
f92ce9
+		LDAPMod *mod, char *errorbuf, size_t errorbufsize, int is_internal_operation);
f92ce9
 static int schema_add_attribute ( Slapi_PBlock *pb, LDAPMod *mod,
f92ce9
 		char *errorbuf, size_t errorbufsize, int schema_ds4x_compat );
f92ce9
 static int schema_add_objectclass ( Slapi_PBlock *pb, LDAPMod *mod,
f92ce9
@@ -2074,7 +2075,9 @@ modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entr
f92ce9
   int schema_modify_enabled = config_get_schemamod();
f92ce9
   int reapply_mods = 0;
f92ce9
   int is_replicated_operation = 0;
f92ce9
-
f92ce9
+  int is_internal_operation = 0;
f92ce9
+  Slapi_Operation *operation = NULL;
f92ce9
+  
f92ce9
   if (!schema_modify_enabled) {
f92ce9
 	*returncode = LDAP_UNWILLING_TO_PERFORM;
f92ce9
 	schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
f92ce9
@@ -2085,6 +2088,8 @@ modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entr
f92ce9
 
f92ce9
   slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
f92ce9
   slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
f92ce9
+  slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
f92ce9
+  is_internal_operation = slapi_operation_is_flag_set(operation, SLAPI_OP_FLAG_INTERNAL);
f92ce9
 
f92ce9
   /* In case we receive a schema from a supplier, check if we can accept it
f92ce9
    * (it is a superset of our own schema).
f92ce9
@@ -2153,11 +2158,11 @@ modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entr
f92ce9
 	if (SLAPI_IS_MOD_DELETE(mods[i]->mod_op)) {
f92ce9
 	  if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
f92ce9
 		*returncode = schema_delete_objectclasses (entryBefore, mods[i],
f92ce9
-					returntext, SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat );
f92ce9
+					returntext, SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat, is_internal_operation);
f92ce9
 	  }
f92ce9
 	  else if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
f92ce9
 		*returncode = schema_delete_attributes (entryBefore, mods[i],
f92ce9
-					returntext, SLAPI_DSE_RETURNTEXT_SIZE );
f92ce9
+					returntext, SLAPI_DSE_RETURNTEXT_SIZE, is_internal_operation);
f92ce9
 	  }
f92ce9
 	  else {
f92ce9
 		*returncode= LDAP_NO_SUCH_ATTRIBUTE;
f92ce9
@@ -2196,6 +2201,7 @@ modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entr
f92ce9
 		  schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
f92ce9
 					schema_errprefix_generic, mods[i]->mod_type, 
f92ce9
 					"Replace is not allowed on the subschema subentry" );
f92ce9
+                  slapi_log_error(SLAPI_LOG_REPL, "schema", "modify_schema_dse: Replace is not allowed on the subschema subentry\n");
f92ce9
 		  rc = SLAPI_DSE_CALLBACK_ERROR;
f92ce9
 	  } else {
f92ce9
 		  if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {			  
f92ce9
@@ -2264,7 +2270,7 @@ modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entr
f92ce9
 		 * Add a new objectclass
f92ce9
 		 */
f92ce9
 		*returncode = schema_add_objectclass ( pb, mods[i], returntext,
f92ce9
-				SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat );
f92ce9
+				SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat);
f92ce9
 	  }
f92ce9
 	  else {
f92ce9
 		if ( schema_ds4x_compat ) {
f92ce9
@@ -2465,16 +2471,20 @@ oc_add_nolock(struct objclass *newoc)
f92ce9
  */
f92ce9
 static int 
f92ce9
 schema_delete_objectclasses( Slapi_Entry *entryBefore, LDAPMod *mod,
f92ce9
-		char *errorbuf, size_t errorbufsize, int schema_ds4x_compat )
f92ce9
+		char *errorbuf, size_t errorbufsize, int schema_ds4x_compat, int is_internal_operation)
f92ce9
 {
f92ce9
   int i;
f92ce9
   int rc = LDAP_SUCCESS;	/* optimistic */
f92ce9
   struct objclass *poc, *poc2,  *delete_oc = NULL;
f92ce9
   
f92ce9
   if ( NULL == mod->mod_bvalues ) {
f92ce9
-		schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
f92ce9
-				NULL, "Cannot remove all schema object classes" );
f92ce9
+	if (is_internal_operation) {
f92ce9
+		slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_objectclasses: Remove all objectclass in Internal op\n");
f92ce9
+	} else {
f92ce9
+		schema_create_errormsg(errorbuf, errorbufsize, schema_errprefix_oc,
f92ce9
+			NULL, "Cannot remove all schema object classes");
f92ce9
 		return LDAP_UNWILLING_TO_PERFORM;
f92ce9
+	}
f92ce9
   }
f92ce9
 
f92ce9
   for (i = 0; mod->mod_bvalues[i]; i++) {
f92ce9
@@ -2492,11 +2502,19 @@ schema_delete_objectclasses( Slapi_Entry *entryBefore, LDAPMod *mod,
f92ce9
 	  for (poc2 = g_get_global_oc_nolock(); poc2 != NULL; poc2 = poc2->oc_next) {
f92ce9
 		if (poc2->oc_superior &&  
f92ce9
 			(strcasecmp (poc2->oc_superior, delete_oc->oc_name) == 0)) {
f92ce9
-		  schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
f92ce9
+			if (is_internal_operation) {
f92ce9
+				slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_objectclasses: Should not delete object class (%s) which has child object classes"
f92ce9
+					". But accept it because it is internal operation\n",
f92ce9
+					delete_oc->oc_name);
f92ce9
+			} else {
f92ce9
+				schema_create_errormsg(errorbuf, errorbufsize, schema_errprefix_oc,
f92ce9
 					delete_oc->oc_name, "Cannot delete an object class"
f92ce9
-					" which has child object classes" );
f92ce9
-		  rc = LDAP_UNWILLING_TO_PERFORM;
f92ce9
-		  goto unlock_and_return;
f92ce9
+					" which has child object classes");
f92ce9
+				slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_objectclasses: Cannot delete an object class (%s) which has child object classes\n",
f92ce9
+					delete_oc->oc_name);
f92ce9
+				rc = LDAP_UNWILLING_TO_PERFORM;
f92ce9
+				goto unlock_and_return;
f92ce9
+			}
f92ce9
 		}
f92ce9
 	  }
f92ce9
 
f92ce9
@@ -2505,10 +2523,19 @@ schema_delete_objectclasses( Slapi_Entry *entryBefore, LDAPMod *mod,
f92ce9
 	  }
f92ce9
 	  
f92ce9
 	  else {
f92ce9
-		schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
f92ce9
-				delete_oc->oc_name, "Cannot delete a standard object class" );
f92ce9
-		rc = LDAP_UNWILLING_TO_PERFORM;
f92ce9
-		goto unlock_and_return;
f92ce9
+		  if (is_internal_operation) {
f92ce9
+			  slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_objectclasses: Should not delete a standard object class (%s)"
f92ce9
+				  ". But accept it because it is internal operation\n",
f92ce9
+				  delete_oc->oc_name);
f92ce9
+			  oc_delete_nolock (poc->oc_name);
f92ce9
+		  } else {
f92ce9
+			  schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
f92ce9
+				  delete_oc->oc_name, "Cannot delete a standard object class" );
f92ce9
+			  slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_objectclasses: Cannot delete a standard object class (%s)\n",
f92ce9
+				  delete_oc->oc_name);
f92ce9
+			  rc = LDAP_UNWILLING_TO_PERFORM;
f92ce9
+			  goto unlock_and_return;
f92ce9
+		  }
f92ce9
 	  }
f92ce9
 	}
f92ce9
 	else {
f92ce9
@@ -2552,7 +2579,7 @@ schema_return(int rc,struct sizedbuffer * psb1,struct sizedbuffer *psb2,struct s
f92ce9
  */
f92ce9
 static int 
f92ce9
 schema_delete_attributes ( Slapi_Entry *entryBefore, LDAPMod *mod,
f92ce9
-		char *errorbuf, size_t errorbufsize)
f92ce9
+		char *errorbuf, size_t errorbufsize, int is_internal_operation)
f92ce9
 {
f92ce9
   char *attr_ldif, *oc_list_type = "";
f92ce9
   asyntaxinfo *a;
f92ce9
@@ -2563,10 +2590,14 @@ schema_delete_attributes ( Slapi_Entry *entryBefore, LDAPMod *mod,
f92ce9
   struct sizedbuffer *psbAttrSyntax= sizedbuffer_construct(BUFSIZ);
f92ce9
 
f92ce9
   if (NULL == mod->mod_bvalues) {
f92ce9
-	schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
f92ce9
+	  if (is_internal_operation) {
f92ce9
+		slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_attributes: Remove all attributetypes in Internal op\n");
f92ce9
+	  } else {
f92ce9
+		schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
f92ce9
 			NULL, "Cannot remove all schema attribute types" );
f92ce9
-    return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
f92ce9
+		return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
f92ce9
 			psbAttrSyntax,NULL);
f92ce9
+	  }
f92ce9
   }
f92ce9
 
f92ce9
   for (i = 0; mod->mod_bvalues[i]; i++) {
f92ce9
@@ -2592,12 +2623,20 @@ schema_delete_attributes ( Slapi_Entry *entryBefore, LDAPMod *mod,
f92ce9
 	if ((a = attr_syntax_get_by_name ( psbAttrName->buffer)) != NULL ) {
f92ce9
 	  /* only modify attrs which were user defined */
f92ce9
 	  if (a->asi_flags & SLAPI_ATTR_FLAG_STD_ATTR) {
f92ce9
-		schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
f92ce9
-				psbAttrName->buffer,
f92ce9
-				"Cannot delete a standard attribute type" );
f92ce9
-		attr_syntax_return( a );
f92ce9
-        return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
f92ce9
-				psbAttrSyntax,NULL);
f92ce9
+		  if (is_internal_operation) {
f92ce9
+			  slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_attributes: Should not delete a standard attribute type (%s)"
f92ce9
+				  ". But accept it because it is internal operation\n",
f92ce9
+				  psbAttrName->buffer);
f92ce9
+		  } else {
f92ce9
+			  schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
f92ce9
+				  psbAttrName->buffer,
f92ce9
+				  "Cannot delete a standard attribute type");
f92ce9
+			  slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_attributes: Cannot delete a standard attribute type (%s)\n",
f92ce9
+				  psbAttrName->buffer);
f92ce9
+			  attr_syntax_return(a);
f92ce9
+			  return schema_return(LDAP_UNWILLING_TO_PERFORM, psbAttrOid, psbAttrName,
f92ce9
+				  psbAttrSyntax, NULL);
f92ce9
+		  }
f92ce9
 	  }
f92ce9
 
f92ce9
 	  /* Do not allow deletion if referenced by an object class. */
f92ce9
@@ -2627,17 +2666,32 @@ schema_delete_attributes ( Slapi_Entry *entryBefore, LDAPMod *mod,
f92ce9
 	    }
f92ce9
 
f92ce9
 		if (attr_in_use_by_an_oc) {
f92ce9
-		  schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
f92ce9
-				psbAttrName->buffer, "Is included in the %s list for object class %s.  Cannot delete.",
f92ce9
-				oc_list_type, oc->oc_name );
f92ce9
-		  break;
f92ce9
+			if (is_internal_operation) {
f92ce9
+				slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_attributes: Should not delete an attribute (%s) used in oc (%s)"
f92ce9
+					". But accept it because it is internal operation\n",
f92ce9
+					oc_list_type, oc->oc_name);
f92ce9
+			} else {
f92ce9
+				schema_create_errormsg(errorbuf, errorbufsize, schema_errprefix_at,
f92ce9
+					psbAttrName->buffer, "Is included in the %s list for object class %s.  Cannot delete.",
f92ce9
+					oc_list_type, oc->oc_name);
f92ce9
+				slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_attributes: Could delete an attribute (%s) used in oc (%s)"
f92ce9
+					". But accept it because it is internal operation\n",
f92ce9
+					oc_list_type, oc->oc_name);
f92ce9
+				break;
f92ce9
+			}
f92ce9
 		}
f92ce9
 	  }
f92ce9
 	  oc_unlock();
f92ce9
 	  if (attr_in_use_by_an_oc) {
f92ce9
-		attr_syntax_return( a );
f92ce9
-        return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
f92ce9
-				psbAttrSyntax,NULL);
f92ce9
+		if (is_internal_operation) {
f92ce9
+			slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_delete_attributes: Should not delete an attribute used in oc"
f92ce9
+				". But accept it because it is internal operation\n");
f92ce9
+
f92ce9
+		} else {
f92ce9
+			attr_syntax_return(a);
f92ce9
+			return schema_return(LDAP_UNWILLING_TO_PERFORM, psbAttrOid, psbAttrName,
f92ce9
+				psbAttrSyntax, NULL);
f92ce9
+		}
f92ce9
 	  }
f92ce9
 
f92ce9
 	  /* Delete it. */
f92ce9
@@ -2744,7 +2798,10 @@ add_oc_internal(struct objclass *pnew_oc, char *errorbuf, size_t errorbufsize,
f92ce9
 		}
f92ce9
 	}
f92ce9
 
f92ce9
-	/* check to see if the superior oc exists */
f92ce9
+	/* check to see if the superior oc exists 
f92ce9
+	 * This is not enforced for internal op (when learning new schema
f92ce9
+	 * definitions from a replication session) 
f92ce9
+	 */
f92ce9
 	if (!rc && pnew_oc->oc_superior &&
f92ce9
 				((psup_oc = oc_find_nolock (pnew_oc->oc_superior, NULL, PR_FALSE)) == NULL)) {
f92ce9
 		schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
f92ce9
@@ -2798,7 +2855,10 @@ add_oc_internal(struct objclass *pnew_oc, char *errorbuf, size_t errorbufsize,
f92ce9
 	    sizedbuffer_destroy(psbOcOid);	
f92ce9
 	}
f92ce9
 
f92ce9
-	/* check to see if the oc's attributes are valid */
f92ce9
+	/* check to see if the oc's attributes are valid 
f92ce9
+	 * This is not checked if this is an internal operation (learning schema
f92ce9
+	 * definitions from a replication session)
f92ce9
+	 */
f92ce9
 	if (!rc && !(flags & DSE_SCHEMA_NO_CHECK) &&
f92ce9
 		schema_check_oc_attrs ( pnew_oc, errorbuf, errorbufsize,
f92ce9
 								0 /* don't strip options */ ) == 0 ) {
f92ce9
@@ -6265,11 +6325,101 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
f92ce9
         
f92ce9
         return rc;
f92ce9
 }
f92ce9
+
f92ce9
+static char *
f92ce9
+schema_oc_to_string(struct objclass *oc)
f92ce9
+{
f92ce9
+    char *oc_str;
f92ce9
+    int i;
f92ce9
+    int size = 0;
f92ce9
+    
f92ce9
+    /* Compute the size of the string that can contain
f92ce9
+     * the oc definition and allocates it
f92ce9
+     */
f92ce9
+    if (oc->oc_oid)  size += strlen(oc->oc_oid);
f92ce9
+    if (oc->oc_name) size += strlen(oc->oc_name);
f92ce9
+    if (oc->oc_desc) size += strlen(oc->oc_desc);
f92ce9
+    if (oc->oc_orig_required) {
f92ce9
+        for (i =0; oc->oc_orig_required[i] != NULL; i++) {
f92ce9
+            size += strlen(oc->oc_orig_required[i]);
f92ce9
+            size += 3;
f92ce9
+        }       
f92ce9
+    }
f92ce9
+    if (oc->oc_orig_allowed) {
f92ce9
+        for (i =0; oc->oc_orig_allowed[i] != NULL; i++) {
f92ce9
+            size += strlen(oc->oc_orig_allowed[i]);
f92ce9
+            size += 3;
f92ce9
+        }
f92ce9
+    }
f92ce9
+    size += strlen(schema_oc_kind_strings_with_spaces[oc->oc_kind]);
f92ce9
+    
f92ce9
+    size += 128; /* for all keywords: NAME, DESC, SUP... */
f92ce9
+    if ((oc_str = (char *) slapi_ch_calloc(1, size)) == NULL) {
f92ce9
+        return NULL;
f92ce9
+    }
f92ce9
+    
f92ce9
+    /* OID + name */
f92ce9
+    sprintf(oc_str, "( %s NAME '%s'", (oc->oc_oid) ? oc->oc_oid : "", oc->oc_name);
f92ce9
+    
f92ce9
+    /* description */
f92ce9
+    strcat(oc_str, " DESC '");
f92ce9
+    if (oc->oc_desc) {
f92ce9
+        strcat(oc_str, oc->oc_desc);
f92ce9
+    }
f92ce9
+    strcat(oc_str, "'");
f92ce9
+    
f92ce9
+    /* SUP */
f92ce9
+    if (oc->oc_superior) {
f92ce9
+        strcat(oc_str, " SUP '");
f92ce9
+        strcat(oc_str, oc->oc_superior);
f92ce9
+        strcat(oc_str, "'");
f92ce9
+    }
f92ce9
+    
f92ce9
+    /* oc_kind */
f92ce9
+    strcat(oc_str, schema_oc_kind_strings_with_spaces[oc->oc_kind]);
f92ce9
+    
f92ce9
+    /* MUST */
f92ce9
+    if (oc->oc_orig_required) {
f92ce9
+        strcat(oc_str, " MUST ( ");
f92ce9
+        for ( i = 0; oc->oc_orig_required[i] != NULL; ++i ) {
f92ce9
+            if (i > 0) {
f92ce9
+                strcat(oc_str, " $ ");
f92ce9
+            }
f92ce9
+            strcat(oc_str, oc->oc_orig_required[i]);
f92ce9
+        }
f92ce9
+        strcat(oc_str, " ) ");
f92ce9
+    }
f92ce9
+    
f92ce9
+    /* MAY */
f92ce9
+    if (oc->oc_orig_allowed) {
f92ce9
+        strcat(oc_str, " MAY ( ");
f92ce9
+        for ( i = 0; oc->oc_orig_allowed[i] != NULL; ++i ) {
f92ce9
+            if (i > 0) {
f92ce9
+                strcat(oc_str, " $ ");
f92ce9
+            }
f92ce9
+            strcat(oc_str, oc->oc_orig_allowed[i]);
f92ce9
+        }
f92ce9
+        strcat(oc_str, " ) ");
f92ce9
+    }
f92ce9
+    
f92ce9
+    /* flags */
f92ce9
+    if (oc->oc_flags & OC_FLAG_USER_OC) {
f92ce9
+        strcat(oc_str, " X-ORIGIN 'blahblahblah'");
f92ce9
+    }
f92ce9
+    
f92ce9
+    strcat(oc_str, " )");
f92ce9
+    slapi_log_error(SLAPI_LOG_REPL, "schema", "schema_oc_to_string: replace (old[%d]=%s)\n",
f92ce9
+                                                size, oc_str);
f92ce9
+    
f92ce9
+    return(oc_str);
f92ce9
+    
f92ce9
+}
f92ce9
 /* call must hold oc_lock at least in read */
f92ce9
 static struct schema_mods_indexes *
f92ce9
 schema_list_oc2learn(struct objclass *oc_remote_list, struct objclass *oc_local_list, int replica_role) {
f92ce9
         struct objclass *oc_remote, *oc_local;
f92ce9
         struct schema_mods_indexes *head = NULL, *mods_index;
f92ce9
+        struct schema_mods_indexes *tail = NULL;
f92ce9
         int index = 0;
f92ce9
         int repl_schema_policy;
f92ce9
         const char *message;
f92ce9
@@ -6309,11 +6459,22 @@ schema_list_oc2learn(struct objclass *oc_remote_list, struct objclass *oc_local_
f92ce9
                                 continue;
f92ce9
                         }
f92ce9
                         
f92ce9
-                        /* insert it in the list */
f92ce9
+                        /* insert it at the end of the list 
f92ce9
+			 * to keep the order of the original schema
f92ce9
+			 * For example superior oc should be declared first
f92ce9
+			 */
f92ce9
                         mods_index->index     = index;
f92ce9
-                        mods_index->next      = head;
f92ce9
+                        mods_index->next      = NULL;
f92ce9
                         mods_index->new_value = NULL;
f92ce9
-                        head                  = mods_index;
f92ce9
+                        if (oc_local) {
f92ce9
+                            mods_index->old_value = schema_oc_to_string(oc_local);
f92ce9
+                        }
f92ce9
+                        if (head == NULL) {
f92ce9
+                            head = mods_index;
f92ce9
+                        } else {
f92ce9
+                            tail->next = mods_index;
f92ce9
+                        }
f92ce9
+                        tail = mods_index;
f92ce9
                 }
f92ce9
         }
f92ce9
         slapi_rwlock_unlock( schema_policy_lock );
f92ce9
@@ -7173,17 +7334,27 @@ modify_schema_internal_mod(Slapi_DN *sdn, Slapi_Mods *smods)
f92ce9
 	/* do modify */
f92ce9
 	slapi_modify_internal_pb (newpb);
f92ce9
 	slapi_pblock_get (newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
f92ce9
-        if (op_result == LDAP_SUCCESS) {              
f92ce9
-                /* Update the schema csn if the operation succeeded */
f92ce9
-                schema_csn = csn_new();
f92ce9
-                if (NULL != schema_csn) {
f92ce9
-                        csn_set_replicaid(schema_csn, 0);
f92ce9
-                        csn_set_time(schema_csn, current_time());
f92ce9
-                        g_set_global_schema_csn(schema_csn);
f92ce9
-                }
f92ce9
-        }
f92ce9
+	if (op_result == LDAP_SUCCESS) {
f92ce9
+		char *type;
f92ce9
 
f92ce9
-        slapi_pblock_destroy(newpb);
f92ce9
+		if (smods && smods->mods) {
f92ce9
+			type = smods->mods[0]->mod_type;
f92ce9
+		} else {
f92ce9
+			type = "unknown";
f92ce9
+		}
f92ce9
+		slapi_log_error(SLAPI_LOG_REPL, "schema", "modify_schema_internal_mod: successfully learn %s definitions\n", type);
f92ce9
+		/* Update the schema csn if the operation succeeded */
f92ce9
+		schema_csn = csn_new();
f92ce9
+		if (NULL != schema_csn) {
f92ce9
+			csn_set_replicaid(schema_csn, 0);
f92ce9
+			csn_set_time(schema_csn, current_time());
f92ce9
+			g_set_global_schema_csn(schema_csn);
f92ce9
+		}
f92ce9
+	} else {
f92ce9
+		slapi_log_error(SLAPI_LOG_FATAL, "schema", "modify_schema_internal_mod: fail to learn schema definitions (%d) \n", op_result);
f92ce9
+	}
f92ce9
+
f92ce9
+	slapi_pblock_destroy(newpb);
f92ce9
 }
f92ce9
 
f92ce9
 /* Prepare slapi_mods for the internal mod 
f92ce9
@@ -7191,32 +7362,80 @@ modify_schema_internal_mod(Slapi_DN *sdn, Slapi_Mods *smods)
f92ce9
  */
f92ce9
 static void
f92ce9
 modify_schema_prepare_mods(Slapi_Mods *smods, char *type, struct schema_mods_indexes *values)
f92ce9
-{   
f92ce9
-        struct schema_mods_indexes *object;
f92ce9
-        struct berval *bv;
f92ce9
-        struct berval **bvps;
f92ce9
-        int nb_values, i;
f92ce9
-            
f92ce9
-        for (object = values, nb_values = 0; object != NULL; object = object->next, nb_values++);
f92ce9
-        bvps = (struct berval **) slapi_ch_calloc(1, (nb_values + 1) * sizeof(struct berval *));
f92ce9
-        
f92ce9
-        
f92ce9
+{
f92ce9
+    struct schema_mods_indexes *object;
f92ce9
+    struct berval *bv;
f92ce9
+    struct berval **bvps_del = NULL;
f92ce9
+    struct berval **bvps_add = NULL;
f92ce9
+    int nb_values_del, nb_values_add, i;
f92ce9
+    int nb_mods;
f92ce9
+
f92ce9
+    /* Checks the values to delete */
f92ce9
+    for (object = values, nb_values_del = 0; object != NULL; object = object->next) {
f92ce9
+        if (object->old_value) {
f92ce9
+            nb_values_del++;
f92ce9
+        }
f92ce9
+    }
f92ce9
+    if (nb_values_del) {
f92ce9
+        bvps_del = (struct berval **) slapi_ch_calloc(1, (nb_values_del + 1) * sizeof (struct berval *));
f92ce9
+
f92ce9
+        for (i = 0, object = values; object != NULL; object = object->next) {
f92ce9
+            if (object->old_value) {
f92ce9
+                bv = (struct berval *) slapi_ch_malloc(sizeof (struct berval));
f92ce9
+                bv->bv_len = strlen(object->old_value);
f92ce9
+                bv->bv_val = (void*) object->old_value;
f92ce9
+                bvps_del[i] = bv;
f92ce9
+                i++;
f92ce9
+                slapi_log_error(SLAPI_LOG_REPL, "schema", "MOD[%d] del (%s): %s\n", i, type, object->old_value);
f92ce9
+            }
f92ce9
+        }
f92ce9
+        bvps_del[nb_values_del] = NULL;
f92ce9
+    }
f92ce9
+
f92ce9
+    /* Checks the values to add */
f92ce9
+    for (object = values, nb_values_add = 0; object != NULL; object = object->next, nb_values_add++);
f92ce9
+
f92ce9
+    if (nb_values_add) {
f92ce9
+        bvps_add = (struct berval **) slapi_ch_calloc(1, (nb_values_add + 1) * sizeof (struct berval *));
f92ce9
+
f92ce9
+
f92ce9
         for (i = 0, object = values; object != NULL; i++, object = object->next) {
f92ce9
-                bv = (struct berval *) slapi_ch_malloc(sizeof(struct berval));
f92ce9
-                bv->bv_len = strlen(object->new_value);
f92ce9
-                bv->bv_val = (void*) object->new_value;
f92ce9
-                bvps[i] = bv;
f92ce9
-                slapi_log_error(SLAPI_LOG_REPL, "schema", "MOD[%d] add (%s): %s\n", i, type, object->new_value);
f92ce9
-        }
f92ce9
-        bvps[nb_values] = NULL;
f92ce9
-        slapi_mods_init (smods, 2);
f92ce9
-        slapi_mods_add_modbvps( smods, LDAP_MOD_ADD, type, bvps );
f92ce9
-        for (i = 0; bvps[i] != NULL; i++) {
f92ce9
-                /* bv_val should not be free. It belongs to the incoming MOD */
f92ce9
-                slapi_ch_free((void **) &bvps[i]);
f92ce9
-        }
f92ce9
-        slapi_ch_free((void **) &bvps);
f92ce9
-        
f92ce9
+            bv = (struct berval *) slapi_ch_malloc(sizeof (struct berval));
f92ce9
+            bv->bv_len = strlen(object->new_value);
f92ce9
+            bv->bv_val = (void*) object->new_value;
f92ce9
+            bvps_add[i] = bv;
f92ce9
+            slapi_log_error(SLAPI_LOG_REPL, "schema", "MOD[%d] add (%s): %s\n", i, type, object->new_value);
f92ce9
+        }
f92ce9
+        bvps_add[nb_values_add] = NULL;
f92ce9
+    }
f92ce9
+
f92ce9
+    /* Prepare the mods */
f92ce9
+    nb_mods = 1;
f92ce9
+    if (bvps_del) nb_mods++;
f92ce9
+    if (bvps_add) nb_mods++;
f92ce9
+    slapi_mods_init(smods, nb_mods);
f92ce9
+    if (bvps_del) slapi_mods_add_modbvps(smods, LDAP_MOD_DELETE, type, bvps_del);
f92ce9
+    if (bvps_add) slapi_mods_add_modbvps(smods, LDAP_MOD_ADD, type, bvps_add);
f92ce9
+
f92ce9
+
f92ce9
+    /* clean up */
f92ce9
+    if (bvps_del) {
f92ce9
+
f92ce9
+        for (i = 0; bvps_del[i] != NULL; i++) {
f92ce9
+            /* bv_val should not be free. It belongs to the incoming MOD */
f92ce9
+            slapi_ch_free((void **) &bvps_del[i]);
f92ce9
+        }
f92ce9
+        slapi_ch_free((void **) &bvps_del);
f92ce9
+    }
f92ce9
+
f92ce9
+    if (bvps_add) {
f92ce9
+
f92ce9
+        for (i = 0; bvps_add[i] != NULL; i++) {
f92ce9
+            /* bv_val should not be free. It belongs to the incoming MOD */
f92ce9
+            slapi_ch_free((void **) &bvps_add[i]);
f92ce9
+        }
f92ce9
+        slapi_ch_free((void **) &bvps_add);
f92ce9
+    }
f92ce9
 }
f92ce9
 
f92ce9
 /* called by modify_schema_dse/supplier_learn_new_definitions to learn new 
f92ce9
-- 
f92ce9
1.9.3
f92ce9