Home | History | Annotate | Line # | Download | only in back-wt
      1 /*	$NetBSD: index.c,v 1.3 2025/09/05 21:16:32 christos Exp $	*/
      2 
      3 /* OpenLDAP WiredTiger backend */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2002-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 /* ACKNOWLEDGEMENTS:
     19  * This work was developed by HAMANO Tsukasa <hamano (at) osstech.co.jp>
     20  * based on back-bdb for inclusion in OpenLDAP Software.
     21  * WiredTiger is a product of MongoDB Inc.
     22  */
     23 
     24 #include <sys/cdefs.h>
     25 __RCSID("$NetBSD: index.c,v 1.3 2025/09/05 21:16:32 christos Exp $");
     26 
     27 #include "portable.h"
     28 
     29 #include <stdio.h>
     30 #include <ac/string.h>
     31 #include "back-wt.h"
     32 #include "slap-config.h"
     33 
     34 static char presence_keyval[] = {0,0};
     35 static struct berval presence_key = BER_BVC(presence_keyval);
     36 
     37 AttrInfo *wt_index_mask(
     38 	Backend *be,
     39 	AttributeDescription *desc,
     40 	struct berval *atname )
     41 {
     42 	AttributeType *at;
     43 	AttrInfo *ai = wt_attr_mask( be->be_private, desc );
     44 
     45 	if( ai ) {
     46 		*atname = desc->ad_cname;
     47 		return ai;
     48 	}
     49 
     50 	/* If there is a tagging option, did we ever index the base
     51 	 * type? If so, check for mask, otherwise it's not there.
     52 	 */
     53 	if( slap_ad_is_tagged( desc ) && desc != desc->ad_type->sat_ad ) {
     54 		/* has tagging option */
     55 		ai = wt_attr_mask( be->be_private, desc->ad_type->sat_ad );
     56 
     57 		if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOTAGS ) ) {
     58 			*atname = desc->ad_type->sat_cname;
     59 			return ai;
     60 		}
     61 	}
     62 
     63 	/* see if supertype defined mask for its subtypes */
     64 	for( at = desc->ad_type; at != NULL ; at = at->sat_sup ) {
     65 		/* If no AD, we've never indexed this type */
     66 		if ( !at->sat_ad ) continue;
     67 
     68 		ai = wt_attr_mask( be->be_private, at->sat_ad );
     69 
     70 		if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOSUBTYPES ) ) {
     71 			*atname = at->sat_cname;
     72 			return ai;
     73 		}
     74 	}
     75 
     76 	return 0;
     77 }
     78 
     79 /* This function is only called when evaluating search filters.
     80  */
     81 int wt_index_param(
     82 	Backend *be,
     83 	AttributeDescription *desc,
     84 	int ftype,
     85 	slap_mask_t *maskp,
     86 	struct berval *prefixp )
     87 {
     88 	AttrInfo *ai;
     89 	slap_mask_t mask, type = 0;
     90 
     91 	ai = wt_index_mask( be, desc, prefixp );
     92 
     93 	if ( !ai ) {
     94 		/* TODO: add monitor */
     95 		return LDAP_INAPPROPRIATE_MATCHING;
     96 	}
     97 	mask = ai->ai_indexmask;
     98 
     99 	switch( ftype ) {
    100 	case LDAP_FILTER_PRESENT:
    101 		type = SLAP_INDEX_PRESENT;
    102 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
    103 			*prefixp = presence_key;
    104 			*maskp = mask;
    105 			return LDAP_SUCCESS;
    106 		}
    107 		break;
    108 
    109 	case LDAP_FILTER_APPROX:
    110 		type = SLAP_INDEX_APPROX;
    111 		if ( desc->ad_type->sat_approx ) {
    112 			if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
    113 				*maskp = mask;
    114 				return LDAP_SUCCESS;
    115 			}
    116 			break;
    117 		}
    118 
    119 		/* Use EQUALITY rule and index for approximate match */
    120 		/* fall thru */
    121 
    122 	case LDAP_FILTER_EQUALITY:
    123 		type = SLAP_INDEX_EQUALITY;
    124 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
    125 			*maskp = mask;
    126 			return LDAP_SUCCESS;
    127 		}
    128 		break;
    129 
    130 	case LDAP_FILTER_SUBSTRINGS:
    131 		type = SLAP_INDEX_SUBSTR;
    132 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
    133 			*maskp = mask;
    134 			return LDAP_SUCCESS;
    135 		}
    136 		break;
    137 
    138 	default:
    139 		return LDAP_OTHER;
    140 	}
    141 
    142 	/* TODO: add monitor index */
    143 	return LDAP_INAPPROPRIATE_MATCHING;
    144 }
    145 
    146 static int indexer(
    147 	Operation *op,
    148 	wt_ctx *wc,
    149 	AttributeDescription *ad,
    150 	struct berval *atname,
    151 	BerVarray vals,
    152 	ID id,
    153 	int opid,
    154 	slap_mask_t mask )
    155 {
    156 	int rc = LDAP_SUCCESS, i;
    157 	struct berval *keys;
    158 	WT_CURSOR *cursor = NULL;
    159 	assert( mask != 0 );
    160 
    161 	cursor = wt_index_open(wc, atname, 1);
    162 	if( !cursor ) {
    163 		Debug( LDAP_DEBUG_ANY,
    164 			   "indexer: open index cursor failed: %s\n",
    165 			   atname->bv_val );
    166 		goto done;
    167 	}
    168 
    169 	if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
    170 		rc = wt_key_change( op->o_bd, cursor, &presence_key, id, opid );
    171 		if( rc ) {
    172 			goto done;
    173 		}
    174 	}
    175 
    176 	if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
    177 		rc = ad->ad_type->sat_equality->smr_indexer(
    178 			LDAP_FILTER_EQUALITY,
    179 			mask,
    180 			ad->ad_type->sat_syntax,
    181 			ad->ad_type->sat_equality,
    182 			atname, vals, &keys, op->o_tmpmemctx );
    183 
    184 		if( rc == LDAP_SUCCESS && keys != NULL ) {
    185 			for( i=0; keys[i].bv_val != NULL; i++ ) {
    186 				rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
    187 				if( rc ) {
    188 					ber_bvarray_free_x( keys, op->o_tmpmemctx );
    189 					goto done;
    190 				}
    191 			}
    192 			ber_bvarray_free_x( keys, op->o_tmpmemctx );
    193 		}
    194 		rc = LDAP_SUCCESS;
    195 	}
    196 
    197 	if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
    198 		rc = ad->ad_type->sat_approx->smr_indexer(
    199 			LDAP_FILTER_APPROX,
    200 			mask,
    201 			ad->ad_type->sat_syntax,
    202 			ad->ad_type->sat_approx,
    203 			atname, vals, &keys, op->o_tmpmemctx );
    204 
    205 		if( rc == LDAP_SUCCESS && keys != NULL ) {
    206 			for( i=0; keys[i].bv_val != NULL; i++ ) {
    207 				rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
    208 				if( rc ) {
    209 					ber_bvarray_free_x( keys, op->o_tmpmemctx );
    210 					goto done;
    211 				}
    212 			}
    213 			ber_bvarray_free_x( keys, op->o_tmpmemctx );
    214 		}
    215 
    216 		rc = LDAP_SUCCESS;
    217 	}
    218 
    219 	if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
    220 		rc = ad->ad_type->sat_substr->smr_indexer(
    221 			LDAP_FILTER_SUBSTRINGS,
    222 			mask,
    223 			ad->ad_type->sat_syntax,
    224 			ad->ad_type->sat_substr,
    225 			atname, vals, &keys, op->o_tmpmemctx );
    226 
    227 		if( rc == LDAP_SUCCESS && keys != NULL ) {
    228 			for( i=0; keys[i].bv_val != NULL; i++ ) {
    229 				rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
    230 				if( rc ) {
    231 					ber_bvarray_free_x( keys, op->o_tmpmemctx );
    232 					goto done;
    233 				}
    234 			}
    235 			ber_bvarray_free_x( keys, op->o_tmpmemctx );
    236 		}
    237 
    238 		rc = LDAP_SUCCESS;
    239 	}
    240 
    241 done:
    242 	cursor->close(cursor);
    243 	return rc;
    244 }
    245 
    246 static int index_at_values(
    247 	Operation *op,
    248 	wt_ctx *wc,
    249 	AttributeDescription *ad,
    250 	AttributeType *type,
    251 	struct berval *tags,
    252 	BerVarray vals,
    253 	ID id,
    254 	int opid )
    255 {
    256 	int rc = LDAP_SUCCESS;
    257 	slap_mask_t mask = 0;
    258 	int ixop = opid;
    259 	AttrInfo *ai = NULL;
    260 
    261 	if ( opid == WT_INDEX_UPDATE_OP )
    262 		ixop = SLAP_INDEX_ADD_OP;
    263 
    264 	if( type->sat_sup ) {
    265 		/* recurse */
    266 		rc = index_at_values( op, wc, NULL,
    267 							  type->sat_sup, tags,
    268 							  vals, id, opid );
    269 
    270 		if( rc ) return rc;
    271 	}
    272 
    273 	/* If this type has no AD, we've never used it before */
    274 	if( type->sat_ad ) {
    275 		ai = wt_attr_mask( op->o_bd->be_private, type->sat_ad );
    276 		if ( ai ) {
    277 			#ifdef LDAP_COMP_MATCH
    278 			/* component indexing */
    279 			if ( ai->ai_cr ) {
    280 				ComponentReference *cr;
    281 				for( cr = ai->ai_cr ; cr ; cr = cr->cr_next ) {
    282 					rc = indexer( op, wc, cr->cr_ad, &type->sat_cname,
    283 								  cr->cr_nvals, id, ixop,
    284 								  cr->cr_indexmask );
    285 				}
    286 			}
    287 			#endif
    288 			ad = type->sat_ad;
    289 			/* If we're updating the index, just set the new bits that aren't
    290              * already in the old mask.
    291              */
    292 			if ( opid == WT_INDEX_UPDATE_OP )
    293 				mask = ai->ai_newmask & ~ai->ai_indexmask;
    294 			else
    295 				/* For regular updates, if there is a newmask use it. Otherwise
    296 				 * just use the old mask.
    297 				 */
    298 				mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
    299 			if( mask ) {
    300 				rc = indexer( op, wc, ad, &type->sat_cname,
    301 							  vals, id, ixop, mask );
    302 				if( rc ) return rc;
    303 			}
    304 		}
    305 	}
    306 
    307 	if( tags->bv_len ) {
    308 		AttributeDescription *desc;
    309 
    310 		desc = ad_find_tags( type, tags );
    311 		if( desc ) {
    312 			ai = wt_attr_mask( op->o_bd->be_private, desc );
    313 
    314 			if( ai ) {
    315 				if ( opid == WT_INDEX_UPDATE_OP )
    316 					mask = ai->ai_newmask & ~ai->ai_indexmask;
    317 				else
    318 					mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
    319 				if ( mask ) {
    320 					rc = indexer( op, wc, desc, &desc->ad_cname,
    321 								  vals, id, ixop, mask );
    322 
    323 					if( rc ) {
    324 						return rc;
    325 					}
    326 				}
    327 			}
    328 		}
    329 	}
    330 
    331 	return LDAP_SUCCESS;
    332 }
    333 
    334 int wt_index_values(
    335 	Operation *op,
    336 	wt_ctx *wc,
    337 	AttributeDescription *desc,
    338 	BerVarray vals,
    339 	ID id,
    340 	int opid )
    341 {
    342 	int rc;
    343 
    344 	/* Never index ID 0 */
    345 	if ( id == 0 )
    346 		return 0;
    347 
    348 	rc = index_at_values( op, wc, desc,
    349 						  desc->ad_type, &desc->ad_tags,
    350 						  vals, id, opid );
    351 
    352 	return rc;
    353 }
    354 
    355 int
    356 wt_index_entry( Operation *op, wt_ctx *wc, int opid, Entry *e )
    357 {
    358 	int rc;
    359 	Attribute *ap = e->e_attrs;
    360 
    361 	if ( e->e_id == 0 )
    362 		return 0;
    363 
    364 	Debug( LDAP_DEBUG_TRACE, "=> index_entry_%s( %ld, \"%s\" )\n",
    365 		   opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
    366 		   (long) e->e_id, e->e_dn ? e->e_dn : "" );
    367 
    368 	for ( ; ap != NULL; ap = ap->a_next ) {
    369 		rc = wt_index_values( op, wc, ap->a_desc,
    370 							  ap->a_nvals, e->e_id, opid );
    371 		if( rc != LDAP_SUCCESS ) {
    372 			Debug( LDAP_DEBUG_TRACE,
    373 				   "<= index_entry_%s( %ld, \"%s\" ) failure\n",
    374 				   opid == SLAP_INDEX_ADD_OP ? "add" : "del",
    375 				   (long) e->e_id, e->e_dn );
    376 			return rc;
    377 		}
    378 	}
    379 
    380 	Debug( LDAP_DEBUG_TRACE, "<= index_entry_%s( %ld, \"%s\" ) success\n",
    381 		   opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
    382 		   (long) e->e_id, e->e_dn ? e->e_dn : "" );
    383 	return 0;
    384 }
    385 
    386 WT_CURSOR *
    387 wt_index_open(wt_ctx *wc, struct berval *name, int create)
    388 {
    389 	WT_CURSOR *cursor = NULL;
    390 	WT_SESSION *session = wc->session;
    391 	char uri[1024];
    392 	int rc;
    393 
    394 	snprintf(uri, sizeof(uri), "table:%s", name->bv_val);
    395 
    396 	rc = session->open_cursor(session, uri, NULL, "overwrite=false", &cursor);
    397 	if (rc == ENOENT && create) {
    398 		rc = session->create(session, uri,
    399 							 "key_format=uQ,"
    400 							 "value_format=x,"
    401 							 "columns=(key, id, none)");
    402 		if( rc ) {
    403 			Debug( LDAP_DEBUG_ANY,
    404 				   "wt_index_open: table \"%s\": "
    405 				   "cannot create index table: %s (%d)\n",
    406 				   uri, wiredtiger_strerror(rc), rc);
    407 			return NULL;
    408 		}
    409 		rc = session->open_cursor(session, uri, NULL,
    410 								  "overwrite=false", &cursor);
    411 	}
    412 	if ( rc ) {
    413 		Debug( LDAP_DEBUG_ANY,
    414 			   "wt_index_open: table \"%s\": "
    415 			   ": open cursor failed: %s (%d)\n",
    416 			   uri, wiredtiger_strerror(rc), rc);
    417 		return NULL;
    418 	}
    419 	return cursor;
    420 }
    421 
    422 /*
    423  * Local variables:
    424  * indent-tabs-mode: t
    425  * tab-width: 4
    426  * c-basic-offset: 4
    427  * End:
    428  */
    429