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