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