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