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