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