1 /* $NetBSD: attr.c,v 1.3 2025/09/05 21:16:31 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 "back-wt.h" 25 #include "slap-config.h" 26 #include "lutil.h" 27 28 /* Find the ad, return -1 if not found, 29 * set point for insertion if ins is non-NULL 30 */ 31 int 32 wt_attr_slot( struct wt_info *wi, AttributeDescription *ad, int *ins ) 33 { 34 unsigned base = 0, cursor = 0; 35 unsigned n = wi->wi_nattrs; 36 int val = 0; 37 38 while ( 0 < n ) { 39 unsigned pivot = n >> 1; 40 cursor = base + pivot; 41 42 val = SLAP_PTRCMP( ad, wi->wi_attrs[cursor]->ai_desc ); 43 if ( val < 0 ) { 44 n = pivot; 45 } else if ( val > 0 ) { 46 base = cursor + 1; 47 n -= pivot + 1; 48 } else { 49 return cursor; 50 } 51 } 52 if ( ins ) { 53 if ( val > 0 ) 54 ++cursor; 55 *ins = cursor; 56 } 57 return -1; 58 } 59 60 static int 61 ainfo_insert( struct wt_info *wi, AttrInfo *a ) 62 { 63 int x = INT_MAX; 64 int i = wt_attr_slot( wi, a->ai_desc, &x ); 65 66 /* Is it a dup? */ 67 if ( i >= 0 ) 68 return -1; 69 70 wi->wi_attrs = ch_realloc( wi->wi_attrs, ( wi->wi_nattrs+1 ) * 71 sizeof( AttrInfo * )); 72 if ( x < wi->wi_nattrs ) 73 AC_MEMCPY( &wi->wi_attrs[x+1], &wi->wi_attrs[x], 74 ( wi->wi_nattrs - x ) * sizeof( AttrInfo *)); 75 wi->wi_attrs[x] = a; 76 wi->wi_nattrs++; 77 return 0; 78 } 79 80 AttrInfo * 81 wt_attr_mask( 82 struct wt_info *wi, 83 AttributeDescription *desc ) 84 { 85 int i = wt_attr_slot( wi, desc, NULL ); 86 return i < 0 ? NULL : wi->wi_attrs[i]; 87 } 88 89 int 90 wt_attr_index_config( 91 struct wt_info *wi, 92 const char *fname, 93 int lineno, 94 int argc, 95 char **argv, 96 struct config_reply_s *c_reply) 97 { 98 int rc = 0; 99 int i; 100 slap_mask_t mask; 101 char **attrs; 102 char **indexes = NULL; 103 104 attrs = ldap_str2charray( argv[0], "," ); 105 106 if( attrs == NULL ) { 107 fprintf( stderr, "%s: line %d: " 108 "no attributes specified: %s\n", 109 fname, lineno, argv[0] ); 110 return LDAP_PARAM_ERROR; 111 } 112 113 if ( argc > 1 ) { 114 indexes = ldap_str2charray( argv[1], "," ); 115 116 if( indexes == NULL ) { 117 fprintf( stderr, "%s: line %d: " 118 "no indexes specified: %s\n", 119 fname, lineno, argv[1] ); 120 rc = LDAP_PARAM_ERROR; 121 goto done; 122 } 123 } 124 125 if( indexes == NULL ) { 126 mask = wi->wi_defaultmask; 127 128 } else { 129 mask = 0; 130 131 for ( i = 0; indexes[i] != NULL; i++ ) { 132 slap_mask_t index; 133 134 rc = slap_str2index( indexes[i], &index ); 135 136 if( rc != LDAP_SUCCESS ) { 137 if ( c_reply ) 138 { 139 snprintf(c_reply->msg, sizeof(c_reply->msg), 140 "index type \"%s\" undefined", indexes[i] ); 141 142 fprintf( stderr, "%s: line %d: %s\n", 143 fname, lineno, c_reply->msg ); 144 } 145 rc = LDAP_PARAM_ERROR; 146 goto done; 147 } 148 149 mask |= index; 150 } 151 } 152 153 if( !mask ) { 154 if ( c_reply ) 155 { 156 snprintf(c_reply->msg, sizeof(c_reply->msg), 157 "no indexes selected" ); 158 fprintf( stderr, "%s: line %d: %s\n", 159 fname, lineno, c_reply->msg ); 160 } 161 rc = LDAP_PARAM_ERROR; 162 goto done; 163 } 164 165 for ( i = 0; attrs[i] != NULL; i++ ) { 166 AttrInfo *a; 167 AttributeDescription *ad; 168 const char *text; 169 #ifdef LDAP_COMP_MATCH 170 ComponentReference* cr = NULL; 171 AttrInfo *a_cr = NULL; 172 #endif 173 174 if( strcasecmp( attrs[i], "default" ) == 0 ) { 175 wi->wi_defaultmask |= mask; 176 continue; 177 } 178 179 #ifdef LDAP_COMP_MATCH 180 if ( is_component_reference( attrs[i] ) ) { 181 rc = extract_component_reference( attrs[i], &cr ); 182 if ( rc != LDAP_SUCCESS ) { 183 if ( c_reply ) 184 { 185 snprintf(c_reply->msg, sizeof(c_reply->msg), 186 "index component reference\"%s\" undefined", 187 attrs[i] ); 188 fprintf( stderr, "%s: line %d: %s\n", 189 fname, lineno, c_reply->msg ); 190 } 191 goto done; 192 } 193 cr->cr_indexmask = mask; 194 /* 195 * After extracting a component reference 196 * only the name of a attribute will be remaining 197 */ 198 } else { 199 cr = NULL; 200 } 201 #endif 202 ad = NULL; 203 rc = slap_str2ad( attrs[i], &ad, &text ); 204 205 if( rc != LDAP_SUCCESS ) { 206 if ( c_reply ) 207 { 208 snprintf(c_reply->msg, sizeof(c_reply->msg), 209 "index attribute \"%s\" undefined", 210 attrs[i] ); 211 212 fprintf( stderr, "%s: line %d: %s\n", 213 fname, lineno, c_reply->msg ); 214 } 215 fail: 216 #ifdef LDAP_COMP_MATCH 217 ch_free( cr ); 218 #endif 219 goto done; 220 } 221 222 if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) { 223 if (c_reply) { 224 snprintf(c_reply->msg, sizeof(c_reply->msg), 225 "index of attribute \"%s\" disallowed", attrs[i] ); 226 fprintf( stderr, "%s: line %d: %s\n", 227 fname, lineno, c_reply->msg ); 228 } 229 rc = LDAP_UNWILLING_TO_PERFORM; 230 goto fail; 231 } 232 233 if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !( 234 ad->ad_type->sat_approx 235 && ad->ad_type->sat_approx->smr_indexer 236 && ad->ad_type->sat_approx->smr_filter ) ) 237 { 238 if (c_reply) { 239 snprintf(c_reply->msg, sizeof(c_reply->msg), 240 "approx index of attribute \"%s\" disallowed", attrs[i] ); 241 fprintf( stderr, "%s: line %d: %s\n", 242 fname, lineno, c_reply->msg ); 243 } 244 rc = LDAP_INAPPROPRIATE_MATCHING; 245 goto fail; 246 } 247 248 if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !( 249 ad->ad_type->sat_equality 250 && ad->ad_type->sat_equality->smr_indexer 251 && ad->ad_type->sat_equality->smr_filter ) ) 252 { 253 if (c_reply) { 254 snprintf(c_reply->msg, sizeof(c_reply->msg), 255 "equality index of attribute \"%s\" disallowed", attrs[i] ); 256 fprintf( stderr, "%s: line %d: %s\n", 257 fname, lineno, c_reply->msg ); 258 } 259 rc = LDAP_INAPPROPRIATE_MATCHING; 260 goto fail; 261 } 262 263 if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !( 264 ad->ad_type->sat_substr 265 && ad->ad_type->sat_substr->smr_indexer 266 && ad->ad_type->sat_substr->smr_filter ) ) 267 { 268 if (c_reply) { 269 snprintf(c_reply->msg, sizeof(c_reply->msg), 270 "substr index of attribute \"%s\" disallowed", attrs[i] ); 271 fprintf( stderr, "%s: line %d: %s\n", 272 fname, lineno, c_reply->msg ); 273 } 274 rc = LDAP_INAPPROPRIATE_MATCHING; 275 goto fail; 276 } 277 278 Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n", 279 ad->ad_cname.bv_val, mask ); 280 281 a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) ); 282 283 #ifdef LDAP_COMP_MATCH 284 a->ai_cr = NULL; 285 #endif 286 a->ai_desc = ad; 287 288 if ( wi->wi_flags & WT_IS_OPEN ) { 289 a->ai_indexmask = 0; 290 a->ai_newmask = mask; 291 } else { 292 a->ai_indexmask = mask; 293 a->ai_newmask = 0; 294 } 295 296 #ifdef LDAP_COMP_MATCH 297 if ( cr ) { 298 a_cr = wt_attr_mask( wi, ad ); 299 if ( a_cr ) { 300 /* 301 * AttrInfo is already in AVL 302 * just add the extracted component reference 303 * in the AttrInfo 304 */ 305 ch_free( a ); 306 rc = insert_component_reference( cr, &a_cr->ai_cr ); 307 if ( rc != LDAP_SUCCESS) { 308 fprintf( stderr, " error during inserting component reference in %s ", attrs[i]); 309 rc = LDAP_PARAM_ERROR; 310 goto fail; 311 } 312 continue; 313 } else { 314 rc = insert_component_reference( cr, &a->ai_cr ); 315 if ( rc != LDAP_SUCCESS) { 316 fprintf( stderr, " error during inserting component reference in %s ", attrs[i]); 317 rc = LDAP_PARAM_ERROR; 318 ch_free( a ); 319 goto fail; 320 } 321 } 322 } 323 #endif 324 rc = ainfo_insert( wi, a ); 325 if( rc ) { 326 if ( wi->wi_flags & WT_IS_OPEN ) { 327 AttrInfo *b = wt_attr_mask( wi, ad ); 328 /* If there is already an index defined for this attribute 329 * it must be replaced. Otherwise we end up with multiple 330 * olcIndex values for the same attribute */ 331 if ( b->ai_indexmask & WT_INDEX_DELETING ) { 332 /* If we were editing this attr, reset it */ 333 b->ai_indexmask &= ~WT_INDEX_DELETING; 334 /* If this is leftover from a previous add, commit it */ 335 if ( b->ai_newmask ) 336 b->ai_indexmask = b->ai_newmask; 337 b->ai_newmask = a->ai_newmask; 338 ch_free( a ); 339 rc = 0; 340 continue; 341 } 342 } 343 if (c_reply) { 344 snprintf(c_reply->msg, sizeof(c_reply->msg), 345 "duplicate index definition for attr \"%s\"", 346 attrs[i] ); 347 fprintf( stderr, "%s: line %d: %s\n", 348 fname, lineno, c_reply->msg ); 349 } 350 351 rc = LDAP_PARAM_ERROR; 352 goto done; 353 } 354 } 355 356 done: 357 ldap_charray_free( attrs ); 358 if ( indexes != NULL ) ldap_charray_free( indexes ); 359 360 return rc; 361 } 362 363 static int 364 wt_attr_index_unparser( void *v1, void *v2 ) 365 { 366 AttrInfo *ai = v1; 367 BerVarray *bva = v2; 368 struct berval bv; 369 char *ptr; 370 371 slap_index2bvlen( ai->ai_indexmask, &bv ); 372 if ( bv.bv_len ) { 373 bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1; 374 ptr = ch_malloc( bv.bv_len+1 ); 375 bv.bv_val = lutil_strcopy(ptr, 376 (const char*)ai->ai_desc->ad_cname.bv_val ); 377 *bv.bv_val++ = ' '; 378 slap_index2bv( ai->ai_indexmask, &bv ); 379 bv.bv_val = ptr; 380 ber_bvarray_add( bva, &bv ); 381 } 382 return 0; 383 } 384 385 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") }; 386 static AttrInfo aidef = { &addef }; 387 388 void 389 wt_attr_index_unparse( struct wt_info *wi, BerVarray *bva ) 390 { 391 int i; 392 393 if ( wi->wi_defaultmask ) { 394 aidef.ai_indexmask = wi->wi_defaultmask; 395 wt_attr_index_unparser( &aidef, bva ); 396 } 397 for ( i=0; i<wi->wi_nattrs; i++ ) 398 wt_attr_index_unparser( wi->wi_attrs[i], bva ); 399 } 400 401 void 402 wt_attr_info_free( AttrInfo *ai ) 403 { 404 #ifdef LDAP_COMP_MATCH 405 free( ai->ai_cr ); 406 #endif 407 free( ai ); 408 } 409 410 void 411 wt_attr_index_destroy( struct wt_info *wi ) 412 { 413 int i; 414 415 for ( i=0; i<wi->wi_nattrs; i++ ) 416 wt_attr_info_free( wi->wi_attrs[i] ); 417 418 free( wi->wi_attrs ); 419 } 420 421 /* 422 * Local variables: 423 * indent-tabs-mode: t 424 * tab-width: 4 425 * c-basic-offset: 4 426 * End: 427 */ 428