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