/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 2008 The OpenLDAP Foundation.
+ * Copyright 2008-2009 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#include "back-ndb.h"
+/* This is a copy from slapd/mods.c, but with compaction tweaked
+ * to swap values from the tail into deleted slots, to reduce the
+ * overall update traffic.
+ */
+static int
+ndb_modify_delete(
+ Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text,
+ char *textbuf, size_t textlen,
+ int *idx )
+{
+ Attribute *a;
+ MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
+ struct berval *cvals;
+ int *id2 = NULL;
+ int i, j, rc = 0, num;
+ unsigned flags;
+ char dummy = '\0';
+
+ /* For ordered vals, we have no choice but to preserve order */
+ if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL )
+ return modify_delete_vindex( e, mod, permissive, text,
+ textbuf, textlen, idx );
+
+ /*
+ * If permissive is set, then the non-existence of an
+ * attribute is not treated as an error.
+ */
+
+ /* delete the entire attribute */
+ if ( mod->sm_values == NULL ) {
+ rc = attr_delete( &e->e_attrs, mod->sm_desc );
+
+ if( permissive ) {
+ rc = LDAP_SUCCESS;
+ } else if( rc != LDAP_SUCCESS ) {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ return rc;
+ }
+
+ /* FIXME: Catch old code that doesn't set sm_numvals.
+ */
+ if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
+ assert( mod->sm_numvals == i );
+ }
+ if ( !idx ) {
+ id2 = (int *)ch_malloc( mod->sm_numvals * sizeof( int ));
+ idx = id2;
+ }
+
+ if( mr == NULL || !mr->smr_match ) {
+ /* disallow specific attributes from being deleted if
+ no equality rule */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no equality matching rule",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto return_result;
+ }
+
+ /* delete specific values - find the attribute first */
+ if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
+ if( permissive ) {
+ rc = LDAP_SUCCESS;
+ goto return_result;
+ }
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_result;
+ }
+
+ if ( mod->sm_nvalues ) {
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
+ | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
+ | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
+ cvals = mod->sm_nvalues;
+ } else {
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
+ cvals = mod->sm_values;
+ }
+
+ /* Locate values to delete */
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
+ unsigned sort;
+ rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ idx[i] = sort;
+ } else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
+ if ( permissive ) {
+ idx[i] = -1;
+ continue;
+ }
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such value",
+ mod->sm_desc->ad_cname.bv_val );
+ goto return_result;
+ } else {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: matching rule failed",
+ mod->sm_desc->ad_cname.bv_val );
+ goto return_result;
+ }
+ }
+
+ num = a->a_numvals;
+
+ /* Delete the values */
+ for ( i = 0; i < mod->sm_numvals; i++ ) {
+ /* Skip permissive values that weren't found */
+ if ( idx[i] < 0 )
+ continue;
+ /* Skip duplicate delete specs */
+ if ( a->a_vals[idx[i]].bv_val == &dummy )
+ continue;
+ /* delete value and mark it as gone */
+ free( a->a_vals[idx[i]].bv_val );
+ a->a_vals[idx[i]].bv_val = &dummy;
+ if( a->a_nvals != a->a_vals ) {
+ free( a->a_nvals[idx[i]].bv_val );
+ a->a_nvals[idx[i]].bv_val = &dummy;
+ }
+ a->a_numvals--;
+ }
+
+ /* compact array */
+ for ( i=0; i<num; i++ ) {
+ if ( a->a_vals[i].bv_val != &dummy )
+ continue;
+ for ( --num; num > i && a->a_vals[num].bv_val == &dummy; num-- )
+ ;
+ a->a_vals[i] = a->a_vals[num];
+ if ( a->a_nvals != a->a_vals )
+ a->a_nvals[i] = a->a_nvals[num];
+ }
+
+ BER_BVZERO( &a->a_vals[num] );
+ if (a->a_nvals != a->a_vals) {
+ BER_BVZERO( &a->a_nvals[num] );
+ }
+
+ /* if no values remain, delete the entire attribute */
+ if ( !a->a_numvals ) {
+ if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
+ /* Can never happen */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+return_result:
+ if ( id2 )
+ ch_free( id2 );
+ return rc;
+}
+
int ndb_modify_internal(
Operation *op,
NdbArgs *NA,
NdbAttrInfo **modai, *atmp;
const NdbDictionary::Dictionary *myDict;
const NdbDictionary::Table *myTable;
- int got_oc = 0, nmods = 0, nai = 0, i;
- int rc, err, indexed = 0;
+ int got_oc = 0, nmods = 0, nai = 0, i, j;
+ int rc, indexed = 0;
+ Attribute *old = NULL;
Debug( LDAP_DEBUG_TRACE, "ndb_modify_internal: 0x%08lx: %s\n",
NA->e->e_id, NA->e->e_dn, 0);
return LDAP_INSUFFICIENT_ACCESS;
}
+ old = attrs_dup( NA->e->e_attrs );
+
for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
mod = &ml->sml_mod;
nmods++;
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: add %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
- err = modify_add_values( NA->e, mod, get_permissiveModify(op),
+ rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
text, textbuf, textlen );
- if( err != LDAP_SUCCESS ) {
+ if( rc != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
- err, *text, 0);
+ rc, *text, 0);
}
break;
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: delete %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
- err = modify_delete_values( NA->e, mod, get_permissiveModify(op),
- text, textbuf, textlen );
- assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
- if( err != LDAP_SUCCESS ) {
+ rc = ndb_modify_delete( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen, NULL );
+ assert( rc != LDAP_TYPE_OR_VALUE_EXISTS );
+ if( rc != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
- err, *text, 0);
+ rc, *text, 0);
}
break;
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: replace %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
- err = modify_replace_values( NA->e, mod, get_permissiveModify(op),
+ rc = modify_replace_values( NA->e, mod, get_permissiveModify(op),
text, textbuf, textlen );
- if( err != LDAP_SUCCESS ) {
+ if( rc != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
- err, *text, 0);
+ rc, *text, 0);
}
break;
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: increment %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
- err = modify_increment_values( NA->e, mod, get_permissiveModify(op),
+ rc = modify_increment_values( NA->e, mod, get_permissiveModify(op),
text, textbuf, textlen );
- if( err != LDAP_SUCCESS ) {
+ if( rc != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: %d %s\n",
- err, *text, 0);
+ rc, *text, 0);
}
break;
mod->sm_desc->ad_cname.bv_val, 0, 0);
mod->sm_op = LDAP_MOD_ADD;
- err = modify_add_values( NA->e, mod, get_permissiveModify(op),
+ rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
text, textbuf, textlen );
mod->sm_op = SLAP_MOD_SOFTADD;
- if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
- err = LDAP_SUCCESS;
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ rc = LDAP_SUCCESS;
}
- if( err != LDAP_SUCCESS ) {
+ if( rc != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
- err, *text, 0);
+ rc, *text, 0);
}
break;
Debug(LDAP_DEBUG_ANY, "ndb_modify_internal: invalid op %d\n",
mod->sm_op, 0, 0);
*text = "Invalid modify operation";
- err = LDAP_OTHER;
+ rc = LDAP_OTHER;
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
- err, *text, 0);
+ rc, *text, 0);
}
- if ( err != LDAP_SUCCESS ) {
- return err;
+ if ( rc != LDAP_SUCCESS ) {
+ attrs_free( old );
+ return rc;
}
/* If objectClass was modified, reset the flags */
}
/* check that the entry still obeys the schema */
- rc = entry_schema_check( op, NA->e, NULL, get_relax(op), 0,
+ rc = entry_schema_check( op, NA->e, NULL, get_relax(op), 0, NULL,
text, textbuf, textlen );
if ( rc != LDAP_SUCCESS || op->o_noop ) {
if ( rc != LDAP_SUCCESS ) {
"entry failed schema check: %s\n",
*text, 0, 0 );
}
+ attrs_free( old );
return rc;
}
if ( got_oc || indexed ) {
rc = ndb_entry_put_info( op->o_bd, NA, 1 );
- if ( rc ) return rc;
+ if ( rc ) {
+ attrs_free( old );
+ return rc;
+ }
}
myDict = NA->ndb->getDictionary();
- /* One operation per table... */
- for ( i=0; i<nai; i++ ) {
- NdbOperation *myOp;
- int j;
+ /* sort modai so that OcInfo's are contiguous */
+ {
+ int j, k;
+ for ( i=0; i<nai; i++ ) {
+ for ( j=i+1; j<nai; j++ ) {
+ if ( modai[i]->na_oi == modai[j]->na_oi )
+ continue;
+ for ( k=j+1; k<nai; k++ ) {
+ if ( modai[i]->na_oi == modai[k]->na_oi ) {
+ atmp = modai[j];
+ modai[j] = modai[k];
+ modai[k] = atmp;
+ break;
+ }
+ }
+ /* there are no more na_oi's that match modai[i] */
+ if ( k == nai ) {
+ i = j;
+ }
+ }
+ }
+ }
- if ( !modai[i] ) continue;
+ /* One call per table... */
+ for ( i=0; i<nai; i += j ) {
atmp = modai[i];
- modai[i] = NULL;
+ for ( j=i+1; j<nai; j++ )
+ if ( atmp->na_oi != modai[j]->na_oi )
+ break;
+ j -= i;
myTable = myDict->getTable( atmp->na_oi->no_table.bv_val );
- if ( !myTable ) continue;
- myOp = NULL;
- nmods = 0;
- rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &atmp, 1, 1, &nmods, &myOp );
- if ( rc ) return rc;
- for ( j=i+1; j<nai; j++ ) {
- if ( !modai[j] ) continue;
- if ( modai[j]->na_oi == atmp->na_oi ) {
- atmp = modai[j];
- modai[j] = NULL;
- rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &atmp, 1, 1, &nmods, &myOp );
- if ( rc ) return rc;
- }
- }
+ if ( !myTable )
+ continue;
+ rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &modai[i], j, old );
+ if ( rc ) break;
}
- return 0;
+ attrs_free( old );
+ return rc;
}
LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
int num_ctrls = 0;
- int rc;
-
Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(ndb_back_modify) ": %s\n",
op->o_req_dn.bv_val, 0, 0 );
goto return_results;
}
if ( NA.ocs ) {
- ber_bvarray_free( NA.ocs );
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
}
ndb_trans_backoff( ++num_retries );
}
}
/* get entry or ancestor */
- rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 0, &matched );
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
switch( rs->sr_err ) {
case 0:
break;
"<=- ndb_back_modify: no such object %s\n",
op->o_req_dn.bv_val, 0, 0 );
rs->sr_matched = matched.bv_val;
+ if (NA.ocs )
+ ndb_check_referral( op, rs, &NA );
goto return_results;
#if 0
case DB_LOCK_DEADLOCK:
}
/* acquire and lock entry */
- rs->sr_err = ndb_entry_get_data( op->o_bd, &NA, 1 );
+ rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
if ( !manageDSAit && is_entry_referral( &e ) ) {
/* entry is a referral, don't allow modify */
}
if( op->o_noop ) {
- if ( ( rs->sr_err = NA.txn->execute( Rollback ) ) != 0 ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
rs->sr_text = "txn_abort (no-op) failed";
} else {
rs->sr_err = LDAP_X_NO_OPERATION;
}
} else {
- if ( ( rs->sr_err = NA.txn->execute( Commit ) ) != 0 ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
rs->sr_text = "txn_commit failed";
} else {
rs->sr_err = LDAP_SUCCESS;
return_results:
if ( NA.ocs ) {
- ber_bvarray_free( NA.ocs );
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
NA.ocs = NULL;
}