2 * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
3 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6 * Copyright (c) 1995 Regents of the University of Michigan.
9 * Redistribution and use in source and binary forms are permitted
10 * provided that this notice is preserved and that due credit is given
11 * to the University of Michigan at Ann Arbor. The name of the University
12 * may not be used to endorse or promote products derived from this
13 * software without specific prior written permission. This software
14 * is provided ``as is'' without express or implied warranty.
21 #undef QUICK_DIRTY_DUPLICATE_CHECK
24 modify_check_duplicates(
25 AttributeDescription *ad,
30 char *textbuf, size_t textlen )
32 int i, j, rc = LDAP_SUCCESS;
33 BerVarray nvals = NULL, nmods;
36 * FIXME: better do the following
38 * - count the existing values
39 * - count the new values
41 * - if the existing values are less than the new ones {
43 * - normalize the existing values
44 * - for each new value {
46 * - check with existing
47 * - cross-check with already normalized new vals
50 * // to be implemented
51 * - for each new value {
53 * - cross-check with already normalized new vals
55 * - for each existing value {
57 * - check with already normalized new values
61 * The first case is good when adding a lot of new values,
62 * and significantly at first import of values (e.g. adding
63 * a new group); the latter case seems to be quite important
64 * as well, because it is likely to be the most frequently
65 * used when administering the entry. The current
66 * implementation will always normalize all the existing
67 * values before checking. If there's no duplicate, the
68 * performances should not change; they will in case of error.
72 for ( j = 0; vals[ j ].bv_val != NULL; j++ )
73 /* count existing values */ ;
75 nvals = ch_calloc( j + 1, sizeof( struct berval ) );
77 /* normalize the existing values first */
78 for ( j = 0; vals[ j ].bv_val != NULL; j++ ) {
79 rc = value_normalize( ad, SLAP_MR_EQUALITY,
80 &vals[ j ], &nvals[ j ], text );
82 /* existing attribute values must normalize */
83 assert( rc == LDAP_SUCCESS );
85 if ( rc != LDAP_SUCCESS ) {
86 nvals[ j ].bv_val = NULL;
90 nvals[ j ].bv_val = NULL;
93 for ( i = 0; mods[ i ].bv_val != NULL; i++ )
94 /* count new values */ ;
96 nmods = ch_calloc( i + 1, sizeof( struct berval ) );
98 for ( i = 0; mods[ i ].bv_val != NULL; i++ ) {
100 rc = value_normalize( ad, SLAP_MR_EQUALITY,
101 &mods[ i ], &nmods[ i ], text );
103 if ( rc != LDAP_SUCCESS ) {
104 nmods[ i ].bv_val = NULL;
109 for ( j = 0; nvals[ j ].bv_val; j++ ) {
110 #ifdef QUICK_DIRTY_DUPLICATE_CHECK
111 if ( bvmatch( &nmods[ i ], &nvals[ j ] ) ) {
112 #else /* !QUICK_DIRTY_DUPLICATE_CHECK */
115 rc = (mr->smr_match)( &match,
116 SLAP_MR_VALUE_SYNTAX_MATCH,
117 ad->ad_type->sat_syntax,
118 mr, &nmods[ i ], &nvals[ j ] );
119 if ( rc != LDAP_SUCCESS ) {
120 nmods[ i + 1 ].bv_val = NULL;
125 #endif /* !QUICK_DIRTY_DUPLICATE_CHECK */
126 snprintf( textbuf, textlen,
127 "%s: value #%d provided more than once",
128 ad->ad_cname.bv_val, i );
129 rc = LDAP_TYPE_OR_VALUE_EXISTS;
130 nmods[ i + 1 ].bv_val = NULL;
136 for ( j = 0; j < i; j++ ) {
137 #ifdef QUICK_DIRTY_DUPLICATE_CHECK
138 if ( bvmatch( &nmods[ i ], &nmods[ j ] ) ) {
139 #else /* !QUICK_DIRTY_DUPLICATE_CHECK */
142 rc = (mr->smr_match)( &match,
143 SLAP_MR_VALUE_SYNTAX_MATCH,
144 ad->ad_type->sat_syntax,
145 mr, &nmods[ i ], &nmods[ j ] );
146 if ( rc != LDAP_SUCCESS ) {
147 nmods[ i + 1 ].bv_val = NULL;
152 #endif /* !QUICK_DIRTY_DUPLICATE_CHECK */
153 snprintf( textbuf, textlen,
154 "%s: value #%d provided more than once",
155 ad->ad_cname.bv_val, j );
156 rc = LDAP_TYPE_OR_VALUE_EXISTS;
157 nmods[ i + 1 ].bv_val = NULL;
162 nmods[ i ].bv_val = NULL;
166 ber_bvarray_free( nvals );
169 ber_bvarray_free( nmods );
180 char *textbuf, size_t textlen
185 MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
188 switch( mod->sm_op ) {
192 case LDAP_MOD_REPLACE:
200 a = attr_find( e->e_attrs, mod->sm_desc );
202 /* check if the values we're adding already exist */
203 if( mr == NULL || !mr->smr_match ) {
205 /* do not allow add of additional attribute
206 if no equality rule exists */
208 snprintf( textbuf, textlen,
209 "modify/%s: %s: no equality matching rule",
210 op, mod->sm_desc->ad_cname.bv_val );
211 return LDAP_INAPPROPRIATE_MATCHING;
214 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
215 /* test asserted values against existing values */
217 for( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
218 if ( bvmatch( &mod->sm_bvalues[i],
221 /* value exists already */
223 snprintf( textbuf, textlen,
224 "modify/%s: %s: value #%i already exists",
225 op, mod->sm_desc->ad_cname.bv_val, j );
226 return LDAP_TYPE_OR_VALUE_EXISTS;
231 /* test asserted values against themselves */
232 for( j = 0; j < i; j++ ) {
233 if ( bvmatch( &mod->sm_bvalues[i],
234 &mod->sm_bvalues[j] ) ) {
236 /* value exists already */
238 snprintf( textbuf, textlen,
239 "modify/%s: %s: value #%i already exists",
240 op, mod->sm_desc->ad_cname.bv_val, j );
241 return LDAP_TYPE_OR_VALUE_EXISTS;
249 * The original code performs ( n ) normalizations
250 * and ( n * ( n - 1 ) / 2 ) matches, which hide
251 * the same number of normalizations. The new code
252 * performs the same number of normalizations ( n )
253 * and ( n * ( n - 1 ) / 2 ) mem compares, far less
254 * expensive than an entire match, if a match is
255 * equivalent to a normalization and a mem compare ...
257 * This is far more memory expensive than the previous,
258 * but it can heavily improve performances when big
259 * chunks of data are added (typical example is a group
260 * with thousands of DN-syntax members; on my system:
261 * for members of 5-RDN DNs,
263 members orig bvmatch (dirty) new
264 1000 0m38.456s 0m0.553s 0m0.608s
265 2000 2m33.341s 0m0.851s 0m1.003s
267 * Moreover, 100 groups with 10000 members each were
268 * added in 37m27.933s (an analogous LDIF file was
269 * loaded into Active Directory in 38m28.682s, BTW).
271 * Maybe we could switch to the new algorithm when
272 * the number of values overcomes a given threshold?
276 const char *text = NULL;
277 char textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
279 if ( mod->sm_bvalues[ 1 ].bv_val == 0 ) {
281 struct berval asserted;
284 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
285 &mod->sm_bvalues[ 0 ], &asserted, &text );
287 if ( rc != LDAP_SUCCESS ) {
291 for ( i = 0; a->a_vals[ i ].bv_val; i++ ) {
294 rc = value_match( &match, mod->sm_desc, mr,
295 SLAP_MR_VALUE_SYNTAX_MATCH,
296 &a->a_vals[ i ], &asserted, &text );
298 if( rc == LDAP_SUCCESS && match == 0 ) {
299 free( asserted.bv_val );
300 return LDAP_TYPE_OR_VALUE_EXISTS;
306 rc = modify_check_duplicates( mod->sm_desc, mr,
307 a ? a->a_vals : NULL, mod->sm_bvalues,
308 &text, textbuf, sizeof( textbuf ) );
310 if ( rc != LDAP_SUCCESS ) {
317 if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 ) {
318 /* this should return result of attr_merge */
320 snprintf( textbuf, textlen,
321 "modify/%s: %s: merge error",
322 op, mod->sm_desc->ad_cname.bv_val );
330 modify_delete_values(
334 char *textbuf, size_t textlen
339 MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
341 /* delete the entire attribute */
342 if ( mod->sm_bvalues == NULL ) {
343 int rc = attr_delete( &e->e_attrs, mod->sm_desc );
345 if( rc != LDAP_SUCCESS ) {
347 snprintf( textbuf, textlen,
348 "modify/delete: %s: no such attribute",
349 mod->sm_desc->ad_cname.bv_val );
350 rc = LDAP_NO_SUCH_ATTRIBUTE;
355 if( mr == NULL || !mr->smr_match ) {
356 /* disallow specific attributes from being deleted if
359 snprintf( textbuf, textlen,
360 "modify/delete: %s: no equality matching rule",
361 mod->sm_desc->ad_cname.bv_val );
362 return LDAP_INAPPROPRIATE_MATCHING;
365 /* delete specific values - find the attribute first */
366 if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
368 snprintf( textbuf, textlen,
369 "modify/delete: %s: no such attribute",
370 mod->sm_desc->ad_cname.bv_val );
371 return LDAP_NO_SUCH_ATTRIBUTE;
374 /* find each value to delete */
375 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
377 struct berval asserted;
379 rc = value_normalize( mod->sm_desc,
385 if( rc != LDAP_SUCCESS ) return rc;
388 for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
390 int rc = value_match( &match, mod->sm_desc, mr,
391 SLAP_MR_VALUE_SYNTAX_MATCH,
392 &a->a_vals[j], &asserted, text );
394 if( rc == LDAP_SUCCESS && match != 0 ) {
398 /* found a matching value */
402 free( a->a_vals[j].bv_val );
403 for ( k = j + 1; a->a_vals[k].bv_val != NULL; k++ ) {
404 a->a_vals[k - 1] = a->a_vals[k];
406 a->a_vals[k - 1].bv_val = NULL;
407 a->a_vals[k - 1].bv_len = 0;
412 free( asserted.bv_val );
414 /* looked through them all w/o finding it */
417 snprintf( textbuf, textlen,
418 "modify/delete: %s: no such value",
419 mod->sm_desc->ad_cname.bv_val );
420 return LDAP_NO_SUCH_ATTRIBUTE;
424 /* if no values remain, delete the entire attribute */
425 if ( a->a_vals[0].bv_val == NULL ) {
426 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
428 snprintf( textbuf, textlen,
429 "modify/delete: %s: no such attribute",
430 mod->sm_desc->ad_cname.bv_val );
431 return LDAP_NO_SUCH_ATTRIBUTE;
439 modify_replace_values(
443 char *textbuf, size_t textlen
446 (void) attr_delete( &e->e_attrs, mod->sm_desc );
448 if ( mod->sm_bvalues ) {
449 return modify_add_values( e, mod, text, textbuf, textlen );
462 if ( mod->sm_type.bv_val)
463 free( mod->sm_type.bv_val );
465 if ( mod->sm_bvalues != NULL )
466 ber_bvarray_free( mod->sm_bvalues );
479 for ( ; ml != NULL; ml = next ) {
482 slap_mod_free( &ml->sml_mod, 0 );