#include "slap.h"
+#undef QUICK_DIRTY_DUPLICATE_CHECK
+
+int
+modify_check_duplicates(
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ BerVarray vals,
+ BerVarray mods,
+ const char **text,
+ char *textbuf, size_t textlen )
+{
+ int i, j, numvals = 0, nummods,
+ rc = LDAP_SUCCESS;
+ BerVarray nvals = NULL, nmods;
+
+ /*
+ * FIXME: better do the following
+ *
+ * - count the existing values
+ * - count the new values
+ *
+ * - if the existing values are less than the new ones {
+ * - normalize all the existing values
+ * - for each new value {
+ * - normalize
+ * - check with existing
+ * - cross-check with already normalized new vals
+ * }
+ * } else {
+ * - for each new value {
+ * - normalize
+ * - cross-check with already normalized new vals
+ * }
+ * - for each existing value {
+ * - normalize
+ * - check with already normalized new values
+ * }
+ * }
+ *
+ * The first case is good when adding a lot of new values,
+ * and significantly at first import of values (e.g. adding
+ * a new group); the latter case seems to be quite important
+ * as well, because it is likely to be the most frequently
+ * used when administering the entry. The current
+ * implementation will always normalize all the existing
+ * values before checking. If there's no duplicate, the
+ * performances should not change; they will in case of error.
+ */
+
+ for ( nummods = 0; mods[ nummods ].bv_val != NULL; nummods++ )
+ /* count new values */ ;
+
+ if ( vals ) {
+ for ( numvals = 0; vals[ numvals ].bv_val != NULL; numvals++ )
+ /* count existing values */ ;
+ }
+
+ if ( numvals > 0 && numvals < nummods ) {
+ nvals = ch_calloc( numvals + 1, sizeof( struct berval ) );
+
+ /* normalize the existing values first */
+ for ( j = 0; vals[ j ].bv_val != NULL; j++ ) {
+ rc = value_normalize( ad, SLAP_MR_EQUALITY,
+ &vals[ j ], &nvals[ j ], text );
+
+ /* existing attribute values must normalize */
+ assert( rc == LDAP_SUCCESS );
+
+ if ( rc != LDAP_SUCCESS ) {
+ nvals[ j ].bv_val = NULL;
+ goto return_results;
+ }
+ }
+ nvals[ j ].bv_val = NULL;
+ }
+
+ /*
+ * If the existing values are less than the new values,
+ * it is more convenient to normalize all the existing
+ * values and test each new value against them first,
+ * then to other already normalized values
+ */
+ nmods = ch_calloc( nummods + 1, sizeof( struct berval ) );
+
+ for ( i = 0; mods[ i ].bv_val != NULL; i++ ) {
+ rc = value_normalize( ad, SLAP_MR_EQUALITY,
+ &mods[ i ], &nmods[ i ], text );
+
+ if ( rc != LDAP_SUCCESS ) {
+ nmods[ i ].bv_val = NULL;
+ goto return_results;
+ }
+
+ if ( numvals > 0 && numvals < nummods ) {
+ for ( j = 0; nvals[ j ].bv_val; j++ ) {
+#ifdef QUICK_DIRTY_DUPLICATE_CHECK
+ if ( bvmatch( &nmods[ i ], &nvals[ j ] ) ) {
+#else /* !QUICK_DIRTY_DUPLICATE_CHECK */
+ int match;
+
+ rc = (mr->smr_match)( &match,
+ SLAP_MR_VALUE_SYNTAX_MATCH,
+ ad->ad_type->sat_syntax,
+ mr, &nmods[ i ], &nvals[ j ] );
+ if ( rc != LDAP_SUCCESS ) {
+ nmods[ i + 1 ].bv_val = NULL;
+ goto return_results;
+ }
+
+ if ( match == 0 ) {
+#endif /* !QUICK_DIRTY_DUPLICATE_CHECK */
+ snprintf( textbuf, textlen,
+ "%s: value #%d provided more than once",
+ ad->ad_cname.bv_val, i );
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ nmods[ i + 1 ].bv_val = NULL;
+ goto return_results;
+ }
+ }
+ }
+
+ for ( j = 0; j < i; j++ ) {
+#ifdef QUICK_DIRTY_DUPLICATE_CHECK
+ if ( bvmatch( &nmods[ i ], &nmods[ j ] ) ) {
+#else /* !QUICK_DIRTY_DUPLICATE_CHECK */
+ int match;
+
+ rc = (mr->smr_match)( &match,
+ SLAP_MR_VALUE_SYNTAX_MATCH,
+ ad->ad_type->sat_syntax,
+ mr, &nmods[ i ], &nmods[ j ] );
+ if ( rc != LDAP_SUCCESS ) {
+ nmods[ i + 1 ].bv_val = NULL;
+ goto return_results;
+ }
+
+ if ( match == 0 ) {
+#endif /* !QUICK_DIRTY_DUPLICATE_CHECK */
+ snprintf( textbuf, textlen,
+ "%s: value #%d provided more than once",
+ ad->ad_cname.bv_val, j );
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ nmods[ i + 1 ].bv_val = NULL;
+ goto return_results;
+ }
+ }
+ }
+ nmods[ i ].bv_val = NULL;
+
+ /*
+ * if new values are more than existing values, it is more
+ * convenient to normalize and check all new values first,
+ * then check each new value against existing values, which
+ * can be normalized in place
+ */
+
+ if ( numvals >= nummods ) {
+ for ( j = 0; vals[ j ].bv_val; j++ ) {
+ struct berval asserted;
+
+ rc = value_normalize( ad, SLAP_MR_EQUALITY,
+ &vals[ j ], &asserted, text );
+
+ if ( rc != LDAP_SUCCESS ) {
+ goto return_results;
+ }
+
+ for ( i = 0; nmods[ i ].bv_val; i++ ) {
+#ifdef QUICK_DIRTY_DUPLICATE_CHECK
+ if ( bvmatch( &nmods[ i ], &asserted ) ) {
+#else /* !QUICK_DIRTY_DUPLICATE_CHECK */
+ int match;
+
+ rc = (mr->smr_match)( &match,
+ SLAP_MR_VALUE_SYNTAX_MATCH,
+ ad->ad_type->sat_syntax,
+ mr, &nmods[ i ], &asserted );
+ if ( rc != LDAP_SUCCESS ) {
+ goto return_results;
+ }
+
+ if ( match == 0 ) {
+#endif /* !QUICK_DIRTY_DUPLICATE_CHECK */
+ snprintf( textbuf, textlen,
+ "%s: value #%d provided more than once",
+ ad->ad_cname.bv_val, j );
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ goto return_results;
+ }
+ }
+
+ }
+ }
+
+return_results:;
+ if ( nvals ) {
+ ber_bvarray_free( nvals );
+ }
+ if ( nmods ) {
+ ber_bvarray_free( nmods );
+ }
+
+ return rc;
+}
+
int
modify_add_values(
Entry *e,
/* test asserted values against existing values */
if( a ) {
for( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
- int rc = ber_bvcmp( &mod->sm_bvalues[i],
- &a->a_vals[j] );
+ if ( bvmatch( &mod->sm_bvalues[i],
+ &a->a_vals[j] ) ) {
- if( rc == 0 ) {
/* value exists already */
*text = textbuf;
snprintf( textbuf, textlen,
/* test asserted values against themselves */
for( j = 0; j < i; j++ ) {
- int rc = ber_bvcmp( &mod->sm_bvalues[i],
- &mod->sm_bvalues[j] );
+ if ( bvmatch( &mod->sm_bvalues[i],
+ &mod->sm_bvalues[j] ) ) {
- if( rc == 0 ) {
/* value exists already */
*text = textbuf;
snprintf( textbuf, textlen,
}
} else {
- for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
- int rc, match;
- struct berval asserted;
- rc = value_normalize( mod->sm_desc,
- SLAP_MR_EQUALITY,
- &mod->sm_bvalues[i],
- &asserted,
- text );
+ /*
+ * The original code performs ( n ) normalizations
+ * and ( n * ( n - 1 ) / 2 ) matches, which hide
+ * the same number of normalizations. The new code
+ * performs the same number of normalizations ( n )
+ * and ( n * ( n - 1 ) / 2 ) mem compares, far less
+ * expensive than an entire match, if a match is
+ * equivalent to a normalization and a mem compare ...
+ *
+ * This is far more memory expensive than the previous,
+ * but it can heavily improve performances when big
+ * chunks of data are added (typical example is a group
+ * with thousands of DN-syntax members; on my system:
+ * for members of 5-RDN DNs,
+
+ members orig bvmatch (dirty) new
+ 1000 0m38.456s 0m0.553s 0m0.608s
+ 2000 2m33.341s 0m0.851s 0m1.003s
+
+ * Moreover, 100 groups with 10000 members each were
+ * added in 37m27.933s (an analogous LDIF file was
+ * loaded into Active Directory in 38m28.682s, BTW).
+ *
+ * Maybe we could switch to the new algorithm when
+ * the number of values overcomes a given threshold?
+ */
+
+ int rc;
+ const char *text = NULL;
+ char textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+
+ if ( mod->sm_bvalues[ 1 ].bv_val == 0 ) {
+ if ( a != NULL ) {
+ struct berval asserted;
+ int i;
+
+ rc = value_normalize( mod->sm_desc, SLAP_MR_EQUALITY,
+ &mod->sm_bvalues[ 0 ], &asserted, &text );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
- if( rc != LDAP_SUCCESS ) return rc;
+ for ( i = 0; a->a_vals[ i ].bv_val; i++ ) {
+ int match;
- if( a ) {
- for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) {
- int rc = value_match( &match, mod->sm_desc, mr,
+ rc = value_match( &match, mod->sm_desc, mr,
SLAP_MR_VALUE_SYNTAX_MATCH,
- &a->a_vals[j], &asserted, text );
+ &a->a_vals[ i ], &asserted, &text );
if( rc == LDAP_SUCCESS && match == 0 ) {
free( asserted.bv_val );
}
}
- for ( j = 0; j < i; j++ ) {
- int rc = value_match( &match, mod->sm_desc, mr,
- SLAP_MR_VALUE_SYNTAX_MATCH,
- &mod->sm_bvalues[j], &asserted, text );
-
- if( rc == LDAP_SUCCESS && match == 0 ) {
- free( asserted.bv_val );
- return LDAP_TYPE_OR_VALUE_EXISTS;
- }
+ } else {
+ rc = modify_check_duplicates( mod->sm_desc, mr,
+ a ? a->a_vals : NULL, mod->sm_bvalues,
+ &text, textbuf, sizeof( textbuf ) );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
}
-
- free( asserted.bv_val );
}
}
return LDAP_NO_SUCH_ATTRIBUTE;
}
- /* find each value to delete */
+ /* find each value to delete
+ *
+ * FIXME: need to optimize this operation too,
+ * see modify_check_duplicates()
+ */
for ( i = 0; mod->sm_bvalues[i].bv_val != NULL; i++ ) {
int rc;
struct berval asserted;
#define SLAP_SCHERR_CLASS_BAD_SUP 4
#define SLAP_SCHERR_CLASS_DUP 5
#define SLAP_SCHERR_ATTR_NOT_FOUND 6
-#define SLAP_SCHERR_ATTR_BAD_USAGE 7
-#define SLAP_SCHERR_ATTR_BAD_SUP 8
-#define SLAP_SCHERR_ATTR_INCOMPLETE 9
-#define SLAP_SCHERR_ATTR_DUP 10
-#define SLAP_SCHERR_MR_NOT_FOUND 11
-#define SLAP_SCHERR_MR_INCOMPLETE 12
-#define SLAP_SCHERR_MR_DUP 13
-#define SLAP_SCHERR_SYN_NOT_FOUND 14
-#define SLAP_SCHERR_SYN_DUP 15
-#define SLAP_SCHERR_NO_NAME 16
-#define SLAP_SCHERR_NOT_SUPPORTED 17
-#define SLAP_SCHERR_BAD_DESCR 18
-#define SLAP_SCHERR_OIDM 19
+#define SLAP_SCHERR_ATTR_BAD_MR 7
+#define SLAP_SCHERR_ATTR_BAD_USAGE 8
+#define SLAP_SCHERR_ATTR_BAD_SUP 9
+#define SLAP_SCHERR_ATTR_INCOMPLETE 10
+#define SLAP_SCHERR_ATTR_DUP 11
+#define SLAP_SCHERR_MR_NOT_FOUND 12
+#define SLAP_SCHERR_MR_INCOMPLETE 13
+#define SLAP_SCHERR_MR_DUP 14
+#define SLAP_SCHERR_SYN_NOT_FOUND 15
+#define SLAP_SCHERR_SYN_DUP 16
+#define SLAP_SCHERR_NO_NAME 17
+#define SLAP_SCHERR_NOT_SUPPORTED 18
+#define SLAP_SCHERR_BAD_DESCR 19
+#define SLAP_SCHERR_OIDM 20
#define SLAP_SCHERR_LAST SLAP_SCHERR_OIDM
typedef union slap_sockaddr {
/*
* null terminated list of syntaxes compatible with this syntax
* note: when MS_EXT is set, this MUST NOT contain the assertion
- * syntax of the rule. When MS_EXT is not set, it MAY.
+ * syntax of the rule. When MS_EXT is not set, it MAY.
*/
Syntax **smr_compat_syntaxes;
#define SLAP_RESTRICT_OP_SEARCH 0x0080U
#define SLAP_RESTRICT_OP_READS \
- ( SLAP_RESTRICT_OP_COMPARE \
+ ( SLAP_RESTRICT_OP_COMPARE \
| SLAP_RESTRICT_OP_SEARCH )
#define SLAP_RESTRICT_OP_WRITES \
( SLAP_RESTRICT_OP_ADD \
struct berval be_rootdn; /* the magic "root" name (DN) for this db */
struct berval be_rootndn; /* the magic "root" normalized name (DN) for this db */
struct berval be_rootpw; /* the magic "root" password for this db */
- unsigned int be_max_deref_depth; /* limit for depth of an alias deref */
+ unsigned int be_max_deref_depth; /* limit for depth of an alias deref */
#define be_sizelimit be_def_limit.lms_s_soft
#define be_timelimit be_def_limit.lms_t_soft
struct slap_limits_set be_def_limit; /* default limits */
ber_int_t msgid));
typedef int (BI_op_extended) LDAP_P((
- BackendDB *be,
- struct slap_conn *conn,
- struct slap_op *op,
+ BackendDB *be,
+ struct slap_conn *conn,
+ struct slap_op *op,
const char *reqoid,
- struct berval * reqdata,
+ struct berval * reqdata,
char **rspoid,
- struct berval ** rspdata,
+ struct berval ** rspdata,
LDAPControl *** rspctrls,
const char ** text,
BerVarray *refs ));
ber_int_t o_pagedresults_size;
PagedResultsState o_pagedresults_state;
+#ifdef LDAP_CLIENT_UPDATE
+ char o_clientupdate;
+ char o_clientupdate_type;
+#define SLAP_LCUP_NONE (0x0)
+#define SLAP_LCUP_SYNC (0x1)
+#define SLAP_LCUP_PERSIST (0x2)
+#define SLAP_LCUP_SYNC_AND_PERSIST (0x3)
+ ber_int_t o_clientupdate_interval;
+ struct berval* o_clientupdate_state;
+#endif
+
#ifdef LDAP_CONNECTIONLESS
Sockaddr o_peeraddr; /* UDP peer address */
#endif
fprintf( stderr, (fmt), (connid), (opid), (arg1), (arg2), (arg3) );\
if ( ldap_syslog & (level) ) \
syslog( ldap_syslog_level, (fmt), (connid), (opid), (arg1), \
- (arg2), (arg3) ); \
+ (arg2), (arg3) ); \
} while (0)
#else
#define Statslog( level, fmt, connid, opid, arg1, arg2, arg3 )