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