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