1 /* $NetBSD: search.c,v 1.1.1.3 2010/12/12 15:23:27 adam Exp $ */ 2 3 /* OpenLDAP: pkg/ldap/servers/slapd/back-sql/search.c,v 1.117.2.13 2010/04/13 20:23:43 kurt Exp */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1999-2010 The OpenLDAP Foundation. 7 * Portions Copyright 1999 Dmitry Kovalev. 8 * Portions Copyright 2002 Pierangelo Masarati. 9 * Portions Copyright 2004 Mark Adamson. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted only as authorized by the OpenLDAP 14 * Public License. 15 * 16 * A copy of this license is available in the file LICENSE in the 17 * top-level directory of the distribution or, alternatively, at 18 * <http://www.OpenLDAP.org/license.html>. 19 */ 20 /* ACKNOWLEDGEMENTS: 21 * This work was initially developed by Dmitry Kovalev for inclusion 22 * by OpenLDAP Software. Additional significant contributors include 23 * Pierangelo Masarati and Mark Adamson. 24 */ 25 26 #include "portable.h" 27 28 #include <stdio.h> 29 #include <sys/types.h> 30 #include "ac/string.h" 31 #include "ac/ctype.h" 32 33 #include "lutil.h" 34 #include "slap.h" 35 #include "proto-sql.h" 36 37 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f ); 38 static int backsql_process_filter_eq( backsql_srch_info *bsi, 39 backsql_at_map_rec *at, 40 int casefold, struct berval *filter_value ); 41 static int backsql_process_filter_like( backsql_srch_info *bsi, 42 backsql_at_map_rec *at, 43 int casefold, struct berval *filter_value ); 44 static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, 45 backsql_at_map_rec *at ); 46 47 /* For LDAP_CONTROL_PAGEDRESULTS, a 32 bit cookie is available to keep track of 48 the state of paged results. The ldap_entries.id and oc_map_id values of the 49 last entry returned are used as the cookie, so 6 bits are used for the OC id 50 and the other 26 for ldap_entries ID number. If your max(oc_map_id) is more 51 than 63, you will need to steal more bits from ldap_entries ID number and 52 put them into the OC ID part of the cookie. */ 53 54 /* NOTE: not supported when BACKSQL_ARBITRARY_KEY is defined */ 55 #ifndef BACKSQL_ARBITRARY_KEY 56 #define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F)) 57 #define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6) 58 #define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F) 59 60 static int parse_paged_cookie( Operation *op, SlapReply *rs ); 61 62 static void send_paged_response( 63 Operation *op, 64 SlapReply *rs, 65 ID *lastid ); 66 #endif /* ! BACKSQL_ARBITRARY_KEY */ 67 68 static int 69 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad ) 70 { 71 int n_attrs = 0; 72 AttributeName *an = NULL; 73 74 if ( bsi->bsi_attrs == NULL ) { 75 return 1; 76 } 77 78 /* 79 * clear the list (retrieve all attrs) 80 */ 81 if ( ad == NULL ) { 82 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx ); 83 bsi->bsi_attrs = NULL; 84 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS; 85 return 1; 86 } 87 88 /* strip ';binary' */ 89 if ( slap_ad_is_binary( ad ) ) { 90 ad = ad->ad_type->sat_ad; 91 } 92 93 for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) { 94 an = &bsi->bsi_attrs[ n_attrs ]; 95 96 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): " 97 "attribute \"%s\" is in list\n", 98 an->an_name.bv_val, 0, 0 ); 99 /* 100 * We can live with strcmp because the attribute 101 * list has been normalized before calling be_search 102 */ 103 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) { 104 return 1; 105 } 106 } 107 108 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): " 109 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 ); 110 111 an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs, 112 sizeof( AttributeName ) * ( n_attrs + 2 ), 113 bsi->bsi_op->o_tmpmemctx ); 114 if ( an == NULL ) { 115 return -1; 116 } 117 118 an[ n_attrs ].an_name = ad->ad_cname; 119 an[ n_attrs ].an_desc = ad; 120 BER_BVZERO( &an[ n_attrs + 1 ].an_name ); 121 122 bsi->bsi_attrs = an; 123 124 return 1; 125 } 126 127 /* 128 * Initializes the search structure. 129 * 130 * If get_base_id != 0, the field bsi_base_id is filled 131 * with the entryID of bsi_base_ndn; it must be freed 132 * by backsql_free_entryID() when no longer required. 133 * 134 * NOTE: base must be normalized 135 */ 136 int 137 backsql_init_search( 138 backsql_srch_info *bsi, 139 struct berval *nbase, 140 int scope, 141 time_t stoptime, 142 Filter *filter, 143 SQLHDBC dbh, 144 Operation *op, 145 SlapReply *rs, 146 AttributeName *attrs, 147 unsigned flags ) 148 { 149 backsql_info *bi = (backsql_info *)op->o_bd->be_private; 150 int rc = LDAP_SUCCESS; 151 152 bsi->bsi_base_ndn = nbase; 153 bsi->bsi_use_subtree_shortcut = 0; 154 BER_BVZERO( &bsi->bsi_base_id.eid_dn ); 155 BER_BVZERO( &bsi->bsi_base_id.eid_ndn ); 156 bsi->bsi_scope = scope; 157 bsi->bsi_filter = filter; 158 bsi->bsi_dbh = dbh; 159 bsi->bsi_op = op; 160 bsi->bsi_rs = rs; 161 bsi->bsi_flags = BSQL_SF_NONE; 162 163 bsi->bsi_attrs = NULL; 164 165 if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) { 166 /* 167 * if requested, simply try to fetch all attributes 168 */ 169 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS; 170 171 } else { 172 if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) { 173 bsi->bsi_flags |= BSQL_SF_ALL_USER; 174 175 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) { 176 bsi->bsi_flags |= BSQL_SF_ALL_OPER; 177 } 178 179 if ( attrs == NULL ) { 180 /* NULL means all user attributes */ 181 bsi->bsi_flags |= BSQL_SF_ALL_USER; 182 183 } else { 184 AttributeName *p; 185 int got_oc = 0; 186 187 bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc( 188 sizeof( AttributeName ), 189 bsi->bsi_op->o_tmpmemctx ); 190 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name ); 191 192 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) { 193 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) { 194 /* handle "*" */ 195 bsi->bsi_flags |= BSQL_SF_ALL_USER; 196 197 /* if all attrs are requested, there's 198 * no need to continue */ 199 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) { 200 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 201 bsi->bsi_op->o_tmpmemctx ); 202 bsi->bsi_attrs = NULL; 203 break; 204 } 205 continue; 206 207 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) { 208 /* handle "+" */ 209 bsi->bsi_flags |= BSQL_SF_ALL_OPER; 210 211 /* if all attrs are requested, there's 212 * no need to continue */ 213 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) { 214 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 215 bsi->bsi_op->o_tmpmemctx ); 216 bsi->bsi_attrs = NULL; 217 break; 218 } 219 continue; 220 221 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_no_attrs ) == 0 ) { 222 /* ignore "1.1" */ 223 continue; 224 225 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) { 226 got_oc = 1; 227 } 228 229 backsql_attrlist_add( bsi, p->an_desc ); 230 } 231 232 if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) { 233 /* add objectClass if not present, 234 * because it is required to understand 235 * if an entry is a referral, an alias 236 * or so... */ 237 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass ); 238 } 239 } 240 241 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) { 242 AttributeName *p; 243 244 /* use hints if available */ 245 for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) { 246 if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) { 247 /* handle "*" */ 248 bsi->bsi_flags |= BSQL_SF_ALL_USER; 249 250 /* if all attrs are requested, there's 251 * no need to continue */ 252 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) { 253 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 254 bsi->bsi_op->o_tmpmemctx ); 255 bsi->bsi_attrs = NULL; 256 break; 257 } 258 continue; 259 260 } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) { 261 /* handle "+" */ 262 bsi->bsi_flags |= BSQL_SF_ALL_OPER; 263 264 /* if all attrs are requested, there's 265 * no need to continue */ 266 if ( BSQL_ISF_ALL_ATTRS( bsi ) ) { 267 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 268 bsi->bsi_op->o_tmpmemctx ); 269 bsi->bsi_attrs = NULL; 270 break; 271 } 272 continue; 273 } 274 275 backsql_attrlist_add( bsi, p->an_desc ); 276 } 277 278 } 279 } 280 281 bsi->bsi_id_list = NULL; 282 bsi->bsi_id_listtail = &bsi->bsi_id_list; 283 bsi->bsi_n_candidates = 0; 284 bsi->bsi_stoptime = stoptime; 285 BER_BVZERO( &bsi->bsi_sel.bb_val ); 286 bsi->bsi_sel.bb_len = 0; 287 BER_BVZERO( &bsi->bsi_from.bb_val ); 288 bsi->bsi_from.bb_len = 0; 289 BER_BVZERO( &bsi->bsi_join_where.bb_val ); 290 bsi->bsi_join_where.bb_len = 0; 291 BER_BVZERO( &bsi->bsi_flt_where.bb_val ); 292 bsi->bsi_flt_where.bb_len = 0; 293 bsi->bsi_filter_oc = NULL; 294 295 if ( BACKSQL_IS_GET_ID( flags ) ) { 296 int matched = BACKSQL_IS_MATCHED( flags ); 297 int getentry = BACKSQL_IS_GET_ENTRY( flags ); 298 int gotit = 0; 299 300 assert( op->o_bd->be_private != NULL ); 301 302 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id, 303 matched, 1 ); 304 305 /* the entry is collected either if requested for by getentry 306 * or if get noSuchObject and requested to climb the tree, 307 * so that a matchedDN or a referral can be returned */ 308 if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) { 309 if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) { 310 assert( bsi->bsi_e != NULL ); 311 312 if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) ) 313 { 314 gotit = 1; 315 } 316 317 /* 318 * let's see if it is a referral and, in case, get it 319 */ 320 backsql_attrlist_add( bsi, slap_schema.si_ad_ref ); 321 rc = backsql_id2entry( bsi, &bsi->bsi_base_id ); 322 if ( rc == LDAP_SUCCESS ) { 323 if ( is_entry_referral( bsi->bsi_e ) ) 324 { 325 BerVarray erefs = get_entry_referrals( op, bsi->bsi_e ); 326 if ( erefs ) { 327 rc = rs->sr_err = LDAP_REFERRAL; 328 rs->sr_ref = referral_rewrite( erefs, 329 &bsi->bsi_e->e_nname, 330 &op->o_req_dn, 331 scope ); 332 ber_bvarray_free( erefs ); 333 334 } else { 335 rc = rs->sr_err = LDAP_OTHER; 336 rs->sr_text = "bad referral object"; 337 } 338 339 } else if ( !gotit ) { 340 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT; 341 } 342 } 343 344 } else { 345 rs->sr_err = rc; 346 } 347 } 348 349 if ( gotit && BACKSQL_IS_GET_OC( flags ) ) { 350 bsi->bsi_base_id.eid_oc = backsql_id2oc( bi, 351 bsi->bsi_base_id.eid_oc_id ); 352 if ( bsi->bsi_base_id.eid_oc == NULL ) { 353 /* error? */ 354 backsql_free_entryID( &bsi->bsi_base_id, 1, 355 op->o_tmpmemctx ); 356 rc = rs->sr_err = LDAP_OTHER; 357 } 358 } 359 } 360 361 bsi->bsi_status = rc; 362 363 switch ( rc ) { 364 case LDAP_SUCCESS: 365 case LDAP_REFERRAL: 366 break; 367 368 default: 369 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, 370 bsi->bsi_op->o_tmpmemctx ); 371 break; 372 } 373 374 return rc; 375 } 376 377 static int 378 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op ) 379 { 380 int res; 381 382 if ( !f ) { 383 return 0; 384 } 385 386 backsql_strfcat_x( &bsi->bsi_flt_where, 387 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ ); 388 389 while ( 1 ) { 390 res = backsql_process_filter( bsi, f ); 391 if ( res < 0 ) { 392 /* 393 * TimesTen : If the query has no answers, 394 * don't bother to run the query. 395 */ 396 return -1; 397 } 398 399 f = f->f_next; 400 if ( f == NULL ) { 401 break; 402 } 403 404 switch ( op ) { 405 case LDAP_FILTER_AND: 406 backsql_strfcat_x( &bsi->bsi_flt_where, 407 bsi->bsi_op->o_tmpmemctx, "l", 408 (ber_len_t)STRLENOF( " AND " ), 409 " AND " ); 410 break; 411 412 case LDAP_FILTER_OR: 413 backsql_strfcat_x( &bsi->bsi_flt_where, 414 bsi->bsi_op->o_tmpmemctx, "l", 415 (ber_len_t)STRLENOF( " OR " ), 416 " OR " ); 417 break; 418 } 419 } 420 421 backsql_strfcat_x( &bsi->bsi_flt_where, 422 bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' ); 423 424 return 1; 425 } 426 427 static int 428 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f, 429 backsql_at_map_rec *at ) 430 { 431 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; 432 int i; 433 int casefold = 0; 434 435 if ( !f ) { 436 return 0; 437 } 438 439 /* always uppercase strings by now */ 440 #ifdef BACKSQL_UPPERCASE_FILTER 441 if ( f->f_sub_desc->ad_type->sat_substr && 442 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr, 443 bi->sql_caseIgnoreMatch ) ) 444 #endif /* BACKSQL_UPPERCASE_FILTER */ 445 { 446 casefold = 1; 447 } 448 449 if ( f->f_sub_desc->ad_type->sat_substr && 450 SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr, 451 bi->sql_telephoneNumberMatch ) ) 452 { 453 454 struct berval bv; 455 ber_len_t i, s, a; 456 457 /* 458 * to check for matching telephone numbers 459 * with intermixed chars, e.g. val='1234' 460 * use 461 * 462 * val LIKE '%1%2%3%4%' 463 */ 464 465 BER_BVZERO( &bv ); 466 if ( f->f_sub_initial.bv_val ) { 467 bv.bv_len += f->f_sub_initial.bv_len; 468 } 469 if ( f->f_sub_any != NULL ) { 470 for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) { 471 bv.bv_len += f->f_sub_any[ a ].bv_len; 472 } 473 } 474 if ( f->f_sub_final.bv_val ) { 475 bv.bv_len += f->f_sub_final.bv_len; 476 } 477 bv.bv_len = 2 * bv.bv_len - 1; 478 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 479 480 s = 0; 481 if ( !BER_BVISNULL( &f->f_sub_initial ) ) { 482 bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ]; 483 for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) { 484 bv.bv_val[ s + 2 * i - 1 ] = '%'; 485 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ]; 486 } 487 bv.bv_val[ s + 2 * i - 1 ] = '%'; 488 s += 2 * i; 489 } 490 491 if ( f->f_sub_any != NULL ) { 492 for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) { 493 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ]; 494 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) { 495 bv.bv_val[ s + 2 * i - 1 ] = '%'; 496 bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ]; 497 } 498 bv.bv_val[ s + 2 * i - 1 ] = '%'; 499 s += 2 * i; 500 } 501 } 502 503 if ( !BER_BVISNULL( &f->f_sub_final ) ) { 504 bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ]; 505 for ( i = 1; i < f->f_sub_final.bv_len; i++ ) { 506 bv.bv_val[ s + 2 * i - 1 ] = '%'; 507 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ]; 508 } 509 bv.bv_val[ s + 2 * i - 1 ] = '%'; 510 s += 2 * i; 511 } 512 513 bv.bv_val[ s - 1 ] = '\0'; 514 515 (void)backsql_process_filter_like( bsi, at, casefold, &bv ); 516 ch_free( bv.bv_val ); 517 518 return 1; 519 } 520 521 /* 522 * When dealing with case-sensitive strings 523 * we may omit normalization; however, normalized 524 * SQL filters are more liberal. 525 */ 526 527 backsql_strfcat_x( &bsi->bsi_flt_where, 528 bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ ); 529 530 /* TimesTen */ 531 Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n", 532 at->bam_ad->ad_cname.bv_val, 0, 0 ); 533 Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val, 534 at->bam_sel_expr_u.bv_val ? "' '" : "", 535 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" ); 536 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 537 /* 538 * If a pre-upper-cased version of the column 539 * or a precompiled upper function exists, use it 540 */ 541 backsql_strfcat_x( &bsi->bsi_flt_where, 542 bsi->bsi_op->o_tmpmemctx, 543 "bl", 544 &at->bam_sel_expr_u, 545 (ber_len_t)STRLENOF( " LIKE '" ), 546 " LIKE '" ); 547 548 } else { 549 backsql_strfcat_x( &bsi->bsi_flt_where, 550 bsi->bsi_op->o_tmpmemctx, 551 "bl", 552 &at->bam_sel_expr, 553 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" ); 554 } 555 556 if ( !BER_BVISNULL( &f->f_sub_initial ) ) { 557 ber_len_t start; 558 559 #ifdef BACKSQL_TRACE 560 Debug( LDAP_DEBUG_TRACE, 561 "==>backsql_process_sub_filter(%s): " 562 "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 563 f->f_sub_initial.bv_val, 0 ); 564 #endif /* BACKSQL_TRACE */ 565 566 start = bsi->bsi_flt_where.bb_val.bv_len; 567 backsql_strfcat_x( &bsi->bsi_flt_where, 568 bsi->bsi_op->o_tmpmemctx, 569 "b", 570 &f->f_sub_initial ); 571 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 572 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 573 } 574 } 575 576 backsql_strfcat_x( &bsi->bsi_flt_where, 577 bsi->bsi_op->o_tmpmemctx, 578 "c", '%' ); 579 580 if ( f->f_sub_any != NULL ) { 581 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) { 582 ber_len_t start; 583 584 #ifdef BACKSQL_TRACE 585 Debug( LDAP_DEBUG_TRACE, 586 "==>backsql_process_sub_filter(%s): " 587 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 588 i, f->f_sub_any[ i ].bv_val ); 589 #endif /* BACKSQL_TRACE */ 590 591 start = bsi->bsi_flt_where.bb_val.bv_len; 592 backsql_strfcat_x( &bsi->bsi_flt_where, 593 bsi->bsi_op->o_tmpmemctx, 594 "bc", 595 &f->f_sub_any[ i ], 596 '%' ); 597 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 598 /* 599 * Note: toupper('%') = '%' 600 */ 601 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 602 } 603 } 604 } 605 606 if ( !BER_BVISNULL( &f->f_sub_final ) ) { 607 ber_len_t start; 608 609 #ifdef BACKSQL_TRACE 610 Debug( LDAP_DEBUG_TRACE, 611 "==>backsql_process_sub_filter(%s): " 612 "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 613 f->f_sub_final.bv_val, 0 ); 614 #endif /* BACKSQL_TRACE */ 615 616 start = bsi->bsi_flt_where.bb_val.bv_len; 617 backsql_strfcat_x( &bsi->bsi_flt_where, 618 bsi->bsi_op->o_tmpmemctx, 619 "b", 620 &f->f_sub_final ); 621 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 622 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 623 } 624 } 625 626 backsql_strfcat_x( &bsi->bsi_flt_where, 627 bsi->bsi_op->o_tmpmemctx, 628 "l", 629 (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" ); 630 631 return 1; 632 } 633 634 static int 635 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls ) 636 { 637 if ( BER_BVISNULL( from_tbls ) ) { 638 return LDAP_SUCCESS; 639 } 640 641 if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) { 642 char *start, *end; 643 struct berval tmp; 644 645 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx ); 646 647 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) { 648 if ( end ) { 649 end[0] = '\0'; 650 } 651 652 if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL ) 653 { 654 backsql_strfcat_x( &bsi->bsi_from, 655 bsi->bsi_op->o_tmpmemctx, 656 "cs", ',', start ); 657 } 658 659 if ( end ) { 660 /* in case there are spaces after the comma... */ 661 for ( start = &end[1]; isspace( start[0] ); start++ ); 662 if ( start[0] ) { 663 end = strchr( start, ',' ); 664 } else { 665 start = NULL; 666 } 667 } else { 668 start = NULL; 669 } 670 } 671 672 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx ); 673 674 } else { 675 backsql_strfcat_x( &bsi->bsi_from, 676 bsi->bsi_op->o_tmpmemctx, 677 "b", from_tbls ); 678 } 679 680 return LDAP_SUCCESS; 681 } 682 683 static int 684 backsql_process_filter( backsql_srch_info *bsi, Filter *f ) 685 { 686 backsql_at_map_rec **vat = NULL; 687 AttributeDescription *ad = NULL; 688 unsigned i; 689 int done = 0; 690 int rc = 0; 691 692 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 ); 693 if ( f->f_choice == SLAPD_FILTER_COMPUTED ) { 694 struct berval flt; 695 char *msg = NULL; 696 697 switch ( f->f_result ) { 698 case LDAP_COMPARE_TRUE: 699 BER_BVSTR( &flt, "10=10" ); 700 msg = "TRUE"; 701 break; 702 703 case LDAP_COMPARE_FALSE: 704 BER_BVSTR( &flt, "11=0" ); 705 msg = "FALSE"; 706 break; 707 708 case SLAPD_COMPARE_UNDEFINED: 709 BER_BVSTR( &flt, "12=0" ); 710 msg = "UNDEFINED"; 711 break; 712 713 default: 714 rc = -1; 715 goto done; 716 } 717 718 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): " 719 "filter computed (%s)\n", msg, 0, 0 ); 720 backsql_strfcat_x( &bsi->bsi_flt_where, 721 bsi->bsi_op->o_tmpmemctx, "b", &flt ); 722 rc = 1; 723 goto done; 724 } 725 726 if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) { 727 backsql_strfcat_x( &bsi->bsi_flt_where, 728 bsi->bsi_op->o_tmpmemctx, 729 "l", 730 (ber_len_t)STRLENOF( "1=0" ), "1=0" ); 731 done = 1; 732 rc = 1; 733 goto done; 734 } 735 736 switch( f->f_choice ) { 737 case LDAP_FILTER_OR: 738 rc = backsql_process_filter_list( bsi, f->f_or, 739 LDAP_FILTER_OR ); 740 done = 1; 741 break; 742 743 case LDAP_FILTER_AND: 744 rc = backsql_process_filter_list( bsi, f->f_and, 745 LDAP_FILTER_AND ); 746 done = 1; 747 break; 748 749 case LDAP_FILTER_NOT: 750 backsql_strfcat_x( &bsi->bsi_flt_where, 751 bsi->bsi_op->o_tmpmemctx, 752 "l", 753 (ber_len_t)STRLENOF( "NOT (" /* ) */ ), 754 "NOT (" /* ) */ ); 755 rc = backsql_process_filter( bsi, f->f_not ); 756 backsql_strfcat_x( &bsi->bsi_flt_where, 757 bsi->bsi_op->o_tmpmemctx, 758 "c", /* ( */ ')' ); 759 done = 1; 760 break; 761 762 case LDAP_FILTER_PRESENT: 763 ad = f->f_desc; 764 break; 765 766 case LDAP_FILTER_EXT: 767 ad = f->f_mra->ma_desc; 768 if ( f->f_mr_dnattrs ) { 769 /* 770 * if dn attrs filtering is requested, better return 771 * success and let test_filter() deal with candidate 772 * selection; otherwise we'd need to set conditions 773 * on the contents of the DN, e.g. "SELECT ... FROM 774 * ldap_entries AS attributeName WHERE attributeName.dn 775 * like '%attributeName=value%'" 776 */ 777 backsql_strfcat_x( &bsi->bsi_flt_where, 778 bsi->bsi_op->o_tmpmemctx, 779 "l", 780 (ber_len_t)STRLENOF( "1=1" ), "1=1" ); 781 bsi->bsi_status = LDAP_SUCCESS; 782 rc = 1; 783 goto done; 784 } 785 break; 786 787 default: 788 ad = f->f_av_desc; 789 break; 790 } 791 792 if ( rc == -1 ) { 793 goto done; 794 } 795 796 if ( done ) { 797 rc = 1; 798 goto done; 799 } 800 801 /* 802 * Turn structuralObjectClass into objectClass 803 */ 804 if ( ad == slap_schema.si_ad_objectClass 805 || ad == slap_schema.si_ad_structuralObjectClass ) 806 { 807 /* 808 * If the filter is LDAP_FILTER_PRESENT, then it's done; 809 * otherwise, let's see if we are lucky: filtering 810 * for "structural" objectclass or ancestor... 811 */ 812 switch ( f->f_choice ) { 813 case LDAP_FILTER_EQUALITY: 814 { 815 ObjectClass *oc = oc_bvfind( &f->f_av_value ); 816 817 if ( oc == NULL ) { 818 Debug( LDAP_DEBUG_TRACE, 819 "backsql_process_filter(): " 820 "unknown objectClass \"%s\" " 821 "in filter\n", 822 f->f_av_value.bv_val, 0, 0 ); 823 bsi->bsi_status = LDAP_OTHER; 824 rc = -1; 825 goto done; 826 } 827 828 /* 829 * "structural" objectClass inheritance: 830 * - a search for "person" will also return 831 * "inetOrgPerson" 832 * - a search for "top" will return everything 833 */ 834 if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) { 835 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" ); 836 837 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses ); 838 839 backsql_strfcat_x( &bsi->bsi_flt_where, 840 bsi->bsi_op->o_tmpmemctx, 841 "lbl", 842 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ), 843 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */, 844 &bsi->bsi_oc->bom_oc->soc_cname, 845 (ber_len_t)STRLENOF( /* ((' */ "'))" ), 846 /* ((' */ "'))" ); 847 bsi->bsi_status = LDAP_SUCCESS; 848 rc = 1; 849 goto done; 850 } 851 852 break; 853 } 854 855 case LDAP_FILTER_PRESENT: 856 backsql_strfcat_x( &bsi->bsi_flt_where, 857 bsi->bsi_op->o_tmpmemctx, 858 "l", 859 (ber_len_t)STRLENOF( "3=3" ), "3=3" ); 860 bsi->bsi_status = LDAP_SUCCESS; 861 rc = 1; 862 goto done; 863 864 /* FIXME: LDAP_FILTER_EXT? */ 865 866 default: 867 Debug( LDAP_DEBUG_TRACE, 868 "backsql_process_filter(): " 869 "illegal/unhandled filter " 870 "on objectClass attribute", 871 0, 0, 0 ); 872 bsi->bsi_status = LDAP_OTHER; 873 rc = -1; 874 goto done; 875 } 876 877 } else if ( ad == slap_schema.si_ad_entryUUID ) { 878 unsigned long oc_id; 879 #ifdef BACKSQL_ARBITRARY_KEY 880 struct berval keyval; 881 #else /* ! BACKSQL_ARBITRARY_KEY */ 882 unsigned long keyval; 883 char keyvalbuf[LDAP_PVT_INTTYPE_CHARS(unsigned long)]; 884 #endif /* ! BACKSQL_ARBITRARY_KEY */ 885 886 switch ( f->f_choice ) { 887 case LDAP_FILTER_EQUALITY: 888 backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval ); 889 890 if ( oc_id != bsi->bsi_oc->bom_id ) { 891 bsi->bsi_status = LDAP_SUCCESS; 892 rc = -1; 893 goto done; 894 } 895 896 #ifdef BACKSQL_ARBITRARY_KEY 897 backsql_strfcat_x( &bsi->bsi_flt_where, 898 bsi->bsi_op->o_tmpmemctx, 899 "bcblbc", 900 &bsi->bsi_oc->bom_keytbl, '.', 901 &bsi->bsi_oc->bom_keycol, 902 STRLENOF( " LIKE '" ), " LIKE '", 903 &keyval, '\'' ); 904 #else /* ! BACKSQL_ARBITRARY_KEY */ 905 snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval ); 906 backsql_strfcat_x( &bsi->bsi_flt_where, 907 bsi->bsi_op->o_tmpmemctx, 908 "bcbcs", 909 &bsi->bsi_oc->bom_keytbl, '.', 910 &bsi->bsi_oc->bom_keycol, '=', keyvalbuf ); 911 #endif /* ! BACKSQL_ARBITRARY_KEY */ 912 break; 913 914 case LDAP_FILTER_PRESENT: 915 backsql_strfcat_x( &bsi->bsi_flt_where, 916 bsi->bsi_op->o_tmpmemctx, 917 "l", 918 (ber_len_t)STRLENOF( "4=4" ), "4=4" ); 919 break; 920 921 default: 922 rc = -1; 923 goto done; 924 } 925 926 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID; 927 rc = 1; 928 goto done; 929 930 #ifdef BACKSQL_SYNCPROV 931 } else if ( ad == slap_schema.si_ad_entryCSN ) { 932 /* 933 * support for syncrepl as producer... 934 */ 935 #if 0 936 if ( !bsi->bsi_op->o_sync ) { 937 /* unsupported at present... */ 938 bsi->bsi_status = LDAP_OTHER; 939 rc = -1; 940 goto done; 941 } 942 #endif 943 944 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID); 945 946 /* if doing a syncrepl, try to return as much as possible, 947 * and always match the filter */ 948 backsql_strfcat_x( &bsi->bsi_flt_where, 949 bsi->bsi_op->o_tmpmemctx, 950 "l", 951 (ber_len_t)STRLENOF( "5=5" ), "5=5" ); 952 953 /* save for later use in operational attributes */ 954 /* FIXME: saves only the first occurrence, because 955 * the filter during updates is written as 956 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))" 957 * so we want our fake entryCSN to match the greatest 958 * value 959 */ 960 if ( bsi->bsi_op->o_private == NULL ) { 961 bsi->bsi_op->o_private = &f->f_av_value; 962 } 963 bsi->bsi_status = LDAP_SUCCESS; 964 965 rc = 1; 966 goto done; 967 #endif /* BACKSQL_SYNCPROV */ 968 969 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) { 970 /* 971 * FIXME: this is not robust; e.g. a filter 972 * '(!(hasSubordinates=TRUE))' fails because 973 * in SQL it would read 'NOT (1=1)' instead 974 * of no condition. 975 * Note however that hasSubordinates is boolean, 976 * so a more appropriate filter would be 977 * '(hasSubordinates=FALSE)' 978 * 979 * A more robust search for hasSubordinates 980 * would * require joining the ldap_entries table 981 * selecting if there are descendants of the 982 * candidate. 983 */ 984 backsql_strfcat_x( &bsi->bsi_flt_where, 985 bsi->bsi_op->o_tmpmemctx, 986 "l", 987 (ber_len_t)STRLENOF( "6=6" ), "6=6" ); 988 if ( ad == slap_schema.si_ad_hasSubordinates ) { 989 /* 990 * instruct candidate selection algorithm 991 * and attribute list to try to detect 992 * if an entry has subordinates 993 */ 994 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE; 995 996 } else { 997 /* 998 * clear attributes to fetch, to require ALL 999 * and try extended match on all attributes 1000 */ 1001 backsql_attrlist_add( bsi, NULL ); 1002 } 1003 rc = 1; 1004 goto done; 1005 } 1006 1007 /* 1008 * attribute inheritance: 1009 */ 1010 if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) { 1011 bsi->bsi_status = LDAP_OTHER; 1012 rc = -1; 1013 goto done; 1014 } 1015 1016 if ( vat == NULL ) { 1017 /* search anyway; other parts of the filter 1018 * may succeeed */ 1019 backsql_strfcat_x( &bsi->bsi_flt_where, 1020 bsi->bsi_op->o_tmpmemctx, 1021 "l", 1022 (ber_len_t)STRLENOF( "7=7" ), "7=7" ); 1023 bsi->bsi_status = LDAP_SUCCESS; 1024 rc = 1; 1025 goto done; 1026 } 1027 1028 /* if required, open extra level of parens */ 1029 done = 0; 1030 if ( vat[0]->bam_next || vat[1] ) { 1031 backsql_strfcat_x( &bsi->bsi_flt_where, 1032 bsi->bsi_op->o_tmpmemctx, 1033 "c", '(' ); 1034 done = 1; 1035 } 1036 1037 i = 0; 1038 next:; 1039 /* apply attr */ 1040 if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) { 1041 return -1; 1042 } 1043 1044 /* if more definitions of the same attr, apply */ 1045 if ( vat[i]->bam_next ) { 1046 backsql_strfcat_x( &bsi->bsi_flt_where, 1047 bsi->bsi_op->o_tmpmemctx, 1048 "l", 1049 STRLENOF( " OR " ), " OR " ); 1050 vat[i] = vat[i]->bam_next; 1051 goto next; 1052 } 1053 1054 /* if more descendants of the same attr, apply */ 1055 i++; 1056 if ( vat[i] ) { 1057 backsql_strfcat_x( &bsi->bsi_flt_where, 1058 bsi->bsi_op->o_tmpmemctx, 1059 "l", 1060 STRLENOF( " OR " ), " OR " ); 1061 goto next; 1062 } 1063 1064 /* if needed, close extra level of parens */ 1065 if ( done ) { 1066 backsql_strfcat_x( &bsi->bsi_flt_where, 1067 bsi->bsi_op->o_tmpmemctx, 1068 "c", ')' ); 1069 } 1070 1071 rc = 1; 1072 1073 done:; 1074 if ( vat ) { 1075 ch_free( vat ); 1076 } 1077 1078 Debug( LDAP_DEBUG_TRACE, 1079 "<==backsql_process_filter() %s\n", 1080 rc == 1 ? "succeeded" : "failed", 0, 0); 1081 1082 return rc; 1083 } 1084 1085 static int 1086 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at, 1087 int casefold, struct berval *filter_value ) 1088 { 1089 /* 1090 * maybe we should check type of at->sel_expr here somehow, 1091 * to know whether upper_func is applicable, but for now 1092 * upper_func stuff is made for Oracle, where UPPER is 1093 * safely applicable to NUMBER etc. 1094 */ 1095 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 1096 ber_len_t start; 1097 1098 backsql_strfcat_x( &bsi->bsi_flt_where, 1099 bsi->bsi_op->o_tmpmemctx, 1100 "cbl", 1101 '(', /* ) */ 1102 &at->bam_sel_expr_u, 1103 (ber_len_t)STRLENOF( "='" ), 1104 "='" ); 1105 1106 start = bsi->bsi_flt_where.bb_val.bv_len; 1107 1108 backsql_strfcat_x( &bsi->bsi_flt_where, 1109 bsi->bsi_op->o_tmpmemctx, 1110 "bl", 1111 filter_value, 1112 (ber_len_t)STRLENOF( /* (' */ "')" ), 1113 /* (' */ "')" ); 1114 1115 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 1116 1117 } else { 1118 backsql_strfcat_x( &bsi->bsi_flt_where, 1119 bsi->bsi_op->o_tmpmemctx, 1120 "cblbl", 1121 '(', /* ) */ 1122 &at->bam_sel_expr, 1123 (ber_len_t)STRLENOF( "='" ), "='", 1124 filter_value, 1125 (ber_len_t)STRLENOF( /* (' */ "')" ), 1126 /* (' */ "')" ); 1127 } 1128 1129 return 1; 1130 } 1131 1132 static int 1133 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at, 1134 int casefold, struct berval *filter_value ) 1135 { 1136 /* 1137 * maybe we should check type of at->sel_expr here somehow, 1138 * to know whether upper_func is applicable, but for now 1139 * upper_func stuff is made for Oracle, where UPPER is 1140 * safely applicable to NUMBER etc. 1141 */ 1142 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 1143 ber_len_t start; 1144 1145 backsql_strfcat_x( &bsi->bsi_flt_where, 1146 bsi->bsi_op->o_tmpmemctx, 1147 "cbl", 1148 '(', /* ) */ 1149 &at->bam_sel_expr_u, 1150 (ber_len_t)STRLENOF( " LIKE '%" ), 1151 " LIKE '%" ); 1152 1153 start = bsi->bsi_flt_where.bb_val.bv_len; 1154 1155 backsql_strfcat_x( &bsi->bsi_flt_where, 1156 bsi->bsi_op->o_tmpmemctx, 1157 "bl", 1158 filter_value, 1159 (ber_len_t)STRLENOF( /* (' */ "%')" ), 1160 /* (' */ "%')" ); 1161 1162 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 1163 1164 } else { 1165 backsql_strfcat_x( &bsi->bsi_flt_where, 1166 bsi->bsi_op->o_tmpmemctx, 1167 "cblbl", 1168 '(', /* ) */ 1169 &at->bam_sel_expr, 1170 (ber_len_t)STRLENOF( " LIKE '%" ), 1171 " LIKE '%", 1172 filter_value, 1173 (ber_len_t)STRLENOF( /* (' */ "%')" ), 1174 /* (' */ "%')" ); 1175 } 1176 1177 return 1; 1178 } 1179 1180 static int 1181 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at ) 1182 { 1183 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; 1184 int casefold = 0; 1185 struct berval *filter_value = NULL; 1186 MatchingRule *matching_rule = NULL; 1187 struct berval ordering = BER_BVC("<="); 1188 1189 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n", 1190 at->bam_ad->ad_cname.bv_val, 0, 0 ); 1191 1192 /* 1193 * need to add this attribute to list of attrs to load, 1194 * so that we can do test_filter() later 1195 */ 1196 backsql_attrlist_add( bsi, at->bam_ad ); 1197 1198 backsql_merge_from_tbls( bsi, &at->bam_from_tbls ); 1199 1200 if ( !BER_BVISNULL( &at->bam_join_where ) 1201 && strstr( bsi->bsi_join_where.bb_val.bv_val, 1202 at->bam_join_where.bv_val ) == NULL ) 1203 { 1204 backsql_strfcat_x( &bsi->bsi_join_where, 1205 bsi->bsi_op->o_tmpmemctx, 1206 "lb", 1207 (ber_len_t)STRLENOF( " AND " ), " AND ", 1208 &at->bam_join_where ); 1209 } 1210 1211 if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) { 1212 backsql_strfcat_x( &bsi->bsi_flt_where, 1213 bsi->bsi_op->o_tmpmemctx, 1214 "l", 1215 (ber_len_t)STRLENOF( "1=0" ), "1=0" ); 1216 return 1; 1217 } 1218 1219 switch ( f->f_choice ) { 1220 case LDAP_FILTER_EQUALITY: 1221 filter_value = &f->f_av_value; 1222 matching_rule = at->bam_ad->ad_type->sat_equality; 1223 1224 goto equality_match; 1225 1226 /* fail over into next case */ 1227 1228 case LDAP_FILTER_EXT: 1229 filter_value = &f->f_mra->ma_value; 1230 matching_rule = f->f_mr_rule; 1231 1232 equality_match:; 1233 /* always uppercase strings by now */ 1234 #ifdef BACKSQL_UPPERCASE_FILTER 1235 if ( SLAP_MR_ASSOCIATED( matching_rule, 1236 bi->sql_caseIgnoreMatch ) ) 1237 #endif /* BACKSQL_UPPERCASE_FILTER */ 1238 { 1239 casefold = 1; 1240 } 1241 1242 /* FIXME: directoryString filtering should use a similar 1243 * approach to deal with non-prettified values like 1244 * " A non prettified value ", by using a LIKE 1245 * filter with all whitespaces collapsed to a single '%' */ 1246 if ( SLAP_MR_ASSOCIATED( matching_rule, 1247 bi->sql_telephoneNumberMatch ) ) 1248 { 1249 struct berval bv; 1250 ber_len_t i; 1251 1252 /* 1253 * to check for matching telephone numbers 1254 * with intermized chars, e.g. val='1234' 1255 * use 1256 * 1257 * val LIKE '%1%2%3%4%' 1258 */ 1259 1260 bv.bv_len = 2 * filter_value->bv_len - 1; 1261 bv.bv_val = ch_malloc( bv.bv_len + 1 ); 1262 1263 bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ]; 1264 for ( i = 1; i < filter_value->bv_len; i++ ) { 1265 bv.bv_val[ 2 * i - 1 ] = '%'; 1266 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ]; 1267 } 1268 bv.bv_val[ 2 * i - 1 ] = '\0'; 1269 1270 (void)backsql_process_filter_like( bsi, at, casefold, &bv ); 1271 ch_free( bv.bv_val ); 1272 1273 break; 1274 } 1275 1276 /* NOTE: this is required by objectClass inheritance 1277 * and auxiliary objectClass use in filters for slightly 1278 * more efficient candidate selection. */ 1279 /* FIXME: a bit too many specializations to deal with 1280 * very specific cases... */ 1281 if ( at->bam_ad == slap_schema.si_ad_objectClass 1282 || at->bam_ad == slap_schema.si_ad_structuralObjectClass ) 1283 { 1284 backsql_strfcat_x( &bsi->bsi_flt_where, 1285 bsi->bsi_op->o_tmpmemctx, 1286 "lbl", 1287 (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ), 1288 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */, 1289 filter_value, 1290 (ber_len_t)STRLENOF( /* (' */ "')" ), 1291 /* (' */ "')" ); 1292 break; 1293 } 1294 1295 /* 1296 * maybe we should check type of at->sel_expr here somehow, 1297 * to know whether upper_func is applicable, but for now 1298 * upper_func stuff is made for Oracle, where UPPER is 1299 * safely applicable to NUMBER etc. 1300 */ 1301 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value ); 1302 break; 1303 1304 case LDAP_FILTER_GE: 1305 ordering.bv_val = ">="; 1306 1307 /* fall thru to next case */ 1308 1309 case LDAP_FILTER_LE: 1310 filter_value = &f->f_av_value; 1311 1312 /* always uppercase strings by now */ 1313 #ifdef BACKSQL_UPPERCASE_FILTER 1314 if ( at->bam_ad->ad_type->sat_ordering && 1315 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering, 1316 bi->sql_caseIgnoreMatch ) ) 1317 #endif /* BACKSQL_UPPERCASE_FILTER */ 1318 { 1319 casefold = 1; 1320 } 1321 1322 /* 1323 * FIXME: should we uppercase the operands? 1324 */ 1325 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) { 1326 ber_len_t start; 1327 1328 backsql_strfcat_x( &bsi->bsi_flt_where, 1329 bsi->bsi_op->o_tmpmemctx, 1330 "cbbc", 1331 '(', /* ) */ 1332 &at->bam_sel_expr_u, 1333 &ordering, 1334 '\'' ); 1335 1336 start = bsi->bsi_flt_where.bb_val.bv_len; 1337 1338 backsql_strfcat_x( &bsi->bsi_flt_where, 1339 bsi->bsi_op->o_tmpmemctx, 1340 "bl", 1341 filter_value, 1342 (ber_len_t)STRLENOF( /* (' */ "')" ), 1343 /* (' */ "')" ); 1344 1345 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] ); 1346 1347 } else { 1348 backsql_strfcat_x( &bsi->bsi_flt_where, 1349 bsi->bsi_op->o_tmpmemctx, 1350 "cbbcbl", 1351 '(' /* ) */ , 1352 &at->bam_sel_expr, 1353 &ordering, 1354 '\'', 1355 &f->f_av_value, 1356 (ber_len_t)STRLENOF( /* (' */ "')" ), 1357 /* ( */ "')" ); 1358 } 1359 break; 1360 1361 case LDAP_FILTER_PRESENT: 1362 backsql_strfcat_x( &bsi->bsi_flt_where, 1363 bsi->bsi_op->o_tmpmemctx, 1364 "lbl", 1365 (ber_len_t)STRLENOF( "NOT (" /* ) */), 1366 "NOT (", /* ) */ 1367 &at->bam_sel_expr, 1368 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ), 1369 /* ( */ " IS NULL)" ); 1370 break; 1371 1372 case LDAP_FILTER_SUBSTRINGS: 1373 backsql_process_sub_filter( bsi, f, at ); 1374 break; 1375 1376 case LDAP_FILTER_APPROX: 1377 /* we do our best */ 1378 1379 /* 1380 * maybe we should check type of at->sel_expr here somehow, 1381 * to know whether upper_func is applicable, but for now 1382 * upper_func stuff is made for Oracle, where UPPER is 1383 * safely applicable to NUMBER etc. 1384 */ 1385 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value ); 1386 break; 1387 1388 default: 1389 /* unhandled filter type; should not happen */ 1390 assert( 0 ); 1391 backsql_strfcat_x( &bsi->bsi_flt_where, 1392 bsi->bsi_op->o_tmpmemctx, 1393 "l", 1394 (ber_len_t)STRLENOF( "8=8" ), "8=8" ); 1395 break; 1396 1397 } 1398 1399 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n", 1400 at->bam_ad->ad_cname.bv_val, 0, 0 ); 1401 1402 return 1; 1403 } 1404 1405 static int 1406 backsql_srch_query( backsql_srch_info *bsi, struct berval *query ) 1407 { 1408 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; 1409 int rc; 1410 1411 assert( query != NULL ); 1412 BER_BVZERO( query ); 1413 1414 bsi->bsi_use_subtree_shortcut = 0; 1415 1416 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 ); 1417 BER_BVZERO( &bsi->bsi_sel.bb_val ); 1418 BER_BVZERO( &bsi->bsi_sel.bb_val ); 1419 bsi->bsi_sel.bb_len = 0; 1420 BER_BVZERO( &bsi->bsi_from.bb_val ); 1421 bsi->bsi_from.bb_len = 0; 1422 BER_BVZERO( &bsi->bsi_join_where.bb_val ); 1423 bsi->bsi_join_where.bb_len = 0; 1424 BER_BVZERO( &bsi->bsi_flt_where.bb_val ); 1425 bsi->bsi_flt_where.bb_len = 0; 1426 1427 backsql_strfcat_x( &bsi->bsi_sel, 1428 bsi->bsi_op->o_tmpmemctx, 1429 "lbcbc", 1430 (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ), 1431 "SELECT DISTINCT ldap_entries.id,", 1432 &bsi->bsi_oc->bom_keytbl, 1433 '.', 1434 &bsi->bsi_oc->bom_keycol, 1435 ',' ); 1436 1437 if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) { 1438 backsql_strfcat_x( &bsi->bsi_sel, 1439 bsi->bsi_op->o_tmpmemctx, 1440 "blbl", 1441 &bi->sql_strcast_func, 1442 (ber_len_t)STRLENOF( "('" /* ') */ ), 1443 "('" /* ') */ , 1444 &bsi->bsi_oc->bom_oc->soc_cname, 1445 (ber_len_t)STRLENOF( /* (' */ "')" ), 1446 /* (' */ "')" ); 1447 } else { 1448 backsql_strfcat_x( &bsi->bsi_sel, 1449 bsi->bsi_op->o_tmpmemctx, 1450 "cbc", 1451 '\'', 1452 &bsi->bsi_oc->bom_oc->soc_cname, 1453 '\'' ); 1454 } 1455 1456 backsql_strfcat_x( &bsi->bsi_sel, 1457 bsi->bsi_op->o_tmpmemctx, 1458 "b", 1459 &bi->sql_dn_oc_aliasing ); 1460 backsql_strfcat_x( &bsi->bsi_from, 1461 bsi->bsi_op->o_tmpmemctx, 1462 "lb", 1463 (ber_len_t)STRLENOF( " FROM ldap_entries," ), 1464 " FROM ldap_entries,", 1465 &bsi->bsi_oc->bom_keytbl ); 1466 1467 backsql_strfcat_x( &bsi->bsi_join_where, 1468 bsi->bsi_op->o_tmpmemctx, 1469 "lbcbl", 1470 (ber_len_t)STRLENOF( " WHERE " ), " WHERE ", 1471 &bsi->bsi_oc->bom_keytbl, 1472 '.', 1473 &bsi->bsi_oc->bom_keycol, 1474 (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ), 1475 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ); 1476 1477 switch ( bsi->bsi_scope ) { 1478 case LDAP_SCOPE_BASE: 1479 if ( BACKSQL_CANUPPERCASE( bi ) ) { 1480 backsql_strfcat_x( &bsi->bsi_join_where, 1481 bsi->bsi_op->o_tmpmemctx, 1482 "bl", 1483 &bi->sql_upper_func, 1484 (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ), 1485 "(ldap_entries.dn)=?" ); 1486 } else { 1487 backsql_strfcat_x( &bsi->bsi_join_where, 1488 bsi->bsi_op->o_tmpmemctx, 1489 "l", 1490 (ber_len_t)STRLENOF( "ldap_entries.dn=?" ), 1491 "ldap_entries.dn=?" ); 1492 } 1493 break; 1494 1495 case BACKSQL_SCOPE_BASE_LIKE: 1496 if ( BACKSQL_CANUPPERCASE( bi ) ) { 1497 backsql_strfcat_x( &bsi->bsi_join_where, 1498 bsi->bsi_op->o_tmpmemctx, 1499 "bl", 1500 &bi->sql_upper_func, 1501 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ), 1502 "(ldap_entries.dn) LIKE ?" ); 1503 } else { 1504 backsql_strfcat_x( &bsi->bsi_join_where, 1505 bsi->bsi_op->o_tmpmemctx, 1506 "l", 1507 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ), 1508 "ldap_entries.dn LIKE ?" ); 1509 } 1510 break; 1511 1512 case LDAP_SCOPE_ONELEVEL: 1513 backsql_strfcat_x( &bsi->bsi_join_where, 1514 bsi->bsi_op->o_tmpmemctx, 1515 "l", 1516 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ), 1517 "ldap_entries.parent=?" ); 1518 break; 1519 1520 case LDAP_SCOPE_SUBORDINATE: 1521 case LDAP_SCOPE_SUBTREE: 1522 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) { 1523 int i; 1524 BackendDB *bd = bsi->bsi_op->o_bd; 1525 1526 assert( bd->be_nsuffix != NULL ); 1527 1528 for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ ) 1529 { 1530 if ( dn_match( &bd->be_nsuffix[ i ], 1531 bsi->bsi_base_ndn ) ) 1532 { 1533 /* pass this to the candidate selection 1534 * routine so that the DN is not bound 1535 * to the select statement */ 1536 bsi->bsi_use_subtree_shortcut = 1; 1537 break; 1538 } 1539 } 1540 } 1541 1542 if ( bsi->bsi_use_subtree_shortcut ) { 1543 /* Skip the base DN filter, as every entry will match it */ 1544 backsql_strfcat_x( &bsi->bsi_join_where, 1545 bsi->bsi_op->o_tmpmemctx, 1546 "l", 1547 (ber_len_t)STRLENOF( "9=9"), "9=9"); 1548 1549 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) { 1550 /* This should always be true... */ 1551 backsql_strfcat_x( &bsi->bsi_join_where, 1552 bsi->bsi_op->o_tmpmemctx, 1553 "b", 1554 &bi->sql_subtree_cond ); 1555 1556 } else if ( BACKSQL_CANUPPERCASE( bi ) ) { 1557 backsql_strfcat_x( &bsi->bsi_join_where, 1558 bsi->bsi_op->o_tmpmemctx, 1559 "bl", 1560 &bi->sql_upper_func, 1561 (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ), 1562 "(ldap_entries.dn) LIKE ?" ); 1563 1564 } else { 1565 backsql_strfcat_x( &bsi->bsi_join_where, 1566 bsi->bsi_op->o_tmpmemctx, 1567 "l", 1568 (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ), 1569 "ldap_entries.dn LIKE ?" ); 1570 } 1571 1572 break; 1573 1574 default: 1575 assert( 0 ); 1576 } 1577 1578 #ifndef BACKSQL_ARBITRARY_KEY 1579 /* If paged results are in effect, ignore low ldap_entries.id numbers */ 1580 if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) { 1581 unsigned long lowid = 0; 1582 1583 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */ 1584 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) ) 1585 { 1586 lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ); 1587 } 1588 1589 if ( lowid ) { 1590 char lowidstring[48]; 1591 int lowidlen; 1592 1593 lowidlen = snprintf( lowidstring, sizeof( lowidstring ), 1594 " AND ldap_entries.id>%lu", lowid ); 1595 backsql_strfcat_x( &bsi->bsi_join_where, 1596 bsi->bsi_op->o_tmpmemctx, 1597 "l", 1598 (ber_len_t)lowidlen, 1599 lowidstring ); 1600 } 1601 } 1602 #endif /* ! BACKSQL_ARBITRARY_KEY */ 1603 1604 rc = backsql_process_filter( bsi, bsi->bsi_filter ); 1605 if ( rc > 0 ) { 1606 struct berbuf bb = BB_NULL; 1607 1608 backsql_strfcat_x( &bb, 1609 bsi->bsi_op->o_tmpmemctx, 1610 "bbblb", 1611 &bsi->bsi_sel.bb_val, 1612 &bsi->bsi_from.bb_val, 1613 &bsi->bsi_join_where.bb_val, 1614 (ber_len_t)STRLENOF( " AND " ), " AND ", 1615 &bsi->bsi_flt_where.bb_val ); 1616 1617 *query = bb.bb_val; 1618 1619 } else if ( rc < 0 ) { 1620 /* 1621 * Indicates that there's no possible way the filter matches 1622 * anything. No need to issue the query 1623 */ 1624 free( query->bv_val ); 1625 BER_BVZERO( query ); 1626 } 1627 1628 bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx ); 1629 BER_BVZERO( &bsi->bsi_sel.bb_val ); 1630 bsi->bsi_sel.bb_len = 0; 1631 bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx ); 1632 BER_BVZERO( &bsi->bsi_from.bb_val ); 1633 bsi->bsi_from.bb_len = 0; 1634 bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx ); 1635 BER_BVZERO( &bsi->bsi_join_where.bb_val ); 1636 bsi->bsi_join_where.bb_len = 0; 1637 bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx ); 1638 BER_BVZERO( &bsi->bsi_flt_where.bb_val ); 1639 bsi->bsi_flt_where.bb_len = 0; 1640 1641 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n", 1642 query->bv_val ? query->bv_val : "NULL", 0, 0 ); 1643 1644 return ( rc <= 0 ? 1 : 0 ); 1645 } 1646 1647 static int 1648 backsql_oc_get_candidates( void *v_oc, void *v_bsi ) 1649 { 1650 backsql_oc_map_rec *oc = v_oc; 1651 backsql_srch_info *bsi = v_bsi; 1652 Operation *op = bsi->bsi_op; 1653 backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private; 1654 struct berval query; 1655 SQLHSTMT sth = SQL_NULL_HSTMT; 1656 RETCODE rc; 1657 int res; 1658 BACKSQL_ROW_NTS row; 1659 int i; 1660 int j; 1661 int n_candidates = bsi->bsi_n_candidates; 1662 1663 /* 1664 * + 1 because we need room for '%'; 1665 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE; 1666 * this makes a subtree 1667 * search for a DN BACKSQL_MAX_DN_LEN long legal 1668 * if it returns that DN only 1669 */ 1670 char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ]; 1671 1672 bsi->bsi_status = LDAP_SUCCESS; 1673 1674 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n", 1675 BACKSQL_OC_NAME( oc ), 0, 0 ); 1676 1677 /* check for abandon */ 1678 if ( op->o_abandon ) { 1679 bsi->bsi_status = SLAPD_ABANDON; 1680 return BACKSQL_AVL_STOP; 1681 } 1682 1683 #ifndef BACKSQL_ARBITRARY_KEY 1684 /* If paged results have already completed this objectClass, skip it */ 1685 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) { 1686 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) ) 1687 { 1688 return BACKSQL_AVL_CONTINUE; 1689 } 1690 } 1691 #endif /* ! BACKSQL_ARBITRARY_KEY */ 1692 1693 if ( bsi->bsi_n_candidates == -1 ) { 1694 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1695 "unchecked limit has been overcome\n", 0, 0, 0 ); 1696 /* should never get here */ 1697 assert( 0 ); 1698 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED; 1699 return BACKSQL_AVL_STOP; 1700 } 1701 1702 bsi->bsi_oc = oc; 1703 res = backsql_srch_query( bsi, &query ); 1704 if ( res ) { 1705 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1706 "error while constructing query for objectclass \"%s\"\n", 1707 oc->bom_oc->soc_cname.bv_val, 0, 0 ); 1708 /* 1709 * FIXME: need to separate errors from legally 1710 * impossible filters 1711 */ 1712 switch ( bsi->bsi_status ) { 1713 case LDAP_SUCCESS: 1714 case LDAP_UNDEFINED_TYPE: 1715 case LDAP_NO_SUCH_OBJECT: 1716 /* we are conservative... */ 1717 default: 1718 bsi->bsi_status = LDAP_SUCCESS; 1719 /* try next */ 1720 return BACKSQL_AVL_CONTINUE; 1721 1722 case LDAP_ADMINLIMIT_EXCEEDED: 1723 case LDAP_OTHER: 1724 /* don't try any more */ 1725 return BACKSQL_AVL_STOP; 1726 } 1727 } 1728 1729 if ( BER_BVISNULL( &query ) ) { 1730 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1731 "could not construct query for objectclass \"%s\"\n", 1732 oc->bom_oc->soc_cname.bv_val, 0, 0 ); 1733 bsi->bsi_status = LDAP_SUCCESS; 1734 return BACKSQL_AVL_CONTINUE; 1735 } 1736 1737 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n", 1738 query.bv_val, 0, 0 ); 1739 1740 rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 ); 1741 bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx ); 1742 BER_BVZERO( &query ); 1743 if ( rc != SQL_SUCCESS ) { 1744 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1745 "error preparing query\n", 0, 0, 0 ); 1746 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc ); 1747 bsi->bsi_status = LDAP_OTHER; 1748 return BACKSQL_AVL_CONTINUE; 1749 } 1750 1751 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 ); 1752 1753 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT, 1754 &bsi->bsi_oc->bom_id ); 1755 if ( rc != SQL_SUCCESS ) { 1756 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1757 "error binding objectclass id parameter\n", 0, 0, 0 ); 1758 bsi->bsi_status = LDAP_OTHER; 1759 return BACKSQL_AVL_CONTINUE; 1760 } 1761 1762 switch ( bsi->bsi_scope ) { 1763 case LDAP_SCOPE_BASE: 1764 case BACKSQL_SCOPE_BASE_LIKE: 1765 /* 1766 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN; 1767 * however this should be handled earlier 1768 */ 1769 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) { 1770 bsi->bsi_status = LDAP_OTHER; 1771 return BACKSQL_AVL_CONTINUE; 1772 } 1773 1774 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val, 1775 bsi->bsi_base_ndn->bv_len + 1 ); 1776 1777 /* uppercase DN only if the stored DN can be uppercased 1778 * for comparison */ 1779 if ( BACKSQL_CANUPPERCASE( bi ) ) { 1780 ldap_pvt_str2upper( tmp_base_ndn ); 1781 } 1782 1783 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n", 1784 tmp_base_ndn, 0, 0 ); 1785 1786 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT, 1787 tmp_base_ndn, BACKSQL_MAX_DN_LEN ); 1788 if ( rc != SQL_SUCCESS ) { 1789 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1790 "error binding base_ndn parameter\n", 0, 0, 0 ); 1791 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 1792 sth, rc ); 1793 bsi->bsi_status = LDAP_OTHER; 1794 return BACKSQL_AVL_CONTINUE; 1795 } 1796 break; 1797 1798 case LDAP_SCOPE_SUBORDINATE: 1799 case LDAP_SCOPE_SUBTREE: 1800 { 1801 /* if short-cutting the search base, 1802 * don't bind any parameter */ 1803 if ( bsi->bsi_use_subtree_shortcut ) { 1804 break; 1805 } 1806 1807 /* 1808 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN; 1809 * however this should be handled earlier 1810 */ 1811 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) { 1812 bsi->bsi_status = LDAP_OTHER; 1813 return BACKSQL_AVL_CONTINUE; 1814 } 1815 1816 /* 1817 * Sets the parameters for the SQL built earlier 1818 * NOTE that all the databases could actually use 1819 * the TimesTen version, which would be cleaner 1820 * and would also eliminate the need for the 1821 * subtree_cond line in the configuration file. 1822 * For now, I'm leaving it the way it is, 1823 * so non-TimesTen databases use the original code. 1824 * But at some point this should get cleaned up. 1825 * 1826 * If "dn" is being used, do a suffix search. 1827 * If "dn_ru" is being used, do a prefix search. 1828 */ 1829 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) { 1830 tmp_base_ndn[ 0 ] = '\0'; 1831 1832 for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1; 1833 j >= 0; i++, j--) { 1834 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ]; 1835 } 1836 1837 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) { 1838 tmp_base_ndn[ i++ ] = ','; 1839 } 1840 1841 tmp_base_ndn[ i ] = '%'; 1842 tmp_base_ndn[ i + 1 ] = '\0'; 1843 1844 } else { 1845 i = 0; 1846 1847 tmp_base_ndn[ i++ ] = '%'; 1848 1849 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) { 1850 tmp_base_ndn[ i++ ] = ','; 1851 } 1852 1853 AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val, 1854 bsi->bsi_base_ndn->bv_len + 1 ); 1855 } 1856 1857 /* uppercase DN only if the stored DN can be uppercased 1858 * for comparison */ 1859 if ( BACKSQL_CANUPPERCASE( bi ) ) { 1860 ldap_pvt_str2upper( tmp_base_ndn ); 1861 } 1862 1863 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) { 1864 Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n", 1865 tmp_base_ndn, 0, 0 ); 1866 } else { 1867 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n", 1868 tmp_base_ndn, 0, 0 ); 1869 } 1870 1871 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT, 1872 tmp_base_ndn, BACKSQL_MAX_DN_LEN ); 1873 if ( rc != SQL_SUCCESS ) { 1874 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1875 "error binding base_ndn parameter (2)\n", 1876 0, 0, 0 ); 1877 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 1878 sth, rc ); 1879 bsi->bsi_status = LDAP_OTHER; 1880 return BACKSQL_AVL_CONTINUE; 1881 } 1882 break; 1883 } 1884 1885 case LDAP_SCOPE_ONELEVEL: 1886 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ); 1887 1888 #ifdef BACKSQL_ARBITRARY_KEY 1889 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n", 1890 bsi->bsi_base_id.eid_id.bv_val, 0, 0 ); 1891 #else /* ! BACKSQL_ARBITRARY_KEY */ 1892 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n", 1893 bsi->bsi_base_id.eid_id, 0, 0 ); 1894 #endif /* ! BACKSQL_ARBITRARY_KEY */ 1895 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT, 1896 &bsi->bsi_base_id.eid_id ); 1897 if ( rc != SQL_SUCCESS ) { 1898 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1899 "error binding base id parameter\n", 0, 0, 0 ); 1900 bsi->bsi_status = LDAP_OTHER; 1901 return BACKSQL_AVL_CONTINUE; 1902 } 1903 break; 1904 } 1905 1906 rc = SQLExecute( sth ); 1907 if ( !BACKSQL_SUCCESS( rc ) ) { 1908 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1909 "error executing query\n", 0, 0, 0 ); 1910 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc ); 1911 SQLFreeStmt( sth, SQL_DROP ); 1912 bsi->bsi_status = LDAP_OTHER; 1913 return BACKSQL_AVL_CONTINUE; 1914 } 1915 1916 backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx ); 1917 rc = SQLFetch( sth ); 1918 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) { 1919 struct berval dn, pdn, ndn; 1920 backsql_entryID *c_id = NULL; 1921 int ret; 1922 1923 ber_str2bv( row.cols[ 3 ], 0, 0, &dn ); 1924 1925 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) { 1926 continue; 1927 } 1928 1929 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx ); 1930 if ( dn.bv_val != row.cols[ 3 ] ) { 1931 free( dn.bv_val ); 1932 } 1933 1934 if ( ret != LDAP_SUCCESS ) { 1935 continue; 1936 } 1937 1938 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) { 1939 goto cleanup; 1940 } 1941 1942 c_id = (backsql_entryID *)op->o_tmpcalloc( 1, 1943 sizeof( backsql_entryID ), op->o_tmpmemctx ); 1944 #ifdef BACKSQL_ARBITRARY_KEY 1945 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id, 1946 op->o_tmpmemctx ); 1947 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval, 1948 op->o_tmpmemctx ); 1949 #else /* ! BACKSQL_ARBITRARY_KEY */ 1950 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) { 1951 goto cleanup; 1952 } 1953 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) { 1954 goto cleanup; 1955 } 1956 #endif /* ! BACKSQL_ARBITRARY_KEY */ 1957 c_id->eid_oc = bsi->bsi_oc; 1958 c_id->eid_oc_id = bsi->bsi_oc->bom_id; 1959 1960 c_id->eid_dn = pdn; 1961 c_id->eid_ndn = ndn; 1962 1963 /* append at end of list ... */ 1964 c_id->eid_next = NULL; 1965 *bsi->bsi_id_listtail = c_id; 1966 bsi->bsi_id_listtail = &c_id->eid_next; 1967 1968 #ifdef BACKSQL_ARBITRARY_KEY 1969 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1970 "added entry id=%s, keyval=%s dn=\"%s\"\n", 1971 c_id->eid_id.bv_val, c_id->eid_keyval.bv_val, 1972 row.cols[ 3 ] ); 1973 #else /* ! BACKSQL_ARBITRARY_KEY */ 1974 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): " 1975 "added entry id=%ld, keyval=%ld dn=\"%s\"\n", 1976 c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] ); 1977 #endif /* ! BACKSQL_ARBITRARY_KEY */ 1978 1979 /* count candidates, for unchecked limit */ 1980 bsi->bsi_n_candidates--; 1981 if ( bsi->bsi_n_candidates == -1 ) { 1982 break; 1983 } 1984 continue; 1985 1986 cleanup:; 1987 if ( !BER_BVISNULL( &pdn ) ) { 1988 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); 1989 } 1990 if ( !BER_BVISNULL( &ndn ) ) { 1991 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 1992 } 1993 if ( c_id != NULL ) { 1994 ch_free( c_id ); 1995 } 1996 } 1997 backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx ); 1998 SQLFreeStmt( sth, SQL_DROP ); 1999 2000 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n", 2001 n_candidates - bsi->bsi_n_candidates, 0, 0 ); 2002 2003 return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE ); 2004 } 2005 2006 int 2007 backsql_search( Operation *op, SlapReply *rs ) 2008 { 2009 backsql_info *bi = (backsql_info *)op->o_bd->be_private; 2010 SQLHDBC dbh = SQL_NULL_HDBC; 2011 int sres; 2012 Entry user_entry = { 0 }, 2013 base_entry = { 0 }; 2014 int manageDSAit = get_manageDSAit( op ); 2015 time_t stoptime = 0; 2016 backsql_srch_info bsi = { 0 }; 2017 backsql_entryID *eid = NULL; 2018 struct berval nbase = BER_BVNULL; 2019 #ifndef BACKSQL_ARBITRARY_KEY 2020 ID lastid = 0; 2021 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2022 2023 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): " 2024 "base=\"%s\", filter=\"%s\", scope=%d,", 2025 op->o_req_ndn.bv_val, 2026 op->ors_filterstr.bv_val, 2027 op->ors_scope ); 2028 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, " 2029 "attributes to load: %s\n", 2030 op->ors_deref, 2031 op->ors_attrsonly, 2032 op->ors_attrs == NULL ? "all" : "custom list" ); 2033 2034 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) { 2035 Debug( LDAP_DEBUG_TRACE, "backsql_search(): " 2036 "search base length (%ld) exceeds max length (%d)\n", 2037 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 ); 2038 /* 2039 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate 2040 * since it is impossible that such a long DN exists 2041 * in the backend 2042 */ 2043 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 2044 send_ldap_result( op, rs ); 2045 return 1; 2046 } 2047 2048 sres = backsql_get_db_conn( op, &dbh ); 2049 if ( sres != LDAP_SUCCESS ) { 2050 Debug( LDAP_DEBUG_TRACE, "backsql_search(): " 2051 "could not get connection handle - exiting\n", 2052 0, 0, 0 ); 2053 rs->sr_err = sres; 2054 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL; 2055 send_ldap_result( op, rs ); 2056 return 1; 2057 } 2058 2059 /* compute it anyway; root does not use it */ 2060 stoptime = op->o_time + op->ors_tlimit; 2061 2062 /* init search */ 2063 bsi.bsi_e = &base_entry; 2064 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn, 2065 op->ors_scope, 2066 stoptime, op->ors_filter, 2067 dbh, op, rs, op->ors_attrs, 2068 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) ); 2069 switch ( rs->sr_err ) { 2070 case LDAP_SUCCESS: 2071 break; 2072 2073 case LDAP_REFERRAL: 2074 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) && 2075 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) ) 2076 { 2077 rs->sr_err = LDAP_SUCCESS; 2078 rs->sr_text = NULL; 2079 rs->sr_matched = NULL; 2080 if ( rs->sr_ref ) { 2081 ber_bvarray_free( rs->sr_ref ); 2082 rs->sr_ref = NULL; 2083 } 2084 break; 2085 } 2086 2087 /* an entry was created; free it */ 2088 entry_clean( bsi.bsi_e ); 2089 2090 /* fall thru */ 2091 2092 default: 2093 if ( !BER_BVISNULL( &base_entry.e_nname ) 2094 && !access_allowed( op, &base_entry, 2095 slap_schema.si_ad_entry, NULL, 2096 ACL_DISCLOSE, NULL ) ) 2097 { 2098 rs->sr_err = LDAP_NO_SUCH_OBJECT; 2099 if ( rs->sr_ref ) { 2100 ber_bvarray_free( rs->sr_ref ); 2101 rs->sr_ref = NULL; 2102 } 2103 rs->sr_matched = NULL; 2104 rs->sr_text = NULL; 2105 } 2106 2107 send_ldap_result( op, rs ); 2108 2109 if ( rs->sr_ref ) { 2110 ber_bvarray_free( rs->sr_ref ); 2111 rs->sr_ref = NULL; 2112 } 2113 2114 if ( !BER_BVISNULL( &base_entry.e_nname ) ) { 2115 entry_clean( &base_entry ); 2116 } 2117 2118 goto done; 2119 } 2120 /* NOTE: __NEW__ "search" access is required 2121 * on searchBase object */ 2122 { 2123 slap_mask_t mask; 2124 2125 if ( get_assert( op ) && 2126 ( test_filter( op, &base_entry, get_assertion( op ) ) 2127 != LDAP_COMPARE_TRUE ) ) 2128 { 2129 rs->sr_err = LDAP_ASSERTION_FAILED; 2130 2131 } 2132 if ( ! access_allowed_mask( op, &base_entry, 2133 slap_schema.si_ad_entry, 2134 NULL, ACL_SEARCH, NULL, &mask ) ) 2135 { 2136 if ( rs->sr_err == LDAP_SUCCESS ) { 2137 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 2138 } 2139 } 2140 2141 if ( rs->sr_err != LDAP_SUCCESS ) { 2142 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) { 2143 rs->sr_err = LDAP_NO_SUCH_OBJECT; 2144 rs->sr_text = NULL; 2145 } 2146 send_ldap_result( op, rs ); 2147 goto done; 2148 } 2149 } 2150 2151 bsi.bsi_e = NULL; 2152 2153 bsi.bsi_n_candidates = 2154 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 : 2155 ( op->ors_limit->lms_s_unchecked == -1 ? -2 : 2156 ( op->ors_limit->lms_s_unchecked ) ) ); 2157 2158 #ifndef BACKSQL_ARBITRARY_KEY 2159 /* If paged results are in effect, check the paging cookie */ 2160 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) { 2161 rs->sr_err = parse_paged_cookie( op, rs ); 2162 if ( rs->sr_err != LDAP_SUCCESS ) { 2163 send_ldap_result( op, rs ); 2164 goto done; 2165 } 2166 } 2167 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2168 2169 switch ( bsi.bsi_scope ) { 2170 case LDAP_SCOPE_BASE: 2171 case BACKSQL_SCOPE_BASE_LIKE: 2172 /* 2173 * probably already found... 2174 */ 2175 bsi.bsi_id_list = &bsi.bsi_base_id; 2176 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next; 2177 break; 2178 2179 case LDAP_SCOPE_SUBTREE: 2180 /* 2181 * if baseObject is defined, and if it is the root 2182 * of the search, add it to the candidate list 2183 */ 2184 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) ) 2185 { 2186 bsi.bsi_id_list = &bsi.bsi_base_id; 2187 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next; 2188 } 2189 2190 /* FALLTHRU */ 2191 default: 2192 2193 /* 2194 * for each objectclass we try to construct query which gets IDs 2195 * of entries matching LDAP query filter and scope (or at least 2196 * candidates), and get the IDs. Do this in ID order for paging. 2197 */ 2198 avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates, 2199 &bsi, BACKSQL_AVL_STOP, AVL_INORDER ); 2200 2201 /* check for abandon */ 2202 if ( op->o_abandon ) { 2203 eid = bsi.bsi_id_list; 2204 rs->sr_err = SLAPD_ABANDON; 2205 goto send_results; 2206 } 2207 } 2208 2209 if ( op->ors_limit != NULL /* isroot == FALSE */ 2210 && op->ors_limit->lms_s_unchecked != -1 2211 && bsi.bsi_n_candidates == -1 ) 2212 { 2213 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 2214 send_ldap_result( op, rs ); 2215 goto done; 2216 } 2217 2218 /* 2219 * now we load candidate entries (only those attributes 2220 * mentioned in attrs and filter), test it against full filter 2221 * and then send to client; don't free entry_id if baseObject... 2222 */ 2223 for ( eid = bsi.bsi_id_list; 2224 eid != NULL; 2225 eid = backsql_free_entryID( 2226 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) ) 2227 { 2228 int rc; 2229 Attribute *a_hasSubordinate = NULL, 2230 *a_entryUUID = NULL, 2231 *a_entryCSN = NULL, 2232 **ap = NULL; 2233 Entry *e = NULL; 2234 2235 /* check for abandon */ 2236 if ( op->o_abandon ) { 2237 rs->sr_err = SLAPD_ABANDON; 2238 goto send_results; 2239 } 2240 2241 /* check time limit */ 2242 if ( op->ors_tlimit != SLAP_NO_LIMIT 2243 && slap_get_time() > stoptime ) 2244 { 2245 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; 2246 rs->sr_ctrls = NULL; 2247 rs->sr_ref = rs->sr_v2ref; 2248 goto send_results; 2249 } 2250 2251 #ifdef BACKSQL_ARBITRARY_KEY 2252 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data " 2253 "for entry id=%s, oc_id=%ld, keyval=%s\n", 2254 eid->eid_id.bv_val, eid->eid_oc_id, 2255 eid->eid_keyval.bv_val ); 2256 #else /* ! BACKSQL_ARBITRARY_KEY */ 2257 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data " 2258 "for entry id=%ld, oc_id=%ld, keyval=%ld\n", 2259 eid->eid_id, eid->eid_oc_id, eid->eid_keyval ); 2260 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2261 2262 /* check scope */ 2263 switch ( op->ors_scope ) { 2264 case LDAP_SCOPE_BASE: 2265 case BACKSQL_SCOPE_BASE_LIKE: 2266 if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) { 2267 goto next_entry2; 2268 } 2269 break; 2270 2271 case LDAP_SCOPE_ONE: 2272 { 2273 struct berval rdn = eid->eid_ndn; 2274 2275 rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," ); 2276 if ( !dnIsOneLevelRDN( &rdn ) ) { 2277 goto next_entry2; 2278 } 2279 /* fall thru */ 2280 } 2281 2282 case LDAP_SCOPE_SUBORDINATE: 2283 /* discard the baseObject entry */ 2284 if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) { 2285 goto next_entry2; 2286 } 2287 /* FALLTHRU */ 2288 case LDAP_SCOPE_SUBTREE: 2289 /* FIXME: this should never fail... */ 2290 if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) { 2291 goto next_entry2; 2292 } 2293 break; 2294 } 2295 2296 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) { 2297 /* don't recollect baseObject... */ 2298 e = bi->sql_baseObject; 2299 2300 } else if ( eid == &bsi.bsi_base_id ) { 2301 /* don't recollect searchBase object... */ 2302 e = &base_entry; 2303 2304 } else { 2305 bsi.bsi_e = &user_entry; 2306 rc = backsql_id2entry( &bsi, eid ); 2307 if ( rc != LDAP_SUCCESS ) { 2308 Debug( LDAP_DEBUG_TRACE, "backsql_search(): " 2309 "error %d in backsql_id2entry() " 2310 "- skipping\n", rc, 0, 0 ); 2311 continue; 2312 } 2313 e = &user_entry; 2314 } 2315 2316 if ( !manageDSAit && 2317 op->ors_scope != LDAP_SCOPE_BASE && 2318 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE && 2319 is_entry_referral( e ) ) 2320 { 2321 BerVarray refs; 2322 2323 refs = get_entry_referrals( op, e ); 2324 if ( !refs ) { 2325 backsql_srch_info bsi2 = { 0 }; 2326 Entry user_entry2 = { 0 }; 2327 2328 /* retry with the full entry... */ 2329 bsi2.bsi_e = &user_entry2; 2330 rc = backsql_init_search( &bsi2, 2331 &e->e_nname, 2332 LDAP_SCOPE_BASE, 2333 (time_t)(-1), NULL, 2334 dbh, op, rs, NULL, 2335 BACKSQL_ISF_GET_ENTRY ); 2336 if ( rc == LDAP_SUCCESS ) { 2337 if ( is_entry_referral( &user_entry2 ) ) 2338 { 2339 refs = get_entry_referrals( op, 2340 &user_entry2 ); 2341 } else { 2342 rs->sr_err = LDAP_OTHER; 2343 } 2344 backsql_entry_clean( op, &user_entry2 ); 2345 } 2346 if ( bsi2.bsi_attrs != NULL ) { 2347 op->o_tmpfree( bsi2.bsi_attrs, 2348 op->o_tmpmemctx ); 2349 } 2350 } 2351 2352 if ( refs ) { 2353 rs->sr_ref = referral_rewrite( refs, 2354 &e->e_name, 2355 &op->o_req_dn, 2356 op->ors_scope ); 2357 ber_bvarray_free( refs ); 2358 } 2359 2360 if ( rs->sr_ref ) { 2361 rs->sr_err = LDAP_REFERRAL; 2362 2363 } else { 2364 rs->sr_text = "bad referral object"; 2365 } 2366 2367 rs->sr_entry = e; 2368 rs->sr_matched = user_entry.e_name.bv_val; 2369 send_search_reference( op, rs ); 2370 2371 ber_bvarray_free( rs->sr_ref ); 2372 rs->sr_ref = NULL; 2373 rs->sr_matched = NULL; 2374 rs->sr_entry = NULL; 2375 if ( rs->sr_err == LDAP_REFERRAL ) { 2376 rs->sr_err = LDAP_SUCCESS; 2377 } 2378 2379 goto next_entry; 2380 } 2381 2382 /* 2383 * We use this flag since we need to parse the filter 2384 * anyway; we should have used the frontend API function 2385 * filter_has_subordinates() 2386 */ 2387 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) { 2388 rc = backsql_has_children( op, dbh, &e->e_nname ); 2389 2390 switch ( rc ) { 2391 case LDAP_COMPARE_TRUE: 2392 case LDAP_COMPARE_FALSE: 2393 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE ); 2394 if ( a_hasSubordinate != NULL ) { 2395 for ( ap = &user_entry.e_attrs; 2396 *ap; 2397 ap = &(*ap)->a_next ); 2398 2399 *ap = a_hasSubordinate; 2400 } 2401 rc = 0; 2402 break; 2403 2404 default: 2405 Debug(LDAP_DEBUG_TRACE, 2406 "backsql_search(): " 2407 "has_children failed( %d)\n", 2408 rc, 0, 0 ); 2409 rc = 1; 2410 goto next_entry; 2411 } 2412 } 2413 2414 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) { 2415 a_entryUUID = backsql_operational_entryUUID( bi, eid ); 2416 if ( a_entryUUID != NULL ) { 2417 if ( ap == NULL ) { 2418 ap = &user_entry.e_attrs; 2419 } 2420 2421 for ( ; *ap; ap = &(*ap)->a_next ); 2422 2423 *ap = a_entryUUID; 2424 } 2425 } 2426 2427 #ifdef BACKSQL_SYNCPROV 2428 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) { 2429 a_entryCSN = backsql_operational_entryCSN( op ); 2430 if ( a_entryCSN != NULL ) { 2431 if ( ap == NULL ) { 2432 ap = &user_entry.e_attrs; 2433 } 2434 2435 for ( ; *ap; ap = &(*ap)->a_next ); 2436 2437 *ap = a_entryCSN; 2438 } 2439 } 2440 #endif /* BACKSQL_SYNCPROV */ 2441 2442 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) 2443 { 2444 #ifndef BACKSQL_ARBITRARY_KEY 2445 /* If paged results are in effect, see if the page limit was exceeded */ 2446 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) { 2447 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) 2448 { 2449 e = NULL; 2450 send_paged_response( op, rs, &lastid ); 2451 goto done; 2452 } 2453 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id ); 2454 } 2455 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2456 rs->sr_attrs = op->ors_attrs; 2457 rs->sr_operational_attrs = NULL; 2458 rs->sr_entry = e; 2459 e->e_private = (void *)eid; 2460 rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0; 2461 /* FIXME: need the whole entry (ITS#3480) */ 2462 rs->sr_err = send_search_entry( op, rs ); 2463 e->e_private = NULL; 2464 rs->sr_entry = NULL; 2465 rs->sr_attrs = NULL; 2466 rs->sr_operational_attrs = NULL; 2467 2468 switch ( rs->sr_err ) { 2469 case LDAP_UNAVAILABLE: 2470 /* 2471 * FIXME: send_search_entry failed; 2472 * better stop 2473 */ 2474 Debug( LDAP_DEBUG_TRACE, "backsql_search(): " 2475 "connection lost\n", 0, 0, 0 ); 2476 goto end_of_search; 2477 2478 case LDAP_SIZELIMIT_EXCEEDED: 2479 goto send_results; 2480 } 2481 } 2482 2483 next_entry:; 2484 if ( e == &user_entry ) { 2485 backsql_entry_clean( op, &user_entry ); 2486 } 2487 2488 next_entry2:; 2489 } 2490 2491 end_of_search:; 2492 if ( rs->sr_nentries > 0 ) { 2493 rs->sr_ref = rs->sr_v2ref; 2494 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS 2495 : LDAP_REFERRAL; 2496 2497 } else { 2498 rs->sr_err = bsi.bsi_status; 2499 } 2500 2501 send_results:; 2502 if ( rs->sr_err != SLAPD_ABANDON ) { 2503 #ifndef BACKSQL_ARBITRARY_KEY 2504 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) { 2505 send_paged_response( op, rs, NULL ); 2506 } else 2507 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2508 { 2509 send_ldap_result( op, rs ); 2510 } 2511 } 2512 2513 /* cleanup in case of abandon */ 2514 for ( ; eid != NULL; 2515 eid = backsql_free_entryID( 2516 eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) ) 2517 ; 2518 2519 backsql_entry_clean( op, &base_entry ); 2520 2521 /* in case we got here accidentally */ 2522 backsql_entry_clean( op, &user_entry ); 2523 2524 if ( rs->sr_v2ref ) { 2525 ber_bvarray_free( rs->sr_v2ref ); 2526 rs->sr_v2ref = NULL; 2527 } 2528 2529 #ifdef BACKSQL_SYNCPROV 2530 if ( op->o_sync ) { 2531 Operation op2 = *op; 2532 SlapReply rs2 = { 0 }; 2533 Entry *e = entry_alloc(); 2534 slap_callback cb = { 0 }; 2535 2536 op2.o_tag = LDAP_REQ_ADD; 2537 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 ); 2538 op2.ora_e = e; 2539 op2.o_callback = &cb; 2540 2541 ber_dupbv( &e->e_name, op->o_bd->be_suffix ); 2542 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix ); 2543 2544 cb.sc_response = slap_null_cb; 2545 2546 op2.o_bd->be_add( &op2, &rs2 ); 2547 2548 if ( op2.ora_e == e ) 2549 entry_free( e ); 2550 } 2551 #endif /* BACKSQL_SYNCPROV */ 2552 2553 done:; 2554 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx ); 2555 2556 if ( bsi.bsi_attrs != NULL ) { 2557 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx ); 2558 } 2559 2560 if ( !BER_BVISNULL( &nbase ) 2561 && nbase.bv_val != op->o_req_ndn.bv_val ) 2562 { 2563 ch_free( nbase.bv_val ); 2564 } 2565 2566 /* restore scope ... FIXME: this should be done before ANY 2567 * frontend call that uses op */ 2568 if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) { 2569 op->ors_scope = LDAP_SCOPE_BASE; 2570 } 2571 2572 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 ); 2573 2574 return rs->sr_err; 2575 } 2576 2577 /* return LDAP_SUCCESS IFF we can retrieve the specified entry. 2578 */ 2579 int 2580 backsql_entry_get( 2581 Operation *op, 2582 struct berval *ndn, 2583 ObjectClass *oc, 2584 AttributeDescription *at, 2585 int rw, 2586 Entry **ent ) 2587 { 2588 backsql_srch_info bsi = { 0 }; 2589 SQLHDBC dbh = SQL_NULL_HDBC; 2590 int rc; 2591 SlapReply rs = { 0 }; 2592 AttributeName anlist[ 2 ]; 2593 2594 *ent = NULL; 2595 2596 rc = backsql_get_db_conn( op, &dbh ); 2597 if ( rc != LDAP_SUCCESS ) { 2598 return rc; 2599 } 2600 2601 if ( at ) { 2602 anlist[ 0 ].an_name = at->ad_cname; 2603 anlist[ 0 ].an_desc = at; 2604 BER_BVZERO( &anlist[ 1 ].an_name ); 2605 } 2606 2607 bsi.bsi_e = entry_alloc(); 2608 rc = backsql_init_search( &bsi, 2609 ndn, 2610 LDAP_SCOPE_BASE, 2611 (time_t)(-1), NULL, 2612 dbh, op, &rs, at ? anlist : NULL, 2613 BACKSQL_ISF_GET_ENTRY ); 2614 2615 if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) { 2616 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx ); 2617 } 2618 2619 if ( rc == LDAP_SUCCESS ) { 2620 2621 #if 0 /* not supported at present */ 2622 /* find attribute values */ 2623 if ( is_entry_alias( bsi.bsi_e ) ) { 2624 Debug( LDAP_DEBUG_ACL, 2625 "<= backsql_entry_get: entry is an alias\n", 2626 0, 0, 0 ); 2627 rc = LDAP_ALIAS_PROBLEM; 2628 goto return_results; 2629 } 2630 #endif 2631 2632 if ( is_entry_referral( bsi.bsi_e ) ) { 2633 Debug( LDAP_DEBUG_ACL, 2634 "<= backsql_entry_get: entry is a referral\n", 2635 0, 0, 0 ); 2636 rc = LDAP_REFERRAL; 2637 goto return_results; 2638 } 2639 2640 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) { 2641 Debug( LDAP_DEBUG_ACL, 2642 "<= backsql_entry_get: " 2643 "failed to find objectClass\n", 2644 0, 0, 0 ); 2645 rc = LDAP_NO_SUCH_ATTRIBUTE; 2646 goto return_results; 2647 } 2648 2649 *ent = bsi.bsi_e; 2650 } 2651 2652 return_results:; 2653 if ( bsi.bsi_attrs != NULL ) { 2654 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx ); 2655 } 2656 2657 if ( rc != LDAP_SUCCESS ) { 2658 if ( bsi.bsi_e ) { 2659 entry_free( bsi.bsi_e ); 2660 } 2661 } 2662 2663 return rc; 2664 } 2665 2666 void 2667 backsql_entry_clean( 2668 Operation *op, 2669 Entry *e ) 2670 { 2671 void *ctx; 2672 2673 ctx = ldap_pvt_thread_pool_context(); 2674 2675 if ( ctx == NULL || ctx != op->o_tmpmemctx ) { 2676 if ( !BER_BVISNULL( &e->e_name ) ) { 2677 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx ); 2678 BER_BVZERO( &e->e_name ); 2679 } 2680 2681 if ( !BER_BVISNULL( &e->e_nname ) ) { 2682 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx ); 2683 BER_BVZERO( &e->e_nname ); 2684 } 2685 } 2686 2687 entry_clean( e ); 2688 } 2689 2690 int 2691 backsql_entry_release( 2692 Operation *op, 2693 Entry *e, 2694 int rw ) 2695 { 2696 backsql_entry_clean( op, e ); 2697 2698 entry_free( e ); 2699 2700 return 0; 2701 } 2702 2703 #ifndef BACKSQL_ARBITRARY_KEY 2704 /* This function is copied verbatim from back-bdb/search.c */ 2705 static int 2706 parse_paged_cookie( Operation *op, SlapReply *rs ) 2707 { 2708 int rc = LDAP_SUCCESS; 2709 PagedResultsState *ps = op->o_pagedresults_state; 2710 2711 /* this function must be invoked only if the pagedResults 2712 * control has been detected, parsed and partially checked 2713 * by the frontend */ 2714 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ); 2715 2716 /* cookie decoding/checks deferred to backend... */ 2717 if ( ps->ps_cookieval.bv_len ) { 2718 PagedResultsCookie reqcookie; 2719 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) { 2720 /* bad cookie */ 2721 rs->sr_text = "paged results cookie is invalid"; 2722 rc = LDAP_PROTOCOL_ERROR; 2723 goto done; 2724 } 2725 2726 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie )); 2727 2728 if ( reqcookie > ps->ps_cookie ) { 2729 /* bad cookie */ 2730 rs->sr_text = "paged results cookie is invalid"; 2731 rc = LDAP_PROTOCOL_ERROR; 2732 goto done; 2733 2734 } else if ( reqcookie < ps->ps_cookie ) { 2735 rs->sr_text = "paged results cookie is invalid or old"; 2736 rc = LDAP_UNWILLING_TO_PERFORM; 2737 goto done; 2738 } 2739 2740 } else { 2741 /* Initial request. Initialize state. */ 2742 ps->ps_cookie = 0; 2743 ps->ps_count = 0; 2744 } 2745 2746 done:; 2747 2748 return rc; 2749 } 2750 2751 /* This function is copied nearly verbatim from back-bdb/search.c */ 2752 static void 2753 send_paged_response( 2754 Operation *op, 2755 SlapReply *rs, 2756 ID *lastid ) 2757 { 2758 LDAPControl ctrl, *ctrls[2]; 2759 BerElementBuffer berbuf; 2760 BerElement *ber = (BerElement *)&berbuf; 2761 PagedResultsCookie respcookie; 2762 struct berval cookie; 2763 2764 Debug(LDAP_DEBUG_ARGS, 2765 "send_paged_response: lastid=0x%08lx nentries=%d\n", 2766 lastid ? *lastid : 0, rs->sr_nentries, NULL ); 2767 2768 BER_BVZERO( &ctrl.ldctl_value ); 2769 ctrls[0] = &ctrl; 2770 ctrls[1] = NULL; 2771 2772 ber_init2( ber, NULL, LBER_USE_DER ); 2773 2774 if ( lastid ) { 2775 respcookie = ( PagedResultsCookie )(*lastid); 2776 cookie.bv_len = sizeof( respcookie ); 2777 cookie.bv_val = (char *)&respcookie; 2778 2779 } else { 2780 respcookie = ( PagedResultsCookie )0; 2781 BER_BVSTR( &cookie, "" ); 2782 } 2783 2784 op->o_conn->c_pagedresults_state.ps_cookie = respcookie; 2785 op->o_conn->c_pagedresults_state.ps_count = 2786 ((PagedResultsState *)op->o_pagedresults_state)->ps_count + 2787 rs->sr_nentries; 2788 2789 /* return size of 0 -- no estimate */ 2790 ber_printf( ber, "{iO}", 0, &cookie ); 2791 2792 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) { 2793 goto done; 2794 } 2795 2796 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; 2797 ctrls[0]->ldctl_iscritical = 0; 2798 2799 rs->sr_ctrls = ctrls; 2800 rs->sr_err = LDAP_SUCCESS; 2801 send_ldap_result( op, rs ); 2802 rs->sr_ctrls = NULL; 2803 2804 done: 2805 (void) ber_free_buf( ber ); 2806 } 2807 #endif /* ! BACKSQL_ARBITRARY_KEY */ 2808