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