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