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