]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/search.c
7b1f960e032086728419737811d272dcb1b70330
[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         rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1245                         &bsi->bsi_oc->bom_id );
1246         if ( rc != SQL_SUCCESS ) {
1247                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1248                         "error binding objectclass id parameter\n", 0, 0, 0 );
1249                 bsi->bsi_status = LDAP_OTHER;
1250                 return BACKSQL_AVL_CONTINUE;
1251         }
1252
1253         switch ( bsi->bsi_scope ) {
1254         case LDAP_SCOPE_BASE:
1255         case BACKSQL_SCOPE_BASE_LIKE:
1256                 /*
1257                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1258                  * however this should be handled earlier
1259                  */
1260                 if ( bsi->bsi_base_dn->bv_len > BACKSQL_MAX_DN_LEN ) {
1261                         bsi->bsi_status = LDAP_OTHER;
1262                         return BACKSQL_AVL_CONTINUE;
1263                 }
1264
1265                 AC_MEMCPY( temp_base_dn, bsi->bsi_base_dn->bv_val,
1266                                 bsi->bsi_base_dn->bv_len + 1 );
1267
1268                 /* uppercase DN only if the stored DN can be uppercased
1269                  * for comparison */
1270                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1271                         ldap_pvt_str2upper( temp_base_dn );
1272                 }
1273
1274                 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1275                                 temp_base_dn, 0, 0 );
1276
1277                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1278                                 temp_base_dn, BACKSQL_MAX_DN_LEN );
1279                 if ( rc != SQL_SUCCESS ) {
1280                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1281                                 "error binding base_dn parameter\n", 0, 0, 0 );
1282                         backsql_PrintErrors( bi->db_env, bsi->bsi_dbh, 
1283                                         sth, rc );
1284                         bsi->bsi_status = LDAP_OTHER;
1285                         return BACKSQL_AVL_CONTINUE;
1286                 }
1287                 break;
1288
1289         case LDAP_SCOPE_SUBTREE: {
1290                 /*
1291                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1292                  * however this should be handled earlier
1293                  */
1294                 if ( bsi->bsi_base_dn->bv_len > BACKSQL_MAX_DN_LEN ) {
1295                         bsi->bsi_status = LDAP_OTHER;
1296                         return BACKSQL_AVL_CONTINUE;
1297                 }
1298
1299                 /* 
1300                  * Sets the parameters for the SQL built earlier
1301                  * NOTE that all the databases could actually use 
1302                  * the TimesTen version, which would be cleaner 
1303                  * and would also eliminate the need for the
1304                  * subtree_cond line in the configuration file.  
1305                  * For now, I'm leaving it the way it is, 
1306                  * so non-TimesTen databases use the original code.
1307                  * But at some point this should get cleaned up.
1308                  *
1309                  * If "dn" is being used, do a suffix search.
1310                  * If "dn_ru" is being used, do a prefix search.
1311                  */
1312                 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1313                         temp_base_dn[ 0 ] = '\0';
1314                         for ( i = 0, j = bsi->bsi_base_dn->bv_len - 1;
1315                                         j >= 0; i++, j--) {
1316                                 temp_base_dn[ i ] = bsi->bsi_base_dn->bv_val[ j ];
1317                         }
1318                         temp_base_dn[ i ] = '%';
1319                         temp_base_dn[ i + 1 ] = '\0';
1320
1321                 } else {
1322                         temp_base_dn[ 0 ] = '%';
1323                         AC_MEMCPY( &temp_base_dn[ 1 ], bsi->bsi_base_dn->bv_val,
1324                                 bsi->bsi_base_dn->bv_len + 1 );
1325                 }
1326
1327                 /* uppercase DN only if the stored DN can be uppercased
1328                  * for comparison */
1329                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1330                         ldap_pvt_str2upper( temp_base_dn );
1331                 }
1332
1333                 Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n", temp_base_dn,
1334                                 0, 0 );
1335
1336                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1337                                 temp_base_dn, BACKSQL_MAX_DN_LEN );
1338                 if ( rc != SQL_SUCCESS ) {
1339                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1340                                 "error binding base_dn parameter (2)\n",
1341                                 0, 0, 0 );
1342                         backsql_PrintErrors( bi->db_env, bsi->bsi_dbh, 
1343                                         sth, rc );
1344                         bsi->bsi_status = LDAP_OTHER;
1345                         return BACKSQL_AVL_CONTINUE;
1346                 }
1347                 break;
1348         }
1349
1350         case LDAP_SCOPE_ONELEVEL:
1351                 res = backsql_dn2id( bi, &base_id, 
1352                                 bsi->bsi_dbh, bsi->bsi_base_dn );
1353                 if ( res != LDAP_SUCCESS ) {
1354                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1355                                 "could not retrieve base_dn id%s\n",
1356                                 res == LDAP_NO_SUCH_OBJECT ? ": no such entry"
1357                                 : "", 0, 0 );
1358                         bsi->bsi_status = res;
1359                         return BACKSQL_AVL_CONTINUE;
1360                 }
1361
1362 #ifdef BACKSQL_ARBITRARY_KEY
1363                 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1364                                 base_id.eid_id.bv_val, 0, 0 );
1365 #else /* ! BACKSQL_ARBITRARY_KEY */
1366                 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n", base_id.eid_id,
1367                                 0, 0 );
1368 #endif /* ! BACKSQL_ARBITRARY_KEY */
1369                 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1370                                 &base_id.eid_id );
1371                 backsql_free_entryID( &base_id, 0 );
1372                 if ( rc != SQL_SUCCESS ) {
1373                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1374                                 "error binding base id parameter\n", 0, 0, 0 );
1375                         bsi->bsi_status = LDAP_OTHER;
1376                         return BACKSQL_AVL_CONTINUE;
1377                 }
1378                 break;
1379         }
1380         
1381         rc = SQLExecute( sth );
1382         if ( !BACKSQL_SUCCESS( rc ) ) {
1383                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1384                         "error executing query\n", 0, 0, 0 );
1385                 backsql_PrintErrors( bi->db_env, bsi->bsi_dbh, sth, rc );
1386                 SQLFreeStmt( sth, SQL_DROP );
1387                 bsi->bsi_status = LDAP_OTHER;
1388                 return BACKSQL_AVL_CONTINUE;
1389         }
1390
1391         backsql_BindRowAsStrings( sth, &row );
1392         rc = SQLFetch( sth );
1393         for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1394                 struct berval           dn;
1395                 backsql_entryID         *c_id = NULL;
1396
1397                 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1398
1399                 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1400                         continue;
1401                 }
1402
1403                 c_id = (backsql_entryID *)ch_calloc( 1, 
1404                                 sizeof( backsql_entryID ) );
1405 #ifdef BACKSQL_ARBITRARY_KEY
1406                 ber_str2bv( row.cols[ 0 ], 0, 1, &c_id->eid_id );
1407                 ber_str2bv( row.cols[ 1 ], 0, 1, &c_id->eid_keyval );
1408 #else /* ! BACKSQL_ARBITRARY_KEY */
1409                 c_id->eid_id = strtol( row.cols[ 0 ], NULL, 0 );
1410                 c_id->eid_keyval = strtol( row.cols[ 1 ], NULL, 0 );
1411 #endif /* ! BACKSQL_ARBITRARY_KEY */
1412                 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1413
1414                 if ( dn.bv_val == row.cols[ 3 ] ) {
1415                         ber_dupbv( &c_id->eid_dn, &dn );
1416                 } else {
1417                         c_id->eid_dn = dn;
1418                 }
1419
1420                 /* append at end of list ... */
1421                 c_id->eid_next = NULL;
1422                 *bsi->bsi_id_listtail = c_id;
1423                 bsi->bsi_id_listtail = &c_id->eid_next;
1424
1425 #ifdef BACKSQL_ARBITRARY_KEY
1426                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1427                         "added entry id=%s, keyval=%s dn=\"%s\"\n",
1428                         c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1429                         row.cols[ 3 ] );
1430 #else /* ! BACKSQL_ARBITRARY_KEY */
1431                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1432                         "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1433                         c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1434 #endif /* ! BACKSQL_ARBITRARY_KEY */
1435
1436                 /* count candidates, for unchecked limit */
1437                 bsi->bsi_n_candidates--;
1438                 if ( bsi->bsi_n_candidates == -1 ) {
1439                         break;
1440                 }
1441         }
1442         backsql_FreeRow( &row );
1443         SQLFreeStmt( sth, SQL_DROP );
1444
1445         Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1446                         n_candidates - bsi->bsi_n_candidates, 0, 0 );
1447
1448         return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1449 }
1450
1451 int
1452 backsql_search( Operation *op, SlapReply *rs )
1453 {
1454         backsql_info            *bi = (backsql_info *)op->o_bd->be_private;
1455         SQLHDBC                 dbh;
1456         int                     sres;
1457         Entry                   user_entry = { 0 };
1458         int                     manageDSAit;
1459         time_t                  stoptime = 0;
1460         backsql_srch_info       srch_info;
1461         backsql_entryID         *eid = NULL;
1462         struct berval           base;
1463
1464         manageDSAit = get_manageDSAit( op );
1465
1466         Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1467                 "base=\"%s\", filter=\"%s\", scope=%d,", 
1468                 op->o_req_ndn.bv_val,
1469                 op->ors_filterstr.bv_val,
1470                 op->ors_scope );
1471         Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1472                 "attributes to load: %s\n",
1473                 op->ors_deref,
1474                 op->ors_attrsonly,
1475                 op->ors_attrs == NULL ? "all" : "custom list" );
1476
1477         if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1478                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1479                         "search base length (%ld) exceeds max length (%d)\n", 
1480                         op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1481                 /*
1482                  * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1483                  * since it is impossible that such a long DN exists
1484                  * in the backend
1485                  */
1486                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1487                 send_ldap_result( op, rs );
1488                 return 1;
1489         }
1490
1491         sres = backsql_get_db_conn( op, &dbh );
1492         if ( sres != LDAP_SUCCESS ) {
1493                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1494                         "could not get connection handle - exiting\n", 
1495                         0, 0, 0 );
1496                 rs->sr_err = sres;
1497                 rs->sr_text = sres == LDAP_OTHER ?  "SQL-backend error" : NULL;
1498                 send_ldap_result( op, rs );
1499                 return 1;
1500         }
1501
1502         /* compute it anyway; root does not use it */
1503         stoptime = op->o_time + op->ors_tlimit;
1504
1505         base = op->o_req_dn;
1506         if ( backsql_api_dn2odbc( op, rs, &base ) ) {
1507                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1508                         "backsql_api_dn2odbc failed\n", 
1509                         0, 0, 0 );
1510                 rs->sr_err = LDAP_OTHER;
1511                 rs->sr_text = "SQL-backend error";
1512                 send_ldap_result( op, rs );
1513                 return 1;
1514         }
1515
1516         backsql_init_search( &srch_info, &base,
1517                         op->ors_scope,
1518                         op->ors_slimit, op->ors_tlimit,
1519                         stoptime, op->ors_filter,
1520                         dbh, op, rs, op->ors_attrs );
1521
1522         /*
1523          * for each objectclass we try to construct query which gets IDs
1524          * of entries matching LDAP query filter and scope (or at least 
1525          * candidates), and get the IDs
1526          */
1527         srch_info.bsi_n_candidates =
1528                 ( op->ors_limit == NULL /* isroot == FALSE */ ? -2 : 
1529                 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
1530                 ( op->ors_limit->lms_s_unchecked ) ) );
1531         avl_apply( bi->oc_by_oc, backsql_oc_get_candidates,
1532                         &srch_info, BACKSQL_AVL_STOP, AVL_INORDER );
1533         if ( op->ors_limit != NULL      /* isroot == TRUE */
1534                         && op->ors_limit->lms_s_unchecked != -1
1535                         && srch_info.bsi_n_candidates == -1 )
1536         {
1537                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1538                 send_ldap_result( op, rs );
1539                 goto done;
1540         }
1541         
1542         /*
1543          * now we load candidate entries (only those attributes 
1544          * mentioned in attrs and filter), test it against full filter 
1545          * and then send to client
1546          */
1547         for ( eid = srch_info.bsi_id_list;
1548                         eid != NULL; 
1549                         eid = backsql_free_entryID( eid, 1 ) )
1550         {
1551                 int             rc;
1552                 Attribute       *hasSubordinate = NULL,
1553                                 *a = NULL;
1554
1555                 /* check for abandon */
1556                 if ( op->o_abandon ) {
1557                         break;
1558                 }
1559
1560                 /* check time limit */
1561                 if ( op->ors_tlimit != SLAP_NO_LIMIT
1562                                 && slap_get_time() > stoptime )
1563                 {
1564                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
1565                         rs->sr_ctrls = NULL;
1566                         rs->sr_ref = rs->sr_v2ref;
1567                         rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1568                                 : LDAP_REFERRAL;
1569                         send_ldap_result( op, rs );
1570                         goto end_of_search;
1571                 }
1572
1573 #ifdef BACKSQL_ARBITRARY_KEY
1574                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1575                         "for entry id=%s, oc_id=%ld, keyval=%s\n",
1576                         eid->eid_id.bv_val, eid->eid_oc_id,
1577                         eid->eid_keyval.bv_val );
1578 #else /* ! BACKSQL_ARBITRARY_KEY */
1579                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1580                         "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
1581                         eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
1582 #endif /* ! BACKSQL_ARBITRARY_KEY */
1583
1584                 srch_info.bsi_e = &user_entry;
1585                 rc = backsql_id2entry( &srch_info, eid );
1586                 if ( rc != LDAP_SUCCESS ) {
1587                         Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1588                                 "error %d in backsql_id2entry() "
1589                                 "- skipping\n", rc, 0, 0 );
1590                         continue;
1591                 }
1592
1593                 /* check scope */
1594                 switch ( op->ors_scope ) {
1595                 case LDAP_SCOPE_BASE:
1596                 case BACKSQL_SCOPE_BASE_LIKE:
1597                         if ( !bvmatch( &user_entry.e_nname, &op->o_req_ndn ) ) {
1598                                 goto next_entry;
1599                         }
1600                         break;
1601
1602                 case LDAP_SCOPE_ONE:
1603                 {
1604                         struct berval   rdn = user_entry.e_nname;
1605                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
1606                         if ( !dnIsOneLevelRDN( &rdn ) ) {
1607                                 goto next_entry;
1608                         }
1609                         /* fall thru */
1610                 }
1611
1612                 case LDAP_SCOPE_SUBTREE:
1613                         if ( !dnIsSuffix( &user_entry.e_nname, &op->o_req_ndn ) ) {
1614                                 goto next_entry;
1615                         }
1616                         break;
1617                 }
1618
1619                 if ( !manageDSAit &&
1620                                 op->ors_scope != LDAP_SCOPE_BASE &&
1621                                 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
1622                                 is_entry_referral( &user_entry ) )
1623                 {
1624                         BerVarray refs;
1625                         struct berval matched_dn;
1626
1627                         ber_dupbv( &matched_dn, &user_entry.e_name );
1628                         refs = get_entry_referrals( op, &user_entry );
1629                         if ( !refs ) {
1630                                 backsql_srch_info       srch_info2 = { 0 };
1631                                 Entry                   user_entry2 = { 0 };
1632
1633                                 /* retry with the full entry... */
1634                                 backsql_init_search( &srch_info2,
1635                                                 &user_entry.e_name,
1636                                                 LDAP_SCOPE_BASE, 
1637                                                 -1, -1, -1, NULL,
1638                                                 dbh, op, rs, NULL );
1639                                 srch_info2.bsi_e = &user_entry2;
1640                                 rc = backsql_id2entry( &srch_info2, eid );
1641                                 if ( rc == LDAP_SUCCESS ) {
1642                                         if ( is_entry_referral( &user_entry2 ) )
1643                                         {
1644                                                 refs = get_entry_referrals( op,
1645                                                                 &user_entry2 );
1646                                         } /* else: FIXME: inconsistency! */
1647                                         entry_clean( &user_entry2 );
1648                                 }
1649                         }
1650
1651                         if ( refs ) {
1652                                 rs->sr_ref = referral_rewrite( refs,
1653                                                 &matched_dn, &op->o_req_dn,
1654                                                 op->ors_scope );
1655                                 ber_bvarray_free( refs );
1656                         }
1657
1658                         if ( !rs->sr_ref ) {
1659                                 rs->sr_text = "bad_referral object";
1660                         }
1661
1662                         rs->sr_err = LDAP_REFERRAL;
1663                         rs->sr_matched = matched_dn.bv_val;
1664                         send_search_reference( op, rs );
1665
1666                         ber_bvarray_free( rs->sr_ref );
1667                         rs->sr_ref = NULL;
1668                         ber_memfree( matched_dn.bv_val );
1669                         rs->sr_matched = NULL;
1670
1671                         goto next_entry;
1672                 }
1673
1674                 /*
1675                  * We use this flag since we need to parse the filter
1676                  * anyway; we should have used the frontend API function
1677                  * filter_has_subordinates()
1678                  */
1679                 if ( srch_info.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
1680                         rc = backsql_has_children( bi, dbh, &user_entry.e_nname );
1681
1682                         switch ( rc ) {
1683                         case LDAP_COMPARE_TRUE:
1684                         case LDAP_COMPARE_FALSE:
1685                                 hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
1686                                 if ( hasSubordinate != NULL ) {
1687                                         for ( a = user_entry.e_attrs; 
1688                                                         a && a->a_next; 
1689                                                         a = a->a_next );
1690
1691                                         a->a_next = hasSubordinate;
1692                                 }
1693                                 rc = 0;
1694                                 break;
1695
1696                         default:
1697                                 Debug(LDAP_DEBUG_TRACE, 
1698                                         "backsql_search(): "
1699                                         "has_children failed( %d)\n", 
1700                                         rc, 0, 0 );
1701                                 rc = 1;
1702                                 goto next_entry;
1703                         }
1704                 }
1705
1706                 if ( test_filter( op, &user_entry, op->ors_filter )
1707                                 == LDAP_COMPARE_TRUE ) {
1708                         if ( hasSubordinate && !( srch_info.bsi_flags & BSQL_SF_ALL_OPER ) 
1709                                         && !ad_inlist( slap_schema.si_ad_hasSubordinates, op->ors_attrs ) ) {
1710                                 a->a_next = NULL;
1711                                 attr_free( hasSubordinate );
1712                                 hasSubordinate = NULL;
1713                         }
1714
1715                         rs->sr_attrs = op->ors_attrs;
1716                         rs->sr_operational_attrs = NULL;
1717                         rs->sr_entry = &user_entry;
1718                         rs->sr_flags = REP_ENTRY_MODIFIABLE;
1719                         sres = send_search_entry( op, rs );
1720                         rs->sr_entry = NULL;
1721                         rs->sr_attrs = NULL;
1722                         rs->sr_operational_attrs = NULL;
1723
1724                         switch ( sres ) {
1725                         case 0:
1726                                 break;
1727
1728                         default:
1729                                 /*
1730                                  * FIXME: send_search_entry failed;
1731                                  * better stop
1732                                  */
1733                         case -1:
1734                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1735                                         "connection lost\n", 0, 0, 0 );
1736                                 goto end_of_search;
1737                         }
1738                 }
1739
1740 next_entry:;
1741                 entry_clean( &user_entry );
1742
1743                 if ( op->ors_slimit != SLAP_NO_LIMIT
1744                                 && rs->sr_nentries >= op->ors_slimit )
1745                 {
1746                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1747                         send_ldap_result( op, rs );
1748                         goto end_of_search;
1749                 }
1750         }
1751
1752 end_of_search:;
1753         /* in case we got here accidentally */
1754         entry_clean( &user_entry );
1755
1756         if ( rs->sr_nentries > 0 ) {
1757                 rs->sr_ref = rs->sr_v2ref;
1758                 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1759                         : LDAP_REFERRAL;
1760
1761         } else {
1762                 rs->sr_err = srch_info.bsi_status;
1763         }
1764         send_ldap_result( op, rs );
1765
1766         if ( rs->sr_v2ref ) {
1767                 ber_bvarray_free( rs->sr_v2ref );
1768                 rs->sr_v2ref = NULL;
1769         }
1770
1771 done:;
1772         ch_free( srch_info.bsi_attrs );
1773         if ( base.bv_val != op->o_req_ndn.bv_val ) {
1774                 ch_free( base.bv_val );
1775         }
1776
1777         Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
1778         return 0;
1779 }
1780
1781 #endif /* SLAPD_SQL */
1782