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