Home | History | Annotate | Line # | Download | only in back-mdb
attr.c revision 1.1.1.1
      1 /*	$NetBSD: attr.c,v 1.1.1.1 2014/05/28 09:58:49 tron Exp $	*/
      2 
      3 /* attr.c - backend routines for dealing with attributes */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2000-2014 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 
     19 #include "portable.h"
     20 
     21 #include <stdio.h>
     22 
     23 #include <ac/socket.h>
     24 #include <ac/string.h>
     25 
     26 #include "slap.h"
     27 #include "back-mdb.h"
     28 #include "config.h"
     29 #include "lutil.h"
     30 
     31 /* Find the ad, return -1 if not found,
     32  * set point for insertion if ins is non-NULL
     33  */
     34 int
     35 mdb_attr_slot( struct mdb_info *mdb, AttributeDescription *ad, int *ins )
     36 {
     37 	unsigned base = 0, cursor = 0;
     38 	unsigned n = mdb->mi_nattrs;
     39 	int val = 0;
     40 
     41 	while ( 0 < n ) {
     42 		unsigned pivot = n >> 1;
     43 		cursor = base + pivot;
     44 
     45 		val = SLAP_PTRCMP( ad, mdb->mi_attrs[cursor]->ai_desc );
     46 		if ( val < 0 ) {
     47 			n = pivot;
     48 		} else if ( val > 0 ) {
     49 			base = cursor + 1;
     50 			n -= pivot + 1;
     51 		} else {
     52 			return cursor;
     53 		}
     54 	}
     55 	if ( ins ) {
     56 		if ( val > 0 )
     57 			++cursor;
     58 		*ins = cursor;
     59 	}
     60 	return -1;
     61 }
     62 
     63 static int
     64 ainfo_insert( struct mdb_info *mdb, AttrInfo *a )
     65 {
     66 	int x;
     67 	int i = mdb_attr_slot( mdb, a->ai_desc, &x );
     68 
     69 	/* Is it a dup? */
     70 	if ( i >= 0 )
     71 		return -1;
     72 
     73 	mdb->mi_attrs = ch_realloc( mdb->mi_attrs, ( mdb->mi_nattrs+1 ) *
     74 		sizeof( AttrInfo * ));
     75 	if ( x < mdb->mi_nattrs )
     76 		AC_MEMCPY( &mdb->mi_attrs[x+1], &mdb->mi_attrs[x],
     77 			( mdb->mi_nattrs - x ) * sizeof( AttrInfo *));
     78 	mdb->mi_attrs[x] = a;
     79 	mdb->mi_nattrs++;
     80 	return 0;
     81 }
     82 
     83 AttrInfo *
     84 mdb_attr_mask(
     85 	struct mdb_info	*mdb,
     86 	AttributeDescription *desc )
     87 {
     88 	int i = mdb_attr_slot( mdb, desc, NULL );
     89 	return i < 0 ? NULL : mdb->mi_attrs[i];
     90 }
     91 
     92 /* Open all un-opened index DB handles */
     93 int
     94 mdb_attr_dbs_open(
     95 	BackendDB *be, MDB_txn *tx0, ConfigReply *cr )
     96 {
     97 	struct mdb_info *mdb = (struct mdb_info *) be->be_private;
     98 	MDB_txn *txn;
     99 	MDB_dbi *dbis = NULL;
    100 	int i, flags;
    101 	int rc;
    102 
    103 	txn = tx0;
    104 	if ( txn == NULL ) {
    105 		rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
    106 		if ( rc ) {
    107 			snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
    108 				"txn_begin failed: %s (%d).",
    109 				be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    110 			Debug( LDAP_DEBUG_ANY,
    111 				LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
    112 				cr->msg, 0, 0 );
    113 			return rc;
    114 		}
    115 		dbis = ch_calloc( 1, mdb->mi_nattrs * sizeof(MDB_dbi) );
    116 	} else {
    117 		rc = 0;
    118 	}
    119 
    120 	flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
    121 	if ( !(slapMode & SLAP_TOOL_READONLY) )
    122 		flags |= MDB_CREATE;
    123 
    124 	for ( i=0; i<mdb->mi_nattrs; i++ ) {
    125 		if ( mdb->mi_attrs[i]->ai_dbi )	/* already open */
    126 			continue;
    127 		rc = mdb_dbi_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
    128 			flags, &mdb->mi_attrs[i]->ai_dbi );
    129 		if ( rc ) {
    130 			snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
    131 				"mdb_dbi_open(%s) failed: %s (%d).",
    132 				be->be_suffix[0].bv_val,
    133 				mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
    134 				mdb_strerror(rc), rc );
    135 			Debug( LDAP_DEBUG_ANY,
    136 				LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
    137 				cr->msg, 0, 0 );
    138 			break;
    139 		}
    140 		/* Remember newly opened DBI handles */
    141 		if ( dbis )
    142 			dbis[i] = mdb->mi_attrs[i]->ai_dbi;
    143 	}
    144 
    145 	/* Only commit if this is our txn */
    146 	if ( tx0 == NULL ) {
    147 		if ( !rc ) {
    148 			rc = mdb_txn_commit( txn );
    149 			if ( rc ) {
    150 				snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
    151 					"txn_commit failed: %s (%d).",
    152 					be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    153 				Debug( LDAP_DEBUG_ANY,
    154 					LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
    155 					cr->msg, 0, 0 );
    156 			}
    157 		} else {
    158 			mdb_txn_abort( txn );
    159 		}
    160 		/* Something failed, forget anything we just opened */
    161 		if ( rc ) {
    162 			for ( i=0; i<mdb->mi_nattrs; i++ ) {
    163 				if ( dbis[i] ) {
    164 					mdb->mi_attrs[i]->ai_dbi = 0;
    165 					mdb->mi_attrs[i]->ai_indexmask |= MDB_INDEX_DELETING;
    166 				}
    167 			}
    168 			mdb_attr_flush( mdb );
    169 		}
    170 		ch_free( dbis );
    171 	}
    172 
    173 	return rc;
    174 }
    175 
    176 void
    177 mdb_attr_dbs_close(
    178 	struct mdb_info *mdb
    179 )
    180 {
    181 	int i;
    182 	for ( i=0; i<mdb->mi_nattrs; i++ )
    183 		if ( mdb->mi_attrs[i]->ai_dbi ) {
    184 			mdb_dbi_close( mdb->mi_dbenv, mdb->mi_attrs[i]->ai_dbi );
    185 			mdb->mi_attrs[i]->ai_dbi = 0;
    186 		}
    187 }
    188 
    189 int
    190 mdb_attr_index_config(
    191 	struct mdb_info	*mdb,
    192 	const char		*fname,
    193 	int			lineno,
    194 	int			argc,
    195 	char		**argv,
    196 	struct		config_reply_s *c_reply)
    197 {
    198 	int rc = 0;
    199 	int	i;
    200 	slap_mask_t mask;
    201 	char **attrs;
    202 	char **indexes = NULL;
    203 
    204 	attrs = ldap_str2charray( argv[0], "," );
    205 
    206 	if( attrs == NULL ) {
    207 		fprintf( stderr, "%s: line %d: "
    208 			"no attributes specified: %s\n",
    209 			fname, lineno, argv[0] );
    210 		return LDAP_PARAM_ERROR;
    211 	}
    212 
    213 	if ( argc > 1 ) {
    214 		indexes = ldap_str2charray( argv[1], "," );
    215 
    216 		if( indexes == NULL ) {
    217 			fprintf( stderr, "%s: line %d: "
    218 				"no indexes specified: %s\n",
    219 				fname, lineno, argv[1] );
    220 			rc = LDAP_PARAM_ERROR;
    221 			goto done;
    222 		}
    223 	}
    224 
    225 	if( indexes == NULL ) {
    226 		mask = mdb->mi_defaultmask;
    227 
    228 	} else {
    229 		mask = 0;
    230 
    231 		for ( i = 0; indexes[i] != NULL; i++ ) {
    232 			slap_mask_t index;
    233 			rc = slap_str2index( indexes[i], &index );
    234 
    235 			if( rc != LDAP_SUCCESS ) {
    236 				if ( c_reply )
    237 				{
    238 					snprintf(c_reply->msg, sizeof(c_reply->msg),
    239 						"index type \"%s\" undefined", indexes[i] );
    240 
    241 					fprintf( stderr, "%s: line %d: %s\n",
    242 						fname, lineno, c_reply->msg );
    243 				}
    244 				rc = LDAP_PARAM_ERROR;
    245 				goto done;
    246 			}
    247 
    248 			mask |= index;
    249 		}
    250 	}
    251 
    252 	if( !mask ) {
    253 		if ( c_reply )
    254 		{
    255 			snprintf(c_reply->msg, sizeof(c_reply->msg),
    256 				"no indexes selected" );
    257 			fprintf( stderr, "%s: line %d: %s\n",
    258 				fname, lineno, c_reply->msg );
    259 		}
    260 		rc = LDAP_PARAM_ERROR;
    261 		goto done;
    262 	}
    263 
    264 	for ( i = 0; attrs[i] != NULL; i++ ) {
    265 		AttrInfo	*a;
    266 		AttributeDescription *ad;
    267 		const char *text;
    268 #ifdef LDAP_COMP_MATCH
    269 		ComponentReference* cr = NULL;
    270 		AttrInfo *a_cr = NULL;
    271 #endif
    272 
    273 		if( strcasecmp( attrs[i], "default" ) == 0 ) {
    274 			mdb->mi_defaultmask |= mask;
    275 			continue;
    276 		}
    277 
    278 #ifdef LDAP_COMP_MATCH
    279 		if ( is_component_reference( attrs[i] ) ) {
    280 			rc = extract_component_reference( attrs[i], &cr );
    281 			if ( rc != LDAP_SUCCESS ) {
    282 				if ( c_reply )
    283 				{
    284 					snprintf(c_reply->msg, sizeof(c_reply->msg),
    285 						"index component reference\"%s\" undefined",
    286 						attrs[i] );
    287 					fprintf( stderr, "%s: line %d: %s\n",
    288 						fname, lineno, c_reply->msg );
    289 				}
    290 				goto done;
    291 			}
    292 			cr->cr_indexmask = mask;
    293 			/*
    294 			 * After extracting a component reference
    295 			 * only the name of a attribute will be remaining
    296 			 */
    297 		} else {
    298 			cr = NULL;
    299 		}
    300 #endif
    301 		ad = NULL;
    302 		rc = slap_str2ad( attrs[i], &ad, &text );
    303 
    304 		if( rc != LDAP_SUCCESS ) {
    305 			if ( c_reply )
    306 			{
    307 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    308 					"index attribute \"%s\" undefined",
    309 					attrs[i] );
    310 
    311 				fprintf( stderr, "%s: line %d: %s\n",
    312 					fname, lineno, c_reply->msg );
    313 			}
    314 			goto done;
    315 		}
    316 
    317 		if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
    318 			if (c_reply) {
    319 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    320 					"index of attribute \"%s\" disallowed", attrs[i] );
    321 				fprintf( stderr, "%s: line %d: %s\n",
    322 					fname, lineno, c_reply->msg );
    323 			}
    324 			rc = LDAP_UNWILLING_TO_PERFORM;
    325 			goto done;
    326 		}
    327 
    328 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
    329 			ad->ad_type->sat_approx
    330 				&& ad->ad_type->sat_approx->smr_indexer
    331 				&& ad->ad_type->sat_approx->smr_filter ) )
    332 		{
    333 			if (c_reply) {
    334 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    335 					"approx index of attribute \"%s\" disallowed", attrs[i] );
    336 				fprintf( stderr, "%s: line %d: %s\n",
    337 					fname, lineno, c_reply->msg );
    338 			}
    339 			rc = LDAP_INAPPROPRIATE_MATCHING;
    340 			goto done;
    341 		}
    342 
    343 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
    344 			ad->ad_type->sat_equality
    345 				&& ad->ad_type->sat_equality->smr_indexer
    346 				&& ad->ad_type->sat_equality->smr_filter ) )
    347 		{
    348 			if (c_reply) {
    349 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    350 					"equality index of attribute \"%s\" disallowed", attrs[i] );
    351 				fprintf( stderr, "%s: line %d: %s\n",
    352 					fname, lineno, c_reply->msg );
    353 			}
    354 			rc = LDAP_INAPPROPRIATE_MATCHING;
    355 			goto done;
    356 		}
    357 
    358 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
    359 			ad->ad_type->sat_substr
    360 				&& ad->ad_type->sat_substr->smr_indexer
    361 				&& ad->ad_type->sat_substr->smr_filter ) )
    362 		{
    363 			if (c_reply) {
    364 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    365 					"substr index of attribute \"%s\" disallowed", attrs[i] );
    366 				fprintf( stderr, "%s: line %d: %s\n",
    367 					fname, lineno, c_reply->msg );
    368 			}
    369 			rc = LDAP_INAPPROPRIATE_MATCHING;
    370 			goto done;
    371 		}
    372 
    373 		Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
    374 			ad->ad_cname.bv_val, mask, 0 );
    375 
    376 		a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
    377 
    378 #ifdef LDAP_COMP_MATCH
    379 		a->ai_cr = NULL;
    380 #endif
    381 		a->ai_cursor = NULL;
    382 		a->ai_flist = NULL;
    383 		a->ai_clist = NULL;
    384 		a->ai_root = NULL;
    385 		a->ai_desc = ad;
    386 		a->ai_dbi = 0;
    387 
    388 		if ( mdb->mi_flags & MDB_IS_OPEN ) {
    389 			a->ai_indexmask = 0;
    390 			a->ai_newmask = mask;
    391 		} else {
    392 			a->ai_indexmask = mask;
    393 			a->ai_newmask = 0;
    394 		}
    395 
    396 #ifdef LDAP_COMP_MATCH
    397 		if ( cr ) {
    398 			a_cr = mdb_attr_mask( mdb, ad );
    399 			if ( a_cr ) {
    400 				/*
    401 				 * AttrInfo is already in AVL
    402 				 * just add the extracted component reference
    403 				 * in the AttrInfo
    404 				 */
    405 				rc = insert_component_reference( cr, &a_cr->ai_cr );
    406 				if ( rc != LDAP_SUCCESS) {
    407 					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
    408 					rc = LDAP_PARAM_ERROR;
    409 					goto done;
    410 				}
    411 				continue;
    412 			} else {
    413 				rc = insert_component_reference( cr, &a->ai_cr );
    414 				if ( rc != LDAP_SUCCESS) {
    415 					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
    416 					rc = LDAP_PARAM_ERROR;
    417 					goto done;
    418 				}
    419 			}
    420 		}
    421 #endif
    422 		rc = ainfo_insert( mdb, a );
    423 		if( rc ) {
    424 			if ( mdb->mi_flags & MDB_IS_OPEN ) {
    425 				AttrInfo *b = mdb_attr_mask( mdb, ad );
    426 				/* If there is already an index defined for this attribute
    427 				 * it must be replaced. Otherwise we end up with multiple
    428 				 * olcIndex values for the same attribute */
    429 				if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
    430 					/* If we were editing this attr, reset it */
    431 					b->ai_indexmask &= ~MDB_INDEX_DELETING;
    432 					/* If this is leftover from a previous add, commit it */
    433 					if ( b->ai_newmask )
    434 						b->ai_indexmask = b->ai_newmask;
    435 					b->ai_newmask = a->ai_newmask;
    436 					ch_free( a );
    437 					rc = 0;
    438 					continue;
    439 				}
    440 			}
    441 			if (c_reply) {
    442 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    443 					"duplicate index definition for attr \"%s\"",
    444 					attrs[i] );
    445 				fprintf( stderr, "%s: line %d: %s\n",
    446 					fname, lineno, c_reply->msg );
    447 			}
    448 
    449 			rc = LDAP_PARAM_ERROR;
    450 			goto done;
    451 		}
    452 	}
    453 
    454 done:
    455 	ldap_charray_free( attrs );
    456 	if ( indexes != NULL ) ldap_charray_free( indexes );
    457 
    458 	return rc;
    459 }
    460 
    461 static int
    462 mdb_attr_index_unparser( void *v1, void *v2 )
    463 {
    464 	AttrInfo *ai = v1;
    465 	BerVarray *bva = v2;
    466 	struct berval bv;
    467 	char *ptr;
    468 
    469 	slap_index2bvlen( ai->ai_indexmask, &bv );
    470 	if ( bv.bv_len ) {
    471 		bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
    472 		ptr = ch_malloc( bv.bv_len+1 );
    473 		bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
    474 		*bv.bv_val++ = ' ';
    475 		slap_index2bv( ai->ai_indexmask, &bv );
    476 		bv.bv_val = ptr;
    477 		ber_bvarray_add( bva, &bv );
    478 	}
    479 	return 0;
    480 }
    481 
    482 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
    483 static AttrInfo aidef = { &addef };
    484 
    485 void
    486 mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
    487 {
    488 	int i;
    489 
    490 	if ( mdb->mi_defaultmask ) {
    491 		aidef.ai_indexmask = mdb->mi_defaultmask;
    492 		mdb_attr_index_unparser( &aidef, bva );
    493 	}
    494 	for ( i=0; i<mdb->mi_nattrs; i++ )
    495 		mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
    496 }
    497 
    498 void
    499 mdb_attr_info_free( AttrInfo *ai )
    500 {
    501 #ifdef LDAP_COMP_MATCH
    502 	free( ai->ai_cr );
    503 #endif
    504 	free( ai );
    505 }
    506 
    507 void
    508 mdb_attr_index_destroy( struct mdb_info *mdb )
    509 {
    510 	int i;
    511 
    512 	for ( i=0; i<mdb->mi_nattrs; i++ )
    513 		mdb_attr_info_free( mdb->mi_attrs[i] );
    514 
    515 	free( mdb->mi_attrs );
    516 }
    517 
    518 void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
    519 {
    520 	int i;
    521 
    522 	i = mdb_attr_slot( mdb, ad, NULL );
    523 	if ( i >= 0 ) {
    524 		mdb_attr_info_free( mdb->mi_attrs[i] );
    525 		mdb->mi_nattrs--;
    526 		for (; i<mdb->mi_nattrs; i++)
    527 			mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
    528 	}
    529 }
    530 
    531 void mdb_attr_flush( struct mdb_info *mdb )
    532 {
    533 	int i;
    534 
    535 	for ( i=0; i<mdb->mi_nattrs; i++ ) {
    536 		if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
    537 			int j;
    538 			mdb_attr_info_free( mdb->mi_attrs[i] );
    539 			mdb->mi_nattrs--;
    540 			for (j=i; j<mdb->mi_nattrs; j++)
    541 				mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
    542 			i--;
    543 		}
    544 	}
    545 }
    546 
    547 int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn )
    548 {
    549 	int i, rc;
    550 	MDB_cursor *mc;
    551 	MDB_val key, data;
    552 	struct berval bdata;
    553 	const char *text;
    554 	AttributeDescription *ad;
    555 
    556 	rc = mdb_cursor_open( txn, mdb->mi_ad2id, &mc );
    557 	if ( rc ) {
    558 		Debug( LDAP_DEBUG_ANY,
    559 			"mdb_ad_read: cursor_open failed %s(%d)\n",
    560 			mdb_strerror(rc), rc, 0);
    561 		return rc;
    562 	}
    563 
    564 	/* our array is 1-based, an index of 0 means no data */
    565 	i = mdb->mi_numads+1;
    566 	key.mv_size = sizeof(int);
    567 	key.mv_data = &i;
    568 
    569 	rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
    570 
    571 	while ( rc == MDB_SUCCESS ) {
    572 		bdata.bv_len = data.mv_size;
    573 		bdata.bv_val = data.mv_data;
    574 		ad = NULL;
    575 		rc = slap_bv2ad( &bdata, &ad, &text );
    576 		if ( rc ) {
    577 			rc = slap_bv2undef_ad( &bdata, &mdb->mi_ads[i], &text, 0 );
    578 		} else {
    579 			if ( ad->ad_index >= MDB_MAXADS ) {
    580 				Debug( LDAP_DEBUG_ANY,
    581 					"mdb_adb_read: too many AttributeDescriptions in use\n",
    582 					0, 0, 0 );
    583 				return LDAP_OTHER;
    584 			}
    585 			mdb->mi_adxs[ad->ad_index] = i;
    586 			mdb->mi_ads[i] = ad;
    587 		}
    588 		i++;
    589 		rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
    590 	}
    591 	mdb->mi_numads = i-1;
    592 
    593 done:
    594 	if ( rc == MDB_NOTFOUND )
    595 		rc = 0;
    596 
    597 	mdb_cursor_close( mc );
    598 
    599 	return rc;
    600 }
    601 
    602 int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad )
    603 {
    604 	int i, rc;
    605 	MDB_val key, val;
    606 
    607 	rc = mdb_ad_read( mdb, txn );
    608 	if (rc)
    609 		return rc;
    610 
    611 	if ( mdb->mi_adxs[ad->ad_index] )
    612 		return 0;
    613 
    614 	i = mdb->mi_numads+1;
    615 	key.mv_size = sizeof(int);
    616 	key.mv_data = &i;
    617 	val.mv_size = ad->ad_cname.bv_len;
    618 	val.mv_data = ad->ad_cname.bv_val;
    619 
    620 	rc = mdb_put( txn, mdb->mi_ad2id, &key, &val, 0 );
    621 	if ( rc == MDB_SUCCESS ) {
    622 		mdb->mi_adxs[ad->ad_index] = i;
    623 		mdb->mi_ads[i] = ad;
    624 		mdb->mi_numads++;
    625 	} else {
    626 		Debug( LDAP_DEBUG_ANY,
    627 			"mdb_ad_get: mdb_put failed %s(%d)\n",
    628 			mdb_strerror(rc), rc, 0);
    629 	}
    630 
    631 	return rc;
    632 }
    633