]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/search.c
declare oc_bvfind_undef()
[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-2004 The OpenLDAP Foundation.
5  * Portions Copyright 1999 Dmitry Kovalev.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Dmitry Kovalev for inclusion
18  * by OpenLDAP Software.
19  */
20
21 #include "portable.h"
22
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include "ac/string.h"
26 #include "ac/ctype.h"
27
28 #include "slap.h"
29 #include "proto-sql.h"
30
31 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
32 static int backsql_process_filter_eq( backsql_srch_info *bsi, 
33                 backsql_at_map_rec *at,
34                 int casefold, struct berval *filter_value );
35 static int backsql_process_filter_like( backsql_srch_info *bsi, 
36                 backsql_at_map_rec *at,
37                 int casefold, struct berval *filter_value );
38 static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, 
39                 backsql_at_map_rec *at );
40
41 static int
42 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
43 {
44         int             n_attrs = 0;
45         AttributeName   *an = NULL;
46
47         if ( bsi->bsi_attrs == NULL ) {
48                 return 1;
49         }
50
51         /*
52          * clear the list (retrieve all attrs)
53          */
54         if ( ad == NULL ) {
55                 ch_free( bsi->bsi_attrs );
56                 bsi->bsi_attrs = NULL;
57                 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
58                 return 1;
59         }
60
61         for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
62                 an = &bsi->bsi_attrs[ n_attrs ];
63                 
64                 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
65                         "attribute \"%s\" is in list\n", 
66                         an->an_name.bv_val, 0, 0 );
67                 /*
68                  * We can live with strcmp because the attribute 
69                  * list has been normalized before calling be_search
70                  */
71                 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
72                         return 1;
73                 }
74         }
75         
76         Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
77                 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
78
79         an = (AttributeName *)ch_realloc( bsi->bsi_attrs,
80                         sizeof( AttributeName ) * ( n_attrs + 2 ) );
81         if ( an == NULL ) {
82                 return -1;
83         }
84
85         an[ n_attrs ].an_name = ad->ad_cname;
86         an[ n_attrs ].an_desc = ad;
87         BER_BVZERO( &an[ n_attrs + 1 ].an_name );
88
89         bsi->bsi_attrs = an;
90         
91         return 1;
92 }
93
94 /*
95  * Initializes the search structure.
96  * 
97  * If get_base_id != 0, the field bsi_base_id is filled 
98  * with the entryID of bsi_base_ndn; it must be freed
99  * by backsql_free_entryID() when no longer required.
100  *
101  * NOTE: base must be normalized
102  */
103 int
104 backsql_init_search(
105         backsql_srch_info       *bsi, 
106         struct berval           *nbase, 
107         int                     scope, 
108         int                     slimit,
109         int                     tlimit,
110         time_t                  stoptime, 
111         Filter                  *filter, 
112         SQLHDBC                 dbh,
113         Operation               *op,
114         SlapReply               *rs,
115         AttributeName           *attrs,
116         unsigned                flags )
117 {
118         AttributeName           *p;
119         int                     rc = LDAP_SUCCESS;
120
121         bsi->bsi_base_ndn = nbase;
122         BER_BVZERO( &bsi->bsi_base_id.eid_dn );
123         BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
124         bsi->bsi_scope = scope;
125         bsi->bsi_slimit = slimit;
126         bsi->bsi_tlimit = tlimit;
127         bsi->bsi_filter = filter;
128         bsi->bsi_dbh = dbh;
129         bsi->bsi_op = op;
130         bsi->bsi_rs = rs;
131         bsi->bsi_flags = BSQL_SF_NONE;
132
133         /*
134          * handle "*"
135          */
136         if ( attrs == NULL ) {
137                 /* also add request for all operational */
138                 bsi->bsi_attrs = NULL;
139                 bsi->bsi_flags |= BSQL_SF_ALL_USER;
140
141         } else {
142                 int     got_oc = 0;
143
144                 bsi->bsi_attrs = (AttributeName *)ch_calloc( 1, 
145                                 sizeof( AttributeName ) );
146                 BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
147                 
148                 for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
149                         /*
150                          * ignore "1.1"; handle "+"
151                          */
152                         if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
153                                 bsi->bsi_flags |= BSQL_SF_ALL_USER;
154                                 continue;
155
156                         } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
157                                 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
158                                 continue;
159
160                         } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
161                                 continue;
162
163                         } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
164                                 got_oc = 1;
165                         }
166
167                         backsql_attrlist_add( bsi, p->an_desc );
168                 }
169
170                 if ( got_oc == 0 ) {
171                         /* add objectClass if not present,
172                          * because it is required to understand
173                          * if an entry is a referral, an alias 
174                          * or so... */
175                         backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
176                 }
177         }
178
179         bsi->bsi_abandon = 0;
180         bsi->bsi_id_list = NULL;
181         bsi->bsi_id_listtail = &bsi->bsi_id_list;
182         bsi->bsi_n_candidates = 0;
183         bsi->bsi_stoptime = stoptime;
184         BER_BVZERO( &bsi->bsi_sel.bb_val );
185         bsi->bsi_sel.bb_len = 0;
186         BER_BVZERO( &bsi->bsi_from.bb_val );
187         bsi->bsi_from.bb_len = 0;
188         BER_BVZERO( &bsi->bsi_join_where.bb_val );
189         bsi->bsi_join_where.bb_len = 0;
190         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
191         bsi->bsi_flt_where.bb_len = 0;
192         bsi->bsi_filter_oc = NULL;
193
194         if ( flags & BACKSQL_ISF_GET_ID ) {
195                 assert( op->o_bd->be_private );
196
197                 rc = backsql_dn2id( op, rs, &bsi->bsi_base_id, dbh, nbase,
198                                 ( flags & BACKSQL_ISF_MUCK ) );
199         }
200
201         return ( bsi->bsi_status = rc );
202 }
203
204 static int
205 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
206 {
207         int             res;
208
209         if ( !f ) {
210                 return 0;
211         }
212
213         backsql_strfcat( &bsi->bsi_flt_where, "c", '(' /* ) */  );
214
215         while ( 1 ) {
216                 res = backsql_process_filter( bsi, f );
217                 if ( res < 0 ) {
218                         /*
219                          * TimesTen : If the query has no answers,
220                          * don't bother to run the query.
221                          */
222                         return -1;
223                 }
224  
225                 f = f->f_next;
226                 if ( f == NULL ) {
227                         break;
228                 }
229
230                 switch ( op ) {
231                 case LDAP_FILTER_AND:
232                         backsql_strfcat( &bsi->bsi_flt_where, "l",
233                                         (ber_len_t)STRLENOF( " AND " ), 
234                                                 " AND " );
235                         break;
236
237                 case LDAP_FILTER_OR:
238                         backsql_strfcat( &bsi->bsi_flt_where, "l",
239                                         (ber_len_t)STRLENOF( " OR " ),
240                                                 " OR " );
241                         break;
242                 }
243         }
244
245         backsql_strfcat( &bsi->bsi_flt_where, "c", /* ( */ ')' );
246
247         return 1;
248 }
249
250 static int
251 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
252         backsql_at_map_rec *at )
253 {
254         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
255         int                     i;
256         int                     casefold = 0;
257
258         if ( !f ) {
259                 return 0;
260         }
261
262         /* always uppercase strings by now */
263 #ifdef BACKSQL_UPPERCASE_FILTER
264         if ( SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
265                         bi->sql_caseIgnoreMatch ) )
266 #endif /* BACKSQL_UPPERCASE_FILTER */
267         {
268                 casefold = 1;
269         }
270
271         if ( SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
272                         bi->sql_telephoneNumberMatch ) )
273         {
274
275                 struct berval   bv;
276                 ber_len_t       i, s, a;
277
278                 /*
279                  * to check for matching telephone numbers
280                  * with intermixed chars, e.g. val='1234'
281                  * use
282                  * 
283                  * val LIKE '%1%2%3%4%'
284                  */
285
286                 BER_BVZERO( &bv );
287                 if ( f->f_sub_initial.bv_val ) {
288                         bv.bv_len += f->f_sub_initial.bv_len;
289                 }
290                 if ( f->f_sub_any != NULL ) {
291                         for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
292                                 bv.bv_len += f->f_sub_any[ a ].bv_len;
293                         }
294                 }
295                 if ( f->f_sub_final.bv_val ) {
296                         bv.bv_len += f->f_sub_final.bv_len;
297                 }
298                 bv.bv_len = 2 * bv.bv_len - 1;
299                 bv.bv_val = ch_malloc( bv.bv_len + 1 );
300
301                 s = 0;
302                 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
303                         bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
304                         for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
305                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
306                                 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
307                         }
308                         bv.bv_val[ s + 2 * i - 1 ] = '%';
309                         s += 2 * i;
310                 }
311
312                 if ( f->f_sub_any != NULL ) {
313                         for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
314                                 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
315                                 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
316                                         bv.bv_val[ s + 2 * i - 1 ] = '%';
317                                         bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
318                                 }
319                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
320                                 s += 2 * i;
321                         }
322                 }
323
324                 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
325                         bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
326                         for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
327                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
328                                 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
329                         }
330                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
331                         s += 2 * i;
332                 }
333
334                 bv.bv_val[ s - 1 ] = '\0';
335
336                 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
337                 ch_free( bv.bv_val );
338
339                 return 1;
340         }
341
342         /*
343          * When dealing with case-sensitive strings 
344          * we may omit normalization; however, normalized
345          * SQL filters are more liberal.
346          */
347
348         backsql_strfcat( &bsi->bsi_flt_where, "c", '(' /* ) */  );
349
350         /* TimesTen */
351         Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
352                 at->bam_ad->ad_cname.bv_val, 0, 0 );
353         Debug(LDAP_DEBUG_TRACE, "   expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
354                 at->bam_sel_expr_u.bv_val ? "' '" : "",
355                 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
356         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
357                 /*
358                  * If a pre-upper-cased version of the column 
359                  * or a precompiled upper function exists, use it
360                  */
361                 backsql_strfcat( &bsi->bsi_flt_where, 
362                                 "bl",
363                                 &at->bam_sel_expr_u,
364                                 (ber_len_t)STRLENOF( " LIKE '" ),
365                                         " LIKE '" );
366
367         } else {
368                 backsql_strfcat( &bsi->bsi_flt_where, "bl",
369                                 &at->bam_sel_expr,
370                                 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
371         }
372  
373         if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
374                 ber_len_t       start;
375
376 #ifdef BACKSQL_TRACE
377                 Debug( LDAP_DEBUG_TRACE, 
378                         "==>backsql_process_sub_filter(%s): "
379                         "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
380                         f->f_sub_initial.bv_val, 0 );
381 #endif /* BACKSQL_TRACE */
382
383                 start = bsi->bsi_flt_where.bb_val.bv_len;
384                 backsql_strfcat( &bsi->bsi_flt_where, "b",
385                                 &f->f_sub_initial );
386                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
387                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
388                 }
389         }
390
391         backsql_strfcat( &bsi->bsi_flt_where, "c", '%' );
392
393         if ( f->f_sub_any != NULL ) {
394                 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
395                         ber_len_t       start;
396
397 #ifdef BACKSQL_TRACE
398                         Debug( LDAP_DEBUG_TRACE, 
399                                 "==>backsql_process_sub_filter(%s): "
400                                 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 
401                                 i, f->f_sub_any[ i ].bv_val );
402 #endif /* BACKSQL_TRACE */
403
404                         start = bsi->bsi_flt_where.bb_val.bv_len;
405                         backsql_strfcat( &bsi->bsi_flt_where,
406                                         "bc",
407                                         &f->f_sub_any[ i ],
408                                         '%' );
409                         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
410                                 /*
411                                  * Note: toupper('%') = '%'
412                                  */
413                                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
414                         }
415                 }
416         }
417
418         if ( !BER_BVISNULL( &f->f_sub_final ) ) {
419                 ber_len_t       start;
420
421 #ifdef BACKSQL_TRACE
422                 Debug( LDAP_DEBUG_TRACE, 
423                         "==>backsql_process_sub_filter(%s): "
424                         "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
425                         f->f_sub_final.bv_val, 0 );
426 #endif /* BACKSQL_TRACE */
427
428                 start = bsi->bsi_flt_where.bb_val.bv_len;
429                 backsql_strfcat( &bsi->bsi_flt_where, "b",
430                                 &f->f_sub_final );
431                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
432                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
433                 }
434         }
435
436         backsql_strfcat( &bsi->bsi_flt_where, "l", 
437                         (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
438  
439         return 1;
440 }
441
442 static int
443 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
444 {
445         if ( BER_BVISNULL( from_tbls ) ) {
446                 return LDAP_SUCCESS;
447         }
448
449         if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
450                 char    *start, *end, *tmp;
451
452                 tmp = ch_strdup( from_tbls->bv_val );
453
454                 for ( start = tmp, end = strchr( start, ',' ); start; ) {
455                         if ( end ) {
456                                 end[0] = '\0';
457                         }
458
459                         if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
460                         {
461                                 backsql_strfcat( &bsi->bsi_from, "cs", ',', start );
462                         }
463
464                         if ( end ) {
465                                 /* in case there are spaces after the comma... */
466                                 for ( start = &end[1]; isspace( start[0] ); start++ );
467                                 if ( start[0] ) {
468                                         end = strchr( start, ',' );
469                                 } else {
470                                         start = NULL;
471                                 }
472                         } else {
473                                 start = NULL;
474                         }
475                 }
476
477                 ch_free( tmp );
478
479         } else {
480                 backsql_strfcat( &bsi->bsi_from, "b", from_tbls );
481         }
482
483         return LDAP_SUCCESS;
484 }
485
486 static int
487 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
488 {
489         backsql_at_map_rec      **vat = NULL;
490         AttributeDescription    *ad = NULL;
491         unsigned                i;
492         int                     done = 0;
493         int                     rc = 0;
494
495         Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
496         if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
497                 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
498                         "invalid filter\n", 0, 0, 0 );
499                 rc = -1;
500                 goto done;
501         }
502
503         switch( f->f_choice ) {
504         case LDAP_FILTER_OR:
505                 rc = backsql_process_filter_list( bsi, f->f_or, 
506                                 LDAP_FILTER_OR );
507                 done = 1;
508                 break;
509                 
510         case LDAP_FILTER_AND:
511                 rc = backsql_process_filter_list( bsi, f->f_and,
512                                 LDAP_FILTER_AND );
513                 done = 1;
514                 break;
515
516         case LDAP_FILTER_NOT:
517                 backsql_strfcat( &bsi->bsi_flt_where, "l",
518                                 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
519                                         "NOT (" /* ) */ );
520                 rc = backsql_process_filter( bsi, f->f_not );
521                 backsql_strfcat( &bsi->bsi_flt_where, "c", /* ( */ ')' );
522                 done = 1;
523                 break;
524
525         case LDAP_FILTER_PRESENT:
526                 ad = f->f_desc;
527                 break;
528                 
529         case LDAP_FILTER_EXT:
530                 ad = f->f_mra->ma_desc;
531                 if ( f->f_mr_dnattrs ) {
532                         /*
533                          * if dn attrs filtering is requested, better return 
534                          * success and let test_filter() deal with candidate
535                          * selection; otherwise we'd need to set conditions
536                          * on the contents of the DN, e.g. "SELECT ... FROM
537                          * ldap_entries AS attributeName WHERE attributeName.dn
538                          * like '%attributeName=value%'"
539                          */
540                         backsql_strfcat( &bsi->bsi_flt_where, "l",
541                                         (ber_len_t)STRLENOF( "1=1" ), "1=1" );
542                         bsi->bsi_status = LDAP_SUCCESS;
543                         rc = 1;
544                         goto done;
545                 }
546                 break;
547                 
548         default:
549                 ad = f->f_av_desc;
550                 break;
551         }
552
553         if ( rc == -1 ) {
554                 goto done;
555         }
556  
557         if ( done ) {
558                 rc = 1;
559                 goto done;
560         }
561
562         /*
563          * Turn structuralObjectClass into objectClass
564          */
565         if ( ad == slap_schema.si_ad_objectClass 
566                         || ad == slap_schema.si_ad_structuralObjectClass )
567         {
568                 /*
569                  * If the filter is LDAP_FILTER_PRESENT, then it's done;
570                  * otherwise, let's see if we are lucky: filtering
571                  * for "structural" objectclass or ancestor...
572                  */
573                 switch ( f->f_choice ) {
574                 case LDAP_FILTER_EQUALITY:
575                 {
576                         ObjectClass     *oc = oc_bvfind( &f->f_av_value );
577
578                         if ( oc == NULL ) {
579                                 Debug( LDAP_DEBUG_TRACE,
580                                                 "backsql_process_filter(): "
581                                                 "unknown objectClass \"%s\" "
582                                                 "in filter\n",
583                                                 f->f_av_value.bv_val, 0, 0 );
584                                 bsi->bsi_status = LDAP_OTHER;
585                                 rc = -1;
586                                 goto done;
587                         }
588
589                         /*
590                          * "structural" objectClass inheritance:
591                          * - a search for "person" will also return 
592                          *   "inetOrgPerson"
593                          * - a search for "top" will return everything
594                          */
595                         if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
596                                 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
597
598                                 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
599
600                                 backsql_strfcat( &bsi->bsi_flt_where, "lbl",
601                                                 (ber_len_t)STRLENOF( "1=1 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
602                                                         "1=1 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
603                                                 &bsi->bsi_oc->bom_oc->soc_cname,
604                                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
605                                                         /* (' */ "')" );
606                                 bsi->bsi_status = LDAP_SUCCESS;
607                                 rc = 1;
608                                 goto done;
609                         }
610
611                         break;
612                 }
613
614                 case LDAP_FILTER_PRESENT:
615                         backsql_strfcat( &bsi->bsi_flt_where, "l",
616                                         (ber_len_t)STRLENOF( "1=1" ), "1=1" );
617                         bsi->bsi_status = LDAP_SUCCESS;
618                         rc = 1;
619                         goto done;
620
621                         /* FIXME: LDAP_FILTER_EXT? */
622                         
623                 default:
624                         Debug( LDAP_DEBUG_TRACE,
625                                         "backsql_process_filter(): "
626                                         "illegal/unhandled filter "
627                                         "on objectClass attribute",
628                                         0, 0, 0 );
629                         bsi->bsi_status = LDAP_OTHER;
630                         rc = -1;
631                         goto done;
632                 }
633
634         } else if ( ad == slap_schema.si_ad_entryUUID ) {
635                 unsigned long   oc_id;
636 #ifdef BACKSQL_ARBITRARY_KEY
637                 struct berval   keyval;
638 #else /* ! BACKSQL_ARBITRARY_KEY */
639                 unsigned long   keyval;
640                 char            keyvalbuf[] = "18446744073709551615";
641 #endif /* ! BACKSQL_ARBITRARY_KEY */
642
643                 switch ( f->f_choice ) {
644                 case LDAP_FILTER_EQUALITY:
645                         backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
646
647                         if ( oc_id != bsi->bsi_oc->bom_id ) {
648                                 bsi->bsi_status = LDAP_SUCCESS;
649                                 rc = -1;
650                                 goto done;
651                         }
652
653 #ifdef BACKSQL_ARBITRARY_KEY
654                         backsql_strfcat( &bsi->bsi_flt_where, "bcblbc",
655                                         &bsi->bsi_oc->bom_keytbl, '.',
656                                         &bsi->bsi_oc->bom_keycol,
657                                         STRLENOF( " LIKE '" ), " LIKE '",
658                                         &keyval, '\'' );
659 #else /* ! BACKSQL_ARBITRARY_KEY */
660                         snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
661                         backsql_strfcat( &bsi->bsi_flt_where, "bcbcs",
662                                         &bsi->bsi_oc->bom_keytbl, '.',
663                                         &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
664 #endif /* ! BACKSQL_ARBITRARY_KEY */
665                         break;
666
667                 case LDAP_FILTER_PRESENT:
668                         backsql_strfcat( &bsi->bsi_flt_where, "l",
669                                         (ber_len_t)STRLENOF( "1=1" ), "1=1" );
670                         break;
671
672                 default:
673                         rc = -1;
674                         goto done;
675                 }
676
677                 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
678                 rc = 1;
679                 goto done;
680
681 #ifdef BACKSQL_SYNCPROV
682         } else if ( ad == slap_schema.si_ad_entryCSN ) {
683                 /*
684                  * TODO: introduce appropriate entryCSN filtering
685                  * to support syncrepl as producer...
686                  */
687                 if ( !bsi->bsi_op->o_sync ) {
688                         /* unsupported at present... */
689                         bsi->bsi_status = LDAP_OTHER;
690                         rc = -1;
691                         goto done;
692                 }
693
694                 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
695
696                 /* if doing a syncrepl, try to return as much as possible,
697                  * and always match the filter */
698                 backsql_strfcat( &bsi->bsi_flt_where, "l",
699                                 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
700
701                 /* save for later use in operational attributes */
702                 /* FIXME: saves only the first occurrence, because 
703                  * the filter during updates is written as
704                  * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
705                  * so we want our fake entryCSN to match the greatest
706                  * value
707                  */
708                 if ( bsi->bsi_op->o_private == NULL ) {
709                         bsi->bsi_op->o_private = &f->f_av_value;
710                 }
711                 bsi->bsi_status = LDAP_SUCCESS;
712
713                 rc = 1;
714                 goto done;
715 #endif /* BACKSQL_SYNCPROV */
716
717         } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
718                 /*
719                  * FIXME: this is not robust; e.g. a filter
720                  * '(!(hasSubordinates=TRUE))' fails because
721                  * in SQL it would read 'NOT (1=1)' instead 
722                  * of no condition.  
723                  * Note however that hasSubordinates is boolean, 
724                  * so a more appropriate filter would be 
725                  * '(hasSubordinates=FALSE)'
726                  *
727                  * A more robust search for hasSubordinates
728                  * would * require joining the ldap_entries table
729                  * selecting if there are descendants of the
730                  * candidate.
731                  */
732                 backsql_strfcat( &bsi->bsi_flt_where, "l",
733                                 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
734                 if ( ad == slap_schema.si_ad_hasSubordinates ) {
735                         /*
736                          * instruct candidate selection algorithm
737                          * and attribute list to try to detect
738                          * if an entry has subordinates
739                          */
740                         bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
741
742                 } else {
743                         /*
744                          * clear attributes to fetch, to require ALL
745                          * and try extended match on all attributes
746                          */
747                         backsql_attrlist_add( bsi, NULL );
748                 }
749                 rc = 1;
750                 goto done;
751         }
752
753         /*
754          * attribute inheritance:
755          */
756         if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
757                 bsi->bsi_status = LDAP_OTHER;
758                 rc = -1;
759                 goto done;
760         }
761
762         if ( vat == NULL ) {
763                 /* search anyway; other parts of the filter
764                  * may succeeed */
765                 backsql_strfcat( &bsi->bsi_flt_where, "l",
766                                 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
767                 bsi->bsi_status = LDAP_SUCCESS;
768                 rc = 1;
769                 goto done;
770         }
771
772         /* if required, open extra level of parens */
773         done = 0;
774         if ( vat[0]->bam_next || vat[1] ) {
775                 backsql_strfcat( &bsi->bsi_flt_where, "c", '(' );
776                 done = 1;
777         }
778
779         i = 0;
780 next:;
781         /* apply attr */
782         if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
783                 return -1;
784         }
785
786         /* if more definitions of the same attr, apply */
787         if ( vat[i]->bam_next ) {
788                 backsql_strfcat( &bsi->bsi_flt_where, "l",
789                         STRLENOF( " OR " ), " OR " );
790                 vat[i] = vat[i]->bam_next;
791                 goto next;
792         }
793
794         /* if more descendants of the same attr, apply */
795         i++;
796         if ( vat[i] ) {
797                 backsql_strfcat( &bsi->bsi_flt_where, "l",
798                         STRLENOF( " OR " ), " OR " );
799                 goto next;
800         }
801
802         /* if needed, close extra level of parens */
803         if ( done ) {
804                 backsql_strfcat( &bsi->bsi_flt_where, "c", ')' );
805         }
806
807         rc = 1;
808
809 done:;
810         if ( vat ) {
811                 ch_free( vat );
812         }
813
814         Debug( LDAP_DEBUG_TRACE,
815                         "<==backsql_process_filter() %s\n",
816                         rc == 1 ? "succeeded" : "failed", 0, 0);
817
818         return rc;
819 }
820
821 static int
822 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
823                 int casefold, struct berval *filter_value )
824 {
825         /*
826          * maybe we should check type of at->sel_expr here somehow,
827          * to know whether upper_func is applicable, but for now
828          * upper_func stuff is made for Oracle, where UPPER is
829          * safely applicable to NUMBER etc.
830          */
831         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
832                 ber_len_t       start;
833
834                 backsql_strfcat( &bsi->bsi_flt_where, "cbl",
835                                 '(', /* ) */
836                                 &at->bam_sel_expr_u, 
837                                 (ber_len_t)STRLENOF( "='" ),
838                                         "='" );
839
840                 start = bsi->bsi_flt_where.bb_val.bv_len;
841
842                 backsql_strfcat( &bsi->bsi_flt_where, "bl",
843                                 filter_value, 
844                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
845                                         /* (' */ "')" );
846
847                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
848
849         } else {
850                 backsql_strfcat( &bsi->bsi_flt_where, "cblbl",
851                                 '(', /* ) */
852                                 &at->bam_sel_expr,
853                                 (ber_len_t)STRLENOF( "='" ), "='",
854                                 filter_value,
855                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
856                                         /* (' */ "')" );
857         }
858
859         return 1;
860 }
861         
862 static int
863 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
864                 int casefold, struct berval *filter_value )
865 {
866         /*
867          * maybe we should check type of at->sel_expr here somehow,
868          * to know whether upper_func is applicable, but for now
869          * upper_func stuff is made for Oracle, where UPPER is
870          * safely applicable to NUMBER etc.
871          */
872         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
873                 ber_len_t       start;
874
875                 backsql_strfcat( &bsi->bsi_flt_where, "cbl",
876                                 '(', /* ) */
877                                 &at->bam_sel_expr_u, 
878                                 (ber_len_t)STRLENOF( " LIKE '%" ),
879                                         " LIKE '%" );
880
881                 start = bsi->bsi_flt_where.bb_val.bv_len;
882
883                 backsql_strfcat( &bsi->bsi_flt_where, "bl",
884                                 filter_value, 
885                                 (ber_len_t)STRLENOF( /* (' */ "%')" ),
886                                         /* (' */ "%')" );
887
888                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
889
890         } else {
891                 backsql_strfcat( &bsi->bsi_flt_where, "cblbl",
892                                 '(', /* ) */
893                                 &at->bam_sel_expr,
894                                 (ber_len_t)STRLENOF( " LIKE '%" ),
895                                         " LIKE '%",
896                                 filter_value,
897                                 (ber_len_t)STRLENOF( /* (' */ "%')" ),
898                                         /* (' */ "%')" );
899         }
900
901         return 1;
902 }
903
904 static int
905 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
906 {
907         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
908         int                     casefold = 0;
909         struct berval           *filter_value = NULL;
910         MatchingRule            *matching_rule = NULL;
911         struct berval           ordering = BER_BVC("<=");
912
913         Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
914                 at->bam_ad->ad_cname.bv_val, 0, 0 );
915
916         /*
917          * need to add this attribute to list of attrs to load,
918          * so that we can do test_filter() later
919          */
920         backsql_attrlist_add( bsi, at->bam_ad );
921
922         backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
923
924         if ( !BER_BVISNULL( &at->bam_join_where )
925                         && strstr( bsi->bsi_join_where.bb_val.bv_val,
926                                 at->bam_join_where.bv_val ) == NULL )
927         {
928                 backsql_strfcat( &bsi->bsi_join_where, "lb",
929                                 (ber_len_t)STRLENOF( " AND " ), " AND ",
930                                 &at->bam_join_where );
931         }
932
933         switch ( f->f_choice ) {
934         case LDAP_FILTER_EQUALITY:
935                 filter_value = &f->f_av_value;
936                 matching_rule = at->bam_ad->ad_type->sat_equality;
937
938                 goto equality_match;
939
940                 /* fail over into next case */
941                 
942         case LDAP_FILTER_EXT:
943                 filter_value = &f->f_mra->ma_value;
944                 matching_rule = f->f_mr_rule;
945
946 equality_match:;
947                 /* always uppercase strings by now */
948 #ifdef BACKSQL_UPPERCASE_FILTER
949                 if ( SLAP_MR_ASSOCIATED( matching_rule,
950                                         bi->sql_caseIgnoreMatch ) )
951 #endif /* BACKSQL_UPPERCASE_FILTER */
952                 {
953                         casefold = 1;
954                 }
955
956                 if ( SLAP_MR_ASSOCIATED( matching_rule,
957                                         bi->sql_telephoneNumberMatch ) )
958                 {
959                         struct berval   bv;
960                         ber_len_t       i;
961
962                         /*
963                          * to check for matching telephone numbers
964                          * with intermized chars, e.g. val='1234'
965                          * use
966                          * 
967                          * val LIKE '%1%2%3%4%'
968                          */
969
970                         bv.bv_len = 2 * filter_value->bv_len - 1;
971                         bv.bv_val = ch_malloc( bv.bv_len + 1 );
972
973                         bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
974                         for ( i = 1; i < filter_value->bv_len; i++ ) {
975                                 bv.bv_val[ 2 * i - 1 ] = '%';
976                                 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
977                         }
978                         bv.bv_val[ 2 * i - 1 ] = '\0';
979
980                         (void)backsql_process_filter_like( bsi, at, casefold, &bv );
981                         ch_free( bv.bv_val );
982
983                         break;
984                 }
985
986                 /* NOTE: this is required by objectClass inheritance 
987                  * and auxiliary objectClass use in filters for slightly
988                  * more efficient candidate selection. */
989                 /* FIXME: a bit too many specializations to deal with
990                  * very specific cases... */
991                 if ( at->bam_ad == slap_schema.si_ad_objectClass
992                                 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
993                 {
994                         backsql_strfcat( &bsi->bsi_flt_where, "lbl",
995                                         (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
996                                                 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
997                                         filter_value,
998                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
999                                                 /* (' */ "')" );
1000                         break;
1001                 }
1002
1003                 /*
1004                  * maybe we should check type of at->sel_expr here somehow,
1005                  * to know whether upper_func is applicable, but for now
1006                  * upper_func stuff is made for Oracle, where UPPER is
1007                  * safely applicable to NUMBER etc.
1008                  */
1009                 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1010                 break;
1011
1012         case LDAP_FILTER_GE:
1013                 ordering.bv_val = ">=";
1014
1015                 /* fall thru to next case */
1016                 
1017         case LDAP_FILTER_LE:
1018                 /* always uppercase strings by now */
1019 #ifdef BACKSQL_UPPERCASE_FILTER
1020                 if ( SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1021                                 bi->sql_caseIgnoreMatch ) )
1022 #endif /* BACKSQL_UPPERCASE_FILTER */
1023                 {
1024                         casefold = 1;
1025                 }
1026
1027                 /*
1028                  * FIXME: should we uppercase the operands?
1029                  */
1030                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1031                         ber_len_t       start;
1032
1033                         backsql_strfcat( &bsi->bsi_flt_where, "cbbc",
1034                                         '(', /* ) */
1035                                         &at->bam_sel_expr_u, 
1036                                         &ordering,
1037                                         '\'' );
1038
1039                         start = bsi->bsi_flt_where.bb_val.bv_len;
1040
1041                         backsql_strfcat( &bsi->bsi_flt_where, "bl",
1042                                         filter_value, 
1043                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
1044                                                 /* (' */ "')" );
1045
1046                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1047                 
1048                 } else {
1049                         backsql_strfcat( &bsi->bsi_flt_where, "cbbcbl",
1050                                         '(' /* ) */ ,
1051                                         &at->bam_sel_expr,
1052                                         &ordering,
1053                                         '\'',
1054                                         &f->f_av_value,
1055                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
1056                                                 /* ( */ "')" );
1057                 }
1058                 break;
1059
1060         case LDAP_FILTER_PRESENT:
1061                 backsql_strfcat( &bsi->bsi_flt_where, "lbl",
1062                                 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1063                                         "NOT (", /* ) */
1064                                 &at->bam_sel_expr, 
1065                                 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1066                                         /* ( */ " IS NULL)" );
1067                 break;
1068
1069         case LDAP_FILTER_SUBSTRINGS:
1070                 backsql_process_sub_filter( bsi, f, at );
1071                 break;
1072
1073         case LDAP_FILTER_APPROX:
1074                 /* we do our best */
1075
1076                 /*
1077                  * maybe we should check type of at->sel_expr here somehow,
1078                  * to know whether upper_func is applicable, but for now
1079                  * upper_func stuff is made for Oracle, where UPPER is
1080                  * safely applicable to NUMBER etc.
1081                  */
1082                 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1083                 break;
1084
1085         default:
1086                 /* unhandled filter type; should not happen */
1087                 assert( 0 );
1088                 backsql_strfcat( &bsi->bsi_flt_where, "l",
1089                                 (ber_len_t)STRLENOF( "1=1" ), "1=1" );
1090                 break;
1091
1092         }
1093
1094         Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1095                 at->bam_ad->ad_cname.bv_val, 0, 0 );
1096
1097         return 1;
1098 }
1099
1100 static int
1101 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1102 {
1103         backsql_info    *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1104         int             rc;
1105
1106         assert( query );
1107         BER_BVZERO( query );
1108
1109         Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1110         BER_BVZERO( &bsi->bsi_sel.bb_val );
1111         BER_BVZERO( &bsi->bsi_sel.bb_val );
1112         bsi->bsi_sel.bb_len = 0;
1113         BER_BVZERO( &bsi->bsi_from.bb_val );
1114         bsi->bsi_from.bb_len = 0;
1115         BER_BVZERO( &bsi->bsi_join_where.bb_val );
1116         bsi->bsi_join_where.bb_len = 0;
1117         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1118         bsi->bsi_flt_where.bb_len = 0;
1119
1120         backsql_strfcat( &bsi->bsi_sel, "lbcbc",
1121                         (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1122                                 "SELECT DISTINCT ldap_entries.id,", 
1123                         &bsi->bsi_oc->bom_keytbl, 
1124                         '.', 
1125                         &bsi->bsi_oc->bom_keycol, 
1126                         ',' );
1127
1128         if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1129                 backsql_strfcat( &bsi->bsi_sel, "blbl",
1130                                 &bi->sql_strcast_func, 
1131                                 (ber_len_t)STRLENOF( "('" /* ') */ ),
1132                                         "('" /* ') */ ,
1133                                 &bsi->bsi_oc->bom_oc->soc_cname,
1134                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
1135                                         /* (' */ "')" );
1136         } else {
1137                 backsql_strfcat( &bsi->bsi_sel, "cbc",
1138                                 '\'',
1139                                 &bsi->bsi_oc->bom_oc->soc_cname,
1140                                 '\'' );
1141         }
1142 #ifdef BACKSQL_ALIASING_QUOTE
1143         backsql_strfcat( &bsi->bsi_sel, "lclcl",
1144                         (ber_len_t)STRLENOF( " " BACKSQL_ALIASING ),
1145                                 " " BACKSQL_ALIASING,
1146                         BACKSQL_ALIASING_QUOTE,
1147                         (ber_len_t)STRLENOF( "objectClass" ),
1148                                 "objectClass",
1149                         BACKSQL_ALIASING_QUOTE,
1150                         (ber_len_t)STRLENOF( ",ldap_entries.dn " BACKSQL_ALIASING "dn" ),
1151                                 ",ldap_entries.dn " BACKSQL_ALIASING "dn" );
1152 #else /* ! BACKSQL_ALIASING_QUOTE */
1153         backsql_strfcat( &bsi->bsi_sel, "l",
1154                         (ber_len_t)STRLENOF( " " BACKSQL_ALIASING "objectClass,ldap_entries.dn " BACKSQL_ALIASING "dn" ),
1155                                 " " BACKSQL_ALIASING "objectClass,ldap_entries.dn " BACKSQL_ALIASING "dn" );
1156 #endif /* ! BACKSQL_ALIASING_QUOTE */
1157
1158         backsql_strfcat( &bsi->bsi_from, "lb",
1159                         (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1160                                 " FROM ldap_entries,",
1161                         &bsi->bsi_oc->bom_keytbl );
1162
1163         backsql_strfcat( &bsi->bsi_join_where, "lbcbl",
1164                         (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1165                         &bsi->bsi_oc->bom_keytbl,
1166                         '.',
1167                         &bsi->bsi_oc->bom_keycol,
1168                         (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1169                                 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1170
1171         switch ( bsi->bsi_scope ) {
1172         case LDAP_SCOPE_BASE:
1173                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1174                         backsql_strfcat( &bsi->bsi_join_where, "bl",
1175                                         &bi->sql_upper_func,
1176                                         (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1177                                                 "(ldap_entries.dn)=?" );
1178                 } else {
1179                         backsql_strfcat( &bsi->bsi_join_where, "l",
1180                                         (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1181                                                 "ldap_entries.dn=?" );
1182                 }
1183                 break;
1184                 
1185         case BACKSQL_SCOPE_BASE_LIKE:
1186                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1187                         backsql_strfcat( &bsi->bsi_join_where, "bl",
1188                                         &bi->sql_upper_func,
1189                                         (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1190                                                 "(ldap_entries.dn) LIKE ?" );
1191                 } else {
1192                         backsql_strfcat( &bsi->bsi_join_where, "l",
1193                                         (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1194                                                 "ldap_entries.dn LIKE ?" );
1195                 }
1196                 break;
1197                 
1198         case LDAP_SCOPE_ONELEVEL:
1199                 backsql_strfcat( &bsi->bsi_join_where, "l",
1200                                 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1201                                         "ldap_entries.parent=?" );
1202                 break;
1203
1204 #ifdef LDAP_SCOPE_SUBORDINATE
1205         case LDAP_SCOPE_SUBORDINATE:
1206 #endif /* LDAP_SCOPE_SUBORDINATE */
1207         case LDAP_SCOPE_SUBTREE:
1208                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1209                         backsql_strfcat( &bsi->bsi_join_where, "bl",
1210                                         &bi->sql_upper_func,
1211                                         (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1212                                                 "(ldap_entries.dn) LIKE ?"  );
1213                 } else {
1214                         backsql_strfcat( &bsi->bsi_join_where, "l",
1215                                         (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1216                                                 "ldap_entries.dn LIKE ?" );
1217                 }
1218
1219                 break;
1220
1221         default:
1222                 assert( 0 );
1223         }
1224
1225         rc = backsql_process_filter( bsi, bsi->bsi_filter );
1226         if ( rc > 0 ) {
1227                 struct berbuf   bb = BB_NULL;
1228
1229                 backsql_strfcat( &bb, "bbblb",
1230                                 &bsi->bsi_sel.bb_val,
1231                                 &bsi->bsi_from.bb_val, 
1232                                 &bsi->bsi_join_where.bb_val,
1233                                 (ber_len_t)STRLENOF( " AND " ), " AND ",
1234                                 &bsi->bsi_flt_where.bb_val );
1235
1236                 *query = bb.bb_val;
1237
1238         } else if ( rc < 0 ) {
1239                 /* 
1240                  * Indicates that there's no possible way the filter matches
1241                  * anything.  No need to issue the query
1242                  */
1243                 free( query->bv_val );
1244                 BER_BVZERO( query );
1245         }
1246  
1247         free( bsi->bsi_sel.bb_val.bv_val );
1248         BER_BVZERO( &bsi->bsi_sel.bb_val );
1249         bsi->bsi_sel.bb_len = 0;
1250         free( bsi->bsi_from.bb_val.bv_val );
1251         BER_BVZERO( &bsi->bsi_from.bb_val );
1252         bsi->bsi_from.bb_len = 0;
1253         free( bsi->bsi_join_where.bb_val.bv_val );
1254         BER_BVZERO( &bsi->bsi_join_where.bb_val );
1255         bsi->bsi_join_where.bb_len = 0;
1256         free( bsi->bsi_flt_where.bb_val.bv_val );
1257         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1258         bsi->bsi_flt_where.bb_len = 0;
1259         
1260         Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1261                 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1262         
1263         return ( rc <= 0 ? 1 : 0 );
1264 }
1265
1266 static int
1267 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1268 {
1269         backsql_oc_map_rec      *oc = v_oc;
1270         backsql_srch_info       *bsi = v_bsi;
1271         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1272         struct berval           query;
1273         SQLHSTMT                sth;
1274         RETCODE                 rc;
1275         int                     res;
1276         BACKSQL_ROW_NTS         row;
1277         int                     i;
1278         int                     j;
1279         int                     n_candidates = bsi->bsi_n_candidates;
1280
1281         /* 
1282          * + 1 because we need room for '%';
1283          * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1284          * this makes a subtree
1285          * search for a DN BACKSQL_MAX_DN_LEN long legal 
1286          * if it returns that DN only
1287          */
1288         char                    tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1289
1290         bsi->bsi_status = LDAP_SUCCESS;
1291  
1292         Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1293                         BACKSQL_OC_NAME( oc ), 0, 0 );
1294
1295         if ( bsi->bsi_n_candidates == -1 ) {
1296                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1297                         "unchecked limit has been overcome\n", 0, 0, 0 );
1298                 /* should never get here */
1299                 assert( 0 );
1300                 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1301                 return BACKSQL_AVL_STOP;
1302         }
1303         
1304         bsi->bsi_oc = oc;
1305         res = backsql_srch_query( bsi, &query );
1306         if ( res ) {
1307                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1308                         "error while constructing query for objectclass \"%s\"\n",
1309                         oc->bom_oc->soc_cname.bv_val, 0, 0 );
1310                 /*
1311                  * FIXME: need to separate errors from legally
1312                  * impossible filters
1313                  */
1314                 switch ( bsi->bsi_status ) {
1315                 case LDAP_SUCCESS:
1316                 case LDAP_UNDEFINED_TYPE:
1317                 case LDAP_NO_SUCH_OBJECT:
1318                         /* we are conservative... */
1319                 default:
1320                         bsi->bsi_status = LDAP_SUCCESS;
1321                         /* try next */
1322                         return BACKSQL_AVL_CONTINUE;
1323
1324                 case LDAP_ADMINLIMIT_EXCEEDED:
1325                 case LDAP_OTHER:
1326                         /* don't try any more */
1327                         return BACKSQL_AVL_STOP;
1328                 }
1329         }
1330
1331         if ( BER_BVISNULL( &query ) ) {
1332                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1333                         "could not construct query for objectclass \"%s\"\n",
1334                         oc->bom_oc->soc_cname.bv_val, 0, 0 );
1335                 bsi->bsi_status = LDAP_SUCCESS;
1336                 return BACKSQL_AVL_CONTINUE;
1337         }
1338
1339         Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n", 
1340                         query.bv_val, 0, 0 );
1341
1342         rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1343         free( query.bv_val );
1344         BER_BVZERO( &query );
1345         if ( rc != SQL_SUCCESS ) {
1346                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1347                         "error preparing query\n", 0, 0, 0 );
1348                 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1349                 bsi->bsi_status = LDAP_OTHER;
1350                 return BACKSQL_AVL_CONTINUE;
1351         }
1352         
1353         Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1354
1355         rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1356                         &bsi->bsi_oc->bom_id );
1357         if ( rc != SQL_SUCCESS ) {
1358                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1359                         "error binding objectclass id parameter\n", 0, 0, 0 );
1360                 bsi->bsi_status = LDAP_OTHER;
1361                 return BACKSQL_AVL_CONTINUE;
1362         }
1363
1364         switch ( bsi->bsi_scope ) {
1365         case LDAP_SCOPE_BASE:
1366         case BACKSQL_SCOPE_BASE_LIKE:
1367                 /*
1368                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1369                  * however this should be handled earlier
1370                  */
1371                 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1372                         bsi->bsi_status = LDAP_OTHER;
1373                         return BACKSQL_AVL_CONTINUE;
1374                 }
1375
1376                 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1377                                 bsi->bsi_base_ndn->bv_len + 1 );
1378
1379                 /* uppercase DN only if the stored DN can be uppercased
1380                  * for comparison */
1381                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1382                         ldap_pvt_str2upper( tmp_base_ndn );
1383                 }
1384
1385                 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1386                                 tmp_base_ndn, 0, 0 );
1387
1388                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1389                                 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1390                 if ( rc != SQL_SUCCESS ) {
1391                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1392                                 "error binding base_ndn parameter\n", 0, 0, 0 );
1393                         backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 
1394                                         sth, rc );
1395                         bsi->bsi_status = LDAP_OTHER;
1396                         return BACKSQL_AVL_CONTINUE;
1397                 }
1398                 break;
1399
1400 #ifdef LDAP_SCOPE_SUBORDINATE
1401         case LDAP_SCOPE_SUBORDINATE:
1402 #endif /* LDAP_SCOPE_SUBORDINATE */
1403         case LDAP_SCOPE_SUBTREE:
1404         {
1405                 /*
1406                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1407                  * however this should be handled earlier
1408                  */
1409                 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1410                         bsi->bsi_status = LDAP_OTHER;
1411                         return BACKSQL_AVL_CONTINUE;
1412                 }
1413
1414                 /* 
1415                  * Sets the parameters for the SQL built earlier
1416                  * NOTE that all the databases could actually use 
1417                  * the TimesTen version, which would be cleaner 
1418                  * and would also eliminate the need for the
1419                  * subtree_cond line in the configuration file.  
1420                  * For now, I'm leaving it the way it is, 
1421                  * so non-TimesTen databases use the original code.
1422                  * But at some point this should get cleaned up.
1423                  *
1424                  * If "dn" is being used, do a suffix search.
1425                  * If "dn_ru" is being used, do a prefix search.
1426                  */
1427                 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1428                         tmp_base_ndn[ 0 ] = '\0';
1429
1430                         for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1431                                         j >= 0; i++, j--) {
1432                                 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1433                         }
1434
1435 #ifdef LDAP_SCOPE_SUBORDINATE
1436                         if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1437                                 tmp_base_ndn[ i++ ] = ',';
1438                         }
1439 #endif /* LDAP_SCOPE_SUBORDINATE */
1440
1441                         tmp_base_ndn[ i ] = '%';
1442                         tmp_base_ndn[ i + 1 ] = '\0';
1443
1444                 } else {
1445                         i = 0;
1446
1447                         tmp_base_ndn[ i++ ] = '%';
1448
1449 #ifdef LDAP_SCOPE_SUBORDINATE
1450                         if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1451                                 tmp_base_ndn[ i++ ] = ',';
1452                         }
1453 #endif /* LDAP_SCOPE_SUBORDINATE */
1454
1455                         AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1456                                 bsi->bsi_base_ndn->bv_len + 1 );
1457                 }
1458
1459                 /* uppercase DN only if the stored DN can be uppercased
1460                  * for comparison */
1461                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1462                         ldap_pvt_str2upper( tmp_base_ndn );
1463                 }
1464
1465 #ifdef LDAP_SCOPE_SUBORDINATE
1466                 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1467                         Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1468                                 tmp_base_ndn, 0, 0 );
1469                 } else 
1470 #endif /* LDAP_SCOPE_SUBORDINATE */
1471                 {
1472                         Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1473                                 tmp_base_ndn, 0, 0 );
1474                 }
1475
1476                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1477                                 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1478                 if ( rc != SQL_SUCCESS ) {
1479                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1480                                 "error binding base_ndn parameter (2)\n",
1481                                 0, 0, 0 );
1482                         backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 
1483                                         sth, rc );
1484                         bsi->bsi_status = LDAP_OTHER;
1485                         return BACKSQL_AVL_CONTINUE;
1486                 }
1487                 break;
1488         }
1489
1490         case LDAP_SCOPE_ONELEVEL:
1491                 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1492
1493 #ifdef BACKSQL_ARBITRARY_KEY
1494                 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1495                                 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1496 #else /* ! BACKSQL_ARBITRARY_KEY */
1497                 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1498                                 bsi->bsi_base_id.eid_id, 0, 0 );
1499 #endif /* ! BACKSQL_ARBITRARY_KEY */
1500                 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1501                                 &bsi->bsi_base_id.eid_id );
1502                 if ( rc != SQL_SUCCESS ) {
1503                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1504                                 "error binding base id parameter\n", 0, 0, 0 );
1505                         bsi->bsi_status = LDAP_OTHER;
1506                         return BACKSQL_AVL_CONTINUE;
1507                 }
1508                 break;
1509         }
1510         
1511         rc = SQLExecute( sth );
1512         if ( !BACKSQL_SUCCESS( rc ) ) {
1513                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1514                         "error executing query\n", 0, 0, 0 );
1515                 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1516                 SQLFreeStmt( sth, SQL_DROP );
1517                 bsi->bsi_status = LDAP_OTHER;
1518                 return BACKSQL_AVL_CONTINUE;
1519         }
1520
1521         backsql_BindRowAsStrings( sth, &row );
1522         rc = SQLFetch( sth );
1523         for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1524                 struct berval           dn, pdn, ndn;
1525                 backsql_entryID         *c_id = NULL;
1526                 int                     ret;
1527
1528                 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1529
1530                 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1531                         continue;
1532                 }
1533
1534                 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, NULL );
1535                 if ( dn.bv_val != row.cols[ 3 ] ) {
1536                         free( dn.bv_val );
1537                 }
1538
1539                 if ( ret != LDAP_SUCCESS ) {
1540                         continue;
1541                 }
1542
1543                 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1544                         free( pdn.bv_val );
1545                         free( ndn.bv_val );
1546                         continue;
1547                 }
1548
1549                 c_id = (backsql_entryID *)ch_calloc( 1, 
1550                                 sizeof( backsql_entryID ) );
1551 #ifdef BACKSQL_ARBITRARY_KEY
1552                 ber_str2bv( row.cols[ 0 ], 0, 1, &c_id->eid_id );
1553                 ber_str2bv( row.cols[ 1 ], 0, 1, &c_id->eid_keyval );
1554 #else /* ! BACKSQL_ARBITRARY_KEY */
1555                 c_id->eid_id = strtol( row.cols[ 0 ], NULL, 0 );
1556                 c_id->eid_keyval = strtol( row.cols[ 1 ], NULL, 0 );
1557 #endif /* ! BACKSQL_ARBITRARY_KEY */
1558                 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1559
1560                 c_id->eid_dn = pdn;
1561                 c_id->eid_ndn = ndn;
1562
1563                 /* append at end of list ... */
1564                 c_id->eid_next = NULL;
1565                 *bsi->bsi_id_listtail = c_id;
1566                 bsi->bsi_id_listtail = &c_id->eid_next;
1567
1568 #ifdef BACKSQL_ARBITRARY_KEY
1569                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1570                         "added entry id=%s, keyval=%s dn=\"%s\"\n",
1571                         c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1572                         row.cols[ 3 ] );
1573 #else /* ! BACKSQL_ARBITRARY_KEY */
1574                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1575                         "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1576                         c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1577 #endif /* ! BACKSQL_ARBITRARY_KEY */
1578
1579                 /* count candidates, for unchecked limit */
1580                 bsi->bsi_n_candidates--;
1581                 if ( bsi->bsi_n_candidates == -1 ) {
1582                         break;
1583                 }
1584         }
1585         backsql_FreeRow( &row );
1586         SQLFreeStmt( sth, SQL_DROP );
1587
1588         Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1589                         n_candidates - bsi->bsi_n_candidates, 0, 0 );
1590
1591         return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1592 }
1593
1594 int
1595 backsql_search( Operation *op, SlapReply *rs )
1596 {
1597         backsql_info            *bi = (backsql_info *)op->o_bd->be_private;
1598         SQLHDBC                 dbh;
1599         int                     sres;
1600         Entry                   user_entry = { 0 };
1601         int                     manageDSAit;
1602         time_t                  stoptime = 0;
1603         backsql_srch_info       bsi;
1604         backsql_entryID         *eid = NULL;
1605         struct berval           nbase = BER_BVNULL,
1606                                 realndn = BER_BVNULL;
1607
1608         manageDSAit = get_manageDSAit( op );
1609
1610         Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1611                 "base=\"%s\", filter=\"%s\", scope=%d,", 
1612                 op->o_req_ndn.bv_val,
1613                 op->ors_filterstr.bv_val,
1614                 op->ors_scope );
1615         Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1616                 "attributes to load: %s\n",
1617                 op->ors_deref,
1618                 op->ors_attrsonly,
1619                 op->ors_attrs == NULL ? "all" : "custom list" );
1620
1621         if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1622                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1623                         "search base length (%ld) exceeds max length (%d)\n", 
1624                         op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1625                 /*
1626                  * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1627                  * since it is impossible that such a long DN exists
1628                  * in the backend
1629                  */
1630                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1631                 send_ldap_result( op, rs );
1632                 return 1;
1633         }
1634
1635         sres = backsql_get_db_conn( op, &dbh );
1636         if ( sres != LDAP_SUCCESS ) {
1637                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1638                         "could not get connection handle - exiting\n", 
1639                         0, 0, 0 );
1640                 rs->sr_err = sres;
1641                 rs->sr_text = sres == LDAP_OTHER ?  "SQL-backend error" : NULL;
1642                 send_ldap_result( op, rs );
1643                 return 1;
1644         }
1645
1646         /* compute it anyway; root does not use it */
1647         stoptime = op->o_time + op->ors_tlimit;
1648
1649         realndn = op->o_req_ndn;
1650         if ( backsql_api_dn2odbc( op, rs, &realndn ) ) {
1651                 Debug( LDAP_DEBUG_TRACE, "   backsql_search(\"%s\"): "
1652                         "backsql_api_dn2odbc(\"%s\") failed\n", 
1653                         op->o_req_ndn.bv_val, realndn.bv_val, 0 );
1654                 rs->sr_err = LDAP_OTHER;
1655                 rs->sr_text = "SQL-backend error";
1656                 send_ldap_result( op, rs );
1657                 goto done;
1658         }
1659
1660         /* init search */
1661         rs->sr_err = backsql_init_search( &bsi, &realndn,
1662                         op->ors_scope,
1663                         op->ors_slimit, op->ors_tlimit,
1664                         stoptime, op->ors_filter,
1665                         dbh, op, rs, op->ors_attrs,
1666                         ( BACKSQL_ISF_GET_ID | BACKSQL_ISF_MUCK ) );
1667         if ( rs->sr_err != LDAP_SUCCESS ) {
1668                 send_ldap_result( op, rs );
1669                 goto done;
1670         }
1671
1672         bsi.bsi_n_candidates =
1673                 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 : 
1674                 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
1675                 ( op->ors_limit->lms_s_unchecked ) ) );
1676
1677         switch ( bsi.bsi_scope ) {
1678         case LDAP_SCOPE_BASE:
1679         case BACKSQL_SCOPE_BASE_LIKE:
1680                 /*
1681                  * probably already found...
1682                  */
1683                 bsi.bsi_id_list = &bsi.bsi_base_id;
1684                 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
1685                 break;
1686
1687         case LDAP_SCOPE_SUBTREE:
1688                 /*
1689                  * if baseObject is defined, and if it is the root 
1690                  * of the search, add it to the candidate list
1691                  */
1692                 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
1693                 {
1694                         bsi.bsi_id_list = &bsi.bsi_base_id;
1695                         bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
1696                 }
1697
1698                 /* FALLTHRU */
1699         default:
1700
1701                 /*
1702                  * for each objectclass we try to construct query which gets IDs
1703                  * of entries matching LDAP query filter and scope (or at least 
1704                  * candidates), and get the IDs
1705                  */
1706                 avl_apply( bi->sql_oc_by_oc, backsql_oc_get_candidates,
1707                                 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
1708         }
1709
1710         if ( op->ors_limit != NULL      /* isroot == FALSE */
1711                         && op->ors_limit->lms_s_unchecked != -1
1712                         && bsi.bsi_n_candidates == -1 )
1713         {
1714                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1715                 send_ldap_result( op, rs );
1716                 goto done;
1717         }
1718
1719         /*
1720          * now we load candidate entries (only those attributes 
1721          * mentioned in attrs and filter), test it against full filter 
1722          * and then send to client; don't free entry_id if baseObject...
1723          */
1724         for ( eid = bsi.bsi_id_list;
1725                         eid != NULL; 
1726                         eid = backsql_free_entryID( eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
1727         {
1728                 int             rc;
1729                 Attribute       *a_hasSubordinate = NULL,
1730                                 *a_entryUUID = NULL,
1731                                 *a_entryCSN = NULL,
1732                                 **ap = NULL;
1733                 Entry           *e = NULL;
1734
1735                 /* check for abandon */
1736                 if ( op->o_abandon ) {
1737                         break;
1738                 }
1739
1740                 /* check time limit */
1741                 if ( op->ors_tlimit != SLAP_NO_LIMIT
1742                                 && slap_get_time() > stoptime )
1743                 {
1744                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
1745                         rs->sr_ctrls = NULL;
1746                         rs->sr_ref = rs->sr_v2ref;
1747                         rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1748                                 : LDAP_REFERRAL;
1749                         send_ldap_result( op, rs );
1750                         goto end_of_search;
1751                 }
1752
1753 #ifdef BACKSQL_ARBITRARY_KEY
1754                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1755                         "for entry id=%s, oc_id=%ld, keyval=%s\n",
1756                         eid->eid_id.bv_val, eid->eid_oc_id,
1757                         eid->eid_keyval.bv_val );
1758 #else /* ! BACKSQL_ARBITRARY_KEY */
1759                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1760                         "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
1761                         eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
1762 #endif /* ! BACKSQL_ARBITRARY_KEY */
1763
1764                 /* check scope */
1765                 switch ( op->ors_scope ) {
1766                 case LDAP_SCOPE_BASE:
1767                 case BACKSQL_SCOPE_BASE_LIKE:
1768                         if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
1769                                 goto next_entry2;
1770                         }
1771                         break;
1772
1773                 case LDAP_SCOPE_ONE:
1774                 {
1775                         struct berval   rdn = eid->eid_ndn;
1776
1777                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
1778                         if ( !dnIsOneLevelRDN( &rdn ) ) {
1779                                 goto next_entry2;
1780                         }
1781                         /* fall thru */
1782                 }
1783
1784 #ifdef LDAP_SCOPE_SUBORDINATE
1785                 case LDAP_SCOPE_SUBORDINATE:
1786                         /* discard the baseObject entry */
1787                         if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
1788                                 goto next_entry2;
1789                         }
1790                 /* FALLTHRU */
1791 #endif /* LDAP_SCOPE_SUBORDINATE */
1792
1793                 case LDAP_SCOPE_SUBTREE:
1794                         /* FIXME: this should never fail... */
1795                         if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
1796                                 goto next_entry2;
1797                         }
1798                         break;
1799                 }
1800
1801                 /* don't recollect baseObject ... */
1802                 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
1803                         e = bi->sql_baseObject;
1804
1805                 } else {
1806                         bsi.bsi_e = &user_entry;
1807                         rc = backsql_id2entry( &bsi, eid );
1808                         if ( rc != LDAP_SUCCESS ) {
1809                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1810                                         "error %d in backsql_id2entry() "
1811                                         "- skipping\n", rc, 0, 0 );
1812                                 continue;
1813                         }
1814
1815                         e = &user_entry;
1816                 }
1817
1818                 if ( !manageDSAit &&
1819                                 op->ors_scope != LDAP_SCOPE_BASE &&
1820                                 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
1821                                 is_entry_referral( e ) )
1822                 {
1823                         BerVarray refs;
1824
1825                         refs = get_entry_referrals( op, e );
1826                         if ( !refs ) {
1827                                 backsql_srch_info       bsi2 = { 0 };
1828                                 Entry                   user_entry2 = { 0 };
1829
1830                                 /* retry with the full entry... */
1831                                 (void)backsql_init_search( &bsi2,
1832                                                 &e->e_nname,
1833                                                 LDAP_SCOPE_BASE, 
1834                                                 -1, -1, -1, NULL,
1835                                                 dbh, op, rs, NULL,
1836                                                 BACKSQL_ISF_MUCK );
1837                                 bsi2.bsi_e = &user_entry2;
1838                                 rc = backsql_id2entry( &bsi2, eid );
1839                                 if ( rc == LDAP_SUCCESS ) {
1840                                         if ( is_entry_referral( &user_entry2 ) )
1841                                         {
1842                                                 refs = get_entry_referrals( op,
1843                                                                 &user_entry2 );
1844                                         } /* else: FIXME: inconsistency! */
1845                                         entry_clean( &user_entry2 );
1846                                 }
1847                         }
1848
1849                         if ( refs ) {
1850                                 rs->sr_ref = referral_rewrite( refs,
1851                                                 &e->e_name,
1852                                                 &op->o_req_dn,
1853                                                 op->ors_scope );
1854                                 ber_bvarray_free( refs );
1855                         }
1856
1857                         if ( !rs->sr_ref ) {
1858                                 rs->sr_text = "bad_referral object";
1859                         }
1860
1861                         rs->sr_entry = e;
1862                         rs->sr_err = LDAP_REFERRAL;
1863                         rs->sr_matched = user_entry.e_name.bv_val;
1864                         send_search_reference( op, rs );
1865
1866                         ber_bvarray_free( rs->sr_ref );
1867                         rs->sr_ref = NULL;
1868                         rs->sr_matched = NULL;
1869                         rs->sr_entry = NULL;
1870
1871                         goto next_entry;
1872                 }
1873
1874                 /*
1875                  * We use this flag since we need to parse the filter
1876                  * anyway; we should have used the frontend API function
1877                  * filter_has_subordinates()
1878                  */
1879                 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
1880                         rc = backsql_has_children( bi, dbh, &e->e_nname );
1881
1882                         switch ( rc ) {
1883                         case LDAP_COMPARE_TRUE:
1884                         case LDAP_COMPARE_FALSE:
1885                                 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
1886                                 if ( a_hasSubordinate != NULL ) {
1887                                         for ( ap = &user_entry.e_attrs; 
1888                                                         *ap; 
1889                                                         ap = &(*ap)->a_next );
1890
1891                                         *ap = a_hasSubordinate;
1892                                 }
1893                                 rc = 0;
1894                                 break;
1895
1896                         default:
1897                                 Debug(LDAP_DEBUG_TRACE, 
1898                                         "backsql_search(): "
1899                                         "has_children failed( %d)\n", 
1900                                         rc, 0, 0 );
1901                                 rc = 1;
1902                                 goto next_entry;
1903                         }
1904                 }
1905
1906                 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
1907                         a_entryUUID = backsql_operational_entryUUID( bi, eid );
1908                         if ( a_entryUUID != NULL ) {
1909                                 if ( ap == NULL ) {
1910                                         ap = &user_entry.e_attrs;
1911                                 }
1912
1913                                 for ( ; *ap; ap = &(*ap)->a_next );
1914
1915                                 *ap = a_entryUUID;
1916                         }
1917                 }
1918
1919 #ifdef BACKSQL_SYNCPROV
1920                 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
1921                         a_entryCSN = backsql_operational_entryCSN( op );
1922                         if ( a_entryCSN != NULL ) {
1923                                 if ( ap == NULL ) {
1924                                         ap = &user_entry.e_attrs;
1925                                 }
1926
1927                                 for ( ; *ap; ap = &(*ap)->a_next );
1928
1929                                 *ap = a_entryCSN;
1930                         }
1931                 }
1932 #endif /* BACKSQL_SYNCPROV */
1933
1934                 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
1935                 {
1936                         rs->sr_attrs = op->ors_attrs;
1937                         rs->sr_operational_attrs = NULL;
1938                         rs->sr_entry = e;
1939                         if ( e == &user_entry ) {
1940                                 rs->sr_flags = REP_ENTRY_MODIFIABLE;
1941                         }
1942                         sres = send_search_entry( op, rs );
1943                         rs->sr_entry = NULL;
1944                         rs->sr_attrs = NULL;
1945                         rs->sr_operational_attrs = NULL;
1946
1947                         switch ( sres ) {
1948                         case 0:
1949                                 break;
1950
1951                         default:
1952                                 /*
1953                                  * FIXME: send_search_entry failed;
1954                                  * better stop
1955                                  */
1956                         case -1:
1957                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1958                                         "connection lost\n", 0, 0, 0 );
1959                                 goto end_of_search;
1960                         }
1961                 }
1962
1963 next_entry:;
1964                 entry_clean( &user_entry );
1965
1966 next_entry2:;
1967                 if ( op->ors_slimit != SLAP_NO_LIMIT
1968                                 && rs->sr_nentries >= op->ors_slimit )
1969                 {
1970                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1971                         send_ldap_result( op, rs );
1972                         goto end_of_search;
1973                 }
1974         }
1975
1976 end_of_search:;
1977         /* in case we got here accidentally */
1978         entry_clean( &user_entry );
1979
1980         if ( rs->sr_nentries > 0 ) {
1981                 rs->sr_ref = rs->sr_v2ref;
1982                 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1983                         : LDAP_REFERRAL;
1984
1985         } else {
1986                 rs->sr_err = bsi.bsi_status;
1987         }
1988         send_ldap_result( op, rs );
1989
1990         if ( rs->sr_v2ref ) {
1991                 ber_bvarray_free( rs->sr_v2ref );
1992                 rs->sr_v2ref = NULL;
1993         }
1994
1995 #ifdef BACKSQL_SYNCPROV
1996         if ( op->o_sync ) {
1997                 Operation       op2 = *op;
1998                 SlapReply       rs2 = { 0 };
1999                 Entry           e = { 0 };
2000                 slap_callback   cb = { 0 };
2001
2002                 op2.o_tag = LDAP_REQ_ADD;
2003                 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0, 0 );
2004                 op2.ora_e = &e;
2005                 op2.o_callback = &cb;
2006
2007                 e.e_name = op->o_bd->be_suffix[0];
2008                 e.e_nname = op->o_bd->be_nsuffix[0];
2009
2010                 cb.sc_response = slap_null_cb;
2011
2012                 op2.o_bd->be_add( &op2, &rs2 );
2013         }
2014 #endif /* BACKSQL_SYNCPROV */
2015
2016 done:;
2017         if ( !BER_BVISNULL( &realndn ) && realndn.bv_val != op->o_req_ndn.bv_val ) {
2018                 ch_free( realndn.bv_val );
2019         }
2020
2021         if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2022                 (void)backsql_free_entryID( &bsi.bsi_base_id, 0 );
2023         }
2024
2025         if ( bsi.bsi_attrs ) {
2026                 ch_free( bsi.bsi_attrs );
2027         }
2028
2029         if ( !BER_BVISNULL( &nbase )
2030                         && nbase.bv_val != op->o_req_ndn.bv_val )
2031         {
2032                 ch_free( nbase.bv_val );
2033         }
2034
2035         /* restore scope ... FIXME: this should be done before ANY
2036          * frontend call that uses op */
2037         if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2038                 op->ors_scope = LDAP_SCOPE_BASE;
2039         }
2040
2041         Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2042         return 0;
2043 }
2044
2045 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2046  */
2047 int
2048 backsql_entry_get(
2049                 Operation               *op,
2050                 struct berval           *ndn,
2051                 ObjectClass             *oc,
2052                 AttributeDescription    *at,
2053                 int                     rw,
2054                 Entry                   **ent )
2055 {
2056         backsql_srch_info       bsi;
2057         SQLHDBC                 dbh;
2058         int                     rc;
2059         SlapReply               rs = { 0 };
2060         AttributeName           anlist[ 2 ];
2061
2062         rc = backsql_get_db_conn( op, &dbh );
2063         if ( !dbh ) {
2064                 return LDAP_OTHER;
2065         }
2066
2067         if ( at ) {
2068                 anlist[ 0 ].an_name = at->ad_cname;
2069                 anlist[ 0 ].an_desc = at;
2070                 BER_BVZERO( &anlist[ 1 ].an_name );
2071         }
2072
2073         rc = backsql_init_search( &bsi,
2074                         ndn,
2075                         LDAP_SCOPE_BASE, 
2076                         SLAP_NO_LIMIT, SLAP_NO_LIMIT, -1, NULL,
2077                         dbh, op, &rs, at ? anlist : NULL,
2078                         ( BACKSQL_ISF_GET_ID | BACKSQL_ISF_MUCK ) );
2079         if ( rc != LDAP_SUCCESS ) {
2080                 return rc;
2081         }
2082
2083         bsi.bsi_e = ch_malloc( sizeof( Entry ) );
2084         rc = backsql_id2entry( &bsi, &bsi.bsi_base_id );
2085
2086         if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2087                 (void)backsql_free_entryID( &bsi.bsi_base_id, 0 );
2088         }
2089
2090         if ( rc == LDAP_SUCCESS ) {
2091
2092 #if 0 /* not supported at present */
2093                 /* find attribute values */
2094                 if ( is_entry_alias( bsi.bsi_e ) ) {
2095                         Debug( LDAP_DEBUG_ACL,
2096                                 "<= backsql_entry_get: entry is an alias\n",
2097                                 0, 0, 0 );
2098                         rc = LDAP_ALIAS_PROBLEM;
2099                         goto return_results;
2100                 }
2101 #endif
2102
2103                 if ( is_entry_referral( bsi.bsi_e ) ) {
2104                         Debug( LDAP_DEBUG_ACL,
2105                                 "<= backsql_entry_get: entry is a referral\n",
2106                                 0, 0, 0 );
2107                         rc = LDAP_REFERRAL;
2108                         goto return_results;
2109                 }
2110
2111                 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2112                         Debug( LDAP_DEBUG_ACL,
2113                                         "<= backsql_entry_get: "
2114                                         "failed to find objectClass\n",
2115                                         0, 0, 0 ); 
2116                         rc = LDAP_NO_SUCH_ATTRIBUTE;
2117                         goto return_results;
2118                 }
2119
2120                 *ent = bsi.bsi_e;
2121         }
2122
2123 return_results:;
2124         if ( rc != LDAP_SUCCESS ) {
2125                 if ( bsi.bsi_e ) {
2126                         entry_free( bsi.bsi_e );
2127                 }
2128         }
2129
2130         return rc;
2131 }
2132