Home | History | Annotate | Line # | Download | only in back-mdb
attr.c revision 1.4
      1 /*	$NetBSD: attr.c,v 1.4 2025/09/05 21:16:27 christos 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-2024 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 <sys/cdefs.h>
     20 __RCSID("$NetBSD: attr.c,v 1.4 2025/09/05 21:16:27 christos Exp $");
     21 
     22 #include "portable.h"
     23 
     24 #include <stdio.h>
     25 
     26 #include <ac/socket.h>
     27 #include <ac/string.h>
     28 
     29 #include "slap.h"
     30 #include "back-mdb.h"
     31 #include "slap-config.h"
     32 #include "lutil.h"
     33 
     34 /* Find the ad, return -1 if not found,
     35  * set point for insertion if ins is non-NULL
     36  */
     37 int
     38 mdb_attr_slot( struct mdb_info *mdb, AttributeDescription *ad, int *ins )
     39 {
     40 	unsigned base = 0, cursor = 0;
     41 	unsigned n = mdb->mi_nattrs;
     42 	int val = 0;
     43 
     44 	while ( 0 < n ) {
     45 		unsigned pivot = n >> 1;
     46 		cursor = base + pivot;
     47 
     48 		val = SLAP_PTRCMP( ad, mdb->mi_attrs[cursor]->ai_desc );
     49 		if ( val < 0 ) {
     50 			n = pivot;
     51 		} else if ( val > 0 ) {
     52 			base = cursor + 1;
     53 			n -= pivot + 1;
     54 		} else {
     55 			return cursor;
     56 		}
     57 	}
     58 	if ( ins ) {
     59 		if ( val > 0 )
     60 			++cursor;
     61 		*ins = cursor;
     62 	}
     63 	return -1;
     64 }
     65 
     66 static int
     67 ainfo_insert( struct mdb_info *mdb, AttrInfo *a )
     68 {
     69 	int x;
     70 	int i = mdb_attr_slot( mdb, a->ai_desc, &x );
     71 
     72 	/* Is it a dup? */
     73 	if ( i >= 0 )
     74 		return -1;
     75 
     76 	mdb->mi_attrs = ch_realloc( mdb->mi_attrs, ( mdb->mi_nattrs+1 ) *
     77 		sizeof( AttrInfo * ));
     78 	if ( x < mdb->mi_nattrs )
     79 		AC_MEMCPY( &mdb->mi_attrs[x+1], &mdb->mi_attrs[x],
     80 			( mdb->mi_nattrs - x ) * sizeof( AttrInfo *));
     81 	mdb->mi_attrs[x] = a;
     82 	mdb->mi_nattrs++;
     83 	return 0;
     84 }
     85 
     86 AttrInfo *
     87 mdb_attr_mask(
     88 	struct mdb_info	*mdb,
     89 	AttributeDescription *desc )
     90 {
     91 	int i = mdb_attr_slot( mdb, desc, NULL );
     92 	return i < 0 ? NULL : mdb->mi_attrs[i];
     93 }
     94 
     95 /* Open all un-opened index DB handles */
     96 int
     97 mdb_attr_dbs_open(
     98 	BackendDB *be, MDB_txn *tx0, ConfigReply *cr )
     99 {
    100 	struct mdb_info *mdb = (struct mdb_info *) be->be_private;
    101 	MDB_txn *txn;
    102 	MDB_dbi *dbis = NULL;
    103 	int i, flags;
    104 	int rc;
    105 
    106 	if ( !mdb->mi_nattrs )
    107 		return 0;
    108 
    109 	txn = tx0;
    110 	if ( txn == NULL ) {
    111 		rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
    112 		if ( rc ) {
    113 			snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
    114 				"txn_begin failed: %s (%d).",
    115 				be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    116 			Debug( LDAP_DEBUG_ANY,
    117 				LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
    118 				cr->msg );
    119 			return rc;
    120 		}
    121 		dbis = ch_calloc( 1, mdb->mi_nattrs * sizeof(MDB_dbi) );
    122 	} else {
    123 		rc = 0;
    124 	}
    125 
    126 	flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
    127 	if ( !(slapMode & SLAP_TOOL_READONLY) )
    128 		flags |= MDB_CREATE;
    129 
    130 	for ( i=0; i<mdb->mi_nattrs; i++ ) {
    131 		if ( mdb->mi_attrs[i]->ai_dbi )	/* already open */
    132 			continue;
    133 		if ( !( mdb->mi_attrs[i]->ai_indexmask || mdb->mi_attrs[i]->ai_newmask ))	/* not an index record */
    134 			continue;
    135 		rc = mdb_dbi_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
    136 			flags, &mdb->mi_attrs[i]->ai_dbi );
    137 		if ( rc ) {
    138 			snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
    139 				"mdb_dbi_open(%s) failed: %s (%d).",
    140 				be->be_suffix[0].bv_val,
    141 				mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
    142 				mdb_strerror(rc), rc );
    143 			Debug( LDAP_DEBUG_ANY,
    144 				LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
    145 				cr->msg );
    146 			break;
    147 		}
    148 		/* Remember newly opened DBI handles */
    149 		if ( dbis )
    150 			dbis[i] = mdb->mi_attrs[i]->ai_dbi;
    151 	}
    152 
    153 	/* Only commit if this is our txn */
    154 	if ( tx0 == NULL ) {
    155 		if ( !rc ) {
    156 			rc = mdb_txn_commit( txn );
    157 			if ( rc ) {
    158 				snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
    159 					"txn_commit failed: %s (%d).",
    160 					be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    161 				Debug( LDAP_DEBUG_ANY,
    162 					LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
    163 					cr->msg );
    164 			}
    165 		} else {
    166 			mdb_txn_abort( txn );
    167 		}
    168 		/* Something failed, forget anything we just opened */
    169 		if ( rc ) {
    170 			for ( i=0; i<mdb->mi_nattrs; i++ ) {
    171 				if ( dbis[i] ) {
    172 					mdb->mi_attrs[i]->ai_dbi = 0;
    173 					mdb->mi_attrs[i]->ai_indexmask |= MDB_INDEX_DELETING;
    174 				}
    175 			}
    176 			mdb_attr_flush( mdb );
    177 		}
    178 		ch_free( dbis );
    179 	}
    180 
    181 	return rc;
    182 }
    183 
    184 void
    185 mdb_attr_dbs_close(
    186 	struct mdb_info *mdb
    187 )
    188 {
    189 	int i;
    190 	for ( i=0; i<mdb->mi_nattrs; i++ )
    191 		if ( mdb->mi_attrs[i]->ai_dbi ) {
    192 			mdb_dbi_close( mdb->mi_dbenv, mdb->mi_attrs[i]->ai_dbi );
    193 			mdb->mi_attrs[i]->ai_dbi = 0;
    194 		}
    195 }
    196 
    197 int
    198 mdb_attr_index_config(
    199 	struct mdb_info	*mdb,
    200 	const char		*fname,
    201 	int			lineno,
    202 	int			argc,
    203 	char		**argv,
    204 	struct		config_reply_s *c_reply)
    205 {
    206 	int rc = 0;
    207 	int	i;
    208 	slap_mask_t mask;
    209 	char **attrs;
    210 	char **indexes = NULL;
    211 
    212 	attrs = ldap_str2charray( argv[0], "," );
    213 
    214 	if( attrs == NULL ) {
    215 		fprintf( stderr, "%s: line %d: "
    216 			"no attributes specified: %s\n",
    217 			fname, lineno, argv[0] );
    218 		return LDAP_PARAM_ERROR;
    219 	}
    220 
    221 	if ( argc > 1 ) {
    222 		indexes = ldap_str2charray( argv[1], "," );
    223 
    224 		if( indexes == NULL ) {
    225 			fprintf( stderr, "%s: line %d: "
    226 				"no indexes specified: %s\n",
    227 				fname, lineno, argv[1] );
    228 			rc = LDAP_PARAM_ERROR;
    229 			goto done;
    230 		}
    231 	}
    232 
    233 	if( indexes == NULL ) {
    234 		mask = mdb->mi_defaultmask;
    235 
    236 	} else {
    237 		mask = 0;
    238 
    239 		for ( i = 0; indexes[i] != NULL; i++ ) {
    240 			slap_mask_t index;
    241 			rc = slap_str2index( indexes[i], &index );
    242 
    243 			if( rc != LDAP_SUCCESS ) {
    244 				if ( c_reply )
    245 				{
    246 					snprintf(c_reply->msg, sizeof(c_reply->msg),
    247 						"index type \"%s\" undefined", indexes[i] );
    248 
    249 					fprintf( stderr, "%s: line %d: %s\n",
    250 						fname, lineno, c_reply->msg );
    251 				}
    252 				rc = LDAP_PARAM_ERROR;
    253 				goto done;
    254 			}
    255 
    256 			mask |= index;
    257 		}
    258 	}
    259 
    260 	if( !mask ) {
    261 		if ( c_reply )
    262 		{
    263 			snprintf(c_reply->msg, sizeof(c_reply->msg),
    264 				"no indexes selected" );
    265 			fprintf( stderr, "%s: line %d: %s\n",
    266 				fname, lineno, c_reply->msg );
    267 		}
    268 		rc = LDAP_PARAM_ERROR;
    269 		goto done;
    270 	}
    271 
    272 	for ( i = 0; attrs[i] != NULL; i++ ) {
    273 		AttrInfo	*a;
    274 		AttributeDescription *ad;
    275 		const char *text;
    276 #ifdef LDAP_COMP_MATCH
    277 		ComponentReference* cr = NULL;
    278 		AttrInfo *a_cr = NULL;
    279 #endif
    280 
    281 		if( strcasecmp( attrs[i], "default" ) == 0 ) {
    282 			mdb->mi_defaultmask |= mask;
    283 			continue;
    284 		}
    285 
    286 #ifdef LDAP_COMP_MATCH
    287 		if ( is_component_reference( attrs[i] ) ) {
    288 			rc = extract_component_reference( attrs[i], &cr );
    289 			if ( rc != LDAP_SUCCESS ) {
    290 				if ( c_reply )
    291 				{
    292 					snprintf(c_reply->msg, sizeof(c_reply->msg),
    293 						"index component reference\"%s\" undefined",
    294 						attrs[i] );
    295 					fprintf( stderr, "%s: line %d: %s\n",
    296 						fname, lineno, c_reply->msg );
    297 				}
    298 				goto done;
    299 			}
    300 			cr->cr_indexmask = mask;
    301 			/*
    302 			 * After extracting a component reference
    303 			 * only the name of a attribute will be remaining
    304 			 */
    305 		} else {
    306 			cr = NULL;
    307 		}
    308 #endif
    309 		ad = NULL;
    310 		rc = slap_str2ad( attrs[i], &ad, &text );
    311 
    312 		if( rc != LDAP_SUCCESS ) {
    313 			if ( c_reply )
    314 			{
    315 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    316 					"index attribute \"%s\" undefined",
    317 					attrs[i] );
    318 
    319 				fprintf( stderr, "%s: line %d: %s\n",
    320 					fname, lineno, c_reply->msg );
    321 			}
    322 fail:
    323 #ifdef LDAP_COMP_MATCH
    324 			ch_free( cr );
    325 #endif
    326 			goto done;
    327 		}
    328 
    329 		if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
    330 			if (c_reply) {
    331 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    332 					"index of attribute \"%s\" disallowed", attrs[i] );
    333 				fprintf( stderr, "%s: line %d: %s\n",
    334 					fname, lineno, c_reply->msg );
    335 			}
    336 			rc = LDAP_UNWILLING_TO_PERFORM;
    337 			goto fail;
    338 		}
    339 
    340 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
    341 			ad->ad_type->sat_approx
    342 				&& ad->ad_type->sat_approx->smr_indexer
    343 				&& ad->ad_type->sat_approx->smr_filter ) )
    344 		{
    345 			if (c_reply) {
    346 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    347 					"approx index of attribute \"%s\" disallowed", attrs[i] );
    348 				fprintf( stderr, "%s: line %d: %s\n",
    349 					fname, lineno, c_reply->msg );
    350 			}
    351 			rc = LDAP_INAPPROPRIATE_MATCHING;
    352 			goto fail;
    353 		}
    354 
    355 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
    356 			ad->ad_type->sat_equality
    357 				&& ad->ad_type->sat_equality->smr_indexer
    358 				&& ad->ad_type->sat_equality->smr_filter ) )
    359 		{
    360 			if (c_reply) {
    361 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    362 					"equality index of attribute \"%s\" disallowed", attrs[i] );
    363 				fprintf( stderr, "%s: line %d: %s\n",
    364 					fname, lineno, c_reply->msg );
    365 			}
    366 			rc = LDAP_INAPPROPRIATE_MATCHING;
    367 			goto fail;
    368 		}
    369 
    370 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
    371 			ad->ad_type->sat_substr
    372 				&& ad->ad_type->sat_substr->smr_indexer
    373 				&& ad->ad_type->sat_substr->smr_filter ) )
    374 		{
    375 			if (c_reply) {
    376 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    377 					"substr index of attribute \"%s\" disallowed", attrs[i] );
    378 				fprintf( stderr, "%s: line %d: %s\n",
    379 					fname, lineno, c_reply->msg );
    380 			}
    381 			rc = LDAP_INAPPROPRIATE_MATCHING;
    382 			goto fail;
    383 		}
    384 
    385 		Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
    386 			ad->ad_cname.bv_val, mask );
    387 
    388 		a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
    389 
    390 #ifdef LDAP_COMP_MATCH
    391 		a->ai_cr = NULL;
    392 #endif
    393 		a->ai_cursor = NULL;
    394 		a->ai_root = NULL;
    395 		a->ai_desc = ad;
    396 		a->ai_dbi = 0;
    397 		a->ai_multi_hi = UINT_MAX;
    398 		a->ai_multi_lo = UINT_MAX;
    399 
    400 		if ( mdb->mi_flags & MDB_IS_OPEN ) {
    401 			a->ai_indexmask = 0;
    402 			a->ai_newmask = mask;
    403 		} else {
    404 			a->ai_indexmask = mask;
    405 			a->ai_newmask = 0;
    406 		}
    407 
    408 #ifdef LDAP_COMP_MATCH
    409 		if ( cr ) {
    410 			a_cr = mdb_attr_mask( mdb, ad );
    411 			if ( a_cr ) {
    412 				/*
    413 				 * AttrInfo is already in AVL
    414 				 * just add the extracted component reference
    415 				 * in the AttrInfo
    416 				 */
    417 				ch_free( a );
    418 				rc = insert_component_reference( cr, &a_cr->ai_cr );
    419 				if ( rc != LDAP_SUCCESS) {
    420 					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
    421 					rc = LDAP_PARAM_ERROR;
    422 					goto fail;
    423 				}
    424 				continue;
    425 			} else {
    426 				rc = insert_component_reference( cr, &a->ai_cr );
    427 				if ( rc != LDAP_SUCCESS) {
    428 					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
    429 					rc = LDAP_PARAM_ERROR;
    430 					ch_free( a );
    431 					goto fail;
    432 				}
    433 			}
    434 		}
    435 #endif
    436 		rc = ainfo_insert( mdb, a );
    437 		if( rc ) {
    438 			AttrInfo *b = mdb_attr_mask( mdb, ad );
    439 			/* If this is just a multival record, reuse it for index info */
    440 			if ( !( b->ai_indexmask || b->ai_newmask ) && b->ai_multi_lo < UINT_MAX ) {
    441 				b->ai_indexmask = a->ai_indexmask;
    442 				b->ai_newmask = a->ai_newmask;
    443 				ch_free( a );
    444 				rc = 0;
    445 				continue;
    446 			}
    447 			if ( mdb->mi_flags & MDB_IS_OPEN ) {
    448 				/* If there is already an index defined for this attribute
    449 				 * it must be replaced. Otherwise we end up with multiple
    450 				 * olcIndex values for the same attribute */
    451 				if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
    452 					/* If we were editing this attr, reset it */
    453 					b->ai_indexmask &= ~MDB_INDEX_DELETING;
    454 					/* If this is leftover from a previous add, commit it */
    455 					if ( b->ai_newmask )
    456 						b->ai_indexmask = b->ai_newmask;
    457 					/* If the mask changed, remember it */
    458 					if ( b->ai_indexmask != a->ai_newmask )
    459 						b->ai_newmask = a->ai_newmask;
    460 					else	/* else ignore it */
    461 						b->ai_newmask = 0;
    462 					ch_free( a );
    463 					rc = 0;
    464 					continue;
    465 				}
    466 			}
    467 			if (c_reply) {
    468 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    469 					"duplicate index definition for attr \"%s\"",
    470 					attrs[i] );
    471 				fprintf( stderr, "%s: line %d: %s\n",
    472 					fname, lineno, c_reply->msg );
    473 			}
    474 
    475 			rc = LDAP_PARAM_ERROR;
    476 			goto done;
    477 		}
    478 	}
    479 
    480 done:
    481 	ldap_charray_free( attrs );
    482 	if ( indexes != NULL ) ldap_charray_free( indexes );
    483 
    484 	return rc;
    485 }
    486 
    487 static int
    488 mdb_attr_index_unparser( void *v1, void *v2 )
    489 {
    490 	AttrInfo *ai = v1;
    491 	BerVarray *bva = v2;
    492 	struct berval bv;
    493 	char *ptr;
    494 
    495 	slap_index2bvlen( ai->ai_indexmask, &bv );
    496 	if ( bv.bv_len ) {
    497 		bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
    498 		ptr = ch_malloc( bv.bv_len+1 );
    499 		bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
    500 		*bv.bv_val++ = ' ';
    501 		slap_index2bv( ai->ai_indexmask, &bv );
    502 		bv.bv_val = ptr;
    503 		ber_bvarray_add( bva, &bv );
    504 	}
    505 	return 0;
    506 }
    507 
    508 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
    509 static AttrInfo aidef = { &addef };
    510 
    511 void
    512 mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
    513 {
    514 	int i;
    515 
    516 	if ( mdb->mi_defaultmask ) {
    517 		aidef.ai_indexmask = mdb->mi_defaultmask;
    518 		mdb_attr_index_unparser( &aidef, bva );
    519 	}
    520 	for ( i=0; i<mdb->mi_nattrs; i++ )
    521 		if ( mdb->mi_attrs[i]->ai_indexmask )
    522 			mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
    523 }
    524 
    525 int
    526 mdb_attr_multi_config(
    527 	struct mdb_info	*mdb,
    528 	const char		*fname,
    529 	int			lineno,
    530 	int			argc,
    531 	char		**argv,
    532 	struct		config_reply_s *c_reply)
    533 {
    534 	int rc = 0;
    535 	int	i;
    536 	unsigned hi,lo;
    537 	char **attrs, *next, *s;
    538 
    539 	attrs = ldap_str2charray( argv[0], "," );
    540 
    541 	if( attrs == NULL ) {
    542 		fprintf( stderr, "%s: line %d: "
    543 			"no attributes specified: %s\n",
    544 			fname, lineno, argv[0] );
    545 		return LDAP_PARAM_ERROR;
    546 	}
    547 
    548 	hi = strtoul( argv[1], &next, 10 );
    549 	if ( next == argv[1] || next[0] != ',' )
    550 		goto badval;
    551 	s = next+1;
    552 	lo = strtoul( s, &next, 10 );
    553 	if ( next == s || next[0] != '\0' )
    554 		goto badval;
    555 
    556 	if ( lo > hi ) {
    557 badval:
    558 		snprintf(c_reply->msg, sizeof(c_reply->msg),
    559 			"invalid hi/lo thresholds" );
    560 		fprintf( stderr, "%s: line %d: %s\n",
    561 			fname, lineno, c_reply->msg );
    562 		return LDAP_PARAM_ERROR;
    563 	}
    564 
    565 	for ( i = 0; attrs[i] != NULL; i++ ) {
    566 		AttrInfo	*a;
    567 		AttributeDescription *ad;
    568 		const char *text;
    569 
    570 		if( strcasecmp( attrs[i], "default" ) == 0 ) {
    571 			mdb->mi_multi_hi = hi;
    572 			mdb->mi_multi_lo = lo;
    573 			continue;
    574 		}
    575 
    576 		ad = NULL;
    577 		rc = slap_str2ad( attrs[i], &ad, &text );
    578 
    579 		if( rc != LDAP_SUCCESS ) {
    580 			if ( c_reply )
    581 			{
    582 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    583 					"multival attribute \"%s\" undefined",
    584 					attrs[i] );
    585 
    586 				fprintf( stderr, "%s: line %d: %s\n",
    587 					fname, lineno, c_reply->msg );
    588 			}
    589 fail:
    590 			goto done;
    591 		}
    592 
    593 		a = (AttrInfo *) ch_calloc( 1, sizeof(AttrInfo) );
    594 
    595 		a->ai_desc = ad;
    596 		a->ai_multi_hi = hi;
    597 		a->ai_multi_lo = lo;
    598 
    599 		rc = ainfo_insert( mdb, a );
    600 		if( rc ) {
    601 			AttrInfo *b = mdb_attr_mask( mdb, ad );
    602 			/* If this is just an index record, reuse it for multival info */
    603 			if ( b->ai_multi_lo == UINT_MAX ) {
    604 				b->ai_multi_hi = a->ai_multi_hi;
    605 				b->ai_multi_lo = a->ai_multi_lo;
    606 				ch_free( a );
    607 				rc = 0;
    608 				continue;
    609 			}
    610 			if (c_reply) {
    611 				snprintf(c_reply->msg, sizeof(c_reply->msg),
    612 					"duplicate multival definition for attr \"%s\"",
    613 					attrs[i] );
    614 				fprintf( stderr, "%s: line %d: %s\n",
    615 					fname, lineno, c_reply->msg );
    616 			}
    617 
    618 			rc = LDAP_PARAM_ERROR;
    619 			goto done;
    620 		}
    621 	}
    622 
    623 done:
    624 	ldap_charray_free( attrs );
    625 
    626 	return rc;
    627 }
    628 
    629 static int
    630 mdb_attr_multi_unparser( void *v1, void *v2 )
    631 {
    632 	AttrInfo *ai = v1;
    633 	BerVarray *bva = v2;
    634 	struct berval bv;
    635 	char digbuf[sizeof("4294967296,4294967296")];
    636 	char *ptr;
    637 
    638 	bv.bv_len = snprintf( digbuf, sizeof(digbuf), "%u,%u",
    639 		ai->ai_multi_hi, ai->ai_multi_lo );
    640 	if ( bv.bv_len ) {
    641 		bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
    642 		ptr = ch_malloc( bv.bv_len+1 );
    643 		bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
    644 		*bv.bv_val++ = ' ';
    645 		strcpy(bv.bv_val, digbuf);
    646 		bv.bv_val = ptr;
    647 		ber_bvarray_add( bva, &bv );
    648 	}
    649 	return 0;
    650 }
    651 
    652 void
    653 mdb_attr_multi_unparse( struct mdb_info *mdb, BerVarray *bva )
    654 {
    655 	int i;
    656 
    657 	if ( mdb->mi_multi_hi < UINT_MAX ) {
    658 		aidef.ai_multi_hi = mdb->mi_multi_hi;
    659 		aidef.ai_multi_lo = mdb->mi_multi_lo;
    660 		mdb_attr_multi_unparser( &aidef, bva );
    661 	}
    662 	for ( i=0; i<mdb->mi_nattrs; i++ )
    663 		if ( mdb->mi_attrs[i]->ai_multi_hi < UINT_MAX )
    664 			mdb_attr_multi_unparser( mdb->mi_attrs[i], bva );
    665 }
    666 
    667 void
    668 mdb_attr_multi_thresh( struct mdb_info *mdb, AttributeDescription *ad, unsigned *hi, unsigned *lo )
    669 {
    670 	AttrInfo *ai = mdb_attr_mask( mdb, ad );
    671 	if ( ai && ai->ai_multi_hi < UINT_MAX )
    672 	{
    673 		if ( hi )
    674 			*hi = ai->ai_multi_hi;
    675 		if ( lo )
    676 			*lo = ai->ai_multi_lo;
    677 	} else
    678 	{
    679 		if ( hi )
    680 			*hi = mdb->mi_multi_hi;
    681 		if ( lo )
    682 			*lo = mdb->mi_multi_lo;
    683 	}
    684 }
    685 
    686 void
    687 mdb_attr_info_free( AttrInfo *ai )
    688 {
    689 #ifdef LDAP_COMP_MATCH
    690 	free( ai->ai_cr );
    691 #endif
    692 	free( ai );
    693 }
    694 
    695 void
    696 mdb_attr_index_destroy( struct mdb_info *mdb )
    697 {
    698 	int i;
    699 
    700 	for ( i=0; i<mdb->mi_nattrs; i++ )
    701 		mdb_attr_info_free( mdb->mi_attrs[i] );
    702 
    703 	free( mdb->mi_attrs );
    704 }
    705 
    706 void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
    707 {
    708 	int i;
    709 
    710 	i = mdb_attr_slot( mdb, ad, NULL );
    711 	if ( i >= 0 ) {
    712 		mdb_attr_info_free( mdb->mi_attrs[i] );
    713 		mdb->mi_nattrs--;
    714 		for (; i<mdb->mi_nattrs; i++)
    715 			mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
    716 	}
    717 }
    718 
    719 void mdb_attr_flush( struct mdb_info *mdb )
    720 {
    721 	int i;
    722 
    723 	for ( i=0; i<mdb->mi_nattrs; i++ ) {
    724 		if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
    725 			/* if this is also a multival rec, just clear index */
    726 			if ( mdb->mi_attrs[i]->ai_multi_lo < UINT_MAX ) {
    727 				mdb->mi_attrs[i]->ai_indexmask = 0;
    728 				mdb->mi_attrs[i]->ai_newmask = 0;
    729 			} else {
    730 				int j;
    731 				mdb_attr_info_free( mdb->mi_attrs[i] );
    732 				mdb->mi_nattrs--;
    733 				for (j=i; j<mdb->mi_nattrs; j++)
    734 					mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
    735 				i--;
    736 			}
    737 		}
    738 	}
    739 }
    740 
    741 int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn )
    742 {
    743 	int i, rc;
    744 	MDB_cursor *mc;
    745 	MDB_val key, data;
    746 	struct berval bdata;
    747 	const char *text;
    748 	AttributeDescription *ad;
    749 
    750 	rc = mdb_cursor_open( txn, mdb->mi_ad2id, &mc );
    751 	if ( rc ) {
    752 		Debug( LDAP_DEBUG_ANY,
    753 			"mdb_ad_read: cursor_open failed %s(%d)\n",
    754 			mdb_strerror(rc), rc );
    755 		return rc;
    756 	}
    757 
    758 	/* our array is 1-based, an index of 0 means no data */
    759 	i = mdb->mi_numads+1;
    760 	key.mv_size = sizeof(int);
    761 	key.mv_data = &i;
    762 
    763 	rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
    764 
    765 	while ( rc == MDB_SUCCESS ) {
    766 		bdata.bv_len = data.mv_size;
    767 		bdata.bv_val = data.mv_data;
    768 		ad = NULL;
    769 		rc = slap_bv2ad( &bdata, &ad, &text );
    770 		if ( rc ) {
    771 			rc = slap_bv2undef_ad( &bdata, &mdb->mi_ads[i], &text, 0 );
    772 		} else {
    773 			if ( ad->ad_index >= MDB_MAXADS ) {
    774 				Debug( LDAP_DEBUG_ANY,
    775 					"mdb_adb_read: too many AttributeDescriptions in use\n" );
    776 				return LDAP_OTHER;
    777 			}
    778 			mdb->mi_adxs[ad->ad_index] = i;
    779 			mdb->mi_ads[i] = ad;
    780 		}
    781 		i++;
    782 		rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
    783 	}
    784 	mdb->mi_numads = i-1;
    785 
    786 done:
    787 	if ( rc == MDB_NOTFOUND )
    788 		rc = 0;
    789 
    790 	mdb_cursor_close( mc );
    791 
    792 	return rc;
    793 }
    794 
    795 int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad )
    796 {
    797 	int i, rc;
    798 	MDB_val key, val;
    799 
    800 	rc = mdb_ad_read( mdb, txn );
    801 	if (rc)
    802 		return rc;
    803 
    804 	if ( mdb->mi_adxs[ad->ad_index] )
    805 		return 0;
    806 
    807 	i = mdb->mi_numads+1;
    808 	key.mv_size = sizeof(int);
    809 	key.mv_data = &i;
    810 	val.mv_size = ad->ad_cname.bv_len;
    811 	val.mv_data = ad->ad_cname.bv_val;
    812 
    813 	rc = mdb_put( txn, mdb->mi_ad2id, &key, &val, 0 );
    814 	if ( rc == MDB_SUCCESS ) {
    815 		mdb->mi_adxs[ad->ad_index] = i;
    816 		mdb->mi_ads[i] = ad;
    817 		mdb->mi_numads = i;
    818 	} else {
    819 		Debug( LDAP_DEBUG_ANY,
    820 			"mdb_ad_get: mdb_put failed %s(%d)\n",
    821 			mdb_strerror(rc), rc );
    822 	}
    823 
    824 	return rc;
    825 }
    826 
    827 void mdb_ad_unwind( struct mdb_info *mdb, int prev_ads )
    828 {
    829 	int i;
    830 
    831 	for (i=mdb->mi_numads; i>prev_ads; i--) {
    832 		mdb->mi_adxs[mdb->mi_ads[i]->ad_index] = 0;
    833 		mdb->mi_ads[i] = NULL;
    834 	}
    835 	mdb->mi_numads = i;
    836 }
    837