]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/search.c
Completely untested built-in EXTERNAL implementation
[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-2003 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 "slap.h"
29 #include "lber_pvt.h"
30 #include "ldap_pvt.h"
31 #include "back-sql.h"
32 #include "sql-wrap.h"
33 #include "schema-map.h"
34 #include "entry-id.h"
35 #include "util.h"
36
37 #define BACKSQL_STOP            0
38 #define BACKSQL_CONTINUE        1
39
40 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
41
42 static int
43 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
44 {
45         int             n_attrs = 0;
46         AttributeName   *an = NULL;
47
48         if ( bsi->attrs == NULL ) {
49                 return 1;
50         }
51
52         /*
53          * clear the list (retrieve all attrs)
54          */
55         if ( ad == NULL ) {
56                 ch_free( bsi->attrs );
57                 bsi->attrs = NULL;
58                 return 1;
59         }
60
61         for ( ; bsi->attrs[ n_attrs ].an_name.bv_val; n_attrs++ ) {
62                 an = &bsi->attrs[ n_attrs ];
63                 
64                 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
65                         "attribute '%s' is in list\n", 
66                         an->an_name.bv_val, 0, 0 );
67                 /*
68                  * We can live with strcmp because the attribute 
69                  * list has been normalized before calling be_search
70                  */
71                 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
72                         return 1;
73                 }
74         }
75         
76         Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
77                 "adding '%s' to list\n", ad->ad_cname.bv_val, 0, 0 );
78
79         an = (AttributeName *)ch_realloc( bsi->attrs,
80                         sizeof( AttributeName ) * ( n_attrs + 2 ) );
81         if ( an == NULL ) {
82                 return -1;
83         }
84
85         an[ n_attrs ].an_name = ad->ad_cname;
86         an[ n_attrs ].an_desc = ad;
87         an[ n_attrs + 1 ].an_name.bv_val = NULL;
88         an[ n_attrs + 1 ].an_name.bv_len = 0;
89
90         bsi->attrs = an;
91         
92         return 1;
93 }
94
95 void
96 backsql_init_search(
97         backsql_srch_info       *bsi, 
98         struct berval           *base, 
99         int                     scope, 
100         int                     slimit,
101         int                     tlimit,
102         time_t                  stoptime, 
103         Filter                  *filter, 
104         SQLHDBC                 dbh,
105         Operation               *op,
106         AttributeName           *attrs )
107 {
108         AttributeName           *p;
109         
110         bsi->base_dn = base;
111         bsi->scope = scope;
112         bsi->slimit = slimit;
113         bsi->tlimit = tlimit;
114         bsi->filter = filter;
115         bsi->dbh = dbh;
116         bsi->op = op;
117         bsi->bsi_flags = 0;
118
119         /*
120          * handle "*"
121          */
122         if ( attrs == NULL || an_find( attrs, &AllUser ) ) {
123                 bsi->attrs = NULL;
124
125         } else {
126                 bsi->attrs = (AttributeName *)ch_calloc( 1, 
127                                 sizeof( AttributeName ) );
128                 bsi->attrs[ 0 ].an_name.bv_val = NULL;
129                 bsi->attrs[ 0 ].an_name.bv_len = 0;
130                 
131                 for ( p = attrs; p->an_name.bv_val; p++ ) {
132                         /*
133                          * ignore "1.1"; handle "+"
134                          */
135                         if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
136                                 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
137                                 continue;
138
139                         } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
140                                 continue;
141                         }
142
143                         backsql_attrlist_add( bsi, p->an_desc );
144                 }
145         }
146
147         bsi->abandon = 0;
148         bsi->id_list = NULL;
149         bsi->n_candidates = 0;
150         bsi->stoptime = stoptime;
151         bsi->sel.bb_val.bv_val = NULL;
152         bsi->sel.bb_val.bv_len = 0;
153         bsi->sel.bb_len = 0;
154         bsi->from.bb_val.bv_val = NULL;
155         bsi->from.bb_val.bv_len = 0;
156         bsi->from.bb_len = 0;
157         bsi->join_where.bb_val.bv_val = NULL;
158         bsi->join_where.bb_val.bv_len = 0;
159         bsi->join_where.bb_len = 0;
160         bsi->flt_where.bb_val.bv_val = NULL;
161         bsi->flt_where.bb_val.bv_len = 0;
162         bsi->flt_where.bb_len = 0;
163
164         bsi->status = LDAP_SUCCESS;
165 }
166
167 static int
168 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
169 {
170         int             res;
171
172         if ( !f ) {
173                 return 0;
174         }
175
176         backsql_strfcat( &bsi->flt_where, "c", '(' /* ) */  );
177
178         while ( 1 ) {
179                 res = backsql_process_filter( bsi, f );
180                 if ( res < 0 ) {
181                         /*
182                          * TimesTen : If the query has no answers,
183                          * don't bother to run the query.
184                          */
185                         return -1;
186                 }
187  
188                 f = f->f_next;
189                 if ( f == NULL ) {
190                         break;
191                 }
192
193                 switch ( op ) {
194                 case LDAP_FILTER_AND:
195                         backsql_strfcat( &bsi->flt_where, "l",
196                                         (ber_len_t)sizeof( " AND " ) - 1, 
197                                                 " AND " );
198                         break;
199
200                 case LDAP_FILTER_OR:
201                         backsql_strfcat( &bsi->flt_where, "l",
202                                         (ber_len_t)sizeof( " OR " ) - 1,
203                                                 " OR " );
204                         break;
205                 }
206         }
207
208         backsql_strfcat( &bsi->flt_where, "c", /* ( */ ')' );
209
210         return 1;
211 }
212
213 static int
214 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f )
215 {
216         int                     i;
217         backsql_at_map_rec      *at;
218         backsql_info            *bi = (backsql_info *)bsi->op->o_bd->be_private;
219         int                     casefold = 0;
220
221         if ( !f ) {
222                 return 0;
223         }
224
225         if ( SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
226                         bi->bi_caseIgnoreMatch ) ) {
227                 casefold = 1;
228         }
229
230         at = backsql_ad2at( bsi->oc, f->f_sub_desc );
231
232         assert( at );
233
234         /*
235          * When dealing with case-sensitive strings 
236          * we may omit normalization; however, normalized
237          * SQL filters are more liberal.
238          */
239
240         backsql_strfcat( &bsi->flt_where, "c", '(' /* ) */  );
241
242         /* TimesTen */
243         Debug( LDAP_DEBUG_TRACE, "expr: '%s' '%s'\n", at->sel_expr.bv_val,
244                 at->sel_expr_u.bv_val ? at->sel_expr_u.bv_val : "<NULL>", 0 );
245         if ( casefold && bi->upper_func.bv_val ) {
246                 /*
247                  * If a pre-upper-cased version of the column exists, use it
248                  */
249                 if ( at->sel_expr_u.bv_val ) {
250                         backsql_strfcat( &bsi->flt_where, 
251                                         "bl",
252                                         &at->sel_expr_u,
253                                         (ber_len_t)sizeof( " LIKE '" ) - 1,
254                                                 " LIKE '" );
255                 } else {
256                         backsql_strfcat( &bsi->flt_where, 
257                                         "bcbcl",
258                                         &bi->upper_func,
259                                         '(',
260                                         &at->sel_expr,
261                                         ')', 
262                                         (ber_len_t)sizeof( " LIKE '" ) - 1,
263                                                 " LIKE '" );
264                 }
265         } else {
266                 backsql_strfcat( &bsi->flt_where, "bl",
267                                 &at->sel_expr,
268                                 (ber_len_t)sizeof( " LIKE '" ) - 1, " LIKE '" );
269         }
270  
271         if ( f->f_sub_initial.bv_val != NULL ) {
272                 size_t  start;
273
274                 start = bsi->flt_where.bb_val.bv_len;
275                 backsql_strfcat( &bsi->flt_where, "b",
276                                 &f->f_sub_initial );
277                 if ( casefold && bi->upper_func.bv_val ) {
278                         ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
279                 }
280         }
281
282         backsql_strfcat( &bsi->flt_where, "c", '%' );
283
284         if ( f->f_sub_any != NULL ) {
285                 for ( i = 0; f->f_sub_any[ i ].bv_val != NULL; i++ ) {
286                         size_t  start;
287
288 #ifdef BACKSQL_TRACE
289                         Debug( LDAP_DEBUG_TRACE, 
290                                 "==>backsql_process_sub_filter(): "
291                                 "sub_any='%s'\n", f->f_sub_any[ i ].bv_val,
292                                 0, 0 );
293 #endif /* BACKSQL_TRACE */
294
295                         start = bsi->flt_where.bb_val.bv_len;
296                         backsql_strfcat( &bsi->flt_where,
297                                         "bc",
298                                         &f->f_sub_any[ i ],
299                                         '%' );
300                         if ( casefold && bi->upper_func.bv_val ) {
301                                 /*
302                                  * Note: toupper('%') = '%'
303                                  */
304                                 ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
305                         }
306                 }
307
308                 if ( f->f_sub_final.bv_val != NULL ) {
309                         size_t  start;
310
311                         start = bsi->flt_where.bb_val.bv_len;
312                         backsql_strfcat( &bsi->flt_where, "b",
313                                         &f->f_sub_final );
314                         if ( casefold && bi->upper_func.bv_val ) {
315                                 ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
316                         }
317                 }
318         }
319
320         backsql_strfcat( &bsi->flt_where, "l", 
321                         (ber_len_t)sizeof( /* (' */ "')" ) - 1, /* ( */ "')" );
322  
323         return 1;
324 }
325
326 static int
327 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
328 {
329         backsql_info            *bi = (backsql_info *)bsi->op->o_bd->be_private;
330         backsql_at_map_rec      *at;
331         backsql_at_map_rec      oc_attr = {
332                 slap_schema.si_ad_objectClass, BER_BVC(""), BER_BVC(""), 
333                 BER_BVNULL, NULL, NULL, NULL };
334         AttributeDescription    *ad = NULL;
335         int                     done = 0;
336         int                     casefold = 0;
337         int                     rc = 0;
338         struct berval           *filter_value = NULL;
339         MatchingRule            *matching_rule = NULL;
340         struct berval           ordering = BER_BVC("<=");
341
342         Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
343         if ( f == NULL || f->f_choice == SLAPD_FILTER_COMPUTED ) {
344                 return 0;
345         }
346
347         switch( f->f_choice ) {
348         case LDAP_FILTER_OR:
349                 rc = backsql_process_filter_list( bsi, f->f_or, 
350                                 LDAP_FILTER_OR );
351                 done = 1;
352                 break;
353                 
354         case LDAP_FILTER_AND:
355                 rc = backsql_process_filter_list( bsi, f->f_and,
356                                 LDAP_FILTER_AND );
357                 done = 1;
358                 break;
359
360         case LDAP_FILTER_NOT:
361                 backsql_strfcat( &bsi->flt_where, "l",
362                                 (ber_len_t)sizeof( "NOT (" /* ) */ ) - 1,
363                                         "NOT (" /* ) */ );
364                 rc = backsql_process_filter( bsi, f->f_not );
365                 backsql_strfcat( &bsi->flt_where, "c", /* ( */ ')' );
366                 done = 1;
367                 break;
368
369         case LDAP_FILTER_PRESENT:
370                 ad = f->f_desc;
371                 break;
372                 
373         case LDAP_FILTER_EXT:
374                 ad = f->f_mra->ma_desc;
375                 break;
376                 
377         default:
378                 ad = f->f_av_desc;
379                 break;
380         }
381
382         if ( rc == -1 ) {
383                 goto impossible;
384         }
385  
386         if ( done ) {
387                 goto done;
388         }
389
390         /*
391          * Turn structuralObjectClass into objectClass
392          */
393         if ( ad == slap_schema.si_ad_objectClass 
394                         || ad == slap_schema.si_ad_structuralObjectClass ) {
395                 struct berbuf           bb = BB_NULL;
396
397                 at = &oc_attr;
398                 backsql_strfcat( &bb, "cbc",
399                                 '\'', 
400                                 &bsi->oc->oc->soc_cname, 
401                                 '\'' );
402                 at->sel_expr = bb.bb_val;
403
404         } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
405                 /*
406                  * FIXME: this is not robust; e.g. a filter
407                  * '(!(hasSubordinates=TRUE))' fails because
408                  * in SQL it would read 'NOT (1=1)' instead 
409                  * of no condition.  
410                  * Note however that hasSubordinates is boolean, 
411                  * so a more appropriate filter would be 
412                  * '(hasSubordinates=FALSE)'
413                  *
414                  * A more robust search for hasSubordinates
415                  * would * require joining the ldap_entries table
416                  * selecting if there are descendants of the
417                  * candidate.
418                  */
419                 backsql_strfcat( &bsi->flt_where, "l",
420                                 (ber_len_t)sizeof( "1=1" ) - 1, "1=1" );
421                 if ( ad == slap_schema.si_ad_hasSubordinates ) {
422                         /*
423                          * instruct candidate selection algorithm
424                          * and attribute list to try to detect
425                          * if an entry has subordinates
426                          */
427                         bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
428
429                 } else {
430                         /*
431                          * clear attributes to fetch, to require ALL
432                          * and try extended match on all attributes
433                          */
434                         backsql_attrlist_add( bsi, NULL );
435                 }
436                 goto done;
437                 
438         } else {
439                 at = backsql_ad2at( bsi->oc, ad );
440         }
441
442         if ( at == NULL ) {
443                 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
444                         "attribute '%s' is not defined for objectclass '%s'\n",
445                         ad->ad_cname.bv_val, BACKSQL_OC_NAME( bsi->oc ), 0 );
446                 backsql_strfcat( &bsi->flt_where, "l",
447                                 (ber_len_t)sizeof( "1=0" ) - 1, "1=0" );
448                 goto impossible;
449         }
450
451         backsql_merge_from_clause( &bsi->from, &at->from_tbls );
452         /*
453          * need to add this attribute to list of attrs to load,
454          * so that we could do test_filter() later
455          */
456         backsql_attrlist_add( bsi, ad );
457
458         if ( at->join_where.bv_val != NULL 
459                         && strstr( bsi->join_where.bb_val.bv_val, at->join_where.bv_val ) == NULL ) {
460                 backsql_strfcat( &bsi->join_where, "lb",
461                                 (ber_len_t)sizeof( " AND " ) - 1, " AND ",
462                                 &at->join_where );
463         }
464
465         switch ( f->f_choice ) {
466         case LDAP_FILTER_EQUALITY:
467                 filter_value = &f->f_av_value;
468                 matching_rule = ad->ad_type->sat_equality;
469
470                 goto equality_match;
471
472                 /* fail over into next case */
473                 
474         case LDAP_FILTER_EXT:
475                 filter_value = &f->f_mra->ma_value;
476                 matching_rule = f->f_mr_rule;
477
478 equality_match:;
479                 if ( SLAP_MR_ASSOCIATED( matching_rule,
480                                         bi->bi_caseIgnoreMatch ) ) {
481                         casefold = 1;
482                 }
483
484                 /*
485                  * maybe we should check type of at->sel_expr here somehow,
486                  * to know whether upper_func is applicable, but for now
487                  * upper_func stuff is made for Oracle, where UPPER is
488                  * safely applicable to NUMBER etc.
489                  */
490                 if ( casefold && bi->upper_func.bv_val ) {
491                         size_t  start;
492
493                         if ( at->sel_expr_u.bv_val ) {
494                                 backsql_strfcat( &bsi->flt_where, "cbl",
495                                                 '(',
496                                                 &at->sel_expr_u, 
497                                                 (ber_len_t)sizeof( "='" ) - 1,
498                                                         "='" );
499                         } else {
500                                 backsql_strfcat( &bsi->flt_where, "cbcbl",
501                                                 '(' /* ) */ ,
502                                                 &bi->upper_func,
503                                                 '(' /* ) */ ,
504                                                 &at->sel_expr,
505                                                 (ber_len_t)sizeof( /* ( */ ")='" ) - 1,
506                                                         /* ( */ ")='" );
507                         }
508
509                         start = bsi->flt_where.bb_val.bv_len;
510
511                         backsql_strfcat( &bsi->flt_where, "bl",
512                                         filter_value, 
513                                         (ber_len_t)sizeof( /* (' */ "')" ) - 1,
514                                                 /* (' */ "')" );
515
516                         ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
517
518                 } else {
519                         backsql_strfcat( &bsi->flt_where, "cblbl",
520                                         '(',
521                                         &at->sel_expr,
522                                         (ber_len_t)sizeof( "='" ) - 1, "='",
523                                         filter_value,
524                                         (ber_len_t)sizeof( /* (' */ "')" ) - 1,
525                                                 /* (' */ "')" );
526                 }
527                 break;
528
529         case LDAP_FILTER_GE:
530                 ordering.bv_val = ">=";
531
532                 /* fall thru to next case */
533                 
534         case LDAP_FILTER_LE:
535                 if ( SLAP_MR_ASSOCIATED( ad->ad_type->sat_ordering,
536                                 bi->bi_caseIgnoreMatch ) ) {
537                         casefold = 1;
538                 }
539
540                 /*
541                  * FIXME: should we uppercase the operands?
542                  */
543                 if ( casefold && bi->upper_func.bv_val ) {
544                         size_t  start;
545
546                         if ( at->sel_expr_u.bv_val ) {
547                                 backsql_strfcat( &bsi->flt_where, "cbbc",
548                                                 '(',
549                                                 &at->sel_expr_u, 
550                                                 &ordering,
551                                                 '\'' );
552                         } else {
553                                 backsql_strfcat( &bsi->flt_where, "cbcbcbc",
554                                                 '(' /* ) */ ,
555                                                 &bi->upper_func,
556                                                 '(' /* ) */ ,
557                                                 &at->sel_expr,
558                                                 /* ( */ ')',
559                                                 &ordering,
560                                                 '\'' );
561                         }
562
563                         start = bsi->flt_where.bb_val.bv_len;
564
565                         backsql_strfcat( &bsi->flt_where, "bl",
566                                         filter_value, 
567                                         (ber_len_t)sizeof( /* (' */ "')" ) - 1,
568                                                 /* (' */ "')" );
569
570                         ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
571                 
572                 } else {
573                         backsql_strfcat( &bsi->flt_where, "cbbcbl",
574                                         '(' /* ) */ ,
575                                         &at->sel_expr,
576                                         &ordering,
577                                         '\'',
578                                         &f->f_av_value,
579                                         (ber_len_t)sizeof( /* (' */ "')" ) - 1,
580                                                 /* ( */ "')" );
581                 }
582                 break;
583
584         case LDAP_FILTER_PRESENT:
585                 backsql_strfcat( &bsi->flt_where, "lbl",
586                                 (ber_len_t)sizeof( "NOT (" ) - 1, "NOT (", 
587                                 &at->sel_expr, 
588                                 (ber_len_t)sizeof( " IS NULL)" ) - 1, " IS NULL)" );
589                 break;
590
591         case LDAP_FILTER_SUBSTRINGS:
592                 backsql_process_sub_filter( bsi, f );
593                 break;
594
595         case LDAP_FILTER_APPROX:
596                 /* we do our best */
597
598                 /*
599                  * maybe we should check type of at->sel_expr here somehow,
600                  * to know whether upper_func is applicable, but for now
601                  * upper_func stuff is made for Oracle, where UPPER is
602                  * safely applicable to NUMBER etc.
603                  */
604                 if ( bi->upper_func.bv_val ) {
605                         size_t  start;
606
607                         if ( at->sel_expr_u.bv_val ) {
608                                 backsql_strfcat( &bsi->flt_where, "cbl",
609                                                 '(',
610                                                 &at->sel_expr_u, 
611                                                 (ber_len_t)sizeof( " LIKE '%" ) - 1,
612                                                         " LIKE '%" );
613                         } else {
614                                 backsql_strfcat( &bsi->flt_where, "cbcbl",
615                                                 '(' /* ) */ ,
616                                                 &bi->upper_func,
617                                                 '(' /* ) */ ,
618                                                 &at->sel_expr,
619                                                 (ber_len_t)sizeof( /* ( */ ") LIKE '%" ) - 1,
620                                                         /* ( */ ") LIKE '%" );
621                         }
622
623                         start = bsi->flt_where.bb_val.bv_len;
624
625                         backsql_strfcat( &bsi->flt_where, "bl",
626                                         &f->f_av_value, 
627                                         (ber_len_t)sizeof( /* (' */ "%')" ) - 1,
628                                                 /* (' */ "%')" );
629
630                         ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
631
632                 } else {
633                         backsql_strfcat( &bsi->flt_where, "cblbl",
634                                         '(',
635                                         &at->sel_expr,
636                                         (ber_len_t)sizeof( " LIKE '%" ) - 1,
637                                                 " LIKE '%",
638                                         &f->f_av_value,
639                                         (ber_len_t)sizeof( /* (' */ "%')" ) - 1,
640                                                 /* (' */ "%')" );
641                 }
642                 break;
643
644         default:
645                 /* unhandled filter type; should not happen */
646                 assert( 0 );
647                 backsql_strfcat( &bsi->flt_where, "l",
648                                 (ber_len_t)sizeof( "1=1" ) - 1, "1=1" );
649                 break;
650
651         }
652
653 done:
654         if ( oc_attr.sel_expr.bv_val != NULL ) {
655                 free( oc_attr.sel_expr.bv_val );
656         }
657         
658         Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter()\n", 0, 0, 0 );
659         return 1;
660
661 impossible:
662         if ( oc_attr.sel_expr.bv_val != NULL ) {
663                 free( oc_attr.sel_expr.bv_val );
664         }
665         Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter() returns -1\n",
666                         0, 0, 0 );
667         return -1;
668 }
669
670 static int
671 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
672 {
673         backsql_info    *bi = (backsql_info *)bsi->op->o_bd->be_private;
674         int             rc;
675
676         assert( query );
677         query->bv_val = NULL;
678         query->bv_len = 0;
679
680         Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
681         bsi->sel.bb_val.bv_val = NULL;
682         bsi->sel.bb_val.bv_len = 0;
683         bsi->sel.bb_len = 0;
684         bsi->from.bb_val.bv_val = NULL;
685         bsi->from.bb_val.bv_len = 0;
686         bsi->from.bb_len = 0;
687         bsi->join_where.bb_val.bv_val = NULL;
688         bsi->join_where.bb_val.bv_len = 0;
689         bsi->join_where.bb_len = 0;
690         bsi->flt_where.bb_val.bv_val = NULL;
691         bsi->flt_where.bb_val.bv_len = 0;
692         bsi->flt_where.bb_len = 0;
693
694         backsql_strfcat( &bsi->sel, "lbcbc",
695                         (ber_len_t)sizeof( "SELECT DISTINCT ldap_entries.id," ) - 1,
696                                 "SELECT DISTINCT ldap_entries.id,", 
697                         &bsi->oc->keytbl, 
698                         '.', 
699                         &bsi->oc->keycol, 
700                         ',' );
701
702         if ( bi->strcast_func.bv_val ) {
703                 backsql_strfcat( &bsi->sel, "blbl",
704                                 &bi->strcast_func, 
705                                 (ber_len_t)sizeof( "('" /* ') */ ) - 1,
706                                         "('" /* ') */ ,
707                                 &bsi->oc->oc->soc_cname,
708                                 (ber_len_t)sizeof( /* (' */ "')" ) - 1,
709                                         /* (' */ "')" );
710         } else {
711                 backsql_strfcat( &bsi->sel, "cbc",
712                                 '\'',
713                                 &bsi->oc->oc->soc_cname,
714                                 '\'' );
715         }
716         backsql_strfcat( &bsi->sel, "l",
717                         (ber_len_t)sizeof( " AS objectClass,ldap_entries.dn AS dn" ) - 1,
718                         " AS objectClass,ldap_entries.dn AS dn" );
719
720         backsql_strfcat( &bsi->from, "lb",
721                         (ber_len_t)sizeof( " FROM ldap_entries," ) - 1,
722                                 " FROM ldap_entries,",
723                         &bsi->oc->keytbl );
724
725         backsql_strfcat( &bsi->join_where, "lbcbl",
726                         (ber_len_t)sizeof( " WHERE " ) - 1, " WHERE ",
727                         &bsi->oc->keytbl,
728                         '.',
729                         &bsi->oc->keycol,
730                         (ber_len_t)sizeof( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ) - 1,
731                                 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
732
733         switch ( bsi->scope ) {
734         case LDAP_SCOPE_BASE:
735                 if ( bi->upper_func.bv_val ) {
736                         backsql_strfcat( &bsi->join_where, "blbcb",
737                                         &bi->upper_func,
738                                         (ber_len_t)sizeof( "(ldap_entries.dn)=" ) - 1,
739                                                 "(ldap_entries.dn)=",
740                                         &bi->upper_func_open,
741                                         '?', 
742                                         &bi->upper_func_close );
743                 } else {
744                         backsql_strfcat( &bsi->join_where, "l",
745                                         (ber_len_t)sizeof( "ldap_entries.dn=?" ) - 1,
746                                                 "ldap_entries.dn=?" );
747                 }
748                 break;
749                 
750         case LDAP_SCOPE_ONELEVEL:
751                 backsql_strfcat( &bsi->join_where, "l",
752                                 (ber_len_t)sizeof( "ldap_entries.parent=?" ) - 1,
753                                         "ldap_entries.parent=?" );
754                 break;
755
756         case LDAP_SCOPE_SUBTREE:
757                 if ( bi->upper_func.bv_val ) {
758                         backsql_strfcat( &bsi->join_where, "blbcb",
759                                         &bi->upper_func,
760                                         (ber_len_t)sizeof( "(ldap_entries.dn) LIKE " ) - 1,
761                                                 "(ldap_entries.dn) LIKE ",
762                                         &bi->upper_func_open,
763                                         '?', 
764                                         &bi->upper_func_close );
765                 } else {
766                         backsql_strfcat( &bsi->join_where, "l",
767                                         (ber_len_t)sizeof( "ldap_entries.dn LIKE ?" ) - 1,
768                                                 "ldap_entries.dn LIKE ?" );
769                 }
770
771                 break;
772
773         default:
774                 assert( 0 );
775         }
776
777         rc = backsql_process_filter( bsi, bsi->filter );
778         if ( rc > 0 ) {
779                 struct berbuf   bb = BB_NULL;
780
781                 backsql_strfcat( &bb, "bbblb",
782                                 &bsi->sel.bb_val,
783                                 &bsi->from.bb_val, 
784                                 &bsi->join_where.bb_val,
785                                 (ber_len_t)sizeof( " AND " ) - 1, " AND ",
786                                 &bsi->flt_where.bb_val );
787
788                 *query = bb.bb_val;
789
790         } else if ( rc < 0 ) {
791                 /* 
792                  * Indicates that there's no possible way the filter matches
793                  * anything.  No need to issue the query
794                  */
795                 free( query->bv_val );
796                 query->bv_val = NULL;
797         }
798  
799         free( bsi->sel.bb_val.bv_val );
800         bsi->sel.bb_val.bv_len = 0;
801         bsi->sel.bb_len = 0;
802         free( bsi->from.bb_val.bv_val );
803         bsi->from.bb_val.bv_len = 0;
804         bsi->from.bb_len = 0;
805         free( bsi->join_where.bb_val.bv_val );
806         bsi->join_where.bb_val.bv_len = 0;
807         bsi->join_where.bb_len = 0;
808         free( bsi->flt_where.bb_val.bv_val );
809         bsi->flt_where.bb_val.bv_len = 0;
810         bsi->flt_where.bb_len = 0;
811         
812         Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
813                 query->bv_val ? query->bv_val : "NULL", 0, 0 );
814         
815         return ( rc <= 0 ? 1 : 0 );
816 }
817
818 static int
819 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
820 {
821         backsql_oc_map_rec      *oc = v_oc;
822         backsql_srch_info       *bsi = v_bsi;
823         backsql_info            *bi = (backsql_info *)bsi->op->o_bd->be_private;
824         struct berval           query;
825         SQLHSTMT                sth;
826         RETCODE                 rc;
827         backsql_entryID         base_id, *c_id;
828         int                     res;
829         BACKSQL_ROW_NTS         row;
830         int                     i;
831         int                     j;
832
833         int                     n_candidates = bsi->n_candidates;
834
835         bsi->status = LDAP_SUCCESS;
836  
837         Debug(  LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc='%s'\n",
838                         BACKSQL_OC_NAME( oc ), 0, 0 );
839
840         if ( bsi->n_candidates == -1 ) {
841                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
842                         "unchecked limit has been overcome\n", 0, 0, 0 );
843                 /* should never get here */
844                 assert( 0 );
845                 bsi->status = LDAP_ADMINLIMIT_EXCEEDED;
846                 return BACKSQL_STOP;
847         }
848         
849         bsi->oc = oc;
850         res = backsql_srch_query( bsi, &query );
851         if ( res ) {
852                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
853                         "error while constructing query for objectclass '%s'\n",
854                         oc->oc->soc_cname.bv_val, 0, 0 );
855                 /*
856                  * FIXME: need to separate errors from legally
857                  * impossible filters
858                  */
859                 bsi->status = LDAP_SUCCESS;
860                 return BACKSQL_CONTINUE;
861         }
862
863         if ( query.bv_val == NULL ) {
864                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
865                         "could not construct query for objectclass '%s'\n",
866                         oc->oc->soc_cname.bv_val, 0, 0 );
867                 bsi->status = LDAP_SUCCESS;
868                 return BACKSQL_CONTINUE;
869         }
870
871         Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n", 
872                         query.bv_val, 0, 0 );
873
874         rc = backsql_Prepare( bsi->dbh, &sth, query.bv_val, 0 );
875         free( query.bv_val );
876         if ( rc != SQL_SUCCESS ) {
877                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
878                         "error preparing query\n", 0, 0, 0 );
879                 backsql_PrintErrors( bi->db_env, bsi->dbh, sth, rc );
880                 bsi->status = LDAP_OTHER;
881                 return BACKSQL_CONTINUE;
882         }
883         
884         Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->oc->id, 0, 0 );
885
886         if ( backsql_BindParamID( sth, 1, &bsi->oc->id ) != SQL_SUCCESS ) {
887                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
888                         "error binding objectclass id parameter\n", 0, 0, 0 );
889                 bsi->status = LDAP_OTHER;
890                 return BACKSQL_CONTINUE;
891         }
892
893         switch ( bsi->scope ) {
894         case LDAP_SCOPE_BASE:
895                 Debug( LDAP_DEBUG_TRACE, "(base)dn: '%s'\n",
896                                 bsi->base_dn->bv_val, 0, 0 );
897
898                 rc = backsql_BindParamStr( sth, 2, bsi->base_dn->bv_val,
899                                 BACKSQL_MAX_DN_LEN );
900                 if ( rc != SQL_SUCCESS ) {
901                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
902                                 "error binding base_dn parameter\n", 0, 0, 0 );
903                         backsql_PrintErrors( bi->db_env, bsi->dbh, 
904                                         sth, rc );
905                         bsi->status = LDAP_OTHER;
906                         return BACKSQL_CONTINUE;
907                 }
908                 break;
909
910         case LDAP_SCOPE_SUBTREE: {
911
912                 /* 
913                  * + 1 because we need room for '%'; this makes a subtree
914                  * search for a DN BACKSQL_MAX_DN_LEN long legal 
915                  * if it returns that DN only
916                  */
917                 char            temp_base_dn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
918
919                 /*
920                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
921                  * however this should be handled earlier
922                  */
923                 assert( bsi->base_dn->bv_len <= BACKSQL_MAX_DN_LEN );
924                         
925                 /* 
926                  * Sets the parameters for the SQL built earlier
927                  * NOTE that all the databases could actually use 
928                  * the TimesTen version, which would be cleaner 
929                  * and would also eliminate the need for the
930                  * subtree_cond line in the configuration file.  
931                  * For now, I'm leaving it the way it is, 
932                  * so non-TimesTen databases use the original code.
933                  * But at some point this should get cleaned up.
934                  *
935                  * If "dn" is being used, do a suffix search.
936                  * If "dn_ru" is being used, do a prefix search.
937                  */
938                 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
939                         temp_base_dn[ 0 ] = '\0';
940                         for ( i = 0, j = bsi->base_dn->bv_len - 1;
941                                         j >= 0; i++, j--) {
942                                 temp_base_dn[ i ] = bsi->base_dn->bv_val[ j ];
943                         }
944                         temp_base_dn[ i ] = '%';
945                         temp_base_dn[ i + 1 ] = '\0';
946
947                 } else {
948                         temp_base_dn[ 0 ] = '%';
949                         AC_MEMCPY( &temp_base_dn[ 1 ], bsi->base_dn->bv_val,
950                                 bsi->base_dn->bv_len + 1 );
951                 }
952                 ldap_pvt_str2upper( temp_base_dn );
953
954                 Debug( LDAP_DEBUG_TRACE, "(sub)dn: '%s'\n", temp_base_dn,
955                                 0, 0 );
956
957                 rc = backsql_BindParamStr( sth, 2, temp_base_dn, 
958                                 BACKSQL_MAX_DN_LEN );
959                 if ( rc != SQL_SUCCESS ) {
960                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
961                                 "error binding base_dn parameter (2)\n",
962                                 0, 0, 0 );
963                         backsql_PrintErrors( bi->db_env, bsi->dbh, 
964                                         sth, rc );
965                         bsi->status = LDAP_OTHER;
966                         return BACKSQL_CONTINUE;
967                 }
968                 break;
969         }
970
971         case LDAP_SCOPE_ONELEVEL:
972                 res = backsql_dn2id( bi, &base_id, 
973                                 bsi->dbh, bsi->base_dn );
974                 if ( res != LDAP_SUCCESS ) {
975                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
976                                 "could not retrieve base_dn id%s\n",
977                                 res == LDAP_NO_SUCH_OBJECT ? ": no such entry"
978                                 : "", 0, 0 );
979                         bsi->status = res;
980                         return BACKSQL_CONTINUE;
981                 }
982                 
983                 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n", base_id.id,
984                                 0, 0 );
985
986                 rc = backsql_BindParamID( sth, 2, &base_id.id );
987                 backsql_free_entryID( &base_id, 0 );
988                 if ( rc != SQL_SUCCESS ) {
989                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
990                                 "error binding base id parameter\n", 0, 0, 0 );
991                         bsi->status = LDAP_OTHER;
992                         return BACKSQL_CONTINUE;
993                 }
994                 break;
995         }
996         
997         rc = SQLExecute( sth );
998         if ( !BACKSQL_SUCCESS( rc ) ) {
999                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1000                         "error executing query\n", 0, 0, 0 );
1001                 backsql_PrintErrors( bi->db_env, bsi->dbh, sth, rc );
1002                 SQLFreeStmt( sth, SQL_DROP );
1003                 bsi->status = LDAP_OTHER;
1004                 return BACKSQL_CONTINUE;
1005         }
1006
1007         backsql_BindRowAsStrings( sth, &row );
1008         rc = SQLFetch( sth );
1009         for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1010                 c_id = (backsql_entryID *)ch_calloc( 1, 
1011                                 sizeof( backsql_entryID ) );
1012                 c_id->id = strtol( row.cols[ 0 ], NULL, 0 );
1013                 c_id->keyval = strtol( row.cols[ 1 ], NULL, 0 );
1014                 c_id->oc_id = bsi->oc->id;
1015                 ber_str2bv( row.cols[ 3 ], 0, 1, &c_id->dn );
1016                 c_id->next = bsi->id_list;
1017                 bsi->id_list = c_id;
1018                 bsi->n_candidates--;
1019
1020                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1021                         "added entry id=%ld, keyval=%ld dn='%s'\n",
1022                         c_id->id, c_id->keyval, row.cols[ 3 ] );
1023
1024                 if ( bsi->n_candidates == -1 ) {
1025                         break;
1026                 }
1027         }
1028         backsql_FreeRow( &row );
1029         SQLFreeStmt( sth, SQL_DROP );
1030
1031         Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1032                         n_candidates - bsi->n_candidates, 0, 0 );
1033
1034         return ( bsi->n_candidates == -1 ? BACKSQL_STOP : BACKSQL_CONTINUE );
1035 }
1036
1037 int
1038 backsql_search( Operation *op, SlapReply *rs )
1039 {
1040         backsql_info            *bi = (backsql_info *)op->o_bd->be_private;
1041         SQLHDBC                 dbh;
1042         int                     sres;
1043         Entry                   *entry, *res;
1044         int                     manageDSAit;
1045         time_t                  stoptime = 0;
1046         backsql_srch_info       srch_info;
1047         backsql_entryID         *eid = NULL;
1048         struct slap_limits_set  *limit = NULL;
1049         int                     isroot = 0;
1050
1051         manageDSAit = get_manageDSAit( op );
1052
1053         Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1054                 "base='%s', filter='%s', scope=%d,", 
1055                 op->o_req_ndn.bv_val,
1056                 op->oq_search.rs_filterstr.bv_val,
1057                 op->oq_search.rs_scope );
1058         Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1059                 "attributes to load: %s\n",
1060                 op->oq_search.rs_deref,
1061                 op->oq_search.rs_attrsonly,
1062                 op->oq_search.rs_attrs == NULL ? "all" : "custom list" );
1063
1064         if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1065                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1066                         "search base length (%ld) exceeds max length (%ld)\n", 
1067                         op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1068                 /*
1069                  * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1070                  * since it is impossible that such a long DN exists
1071                  * in the backend
1072                  */
1073                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1074                 send_ldap_result( op, rs );
1075                 return 1;
1076         }
1077
1078         sres = backsql_get_db_conn( op, &dbh );
1079         if ( sres != LDAP_SUCCESS ) {
1080                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1081                         "could not get connection handle - exiting\n", 
1082                         0, 0, 0 );
1083                 rs->sr_err = sres;
1084                 rs->sr_text = sres == LDAP_OTHER ?  "SQL-backend error" : NULL;
1085                 send_ldap_result( op, rs );
1086                 return 1;
1087         }
1088
1089         /* if not root, get appropriate limits */
1090         if ( be_isroot( op->o_bd, &op->o_ndn ) ) {
1091                 isroot = 1;
1092         } else {
1093                 ( void ) get_limits( op->o_bd, &op->o_ndn, &limit );
1094         }
1095
1096         /* The time/size limits come first because they require very little
1097          * effort, so there's no chance the candidates are selected and then 
1098          * the request is not honored only because of time/size constraints */
1099
1100         /* if no time limit requested, use soft limit (unless root!) */
1101         if ( isroot ) {
1102                 if ( op->oq_search.rs_tlimit == 0 ) {
1103                         op->oq_search.rs_tlimit = -1;   /* allow root to set no limit */
1104                 }
1105
1106                 if ( op->oq_search.rs_slimit == 0 ) {
1107                         op->oq_search.rs_slimit = -1;
1108                 }
1109
1110         } else {
1111                 /* if no limit is required, use soft limit */
1112                 if ( op->oq_search.rs_tlimit <= 0 ) {
1113                         op->oq_search.rs_tlimit = limit->lms_t_soft;
1114
1115                 /* if requested limit higher than hard limit, abort */
1116                 } else if ( op->oq_search.rs_tlimit > limit->lms_t_hard ) {
1117                         /* no hard limit means use soft instead */
1118                         if ( limit->lms_t_hard == 0
1119                                         && limit->lms_t_soft > -1
1120                                         && op->oq_search.rs_tlimit > limit->lms_t_soft ) {
1121                                 op->oq_search.rs_tlimit = limit->lms_t_soft;
1122
1123                         /* positive hard limit means abort */
1124                         } else if ( limit->lms_t_hard > 0 ) {
1125                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1126                                 send_ldap_result( op, rs );
1127                                 return 0;
1128                         }
1129                 
1130                         /* negative hard limit means no limit */
1131                 }
1132                 
1133                 /* if no limit is required, use soft limit */
1134                 if ( op->oq_search.rs_slimit <= 0 ) {
1135                         op->oq_search.rs_slimit = limit->lms_s_soft;
1136
1137                 /* if requested limit higher than hard limit, abort */
1138                 } else if ( op->oq_search.rs_slimit > limit->lms_s_hard ) {
1139                         /* no hard limit means use soft instead */
1140                         if ( limit->lms_s_hard == 0
1141                                         && limit->lms_s_soft > -1
1142                                         && op->oq_search.rs_slimit > limit->lms_s_soft ) {
1143                                 op->oq_search.rs_slimit = limit->lms_s_soft;
1144
1145                         /* positive hard limit means abort */
1146                         } else if ( limit->lms_s_hard > 0 ) {
1147                                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1148                                 send_ldap_result( op, rs );
1149                                 return 0;
1150                         }
1151                         
1152                         /* negative hard limit means no limit */
1153                 }
1154         }
1155
1156         /* compute it anyway; root does not use it */
1157         stoptime = op->o_time + op->oq_search.rs_tlimit;
1158
1159         backsql_init_search( &srch_info, &op->o_req_dn,
1160                         op->oq_search.rs_scope,
1161                         op->oq_search.rs_slimit, op->oq_search.rs_tlimit,
1162                         stoptime, op->oq_search.rs_filter,
1163                         dbh, op, op->oq_search.rs_attrs );
1164
1165         /*
1166          * for each objectclass we try to construct query which gets IDs
1167          * of entries matching LDAP query filter and scope (or at least 
1168          * candidates), and get the IDs
1169          */
1170         srch_info.n_candidates = ( isroot ? -2 : limit->lms_s_unchecked == -1 
1171                         ? -2 : limit->lms_s_unchecked );
1172         avl_apply( bi->oc_by_oc, backsql_oc_get_candidates,
1173                         &srch_info, BACKSQL_STOP, AVL_INORDER );
1174         if ( !isroot && limit->lms_s_unchecked != -1 ) {
1175                 if ( srch_info.n_candidates == -1 ) {
1176                         rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1177                         send_ldap_result( op, rs );
1178                         goto done;
1179                 }
1180         }
1181         
1182         /*
1183          * now we load candidate entries (only those attributes 
1184          * mentioned in attrs and filter), test it against full filter 
1185          * and then send to client
1186          */
1187         for ( eid = srch_info.id_list; eid != NULL; 
1188                         eid = backsql_free_entryID( eid, 1 ) ) {
1189                 Attribute       *hasSubordinate = NULL,
1190                                 *a = NULL;
1191
1192                 /* check for abandon */
1193                 if ( op->o_abandon ) {
1194                         break;
1195                 }
1196
1197                 /* check time limit */
1198                 if ( op->oq_search.rs_tlimit != -1 && slap_get_time() > stoptime ) {
1199                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
1200                         rs->sr_ctrls = NULL;
1201                         rs->sr_ref = rs->sr_v2ref;
1202                         rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1203                                 : LDAP_REFERRAL;
1204                         send_ldap_result( op, rs );
1205                         goto end_of_search;
1206                 }
1207
1208                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1209                         "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
1210                         eid->id, eid->oc_id, eid->keyval );
1211
1212                 entry = (Entry *)ch_calloc( sizeof( Entry ), 1 );
1213                 res = backsql_id2entry( &srch_info, entry, eid );
1214                 if ( res == NULL ) {
1215                         Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1216                                 "error in backsql_id2entry() "
1217                                 "- skipping entry\n", 0, 0, 0 );
1218                         continue;
1219                 }
1220
1221                 if ( !manageDSAit &&
1222                                 op->oq_search.rs_scope != LDAP_SCOPE_BASE &&
1223                                 is_entry_referral( entry ) ) {
1224                         BerVarray refs;
1225                         struct berval matched_dn;
1226
1227                         ber_dupbv( &matched_dn, &entry->e_name );
1228                         refs = get_entry_referrals( op, entry );
1229                         if ( refs ) {
1230                                 rs->sr_ref = referral_rewrite( refs,
1231                                                 &matched_dn, &op->o_req_dn,
1232                                                 op->oq_search.rs_scope );
1233                                 ber_bvarray_free( refs );
1234                         }
1235
1236                         if (!rs->sr_ref) {
1237                                 rs->sr_text = "bad_referral object";
1238                         }
1239
1240                         rs->sr_err = LDAP_REFERRAL;
1241                         rs->sr_matched = matched_dn.bv_val;
1242                         send_search_reference( op, rs );
1243
1244                         ber_bvarray_free( rs->sr_ref );
1245                         rs->sr_ref = NULL;
1246                         ber_memfree( matched_dn.bv_val );
1247                         rs->sr_matched = NULL;
1248
1249                         continue;
1250                 }
1251
1252                 /*
1253                  * We use this flag since we need to parse the filter
1254                  * anyway; we should have used the frontend API function
1255                  * filter_has_subordinates()
1256                  */
1257                 if ( srch_info.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
1258                         int             rc;
1259
1260                         rc = backsql_has_children( bi, dbh, &entry->e_nname );
1261
1262                         switch( rc ) {
1263                         case LDAP_COMPARE_TRUE:
1264                         case LDAP_COMPARE_FALSE:
1265                                 hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
1266                                 if ( hasSubordinate != NULL ) {
1267                                         for ( a = entry->e_attrs; 
1268                                                         a && a->a_next; 
1269                                                         a = a->a_next );
1270
1271                                         a->a_next = hasSubordinate;
1272                                 }
1273                                 rc = 0;
1274                                 break;
1275
1276                         default:
1277                                 Debug(LDAP_DEBUG_TRACE, 
1278                                         "backsql_search(): "
1279                                         "has_children failed( %d)\n", 
1280                                         rc, 0, 0 );
1281                                 rc = 1;
1282                                 break;
1283                         }
1284
1285                         if ( rc ) {
1286                                 continue;
1287                         }
1288                 }
1289
1290                 if ( test_filter( op, entry, op->oq_search.rs_filter )
1291                                 == LDAP_COMPARE_TRUE ) {
1292                         if ( hasSubordinate && !( srch_info.bsi_flags & BSQL_SF_ALL_OPER ) 
1293                                         && !ad_inlist( slap_schema.si_ad_hasSubordinates, op->oq_search.rs_attrs ) ) {
1294                                 a->a_next = NULL;
1295                                 attr_free( hasSubordinate );
1296                                 hasSubordinate = NULL;
1297                         }
1298
1299 #if 0   /* noop is masked SLAP_CTRL_UPDATE */
1300                         if ( op->o_noop ) {
1301                                 sres = 0;
1302                         } else {
1303 #endif
1304                                 rs->sr_attrs = op->oq_search.rs_attrs;
1305                                 rs->sr_entry = entry;
1306                                 sres = send_search_entry( op, rs );
1307                                 rs->sr_entry = NULL;
1308                                 rs->sr_attrs = NULL;
1309 #if 0
1310                         }
1311 #endif
1312
1313                         switch ( sres ) {
1314                         case 0:
1315                                 break;
1316
1317                         case -1:
1318                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1319                                         "connection lost\n", 0, 0, 0 );
1320                                 goto end_of_search;
1321
1322                         default:
1323                                 /*
1324                                  * FIXME: send_search_entry failed;
1325                                  * better stop
1326                                  */
1327                                 break;
1328                         }
1329                 }
1330                 entry_free( entry );
1331
1332                 if ( op->oq_search.rs_slimit != -1 
1333                                 && rs->sr_nentries >= op->oq_search.rs_slimit ) {
1334                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1335                         send_ldap_result( op, rs );
1336                         goto end_of_search;
1337                 }
1338         }
1339
1340 end_of_search:;
1341
1342         if ( rs->sr_nentries > 0 ) {
1343                 rs->sr_ref = rs->sr_v2ref;
1344                 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1345                         : LDAP_REFERRAL;
1346         } else {
1347                 rs->sr_err = srch_info.status;
1348         }
1349         send_ldap_result( op, rs );
1350
1351         if ( rs->sr_v2ref ) {
1352                 ber_bvarray_free( rs->sr_v2ref );
1353                 rs->sr_v2ref = NULL;
1354         }
1355
1356 done:;
1357         ch_free( srch_info.attrs );
1358
1359         Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
1360         return 0;
1361 }
1362
1363 #endif /* SLAPD_SQL */
1364