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