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