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