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