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.
22 modify_check_duplicates(
23 AttributeDescription *ad,
28 char *textbuf, size_t textlen )
30 int i, j, numvals = 0, nummods,
32 BerVarray nvals = NULL, nmods = NULL;
35 * FIXME: better do the following
37 * - count the existing values
38 * - count the new values
40 * - if the existing values are less than the new ones {
41 * - normalize all the existing values
42 * - for each new value {
44 * - check with existing
45 * - cross-check with already normalized new vals
48 * - for each new value {
50 * - cross-check with already normalized new vals
52 * - for each existing value {
54 * - check with already normalized new values
58 * The first case is good when adding a lot of new values,
59 * and significantly at first import of values (e.g. adding
60 * a new group); the latter case seems to be quite important
61 * as well, because it is likely to be the most frequently
62 * used when administering the entry. The current
63 * implementation will always normalize all the existing
64 * values before checking. If there's no duplicate, the
65 * performances should not change; they will in case of error.
68 for ( nummods = 0; mods[ nummods ].bv_val != NULL; nummods++ )
69 /* count new values */ ;
72 for ( numvals = 0; vals[ numvals ].bv_val != NULL; numvals++ )
73 /* count existing values */ ;
75 if ( numvals < nummods ) {
76 nvals = SLAP_CALLOC( numvals + 1, sizeof( struct berval ) );
79 LDAP_LOG( OPERATION, ERR,
80 "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
82 Debug( LDAP_DEBUG_ANY,
83 "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
88 /* normalize the existing values first */
89 for ( j = 0; vals[ j ].bv_val != NULL; j++ ) {
90 rc = value_normalize( ad, SLAP_MR_EQUALITY,
91 &vals[ j ], &nvals[ j ], text );
93 /* existing attribute values must normalize */
94 assert( rc == LDAP_SUCCESS );
96 if ( rc != LDAP_SUCCESS ) {
97 nvals[ j ].bv_val = NULL;
101 nvals[ j ].bv_val = NULL;
106 * If the existing values are less than the new values,
107 * it is more convenient to normalize all the existing
108 * values and test each new value against them first,
109 * then to other already normalized values
111 nmods = SLAP_CALLOC( nummods + 1, sizeof( struct berval ) );
112 if ( nmods == NULL ) {
114 LDAP_LOG( OPERATION, ERR,
115 "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
117 Debug( LDAP_DEBUG_ANY,
118 "modify_check_duplicates: SLAP_CALLOC failed", 0, 0, 0 );
123 for ( i = 0; mods[ i ].bv_val != NULL; i++ ) {
124 rc = value_normalize( ad, SLAP_MR_EQUALITY,
125 &mods[ i ], &nmods[ i ], text );
127 if ( rc != LDAP_SUCCESS ) {
128 nmods[ i ].bv_val = NULL;
132 if ( numvals > 0 && numvals < nummods ) {
133 for ( j = 0; nvals[ j ].bv_val; j++ ) {
136 rc = (*mr->smr_match)( &match,
137 SLAP_MR_VALUE_SYNTAX_MATCH,
138 ad->ad_type->sat_syntax,
139 mr, &nmods[ i ], &nvals[ j ] );
140 if ( rc != LDAP_SUCCESS ) {
141 nmods[ i + 1 ].bv_val = NULL;
143 snprintf( textbuf, textlen,
144 "%s: matching rule failed",
145 ad->ad_cname.bv_val );
151 snprintf( textbuf, textlen,
152 "%s: value #%d provided more than once",
153 ad->ad_cname.bv_val, i );
154 rc = LDAP_TYPE_OR_VALUE_EXISTS;
155 nmods[ i + 1 ].bv_val = NULL;
161 for ( j = 0; j < i; j++ ) {
164 rc = (*mr->smr_match)( &match,
165 SLAP_MR_VALUE_SYNTAX_MATCH,
166 ad->ad_type->sat_syntax,
167 mr, &nmods[ i ], &nmods[ j ] );
168 if ( rc != LDAP_SUCCESS ) {
169 nmods[ i + 1 ].bv_val = NULL;
171 snprintf( textbuf, textlen,
172 "%s: matching rule failed",
173 ad->ad_cname.bv_val );
179 snprintf( textbuf, textlen,
180 "%s: value #%d provided more than once",
181 ad->ad_cname.bv_val, j );
182 rc = LDAP_TYPE_OR_VALUE_EXISTS;
183 nmods[ i + 1 ].bv_val = NULL;
188 nmods[ i ].bv_val = NULL;
191 * if new values are more than existing values, it is more
192 * convenient to normalize and check all new values first,
193 * then check each new value against existing values, which
194 * can be normalized in place
197 if ( numvals >= nummods ) {
198 for ( j = 0; vals[ j ].bv_val; j++ ) {
199 struct berval asserted;
201 rc = value_normalize( ad, SLAP_MR_EQUALITY,
202 &vals[ j ], &asserted, text );
204 if ( rc != LDAP_SUCCESS ) {
208 for ( i = 0; nmods[ i ].bv_val; i++ ) {
211 rc = (*mr->smr_match)( &match,
212 SLAP_MR_VALUE_SYNTAX_MATCH,
213 ad->ad_type->sat_syntax,
214 mr, &nmods[ i ], &asserted );
215 if ( rc != LDAP_SUCCESS ) {
217 snprintf( textbuf, textlen,
218 "%s: matching rule failed",
219 ad->ad_cname.bv_val );
225 snprintf( textbuf, textlen,
226 "%s: value #%d provided more than once",
227 ad->ad_cname.bv_val, j );
228 rc = LDAP_TYPE_OR_VALUE_EXISTS;
238 ber_bvarray_free( nvals );
241 ber_bvarray_free( nmods );
252 char *textbuf, size_t textlen
257 MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
260 switch( mod->sm_op ) {
264 case LDAP_MOD_REPLACE:
272 a = attr_find( e->e_attrs, mod->sm_desc );
274 /* check if the values we're adding already exist */
275 if( mr == NULL || !mr->smr_match ) {
277 /* do not allow add of additional attribute
278 if no equality rule exists */
280 snprintf( textbuf, textlen,
281 "modify/%s: %s: no equality matching rule",
282 op, mod->sm_desc->ad_cname.bv_val );
283 return LDAP_INAPPROPRIATE_MATCHING;
286 for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
287 /* test asserted values against existing values */
289 for( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
290 if ( bvmatch( &mod->sm_bvalues[i],
293 /* value exists already */
295 snprintf( textbuf, textlen,
296 "modify/%s: %s: value #%i already exists",
297 op, mod->sm_desc->ad_cname.bv_val, j );
298 return LDAP_TYPE_OR_VALUE_EXISTS;
303 /* test asserted values against themselves */
304 for( j = 0; j < i; j++ ) {
305 if ( bvmatch( &mod->sm_bvalues[i],
306 &mod->sm_bvalues[j] ) ) {
308 /* value exists already */
310 snprintf( textbuf, textlen,
311 "modify/%s: %s: value #%i already exists",
312 op, mod->sm_desc->ad_cname.bv_val, j );
313 return LDAP_TYPE_OR_VALUE_EXISTS;
320 * The original code performs ( n ) normalizations
321 * and ( n * ( n - 1 ) / 2 ) matches, which hide
322 * the same number of normalizations. The new code
323 * performs the same number of normalizations ( n )
324 * and ( n * ( n - 1 ) / 2 ) mem compares, far less
325 * expensive than an entire match, if a match is
326 * equivalent to a normalization and a mem compare ...
328 * This is far more memory expensive than the previous,
329 * but it can heavily improve performances when big
330 * chunks of data are added (typical example is a group
331 * with thousands of DN-syntax members; on my system:
332 * for members of 5-RDN DNs,
334 members orig bvmatch (dirty) new
335 1000 0m38.456s 0m0.553s 0m0.608s
336 2000 2m33.341s 0m0.851s 0m1.003s
338 * Moreover, 100 groups with 10000 members each were
339 * added in 37m27.933s (an analogous LDIF file was
340 * loaded into Active Directory in 38m28.682s, BTW).
342 * Maybe we could switch to the new algorithm when
343 * the number of values overcomes a given threshold?
348 if ( mod->sm_bvalues[ 1 ].bv_val == 0 ) {
350 struct berval asserted;
353 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
354 &mod->sm_bvalues[ 0 ], &asserted, text );
356 if ( rc != LDAP_SUCCESS ) {
360 for ( i = 0; a->a_vals[ i ].bv_val; i++ ) {
363 rc = value_match( &match, mod->sm_desc, mr,
364 SLAP_MR_VALUE_SYNTAX_MATCH,
365 &a->a_vals[ i ], &asserted, text );
367 if( rc == LDAP_SUCCESS && match == 0 ) {
368 free( asserted.bv_val );
370 snprintf( textbuf, textlen,
371 "modify/%s: %s: value #0 already exists",
372 op, mod->sm_desc->ad_cname.bv_val, 0 );
373 return LDAP_TYPE_OR_VALUE_EXISTS;
379 rc = modify_check_duplicates( mod->sm_desc, mr,
380 a ? a->a_vals : NULL, mod->sm_bvalues,
381 text, textbuf, textlen );
383 if ( rc != LDAP_SUCCESS ) {
390 if( attr_merge( e, mod->sm_desc, mod->sm_bvalues ) != 0 ) {
391 /* this should return result of attr_merge */
393 snprintf( textbuf, textlen,
394 "modify/%s: %s: merge error",
395 op, mod->sm_desc->ad_cname.bv_val );
403 modify_delete_values(
407 char *textbuf, size_t textlen
410 int i, j, k, rc = LDAP_SUCCESS;
412 MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
413 BerVarray nvals = NULL;
416 /* delete the entire attribute */
417 if ( mod->sm_bvalues == NULL ) {
418 rc = attr_delete( &e->e_attrs, mod->sm_desc );
420 if( rc != LDAP_SUCCESS ) {
422 snprintf( textbuf, textlen,
423 "modify/delete: %s: no such attribute",
424 mod->sm_desc->ad_cname.bv_val );
425 rc = LDAP_NO_SUCH_ATTRIBUTE;
430 if( mr == NULL || !mr->smr_match ) {
431 /* disallow specific attributes from being deleted if
434 snprintf( textbuf, textlen,
435 "modify/delete: %s: no equality matching rule",
436 mod->sm_desc->ad_cname.bv_val );
437 return LDAP_INAPPROPRIATE_MATCHING;
440 /* delete specific values - find the attribute first */
441 if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
443 snprintf( textbuf, textlen,
444 "modify/delete: %s: no such attribute",
445 mod->sm_desc->ad_cname.bv_val );
446 return LDAP_NO_SUCH_ATTRIBUTE;
449 /* find each value to delete */
450 for ( j = 0; a->a_vals[ j ].bv_val != NULL; j++ )
451 /* count existing values */ ;
453 nvals = (BerVarray)SLAP_CALLOC( j + 1, sizeof ( struct berval ) );
454 if( nvals == NULL ) {
456 LDAP_LOG( OPERATION, ERR,
457 "modify_delete_values: SLAP_CALLOC failed", 0, 0, 0 );
459 Debug( LDAP_DEBUG_ANY,
460 "modify_delete_values: SLAP_CALLOC failed", 0, 0, 0 );
465 /* normalize existing values */
466 for ( j = 0; a->a_vals[ j ].bv_val != NULL; j++ ) {
467 rc = value_normalize( a->a_desc, SLAP_MR_EQUALITY,
468 &a->a_vals[ j ], &nvals[ j ], text );
470 if ( rc != LDAP_SUCCESS ) {
471 nvals[ j ].bv_val = NULL;
476 for ( i = 0; mod->sm_bvalues[ i ].bv_val != NULL; i++ ) {
477 struct berval asserted;
480 /* normalize the value to be deleted */
481 rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
482 &mod->sm_bvalues[ i ], &asserted, text );
484 if( rc != LDAP_SUCCESS ) {
489 for ( j = 0; nvals[ j ].bv_val != NULL; j++ ) {
492 if ( nvals[ j ].bv_val == &dummy ) {
496 rc = (*mr->smr_match)( &match,
497 SLAP_MR_VALUE_SYNTAX_MATCH,
498 a->a_desc->ad_type->sat_syntax,
499 mr, &nvals[ j ], &asserted );
501 if ( rc != LDAP_SUCCESS ) {
502 free( asserted.bv_val );
504 snprintf( textbuf, textlen,
505 "%s: matching rule failed",
506 mod->sm_desc->ad_cname.bv_val );
516 /* delete value and mark it as dummy */
517 free( nvals[ j ].bv_val );
518 nvals[ j ].bv_val = &dummy;
523 free( asserted.bv_val );
527 snprintf( textbuf, textlen,
528 "modify/delete: %s: no such value",
529 mod->sm_desc->ad_cname.bv_val );
530 rc = LDAP_NO_SUCH_ATTRIBUTE;
535 /* compact array skipping dummies */
536 for ( k = 0, j = 0; nvals[ k ].bv_val != NULL; j++, k++ ) {
538 /* delete and skip dummies */ ;
539 for ( ; nvals[ k ].bv_val == &dummy; k++ ) {
540 free( a->a_vals[ k ].bv_val );
544 a->a_vals[ j ] = a->a_vals[ k ];
547 if ( a->a_vals[ k ].bv_val == NULL ) {
551 a->a_vals[ j ].bv_val = NULL;
553 assert( i == k - j );
555 /* if no values remain, delete the entire attribute */
556 if ( a->a_vals[0].bv_val == NULL ) {
557 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
559 snprintf( textbuf, textlen,
560 "modify/delete: %s: no such attribute",
561 mod->sm_desc->ad_cname.bv_val );
562 rc = LDAP_NO_SUCH_ATTRIBUTE;
568 /* delete the remaining normalized values */
569 for ( j = 0; nvals[ j ].bv_val != NULL; j++ ) {
570 if ( nvals[ j ].bv_val != &dummy ) {
571 ber_memfree( nvals[ j ].bv_val );
574 ber_memfree( nvals );
581 modify_replace_values(
585 char *textbuf, size_t textlen
588 (void) attr_delete( &e->e_attrs, mod->sm_desc );
590 if ( mod->sm_bvalues ) {
591 return modify_add_values( e, mod, text, textbuf, textlen );
604 if ( mod->sm_type.bv_val)
605 free( mod->sm_type.bv_val );
607 if ( mod->sm_bvalues != NULL )
608 ber_bvarray_free( mod->sm_bvalues );
621 for ( ; ml != NULL; ml = next ) {
624 slap_mod_free( &ml->sml_mod, 0 );