]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/search.c
more on ITS#4868
[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-2007 The OpenLDAP Foundation.
5  * Portions Copyright 1999 Dmitry Kovalev.
6  * Portions Copyright 2002 Pierangelo Masarati.
7  * Portions Copyright 2004 Mark Adamson.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Dmitry Kovalev for inclusion
20  * by OpenLDAP Software.  Additional significant contributors include
21  * Pierangelo Masarati and Mark Adamson.
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include "ac/string.h"
29 #include "ac/ctype.h"
30
31 #include "lutil.h"
32 #include "slap.h"
33 #include "proto-sql.h"
34
35 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
36 static int backsql_process_filter_eq( backsql_srch_info *bsi, 
37                 backsql_at_map_rec *at,
38                 int casefold, struct berval *filter_value );
39 static int backsql_process_filter_like( backsql_srch_info *bsi, 
40                 backsql_at_map_rec *at,
41                 int casefold, struct berval *filter_value );
42 static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, 
43                 backsql_at_map_rec *at );
44
45 /* For LDAP_CONTROL_PAGEDRESULTS, a 32 bit cookie is available to keep track of
46    the state of paged results. The ldap_entries.id and oc_map_id values of the
47    last entry returned are used as the cookie, so 6 bits are used for the OC id
48    and the other 26 for ldap_entries ID number. If your max(oc_map_id) is more
49    than 63, you will need to steal more bits from ldap_entries ID number and
50    put them into the OC ID part of the cookie. */
51 #define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F))
52 #define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6)
53 #define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F)
54
55 static int parse_paged_cookie( Operation *op, SlapReply *rs );
56
57 static void send_paged_response( 
58         Operation *op,
59         SlapReply *rs,
60         ID  *lastid );
61
62 static int
63 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
64 {
65         int             n_attrs = 0;
66         AttributeName   *an = NULL;
67
68         if ( bsi->bsi_attrs == NULL ) {
69                 return 1;
70         }
71
72         /*
73          * clear the list (retrieve all attrs)
74          */
75         if ( ad == NULL ) {
76                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx );
77                 bsi->bsi_attrs = NULL;
78                 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
79                 return 1;
80         }
81
82         /* strip ';binary' */
83         if ( slap_ad_is_binary( ad ) ) {
84                 ad = ad->ad_type->sat_ad;
85         }
86
87         for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
88                 an = &bsi->bsi_attrs[ n_attrs ];
89                 
90                 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
91                         "attribute \"%s\" is in list\n", 
92                         an->an_name.bv_val, 0, 0 );
93                 /*
94                  * We can live with strcmp because the attribute 
95                  * list has been normalized before calling be_search
96                  */
97                 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
98                         return 1;
99                 }
100         }
101         
102         Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
103                 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
104
105         an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
106                         sizeof( AttributeName ) * ( n_attrs + 2 ),
107                         bsi->bsi_op->o_tmpmemctx );
108         if ( an == NULL ) {
109                 return -1;
110         }
111
112         an[ n_attrs ].an_name = ad->ad_cname;
113         an[ n_attrs ].an_desc = ad;
114         BER_BVZERO( &an[ n_attrs + 1 ].an_name );
115
116         bsi->bsi_attrs = an;
117         
118         return 1;
119 }
120
121 /*
122  * Initializes the search structure.
123  * 
124  * If get_base_id != 0, the field bsi_base_id is filled 
125  * with the entryID of bsi_base_ndn; it must be freed
126  * by backsql_free_entryID() when no longer required.
127  *
128  * NOTE: base must be normalized
129  */
130 int
131 backsql_init_search(
132         backsql_srch_info       *bsi, 
133         struct berval           *nbase, 
134         int                     scope, 
135         time_t                  stoptime, 
136         Filter                  *filter, 
137         SQLHDBC                 dbh,
138         Operation               *op,
139         SlapReply               *rs,
140         AttributeName           *attrs,
141         unsigned                flags )
142 {
143         backsql_info            *bi = (backsql_info *)op->o_bd->be_private;
144         int                     rc = LDAP_SUCCESS;
145
146         bsi->bsi_base_ndn = nbase;
147         bsi->bsi_use_subtree_shortcut = 0;
148         BER_BVZERO( &bsi->bsi_base_id.eid_dn );
149         BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
150         bsi->bsi_scope = scope;
151         bsi->bsi_filter = filter;
152         bsi->bsi_dbh = dbh;
153         bsi->bsi_op = op;
154         bsi->bsi_rs = rs;
155         bsi->bsi_flags = BSQL_SF_NONE;
156
157         bsi->bsi_attrs = NULL;
158
159         if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) {
160                 /*
161                  * if requested, simply try to fetch all attributes
162                  */
163                 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
164
165         } else {
166                 if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) {
167                         bsi->bsi_flags |= BSQL_SF_ALL_USER;
168
169                 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) {
170                         bsi->bsi_flags |= BSQL_SF_ALL_OPER;
171                 }
172
173                 if ( attrs == NULL ) {
174                         /* NULL means all user attributes */
175                         bsi->bsi_flags |= BSQL_SF_ALL_USER;
176
177                 } else {
178                         AttributeName   *p;
179                         int             got_oc = 0;
180
181                         bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc(
182                                         sizeof( AttributeName ),
183                                         bsi->bsi_op->o_tmpmemctx );
184                         BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
185         
186                         for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
187                                 if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
188                                         /* handle "*" */
189                                         bsi->bsi_flags |= BSQL_SF_ALL_USER;
190
191                                         /* if all attrs are requested, there's
192                                          * no need to continue */
193                                         if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
194                                                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
195                                                                 bsi->bsi_op->o_tmpmemctx );
196                                                 bsi->bsi_attrs = NULL;
197                                                 break;
198                                         }
199                                         continue;
200
201                                 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
202                                         /* handle "+" */
203                                         bsi->bsi_flags |= BSQL_SF_ALL_OPER;
204
205                                         /* if all attrs are requested, there's
206                                          * no need to continue */
207                                         if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
208                                                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
209                                                                 bsi->bsi_op->o_tmpmemctx );
210                                                 bsi->bsi_attrs = NULL;
211                                                 break;
212                                         }
213                                         continue;
214
215                                 } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
216                                         /* ignore "1.1" */
217                                         continue;
218
219                                 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
220                                         got_oc = 1;
221                                 }
222
223                                 backsql_attrlist_add( bsi, p->an_desc );
224                         }
225
226                         if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) {
227                                 /* add objectClass if not present,
228                                  * because it is required to understand
229                                  * if an entry is a referral, an alias 
230                                  * or so... */
231                                 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
232                         }
233                 }
234
235                 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
236                         AttributeName   *p;
237                         
238                         /* use hints if available */
239                         for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
240                                 if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
241                                         /* handle "*" */
242                                         bsi->bsi_flags |= BSQL_SF_ALL_USER;
243
244                                         /* if all attrs are requested, there's
245                                          * no need to continue */
246                                         if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
247                                                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
248                                                                 bsi->bsi_op->o_tmpmemctx );
249                                                 bsi->bsi_attrs = NULL;
250                                                 break;
251                                         }
252                                         continue;
253
254                                 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
255                                         /* handle "+" */
256                                         bsi->bsi_flags |= BSQL_SF_ALL_OPER;
257
258                                         /* if all attrs are requested, there's
259                                          * no need to continue */
260                                         if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
261                                                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
262                                                                 bsi->bsi_op->o_tmpmemctx );
263                                                 bsi->bsi_attrs = NULL;
264                                                 break;
265                                         }
266                                         continue;
267                                 }
268
269                                 backsql_attrlist_add( bsi, p->an_desc );
270                         }
271
272                 }
273         }
274
275         bsi->bsi_id_list = NULL;
276         bsi->bsi_id_listtail = &bsi->bsi_id_list;
277         bsi->bsi_n_candidates = 0;
278         bsi->bsi_stoptime = stoptime;
279         BER_BVZERO( &bsi->bsi_sel.bb_val );
280         bsi->bsi_sel.bb_len = 0;
281         BER_BVZERO( &bsi->bsi_from.bb_val );
282         bsi->bsi_from.bb_len = 0;
283         BER_BVZERO( &bsi->bsi_join_where.bb_val );
284         bsi->bsi_join_where.bb_len = 0;
285         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
286         bsi->bsi_flt_where.bb_len = 0;
287         bsi->bsi_filter_oc = NULL;
288
289         if ( BACKSQL_IS_GET_ID( flags ) ) {
290                 int     matched = BACKSQL_IS_MATCHED( flags );
291                 int     getentry = BACKSQL_IS_GET_ENTRY( flags );
292                 int     gotit = 0;
293
294                 assert( op->o_bd->be_private != NULL );
295
296                 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
297                                 matched, 1 );
298
299                 /* the entry is collected either if requested for by getentry
300                  * or if get noSuchObject and requested to climb the tree,
301                  * so that a matchedDN or a referral can be returned */
302                 if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) {
303                         if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) {
304                                 assert( bsi->bsi_e != NULL );
305                                 
306                                 if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) )
307                                 {
308                                         gotit = 1;
309                                 }
310                         
311                                 /*
312                                  * let's see if it is a referral and, in case, get it
313                                  */
314                                 backsql_attrlist_add( bsi, slap_schema.si_ad_ref );
315                                 rc = backsql_id2entry( bsi, &bsi->bsi_base_id );
316                                 if ( rc == LDAP_SUCCESS ) {
317                                         if ( is_entry_referral( bsi->bsi_e ) )
318                                         {
319                                                 BerVarray erefs = get_entry_referrals( op, bsi->bsi_e );
320                                                 if ( erefs ) {
321                                                         rc = rs->sr_err = LDAP_REFERRAL;
322                                                         rs->sr_ref = referral_rewrite( erefs,
323                                                                         &bsi->bsi_e->e_nname,
324                                                                         &op->o_req_dn,
325                                                                         scope );
326                                                         ber_bvarray_free( erefs );
327         
328                                                 } else {
329                                                         rc = rs->sr_err = LDAP_OTHER;
330                                                         rs->sr_text = "bad referral object";
331                                                 }
332
333                                         } else if ( !gotit ) {
334                                                 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
335                                         }
336                                 }
337
338                         } else {
339                                 rs->sr_err = rc;
340                         }
341                 }
342         }
343
344         bsi->bsi_status = rc;
345
346         switch ( rc ) {
347         case LDAP_SUCCESS:
348         case LDAP_REFERRAL:
349                 break;
350
351         default:
352                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
353                                 bsi->bsi_op->o_tmpmemctx );
354                 break;
355         }
356
357         return rc;
358 }
359
360 static int
361 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
362 {
363         int             res;
364
365         if ( !f ) {
366                 return 0;
367         }
368
369         backsql_strfcat_x( &bsi->bsi_flt_where,
370                         bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */  );
371
372         while ( 1 ) {
373                 res = backsql_process_filter( bsi, f );
374                 if ( res < 0 ) {
375                         /*
376                          * TimesTen : If the query has no answers,
377                          * don't bother to run the query.
378                          */
379                         return -1;
380                 }
381  
382                 f = f->f_next;
383                 if ( f == NULL ) {
384                         break;
385                 }
386
387                 switch ( op ) {
388                 case LDAP_FILTER_AND:
389                         backsql_strfcat_x( &bsi->bsi_flt_where,
390                                         bsi->bsi_op->o_tmpmemctx, "l",
391                                         (ber_len_t)STRLENOF( " AND " ), 
392                                                 " AND " );
393                         break;
394
395                 case LDAP_FILTER_OR:
396                         backsql_strfcat_x( &bsi->bsi_flt_where,
397                                         bsi->bsi_op->o_tmpmemctx, "l",
398                                         (ber_len_t)STRLENOF( " OR " ),
399                                                 " OR " );
400                         break;
401                 }
402         }
403
404         backsql_strfcat_x( &bsi->bsi_flt_where,
405                         bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
406
407         return 1;
408 }
409
410 static int
411 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
412         backsql_at_map_rec *at )
413 {
414         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
415         int                     i;
416         int                     casefold = 0;
417
418         if ( !f ) {
419                 return 0;
420         }
421
422         /* always uppercase strings by now */
423 #ifdef BACKSQL_UPPERCASE_FILTER
424         if ( f->f_sub_desc->ad_type->sat_substr &&
425                         SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
426                                 bi->sql_caseIgnoreMatch ) )
427 #endif /* BACKSQL_UPPERCASE_FILTER */
428         {
429                 casefold = 1;
430         }
431
432         if ( f->f_sub_desc->ad_type->sat_substr &&
433                         SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
434                                 bi->sql_telephoneNumberMatch ) )
435         {
436
437                 struct berval   bv;
438                 ber_len_t       i, s, a;
439
440                 /*
441                  * to check for matching telephone numbers
442                  * with intermixed chars, e.g. val='1234'
443                  * use
444                  * 
445                  * val LIKE '%1%2%3%4%'
446                  */
447
448                 BER_BVZERO( &bv );
449                 if ( f->f_sub_initial.bv_val ) {
450                         bv.bv_len += f->f_sub_initial.bv_len;
451                 }
452                 if ( f->f_sub_any != NULL ) {
453                         for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
454                                 bv.bv_len += f->f_sub_any[ a ].bv_len;
455                         }
456                 }
457                 if ( f->f_sub_final.bv_val ) {
458                         bv.bv_len += f->f_sub_final.bv_len;
459                 }
460                 bv.bv_len = 2 * bv.bv_len - 1;
461                 bv.bv_val = ch_malloc( bv.bv_len + 1 );
462
463                 s = 0;
464                 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
465                         bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
466                         for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
467                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
468                                 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
469                         }
470                         bv.bv_val[ s + 2 * i - 1 ] = '%';
471                         s += 2 * i;
472                 }
473
474                 if ( f->f_sub_any != NULL ) {
475                         for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
476                                 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
477                                 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
478                                         bv.bv_val[ s + 2 * i - 1 ] = '%';
479                                         bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
480                                 }
481                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
482                                 s += 2 * i;
483                         }
484                 }
485
486                 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
487                         bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
488                         for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
489                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
490                                 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
491                         }
492                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
493                         s += 2 * i;
494                 }
495
496                 bv.bv_val[ s - 1 ] = '\0';
497
498                 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
499                 ch_free( bv.bv_val );
500
501                 return 1;
502         }
503
504         /*
505          * When dealing with case-sensitive strings 
506          * we may omit normalization; however, normalized
507          * SQL filters are more liberal.
508          */
509
510         backsql_strfcat_x( &bsi->bsi_flt_where,
511                         bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */  );
512
513         /* TimesTen */
514         Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
515                 at->bam_ad->ad_cname.bv_val, 0, 0 );
516         Debug(LDAP_DEBUG_TRACE, "   expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
517                 at->bam_sel_expr_u.bv_val ? "' '" : "",
518                 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
519         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
520                 /*
521                  * If a pre-upper-cased version of the column 
522                  * or a precompiled upper function exists, use it
523                  */
524                 backsql_strfcat_x( &bsi->bsi_flt_where, 
525                                 bsi->bsi_op->o_tmpmemctx,
526                                 "bl",
527                                 &at->bam_sel_expr_u,
528                                 (ber_len_t)STRLENOF( " LIKE '" ),
529                                         " LIKE '" );
530
531         } else {
532                 backsql_strfcat_x( &bsi->bsi_flt_where,
533                                 bsi->bsi_op->o_tmpmemctx,
534                                 "bl",
535                                 &at->bam_sel_expr,
536                                 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
537         }
538  
539         if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
540                 ber_len_t       start;
541
542 #ifdef BACKSQL_TRACE
543                 Debug( LDAP_DEBUG_TRACE, 
544                         "==>backsql_process_sub_filter(%s): "
545                         "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
546                         f->f_sub_initial.bv_val, 0 );
547 #endif /* BACKSQL_TRACE */
548
549                 start = bsi->bsi_flt_where.bb_val.bv_len;
550                 backsql_strfcat_x( &bsi->bsi_flt_where,
551                                 bsi->bsi_op->o_tmpmemctx,
552                                 "b",
553                                 &f->f_sub_initial );
554                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
555                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
556                 }
557         }
558
559         backsql_strfcat_x( &bsi->bsi_flt_where,
560                         bsi->bsi_op->o_tmpmemctx,
561                         "c", '%' );
562
563         if ( f->f_sub_any != NULL ) {
564                 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
565                         ber_len_t       start;
566
567 #ifdef BACKSQL_TRACE
568                         Debug( LDAP_DEBUG_TRACE, 
569                                 "==>backsql_process_sub_filter(%s): "
570                                 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 
571                                 i, f->f_sub_any[ i ].bv_val );
572 #endif /* BACKSQL_TRACE */
573
574                         start = bsi->bsi_flt_where.bb_val.bv_len;
575                         backsql_strfcat_x( &bsi->bsi_flt_where,
576                                         bsi->bsi_op->o_tmpmemctx,
577                                         "bc",
578                                         &f->f_sub_any[ i ],
579                                         '%' );
580                         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
581                                 /*
582                                  * Note: toupper('%') = '%'
583                                  */
584                                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
585                         }
586                 }
587         }
588
589         if ( !BER_BVISNULL( &f->f_sub_final ) ) {
590                 ber_len_t       start;
591
592 #ifdef BACKSQL_TRACE
593                 Debug( LDAP_DEBUG_TRACE, 
594                         "==>backsql_process_sub_filter(%s): "
595                         "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
596                         f->f_sub_final.bv_val, 0 );
597 #endif /* BACKSQL_TRACE */
598
599                 start = bsi->bsi_flt_where.bb_val.bv_len;
600                 backsql_strfcat_x( &bsi->bsi_flt_where,
601                                 bsi->bsi_op->o_tmpmemctx,
602                                 "b",
603                                 &f->f_sub_final );
604                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
605                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
606                 }
607         }
608
609         backsql_strfcat_x( &bsi->bsi_flt_where,
610                         bsi->bsi_op->o_tmpmemctx,
611                         "l", 
612                         (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
613  
614         return 1;
615 }
616
617 static int
618 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
619 {
620         if ( BER_BVISNULL( from_tbls ) ) {
621                 return LDAP_SUCCESS;
622         }
623
624         if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
625                 char            *start, *end;
626                 struct berval   tmp;
627
628                 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
629
630                 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
631                         if ( end ) {
632                                 end[0] = '\0';
633                         }
634
635                         if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
636                         {
637                                 backsql_strfcat_x( &bsi->bsi_from,
638                                                 bsi->bsi_op->o_tmpmemctx,
639                                                 "cs", ',', start );
640                         }
641
642                         if ( end ) {
643                                 /* in case there are spaces after the comma... */
644                                 for ( start = &end[1]; isspace( start[0] ); start++ );
645                                 if ( start[0] ) {
646                                         end = strchr( start, ',' );
647                                 } else {
648                                         start = NULL;
649                                 }
650                         } else {
651                                 start = NULL;
652                         }
653                 }
654
655                 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
656
657         } else {
658                 backsql_strfcat_x( &bsi->bsi_from,
659                                 bsi->bsi_op->o_tmpmemctx,
660                                 "b", from_tbls );
661         }
662
663         return LDAP_SUCCESS;
664 }
665
666 static int
667 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
668 {
669         backsql_at_map_rec      **vat = NULL;
670         AttributeDescription    *ad = NULL;
671         unsigned                i;
672         int                     done = 0;
673         int                     rc = 0;
674
675         Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
676         if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
677                 struct berval   flt;
678                 char            *msg = NULL;
679
680                 switch ( f->f_result ) {
681                 case LDAP_COMPARE_TRUE:
682                         BER_BVSTR( &flt, "10=10" );
683                         msg = "TRUE";
684                         break;
685
686                 case LDAP_COMPARE_FALSE:
687                         BER_BVSTR( &flt, "11=0" );
688                         msg = "FALSE";
689                         break;
690
691                 case SLAPD_COMPARE_UNDEFINED:
692                         BER_BVSTR( &flt, "12=0" );
693                         msg = "UNDEFINED";
694                         break;
695
696                 default:
697                         rc = -1;
698                         goto done;
699                 }
700
701                 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
702                         "filter computed (%s)\n", msg, 0, 0 );
703                 backsql_strfcat_x( &bsi->bsi_flt_where,
704                                 bsi->bsi_op->o_tmpmemctx, "b", &flt );
705                 rc = 1;
706                 goto done;
707         }
708
709         switch( f->f_choice ) {
710         case LDAP_FILTER_OR:
711                 rc = backsql_process_filter_list( bsi, f->f_or, 
712                                 LDAP_FILTER_OR );
713                 done = 1;
714                 break;
715                 
716         case LDAP_FILTER_AND:
717                 rc = backsql_process_filter_list( bsi, f->f_and,
718                                 LDAP_FILTER_AND );
719                 done = 1;
720                 break;
721
722         case LDAP_FILTER_NOT:
723                 backsql_strfcat_x( &bsi->bsi_flt_where,
724                                 bsi->bsi_op->o_tmpmemctx,
725                                 "l",
726                                 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
727                                         "NOT (" /* ) */ );
728                 rc = backsql_process_filter( bsi, f->f_not );
729                 backsql_strfcat_x( &bsi->bsi_flt_where,
730                                 bsi->bsi_op->o_tmpmemctx,
731                                 "c", /* ( */ ')' );
732                 done = 1;
733                 break;
734
735         case LDAP_FILTER_PRESENT:
736                 ad = f->f_desc;
737                 break;
738                 
739         case LDAP_FILTER_EXT:
740                 ad = f->f_mra->ma_desc;
741                 if ( f->f_mr_dnattrs ) {
742                         /*
743                          * if dn attrs filtering is requested, better return 
744                          * success and let test_filter() deal with candidate
745                          * selection; otherwise we'd need to set conditions
746                          * on the contents of the DN, e.g. "SELECT ... FROM
747                          * ldap_entries AS attributeName WHERE attributeName.dn
748                          * like '%attributeName=value%'"
749                          */
750                         backsql_strfcat_x( &bsi->bsi_flt_where,
751                                         bsi->bsi_op->o_tmpmemctx,
752                                         "l",
753                                         (ber_len_t)STRLENOF( "1=1" ), "1=1" );
754                         bsi->bsi_status = LDAP_SUCCESS;
755                         rc = 1;
756                         goto done;
757                 }
758                 break;
759                 
760         default:
761                 ad = f->f_av_desc;
762                 break;
763         }
764
765         if ( rc == -1 ) {
766                 goto done;
767         }
768  
769         if ( done ) {
770                 rc = 1;
771                 goto done;
772         }
773
774         /*
775          * Turn structuralObjectClass into objectClass
776          */
777         if ( ad == slap_schema.si_ad_objectClass 
778                         || ad == slap_schema.si_ad_structuralObjectClass )
779         {
780                 /*
781                  * If the filter is LDAP_FILTER_PRESENT, then it's done;
782                  * otherwise, let's see if we are lucky: filtering
783                  * for "structural" objectclass or ancestor...
784                  */
785                 switch ( f->f_choice ) {
786                 case LDAP_FILTER_EQUALITY:
787                 {
788                         ObjectClass     *oc = oc_bvfind( &f->f_av_value );
789
790                         if ( oc == NULL ) {
791                                 Debug( LDAP_DEBUG_TRACE,
792                                                 "backsql_process_filter(): "
793                                                 "unknown objectClass \"%s\" "
794                                                 "in filter\n",
795                                                 f->f_av_value.bv_val, 0, 0 );
796                                 bsi->bsi_status = LDAP_OTHER;
797                                 rc = -1;
798                                 goto done;
799                         }
800
801                         /*
802                          * "structural" objectClass inheritance:
803                          * - a search for "person" will also return 
804                          *   "inetOrgPerson"
805                          * - a search for "top" will return everything
806                          */
807                         if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
808                                 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
809
810                                 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
811
812                                 backsql_strfcat_x( &bsi->bsi_flt_where,
813                                                 bsi->bsi_op->o_tmpmemctx,
814                                                 "lbl",
815                                                 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
816                                                         "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
817                                                 &bsi->bsi_oc->bom_oc->soc_cname,
818                                                 (ber_len_t)STRLENOF( /* ((' */ "'))" ),
819                                                         /* ((' */ "'))" );
820                                 bsi->bsi_status = LDAP_SUCCESS;
821                                 rc = 1;
822                                 goto done;
823                         }
824
825                         break;
826                 }
827
828                 case LDAP_FILTER_PRESENT:
829                         backsql_strfcat_x( &bsi->bsi_flt_where,
830                                         bsi->bsi_op->o_tmpmemctx,
831                                         "l",
832                                         (ber_len_t)STRLENOF( "3=3" ), "3=3" );
833                         bsi->bsi_status = LDAP_SUCCESS;
834                         rc = 1;
835                         goto done;
836
837                         /* FIXME: LDAP_FILTER_EXT? */
838                         
839                 default:
840                         Debug( LDAP_DEBUG_TRACE,
841                                         "backsql_process_filter(): "
842                                         "illegal/unhandled filter "
843                                         "on objectClass attribute",
844                                         0, 0, 0 );
845                         bsi->bsi_status = LDAP_OTHER;
846                         rc = -1;
847                         goto done;
848                 }
849
850         } else if ( ad == slap_schema.si_ad_entryUUID ) {
851                 unsigned long   oc_id;
852 #ifdef BACKSQL_ARBITRARY_KEY
853                 struct berval   keyval;
854 #else /* ! BACKSQL_ARBITRARY_KEY */
855                 unsigned long   keyval;
856                 char            keyvalbuf[] = "18446744073709551615";
857 #endif /* ! BACKSQL_ARBITRARY_KEY */
858
859                 switch ( f->f_choice ) {
860                 case LDAP_FILTER_EQUALITY:
861                         backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
862
863                         if ( oc_id != bsi->bsi_oc->bom_id ) {
864                                 bsi->bsi_status = LDAP_SUCCESS;
865                                 rc = -1;
866                                 goto done;
867                         }
868
869 #ifdef BACKSQL_ARBITRARY_KEY
870                         backsql_strfcat_x( &bsi->bsi_flt_where,
871                                         bsi->bsi_op->o_tmpmemctx,
872                                         "bcblbc",
873                                         &bsi->bsi_oc->bom_keytbl, '.',
874                                         &bsi->bsi_oc->bom_keycol,
875                                         STRLENOF( " LIKE '" ), " LIKE '",
876                                         &keyval, '\'' );
877 #else /* ! BACKSQL_ARBITRARY_KEY */
878                         snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
879                         backsql_strfcat_x( &bsi->bsi_flt_where,
880                                         bsi->bsi_op->o_tmpmemctx,
881                                         "bcbcs",
882                                         &bsi->bsi_oc->bom_keytbl, '.',
883                                         &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
884 #endif /* ! BACKSQL_ARBITRARY_KEY */
885                         break;
886
887                 case LDAP_FILTER_PRESENT:
888                         backsql_strfcat_x( &bsi->bsi_flt_where,
889                                         bsi->bsi_op->o_tmpmemctx,
890                                         "l",
891                                         (ber_len_t)STRLENOF( "4=4" ), "4=4" );
892                         break;
893
894                 default:
895                         rc = -1;
896                         goto done;
897                 }
898
899                 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
900                 rc = 1;
901                 goto done;
902
903 #ifdef BACKSQL_SYNCPROV
904         } else if ( ad == slap_schema.si_ad_entryCSN ) {
905                 /*
906                  * support for syncrepl as producer...
907                  */
908 #if 0
909                 if ( !bsi->bsi_op->o_sync ) {
910                         /* unsupported at present... */
911                         bsi->bsi_status = LDAP_OTHER;
912                         rc = -1;
913                         goto done;
914                 }
915 #endif
916
917                 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
918
919                 /* if doing a syncrepl, try to return as much as possible,
920                  * and always match the filter */
921                 backsql_strfcat_x( &bsi->bsi_flt_where,
922                                 bsi->bsi_op->o_tmpmemctx,
923                                 "l",
924                                 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
925
926                 /* save for later use in operational attributes */
927                 /* FIXME: saves only the first occurrence, because 
928                  * the filter during updates is written as
929                  * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
930                  * so we want our fake entryCSN to match the greatest
931                  * value
932                  */
933                 if ( bsi->bsi_op->o_private == NULL ) {
934                         bsi->bsi_op->o_private = &f->f_av_value;
935                 }
936                 bsi->bsi_status = LDAP_SUCCESS;
937
938                 rc = 1;
939                 goto done;
940 #endif /* BACKSQL_SYNCPROV */
941
942         } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
943                 /*
944                  * FIXME: this is not robust; e.g. a filter
945                  * '(!(hasSubordinates=TRUE))' fails because
946                  * in SQL it would read 'NOT (1=1)' instead 
947                  * of no condition.  
948                  * Note however that hasSubordinates is boolean, 
949                  * so a more appropriate filter would be 
950                  * '(hasSubordinates=FALSE)'
951                  *
952                  * A more robust search for hasSubordinates
953                  * would * require joining the ldap_entries table
954                  * selecting if there are descendants of the
955                  * candidate.
956                  */
957                 backsql_strfcat_x( &bsi->bsi_flt_where,
958                                 bsi->bsi_op->o_tmpmemctx,
959                                 "l",
960                                 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
961                 if ( ad == slap_schema.si_ad_hasSubordinates ) {
962                         /*
963                          * instruct candidate selection algorithm
964                          * and attribute list to try to detect
965                          * if an entry has subordinates
966                          */
967                         bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
968
969                 } else {
970                         /*
971                          * clear attributes to fetch, to require ALL
972                          * and try extended match on all attributes
973                          */
974                         backsql_attrlist_add( bsi, NULL );
975                 }
976                 rc = 1;
977                 goto done;
978         }
979
980         /*
981          * attribute inheritance:
982          */
983         if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
984                 bsi->bsi_status = LDAP_OTHER;
985                 rc = -1;
986                 goto done;
987         }
988
989         if ( vat == NULL ) {
990                 /* search anyway; other parts of the filter
991                  * may succeeed */
992                 backsql_strfcat_x( &bsi->bsi_flt_where,
993                                 bsi->bsi_op->o_tmpmemctx,
994                                 "l",
995                                 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
996                 bsi->bsi_status = LDAP_SUCCESS;
997                 rc = 1;
998                 goto done;
999         }
1000
1001         /* if required, open extra level of parens */
1002         done = 0;
1003         if ( vat[0]->bam_next || vat[1] ) {
1004                 backsql_strfcat_x( &bsi->bsi_flt_where,
1005                                 bsi->bsi_op->o_tmpmemctx,
1006                                 "c", '(' );
1007                 done = 1;
1008         }
1009
1010         i = 0;
1011 next:;
1012         /* apply attr */
1013         if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
1014                 return -1;
1015         }
1016
1017         /* if more definitions of the same attr, apply */
1018         if ( vat[i]->bam_next ) {
1019                 backsql_strfcat_x( &bsi->bsi_flt_where,
1020                                 bsi->bsi_op->o_tmpmemctx,
1021                                 "l",
1022                         STRLENOF( " OR " ), " OR " );
1023                 vat[i] = vat[i]->bam_next;
1024                 goto next;
1025         }
1026
1027         /* if more descendants of the same attr, apply */
1028         i++;
1029         if ( vat[i] ) {
1030                 backsql_strfcat_x( &bsi->bsi_flt_where,
1031                                 bsi->bsi_op->o_tmpmemctx,
1032                                 "l",
1033                         STRLENOF( " OR " ), " OR " );
1034                 goto next;
1035         }
1036
1037         /* if needed, close extra level of parens */
1038         if ( done ) {
1039                 backsql_strfcat_x( &bsi->bsi_flt_where,
1040                                 bsi->bsi_op->o_tmpmemctx,
1041                                 "c", ')' );
1042         }
1043
1044         rc = 1;
1045
1046 done:;
1047         if ( vat ) {
1048                 ch_free( vat );
1049         }
1050
1051         Debug( LDAP_DEBUG_TRACE,
1052                         "<==backsql_process_filter() %s\n",
1053                         rc == 1 ? "succeeded" : "failed", 0, 0);
1054
1055         return rc;
1056 }
1057
1058 static int
1059 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
1060                 int casefold, struct berval *filter_value )
1061 {
1062         /*
1063          * maybe we should check type of at->sel_expr here somehow,
1064          * to know whether upper_func is applicable, but for now
1065          * upper_func stuff is made for Oracle, where UPPER is
1066          * safely applicable to NUMBER etc.
1067          */
1068         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1069                 ber_len_t       start;
1070
1071                 backsql_strfcat_x( &bsi->bsi_flt_where,
1072                                 bsi->bsi_op->o_tmpmemctx,
1073                                 "cbl",
1074                                 '(', /* ) */
1075                                 &at->bam_sel_expr_u, 
1076                                 (ber_len_t)STRLENOF( "='" ),
1077                                         "='" );
1078
1079                 start = bsi->bsi_flt_where.bb_val.bv_len;
1080
1081                 backsql_strfcat_x( &bsi->bsi_flt_where,
1082                                 bsi->bsi_op->o_tmpmemctx,
1083                                 "bl",
1084                                 filter_value, 
1085                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
1086                                         /* (' */ "')" );
1087
1088                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1089
1090         } else {
1091                 backsql_strfcat_x( &bsi->bsi_flt_where,
1092                                 bsi->bsi_op->o_tmpmemctx,
1093                                 "cblbl",
1094                                 '(', /* ) */
1095                                 &at->bam_sel_expr,
1096                                 (ber_len_t)STRLENOF( "='" ), "='",
1097                                 filter_value,
1098                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
1099                                         /* (' */ "')" );
1100         }
1101
1102         return 1;
1103 }
1104         
1105 static int
1106 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
1107                 int casefold, struct berval *filter_value )
1108 {
1109         /*
1110          * maybe we should check type of at->sel_expr here somehow,
1111          * to know whether upper_func is applicable, but for now
1112          * upper_func stuff is made for Oracle, where UPPER is
1113          * safely applicable to NUMBER etc.
1114          */
1115         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1116                 ber_len_t       start;
1117
1118                 backsql_strfcat_x( &bsi->bsi_flt_where,
1119                                 bsi->bsi_op->o_tmpmemctx,
1120                                 "cbl",
1121                                 '(', /* ) */
1122                                 &at->bam_sel_expr_u, 
1123                                 (ber_len_t)STRLENOF( " LIKE '%" ),
1124                                         " LIKE '%" );
1125
1126                 start = bsi->bsi_flt_where.bb_val.bv_len;
1127
1128                 backsql_strfcat_x( &bsi->bsi_flt_where,
1129                                 bsi->bsi_op->o_tmpmemctx,
1130                                 "bl",
1131                                 filter_value, 
1132                                 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1133                                         /* (' */ "%')" );
1134
1135                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1136
1137         } else {
1138                 backsql_strfcat_x( &bsi->bsi_flt_where,
1139                                 bsi->bsi_op->o_tmpmemctx,
1140                                 "cblbl",
1141                                 '(', /* ) */
1142                                 &at->bam_sel_expr,
1143                                 (ber_len_t)STRLENOF( " LIKE '%" ),
1144                                         " LIKE '%",
1145                                 filter_value,
1146                                 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1147                                         /* (' */ "%')" );
1148         }
1149
1150         return 1;
1151 }
1152
1153 static int
1154 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
1155 {
1156         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1157         int                     casefold = 0;
1158         struct berval           *filter_value = NULL;
1159         MatchingRule            *matching_rule = NULL;
1160         struct berval           ordering = BER_BVC("<=");
1161
1162         Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
1163                 at->bam_ad->ad_cname.bv_val, 0, 0 );
1164
1165         /*
1166          * need to add this attribute to list of attrs to load,
1167          * so that we can do test_filter() later
1168          */
1169         backsql_attrlist_add( bsi, at->bam_ad );
1170
1171         backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
1172
1173         if ( !BER_BVISNULL( &at->bam_join_where )
1174                         && strstr( bsi->bsi_join_where.bb_val.bv_val,
1175                                 at->bam_join_where.bv_val ) == NULL )
1176         {
1177                 backsql_strfcat_x( &bsi->bsi_join_where,
1178                                 bsi->bsi_op->o_tmpmemctx,
1179                                 "lb",
1180                                 (ber_len_t)STRLENOF( " AND " ), " AND ",
1181                                 &at->bam_join_where );
1182         }
1183
1184         switch ( f->f_choice ) {
1185         case LDAP_FILTER_EQUALITY:
1186                 filter_value = &f->f_av_value;
1187                 matching_rule = at->bam_ad->ad_type->sat_equality;
1188
1189                 goto equality_match;
1190
1191                 /* fail over into next case */
1192                 
1193         case LDAP_FILTER_EXT:
1194                 filter_value = &f->f_mra->ma_value;
1195                 matching_rule = f->f_mr_rule;
1196
1197 equality_match:;
1198                 /* always uppercase strings by now */
1199 #ifdef BACKSQL_UPPERCASE_FILTER
1200                 if ( SLAP_MR_ASSOCIATED( matching_rule,
1201                                         bi->sql_caseIgnoreMatch ) )
1202 #endif /* BACKSQL_UPPERCASE_FILTER */
1203                 {
1204                         casefold = 1;
1205                 }
1206
1207                 /* FIXME: directoryString filtering should use a similar
1208                  * approach to deal with non-prettified values like
1209                  * " A  non    prettified   value  ", by using a LIKE
1210                  * filter with all whitespaces collapsed to a single '%' */
1211                 if ( SLAP_MR_ASSOCIATED( matching_rule,
1212                                         bi->sql_telephoneNumberMatch ) )
1213                 {
1214                         struct berval   bv;
1215                         ber_len_t       i;
1216
1217                         /*
1218                          * to check for matching telephone numbers
1219                          * with intermized chars, e.g. val='1234'
1220                          * use
1221                          * 
1222                          * val LIKE '%1%2%3%4%'
1223                          */
1224
1225                         bv.bv_len = 2 * filter_value->bv_len - 1;
1226                         bv.bv_val = ch_malloc( bv.bv_len + 1 );
1227
1228                         bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
1229                         for ( i = 1; i < filter_value->bv_len; i++ ) {
1230                                 bv.bv_val[ 2 * i - 1 ] = '%';
1231                                 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
1232                         }
1233                         bv.bv_val[ 2 * i - 1 ] = '\0';
1234
1235                         (void)backsql_process_filter_like( bsi, at, casefold, &bv );
1236                         ch_free( bv.bv_val );
1237
1238                         break;
1239                 }
1240
1241                 /* NOTE: this is required by objectClass inheritance 
1242                  * and auxiliary objectClass use in filters for slightly
1243                  * more efficient candidate selection. */
1244                 /* FIXME: a bit too many specializations to deal with
1245                  * very specific cases... */
1246                 if ( at->bam_ad == slap_schema.si_ad_objectClass
1247                                 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
1248                 {
1249                         backsql_strfcat_x( &bsi->bsi_flt_where,
1250                                         bsi->bsi_op->o_tmpmemctx,
1251                                         "lbl",
1252                                         (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1253                                                 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1254                                         filter_value,
1255                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
1256                                                 /* (' */ "')" );
1257                         break;
1258                 }
1259
1260                 /*
1261                  * maybe we should check type of at->sel_expr here somehow,
1262                  * to know whether upper_func is applicable, but for now
1263                  * upper_func stuff is made for Oracle, where UPPER is
1264                  * safely applicable to NUMBER etc.
1265                  */
1266                 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1267                 break;
1268
1269         case LDAP_FILTER_GE:
1270                 ordering.bv_val = ">=";
1271
1272                 /* fall thru to next case */
1273                 
1274         case LDAP_FILTER_LE:
1275                 filter_value = &f->f_av_value;
1276                 
1277                 /* always uppercase strings by now */
1278 #ifdef BACKSQL_UPPERCASE_FILTER
1279                 if ( at->bam_ad->ad_type->sat_ordering &&
1280                                 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1281                                         bi->sql_caseIgnoreMatch ) )
1282 #endif /* BACKSQL_UPPERCASE_FILTER */
1283                 {
1284                         casefold = 1;
1285                 }
1286
1287                 /*
1288                  * FIXME: should we uppercase the operands?
1289                  */
1290                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1291                         ber_len_t       start;
1292
1293                         backsql_strfcat_x( &bsi->bsi_flt_where,
1294                                         bsi->bsi_op->o_tmpmemctx,
1295                                         "cbbc",
1296                                         '(', /* ) */
1297                                         &at->bam_sel_expr_u, 
1298                                         &ordering,
1299                                         '\'' );
1300
1301                         start = bsi->bsi_flt_where.bb_val.bv_len;
1302
1303                         backsql_strfcat_x( &bsi->bsi_flt_where,
1304                                         bsi->bsi_op->o_tmpmemctx,
1305                                         "bl",
1306                                         filter_value, 
1307                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
1308                                                 /* (' */ "')" );
1309
1310                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1311                 
1312                 } else {
1313                         backsql_strfcat_x( &bsi->bsi_flt_where,
1314                                         bsi->bsi_op->o_tmpmemctx,
1315                                         "cbbcbl",
1316                                         '(' /* ) */ ,
1317                                         &at->bam_sel_expr,
1318                                         &ordering,
1319                                         '\'',
1320                                         &f->f_av_value,
1321                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
1322                                                 /* ( */ "')" );
1323                 }
1324                 break;
1325
1326         case LDAP_FILTER_PRESENT:
1327                 backsql_strfcat_x( &bsi->bsi_flt_where,
1328                                 bsi->bsi_op->o_tmpmemctx,
1329                                 "lbl",
1330                                 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1331                                         "NOT (", /* ) */
1332                                 &at->bam_sel_expr, 
1333                                 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1334                                         /* ( */ " IS NULL)" );
1335                 break;
1336
1337         case LDAP_FILTER_SUBSTRINGS:
1338                 backsql_process_sub_filter( bsi, f, at );
1339                 break;
1340
1341         case LDAP_FILTER_APPROX:
1342                 /* we do our best */
1343
1344                 /*
1345                  * maybe we should check type of at->sel_expr here somehow,
1346                  * to know whether upper_func is applicable, but for now
1347                  * upper_func stuff is made for Oracle, where UPPER is
1348                  * safely applicable to NUMBER etc.
1349                  */
1350                 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1351                 break;
1352
1353         default:
1354                 /* unhandled filter type; should not happen */
1355                 assert( 0 );
1356                 backsql_strfcat_x( &bsi->bsi_flt_where,
1357                                 bsi->bsi_op->o_tmpmemctx,
1358                                 "l",
1359                                 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1360                 break;
1361
1362         }
1363
1364         Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1365                 at->bam_ad->ad_cname.bv_val, 0, 0 );
1366
1367         return 1;
1368 }
1369
1370 static int
1371 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1372 {
1373         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1374         int                     rc;
1375
1376         assert( query != NULL );
1377         BER_BVZERO( query );
1378
1379         bsi->bsi_use_subtree_shortcut = 0;
1380
1381         Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1382         BER_BVZERO( &bsi->bsi_sel.bb_val );
1383         BER_BVZERO( &bsi->bsi_sel.bb_val );
1384         bsi->bsi_sel.bb_len = 0;
1385         BER_BVZERO( &bsi->bsi_from.bb_val );
1386         bsi->bsi_from.bb_len = 0;
1387         BER_BVZERO( &bsi->bsi_join_where.bb_val );
1388         bsi->bsi_join_where.bb_len = 0;
1389         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1390         bsi->bsi_flt_where.bb_len = 0;
1391
1392         backsql_strfcat_x( &bsi->bsi_sel,
1393                         bsi->bsi_op->o_tmpmemctx,
1394                         "lbcbc",
1395                         (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1396                                 "SELECT DISTINCT ldap_entries.id,", 
1397                         &bsi->bsi_oc->bom_keytbl, 
1398                         '.', 
1399                         &bsi->bsi_oc->bom_keycol, 
1400                         ',' );
1401
1402         if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1403                 backsql_strfcat_x( &bsi->bsi_sel,
1404                                 bsi->bsi_op->o_tmpmemctx,
1405                                 "blbl",
1406                                 &bi->sql_strcast_func, 
1407                                 (ber_len_t)STRLENOF( "('" /* ') */ ),
1408                                         "('" /* ') */ ,
1409                                 &bsi->bsi_oc->bom_oc->soc_cname,
1410                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
1411                                         /* (' */ "')" );
1412         } else {
1413                 backsql_strfcat_x( &bsi->bsi_sel,
1414                                 bsi->bsi_op->o_tmpmemctx,
1415                                 "cbc",
1416                                 '\'',
1417                                 &bsi->bsi_oc->bom_oc->soc_cname,
1418                                 '\'' );
1419         }
1420
1421         backsql_strfcat_x( &bsi->bsi_sel,
1422                         bsi->bsi_op->o_tmpmemctx,
1423                         "b",
1424                         &bi->sql_dn_oc_aliasing );
1425         backsql_strfcat_x( &bsi->bsi_from,
1426                         bsi->bsi_op->o_tmpmemctx,
1427                         "lb",
1428                         (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1429                                 " FROM ldap_entries,",
1430                         &bsi->bsi_oc->bom_keytbl );
1431
1432         backsql_strfcat_x( &bsi->bsi_join_where,
1433                         bsi->bsi_op->o_tmpmemctx,
1434                         "lbcbl",
1435                         (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1436                         &bsi->bsi_oc->bom_keytbl,
1437                         '.',
1438                         &bsi->bsi_oc->bom_keycol,
1439                         (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1440                                 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1441
1442         switch ( bsi->bsi_scope ) {
1443         case LDAP_SCOPE_BASE:
1444                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1445                         backsql_strfcat_x( &bsi->bsi_join_where,
1446                                         bsi->bsi_op->o_tmpmemctx, 
1447                                         "bl",
1448                                         &bi->sql_upper_func,
1449                                         (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1450                                                 "(ldap_entries.dn)=?" );
1451                 } else {
1452                         backsql_strfcat_x( &bsi->bsi_join_where,
1453                                         bsi->bsi_op->o_tmpmemctx,
1454                                         "l",
1455                                         (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1456                                                 "ldap_entries.dn=?" );
1457                 }
1458                 break;
1459                 
1460         case BACKSQL_SCOPE_BASE_LIKE:
1461                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1462                         backsql_strfcat_x( &bsi->bsi_join_where,
1463                                         bsi->bsi_op->o_tmpmemctx,
1464                                         "bl",
1465                                         &bi->sql_upper_func,
1466                                         (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1467                                                 "(ldap_entries.dn) LIKE ?" );
1468                 } else {
1469                         backsql_strfcat_x( &bsi->bsi_join_where,
1470                                         bsi->bsi_op->o_tmpmemctx,
1471                                         "l",
1472                                         (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1473                                                 "ldap_entries.dn LIKE ?" );
1474                 }
1475                 break;
1476                 
1477         case LDAP_SCOPE_ONELEVEL:
1478                 backsql_strfcat_x( &bsi->bsi_join_where,
1479                                 bsi->bsi_op->o_tmpmemctx,
1480                                 "l",
1481                                 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1482                                         "ldap_entries.parent=?" );
1483                 break;
1484
1485         case LDAP_SCOPE_SUBORDINATE:
1486         case LDAP_SCOPE_SUBTREE:
1487                 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1488                         int             i;
1489                         BackendDB       *bd = bsi->bsi_op->o_bd;
1490
1491                         assert( bd->be_nsuffix != NULL );
1492
1493                         for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1494                         {
1495                                 if ( dn_match( &bd->be_nsuffix[ i ],
1496                                                         bsi->bsi_base_ndn ) )
1497                                 {
1498                                         /* pass this to the candidate selection
1499                                          * routine so that the DN is not bound
1500                                          * to the select statement */
1501                                         bsi->bsi_use_subtree_shortcut = 1;
1502                                         break;
1503                                 }
1504                         }
1505                 }
1506
1507                 if ( bsi->bsi_use_subtree_shortcut ) {
1508                         /* Skip the base DN filter, as every entry will match it */
1509                         backsql_strfcat_x( &bsi->bsi_join_where,
1510                                         bsi->bsi_op->o_tmpmemctx,
1511                                         "l",
1512                                         (ber_len_t)STRLENOF( "9=9"), "9=9");
1513
1514                 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1515                         backsql_strfcat_x( &bsi->bsi_join_where,
1516                                         bsi->bsi_op->o_tmpmemctx,
1517                                         "b",
1518                                         &bi->sql_subtree_cond );
1519
1520                 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1521                         backsql_strfcat_x( &bsi->bsi_join_where,
1522                                         bsi->bsi_op->o_tmpmemctx,
1523                                         "bl",
1524                                         &bi->sql_upper_func,
1525                                         (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1526                                                 "(ldap_entries.dn) LIKE ?"  );
1527
1528                 } else {
1529                         backsql_strfcat_x( &bsi->bsi_join_where,
1530                                         bsi->bsi_op->o_tmpmemctx,
1531                                         "l",
1532                                         (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1533                                                 "ldap_entries.dn LIKE ?" );
1534                 }
1535
1536                 break;
1537
1538         default:
1539                 assert( 0 );
1540         }
1541
1542         /* If paged results are in effect, ignore low ldap_entries.id numbers */
1543         if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
1544                 unsigned long lowid = 0;
1545
1546                 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1547                 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
1548                 {
1549                         lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
1550                 }
1551                 if ( lowid ) {
1552                         char lowidstring[48];
1553                         int  lowidlen;
1554
1555                         lowidlen = snprintf( lowidstring, 48, " AND ldap_entries.id>%d", lowid );
1556                         backsql_strfcat_x( &bsi->bsi_join_where,
1557                                         bsi->bsi_op->o_tmpmemctx,
1558                                         "l",
1559                                         (ber_len_t)lowidlen,
1560                                         lowidstring );
1561                 }
1562         }
1563
1564         rc = backsql_process_filter( bsi, bsi->bsi_filter );
1565         if ( rc > 0 ) {
1566                 struct berbuf   bb = BB_NULL;
1567
1568                 backsql_strfcat_x( &bb,
1569                                 bsi->bsi_op->o_tmpmemctx,
1570                                 "bbblb",
1571                                 &bsi->bsi_sel.bb_val,
1572                                 &bsi->bsi_from.bb_val, 
1573                                 &bsi->bsi_join_where.bb_val,
1574                                 (ber_len_t)STRLENOF( " AND " ), " AND ",
1575                                 &bsi->bsi_flt_where.bb_val );
1576
1577                 *query = bb.bb_val;
1578
1579         } else if ( rc < 0 ) {
1580                 /* 
1581                  * Indicates that there's no possible way the filter matches
1582                  * anything.  No need to issue the query
1583                  */
1584                 free( query->bv_val );
1585                 BER_BVZERO( query );
1586         }
1587  
1588         bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1589         BER_BVZERO( &bsi->bsi_sel.bb_val );
1590         bsi->bsi_sel.bb_len = 0;
1591         bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1592         BER_BVZERO( &bsi->bsi_from.bb_val );
1593         bsi->bsi_from.bb_len = 0;
1594         bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1595         BER_BVZERO( &bsi->bsi_join_where.bb_val );
1596         bsi->bsi_join_where.bb_len = 0;
1597         bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1598         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1599         bsi->bsi_flt_where.bb_len = 0;
1600         
1601         Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1602                 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1603         
1604         return ( rc <= 0 ? 1 : 0 );
1605 }
1606
1607 static int
1608 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1609 {
1610         backsql_oc_map_rec      *oc = v_oc;
1611         backsql_srch_info       *bsi = v_bsi;
1612         Operation               *op = bsi->bsi_op;
1613         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1614         struct berval           query;
1615         SQLHSTMT                sth = SQL_NULL_HSTMT;
1616         RETCODE                 rc;
1617         int                     res;
1618         BACKSQL_ROW_NTS         row;
1619         int                     i;
1620         int                     j;
1621         int                     n_candidates = bsi->bsi_n_candidates;
1622
1623         /* 
1624          * + 1 because we need room for '%';
1625          * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1626          * this makes a subtree
1627          * search for a DN BACKSQL_MAX_DN_LEN long legal 
1628          * if it returns that DN only
1629          */
1630         char                    tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1631
1632         bsi->bsi_status = LDAP_SUCCESS;
1633  
1634         Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1635                         BACKSQL_OC_NAME( oc ), 0, 0 );
1636
1637         /* check for abandon */
1638         if ( op->o_abandon ) {
1639                 bsi->bsi_status = SLAPD_ABANDON;
1640                 return BACKSQL_AVL_STOP;
1641         }
1642
1643         /* If paged results have already completed this objectClass, skip it */
1644         if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1645                 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
1646                 {
1647                         return BACKSQL_AVL_CONTINUE;
1648                 }
1649         }
1650
1651         if ( bsi->bsi_n_candidates == -1 ) {
1652                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1653                         "unchecked limit has been overcome\n", 0, 0, 0 );
1654                 /* should never get here */
1655                 assert( 0 );
1656                 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1657                 return BACKSQL_AVL_STOP;
1658         }
1659         
1660         bsi->bsi_oc = oc;
1661         res = backsql_srch_query( bsi, &query );
1662         if ( res ) {
1663                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1664                         "error while constructing query for objectclass \"%s\"\n",
1665                         oc->bom_oc->soc_cname.bv_val, 0, 0 );
1666                 /*
1667                  * FIXME: need to separate errors from legally
1668                  * impossible filters
1669                  */
1670                 switch ( bsi->bsi_status ) {
1671                 case LDAP_SUCCESS:
1672                 case LDAP_UNDEFINED_TYPE:
1673                 case LDAP_NO_SUCH_OBJECT:
1674                         /* we are conservative... */
1675                 default:
1676                         bsi->bsi_status = LDAP_SUCCESS;
1677                         /* try next */
1678                         return BACKSQL_AVL_CONTINUE;
1679
1680                 case LDAP_ADMINLIMIT_EXCEEDED:
1681                 case LDAP_OTHER:
1682                         /* don't try any more */
1683                         return BACKSQL_AVL_STOP;
1684                 }
1685         }
1686
1687         if ( BER_BVISNULL( &query ) ) {
1688                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1689                         "could not construct query for objectclass \"%s\"\n",
1690                         oc->bom_oc->soc_cname.bv_val, 0, 0 );
1691                 bsi->bsi_status = LDAP_SUCCESS;
1692                 return BACKSQL_AVL_CONTINUE;
1693         }
1694
1695         Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n", 
1696                         query.bv_val, 0, 0 );
1697
1698         rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1699         bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1700         BER_BVZERO( &query );
1701         if ( rc != SQL_SUCCESS ) {
1702                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1703                         "error preparing query\n", 0, 0, 0 );
1704                 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1705                 bsi->bsi_status = LDAP_OTHER;
1706                 return BACKSQL_AVL_CONTINUE;
1707         }
1708         
1709         Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1710
1711         rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1712                         &bsi->bsi_oc->bom_id );
1713         if ( rc != SQL_SUCCESS ) {
1714                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1715                         "error binding objectclass id parameter\n", 0, 0, 0 );
1716                 bsi->bsi_status = LDAP_OTHER;
1717                 return BACKSQL_AVL_CONTINUE;
1718         }
1719
1720         switch ( bsi->bsi_scope ) {
1721         case LDAP_SCOPE_BASE:
1722         case BACKSQL_SCOPE_BASE_LIKE:
1723                 /*
1724                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1725                  * however this should be handled earlier
1726                  */
1727                 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1728                         bsi->bsi_status = LDAP_OTHER;
1729                         return BACKSQL_AVL_CONTINUE;
1730                 }
1731
1732                 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1733                                 bsi->bsi_base_ndn->bv_len + 1 );
1734
1735                 /* uppercase DN only if the stored DN can be uppercased
1736                  * for comparison */
1737                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1738                         ldap_pvt_str2upper( tmp_base_ndn );
1739                 }
1740
1741                 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1742                                 tmp_base_ndn, 0, 0 );
1743
1744                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1745                                 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1746                 if ( rc != SQL_SUCCESS ) {
1747                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1748                                 "error binding base_ndn parameter\n", 0, 0, 0 );
1749                         backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 
1750                                         sth, rc );
1751                         bsi->bsi_status = LDAP_OTHER;
1752                         return BACKSQL_AVL_CONTINUE;
1753                 }
1754                 break;
1755
1756         case LDAP_SCOPE_SUBORDINATE:
1757         case LDAP_SCOPE_SUBTREE:
1758         {
1759                 /* if short-cutting the search base,
1760                  * don't bind any parameter */
1761                 if ( bsi->bsi_use_subtree_shortcut ) {
1762                         break;
1763                 }
1764                 
1765                 /*
1766                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1767                  * however this should be handled earlier
1768                  */
1769                 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1770                         bsi->bsi_status = LDAP_OTHER;
1771                         return BACKSQL_AVL_CONTINUE;
1772                 }
1773
1774                 /* 
1775                  * Sets the parameters for the SQL built earlier
1776                  * NOTE that all the databases could actually use 
1777                  * the TimesTen version, which would be cleaner 
1778                  * and would also eliminate the need for the
1779                  * subtree_cond line in the configuration file.  
1780                  * For now, I'm leaving it the way it is, 
1781                  * so non-TimesTen databases use the original code.
1782                  * But at some point this should get cleaned up.
1783                  *
1784                  * If "dn" is being used, do a suffix search.
1785                  * If "dn_ru" is being used, do a prefix search.
1786                  */
1787                 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1788                         tmp_base_ndn[ 0 ] = '\0';
1789
1790                         for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1791                                         j >= 0; i++, j--) {
1792                                 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1793                         }
1794
1795                         if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1796                                 tmp_base_ndn[ i++ ] = ',';
1797                         }
1798
1799                         tmp_base_ndn[ i ] = '%';
1800                         tmp_base_ndn[ i + 1 ] = '\0';
1801
1802                 } else {
1803                         i = 0;
1804
1805                         tmp_base_ndn[ i++ ] = '%';
1806
1807                         if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1808                                 tmp_base_ndn[ i++ ] = ',';
1809                         }
1810
1811                         AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1812                                 bsi->bsi_base_ndn->bv_len + 1 );
1813                 }
1814
1815                 /* uppercase DN only if the stored DN can be uppercased
1816                  * for comparison */
1817                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1818                         ldap_pvt_str2upper( tmp_base_ndn );
1819                 }
1820
1821                 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1822                         Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1823                                 tmp_base_ndn, 0, 0 );
1824                 } else {
1825                         Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1826                                 tmp_base_ndn, 0, 0 );
1827                 }
1828
1829                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1830                                 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1831                 if ( rc != SQL_SUCCESS ) {
1832                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1833                                 "error binding base_ndn parameter (2)\n",
1834                                 0, 0, 0 );
1835                         backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 
1836                                         sth, rc );
1837                         bsi->bsi_status = LDAP_OTHER;
1838                         return BACKSQL_AVL_CONTINUE;
1839                 }
1840                 break;
1841         }
1842
1843         case LDAP_SCOPE_ONELEVEL:
1844                 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1845
1846 #ifdef BACKSQL_ARBITRARY_KEY
1847                 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1848                                 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1849 #else /* ! BACKSQL_ARBITRARY_KEY */
1850                 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1851                                 bsi->bsi_base_id.eid_id, 0, 0 );
1852 #endif /* ! BACKSQL_ARBITRARY_KEY */
1853                 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1854                                 &bsi->bsi_base_id.eid_id );
1855                 if ( rc != SQL_SUCCESS ) {
1856                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1857                                 "error binding base id parameter\n", 0, 0, 0 );
1858                         bsi->bsi_status = LDAP_OTHER;
1859                         return BACKSQL_AVL_CONTINUE;
1860                 }
1861                 break;
1862         }
1863         
1864         rc = SQLExecute( sth );
1865         if ( !BACKSQL_SUCCESS( rc ) ) {
1866                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1867                         "error executing query\n", 0, 0, 0 );
1868                 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1869                 SQLFreeStmt( sth, SQL_DROP );
1870                 bsi->bsi_status = LDAP_OTHER;
1871                 return BACKSQL_AVL_CONTINUE;
1872         }
1873
1874         backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1875         rc = SQLFetch( sth );
1876         for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1877                 struct berval           dn, pdn, ndn;
1878                 backsql_entryID         *c_id = NULL;
1879                 int                     ret;
1880
1881                 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1882
1883                 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1884                         continue;
1885                 }
1886
1887                 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1888                 if ( dn.bv_val != row.cols[ 3 ] ) {
1889                         free( dn.bv_val );
1890                 }
1891
1892                 if ( ret != LDAP_SUCCESS ) {
1893                         continue;
1894                 }
1895
1896                 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1897                         goto cleanup;
1898                 }
1899
1900                 c_id = (backsql_entryID *)op->o_tmpcalloc( 1, 
1901                                 sizeof( backsql_entryID ), op->o_tmpmemctx );
1902 #ifdef BACKSQL_ARBITRARY_KEY
1903                 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1904                                 op->o_tmpmemctx );
1905                 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1906                                 op->o_tmpmemctx );
1907 #else /* ! BACKSQL_ARBITRARY_KEY */
1908                 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1909                         goto cleanup;
1910                 }
1911                 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1912                         goto cleanup;
1913                 }
1914 #endif /* ! BACKSQL_ARBITRARY_KEY */
1915                 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1916
1917                 c_id->eid_dn = pdn;
1918                 c_id->eid_ndn = ndn;
1919
1920                 /* append at end of list ... */
1921                 c_id->eid_next = NULL;
1922                 *bsi->bsi_id_listtail = c_id;
1923                 bsi->bsi_id_listtail = &c_id->eid_next;
1924
1925 #ifdef BACKSQL_ARBITRARY_KEY
1926                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1927                         "added entry id=%s, keyval=%s dn=\"%s\"\n",
1928                         c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1929                         row.cols[ 3 ] );
1930 #else /* ! BACKSQL_ARBITRARY_KEY */
1931                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1932                         "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1933                         c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1934 #endif /* ! BACKSQL_ARBITRARY_KEY */
1935
1936                 /* count candidates, for unchecked limit */
1937                 bsi->bsi_n_candidates--;
1938                 if ( bsi->bsi_n_candidates == -1 ) {
1939                         break;
1940                 }
1941                 continue;
1942
1943 cleanup:;
1944                 if ( !BER_BVISNULL( &pdn ) ) {
1945                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1946                 }
1947                 if ( !BER_BVISNULL( &ndn ) ) {
1948                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1949                 }
1950                 if ( c_id != NULL ) {
1951                         ch_free( c_id );
1952                 }
1953         }
1954         backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1955         SQLFreeStmt( sth, SQL_DROP );
1956
1957         Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1958                         n_candidates - bsi->bsi_n_candidates, 0, 0 );
1959
1960         return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1961 }
1962
1963 int
1964 backsql_search( Operation *op, SlapReply *rs )
1965 {
1966         backsql_info            *bi = (backsql_info *)op->o_bd->be_private;
1967         SQLHDBC                 dbh = SQL_NULL_HDBC;
1968         int                     sres;
1969         Entry                   user_entry = { 0 },
1970                                 base_entry = { 0 };
1971         int                     manageDSAit = get_manageDSAit( op );
1972         time_t                  stoptime = 0;
1973         backsql_srch_info       bsi = { 0 };
1974         backsql_entryID         *eid = NULL;
1975         struct berval           nbase = BER_BVNULL;
1976         unsigned long           lastid = 0;
1977
1978         Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1979                 "base=\"%s\", filter=\"%s\", scope=%d,", 
1980                 op->o_req_ndn.bv_val,
1981                 op->ors_filterstr.bv_val,
1982                 op->ors_scope );
1983         Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1984                 "attributes to load: %s\n",
1985                 op->ors_deref,
1986                 op->ors_attrsonly,
1987                 op->ors_attrs == NULL ? "all" : "custom list" );
1988
1989         if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1990                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1991                         "search base length (%ld) exceeds max length (%d)\n", 
1992                         op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1993                 /*
1994                  * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1995                  * since it is impossible that such a long DN exists
1996                  * in the backend
1997                  */
1998                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1999                 send_ldap_result( op, rs );
2000                 return 1;
2001         }
2002
2003         sres = backsql_get_db_conn( op, &dbh );
2004         if ( sres != LDAP_SUCCESS ) {
2005                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2006                         "could not get connection handle - exiting\n", 
2007                         0, 0, 0 );
2008                 rs->sr_err = sres;
2009                 rs->sr_text = sres == LDAP_OTHER ?  "SQL-backend error" : NULL;
2010                 send_ldap_result( op, rs );
2011                 return 1;
2012         }
2013
2014         /* compute it anyway; root does not use it */
2015         stoptime = op->o_time + op->ors_tlimit;
2016
2017         /* init search */
2018         bsi.bsi_e = &base_entry;
2019         rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2020                         op->ors_scope,
2021                         stoptime, op->ors_filter,
2022                         dbh, op, rs, op->ors_attrs,
2023                         ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
2024         switch ( rs->sr_err ) {
2025         case LDAP_SUCCESS:
2026                 break;
2027
2028         case LDAP_REFERRAL:
2029                 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
2030                                 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
2031                 {
2032                         rs->sr_err = LDAP_SUCCESS;
2033                         rs->sr_text = NULL;
2034                         rs->sr_matched = NULL;
2035                         if ( rs->sr_ref ) {
2036                                 ber_bvarray_free( rs->sr_ref );
2037                                 rs->sr_ref = NULL;
2038                         }
2039                         break;
2040                 }
2041
2042                 /* an entry was created; free it */
2043                 entry_clean( bsi.bsi_e );
2044
2045                 /* fall thru */
2046
2047         default:
2048                 if ( !BER_BVISNULL( &base_entry.e_nname )
2049                                 && !access_allowed( op, &base_entry,
2050                                         slap_schema.si_ad_entry, NULL,
2051                                         ACL_DISCLOSE, NULL ) )
2052                 {
2053                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
2054                         if ( rs->sr_ref ) {
2055                                 ber_bvarray_free( rs->sr_ref );
2056                                 rs->sr_ref = NULL;
2057                         }
2058                         rs->sr_matched = NULL;
2059                         rs->sr_text = NULL;
2060                 }
2061
2062                 send_ldap_result( op, rs );
2063
2064                 if ( rs->sr_ref ) {
2065                         ber_bvarray_free( rs->sr_ref );
2066                         rs->sr_ref = NULL;
2067                 }
2068
2069                 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2070                         entry_clean( &base_entry );
2071                 }
2072
2073                 goto done;
2074         }
2075         /* NOTE: __NEW__ "search" access is required
2076          * on searchBase object */
2077         {
2078                 slap_mask_t     mask;
2079                 
2080                 if ( get_assert( op ) &&
2081                                 ( test_filter( op, &base_entry, get_assertion( op ) )
2082                                   != LDAP_COMPARE_TRUE ) )
2083                 {
2084                         rs->sr_err = LDAP_ASSERTION_FAILED;
2085                         
2086                 }
2087                 if ( ! access_allowed_mask( op, &base_entry,
2088                                         slap_schema.si_ad_entry,
2089                                         NULL, ACL_SEARCH, NULL, &mask ) )
2090                 {
2091                         if ( rs->sr_err == LDAP_SUCCESS ) {
2092                                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2093                         }
2094                 }
2095
2096                 if ( rs->sr_err != LDAP_SUCCESS ) {
2097                         if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2098                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2099                                 rs->sr_text = NULL;
2100                         }
2101                         send_ldap_result( op, rs );
2102                         goto done;
2103                 }
2104         }
2105
2106         bsi.bsi_e = NULL;
2107
2108         bsi.bsi_n_candidates =
2109                 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 : 
2110                 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2111                 ( op->ors_limit->lms_s_unchecked ) ) );
2112
2113         /* If paged results are in effect, check the paging cookie */
2114         if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
2115                 PagedResultsState *ps = op->o_pagedresults_state;
2116                 rs->sr_err = parse_paged_cookie( op, rs );
2117                 if ( rs->sr_err != LDAP_SUCCESS ) {
2118                         send_ldap_result( op, rs );
2119                         goto done;
2120                 }
2121         }
2122
2123         switch ( bsi.bsi_scope ) {
2124         case LDAP_SCOPE_BASE:
2125         case BACKSQL_SCOPE_BASE_LIKE:
2126                 /*
2127                  * probably already found...
2128                  */
2129                 bsi.bsi_id_list = &bsi.bsi_base_id;
2130                 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2131                 break;
2132
2133         case LDAP_SCOPE_SUBTREE:
2134                 /*
2135                  * if baseObject is defined, and if it is the root 
2136                  * of the search, add it to the candidate list
2137                  */
2138                 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2139                 {
2140                         bsi.bsi_id_list = &bsi.bsi_base_id;
2141                         bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2142                 }
2143
2144                 /* FALLTHRU */
2145         default:
2146
2147                 /*
2148                  * for each objectclass we try to construct query which gets IDs
2149                  * of entries matching LDAP query filter and scope (or at least 
2150                  * candidates), and get the IDs. Do this in ID order for paging.
2151                  */
2152                 avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
2153                                 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2154
2155                 /* check for abandon */
2156                 if ( op->o_abandon ) {
2157                         eid = bsi.bsi_id_list;
2158                         rs->sr_err = SLAPD_ABANDON;
2159                         goto send_results;
2160                 }
2161         }
2162
2163         if ( op->ors_limit != NULL      /* isroot == FALSE */
2164                         && op->ors_limit->lms_s_unchecked != -1
2165                         && bsi.bsi_n_candidates == -1 )
2166         {
2167                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2168                 send_ldap_result( op, rs );
2169                 goto done;
2170         }
2171
2172         /*
2173          * now we load candidate entries (only those attributes 
2174          * mentioned in attrs and filter), test it against full filter 
2175          * and then send to client; don't free entry_id if baseObject...
2176          */
2177         for ( eid = bsi.bsi_id_list;
2178                         eid != NULL; 
2179                         eid = backsql_free_entryID( op,
2180                                 eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
2181         {
2182                 int             rc;
2183                 Attribute       *a_hasSubordinate = NULL,
2184                                 *a_entryUUID = NULL,
2185                                 *a_entryCSN = NULL,
2186                                 **ap = NULL;
2187                 Entry           *e = NULL;
2188
2189                 /* check for abandon */
2190                 if ( op->o_abandon ) {
2191                         rs->sr_err = SLAPD_ABANDON;
2192                         goto send_results;
2193                 }
2194
2195                 /* check time limit */
2196                 if ( op->ors_tlimit != SLAP_NO_LIMIT
2197                                 && slap_get_time() > stoptime )
2198                 {
2199                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2200                         rs->sr_ctrls = NULL;
2201                         rs->sr_ref = rs->sr_v2ref;
2202                         goto send_results;
2203                 }
2204
2205 #ifdef BACKSQL_ARBITRARY_KEY
2206                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2207                         "for entry id=%s, oc_id=%ld, keyval=%s\n",
2208                         eid->eid_id.bv_val, eid->eid_oc_id,
2209                         eid->eid_keyval.bv_val );
2210 #else /* ! BACKSQL_ARBITRARY_KEY */
2211                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2212                         "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2213                         eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
2214 #endif /* ! BACKSQL_ARBITRARY_KEY */
2215
2216                 /* check scope */
2217                 switch ( op->ors_scope ) {
2218                 case LDAP_SCOPE_BASE:
2219                 case BACKSQL_SCOPE_BASE_LIKE:
2220                         if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2221                                 goto next_entry2;
2222                         }
2223                         break;
2224
2225                 case LDAP_SCOPE_ONE:
2226                 {
2227                         struct berval   rdn = eid->eid_ndn;
2228
2229                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2230                         if ( !dnIsOneLevelRDN( &rdn ) ) {
2231                                 goto next_entry2;
2232                         }
2233                         /* fall thru */
2234                 }
2235
2236                 case LDAP_SCOPE_SUBORDINATE:
2237                         /* discard the baseObject entry */
2238                         if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2239                                 goto next_entry2;
2240                         }
2241                         /* FALLTHRU */
2242                 case LDAP_SCOPE_SUBTREE:
2243                         /* FIXME: this should never fail... */
2244                         if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2245                                 goto next_entry2;
2246                         }
2247                         break;
2248                 }
2249
2250                 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2251                         /* don't recollect baseObject... */
2252                         e = bi->sql_baseObject;
2253
2254                 } else if ( eid == &bsi.bsi_base_id ) {
2255                         /* don't recollect searchBase object... */
2256                         e = &base_entry;
2257
2258                 } else {
2259                         bsi.bsi_e = &user_entry;
2260                         rc = backsql_id2entry( &bsi, eid );
2261                         if ( rc != LDAP_SUCCESS ) {
2262                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2263                                         "error %d in backsql_id2entry() "
2264                                         "- skipping\n", rc, 0, 0 );
2265                                 continue;
2266                         }
2267                         e = &user_entry;
2268                 }
2269
2270                 if ( !manageDSAit &&
2271                                 op->ors_scope != LDAP_SCOPE_BASE &&
2272                                 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2273                                 is_entry_referral( e ) )
2274                 {
2275                         BerVarray refs;
2276
2277                         refs = get_entry_referrals( op, e );
2278                         if ( !refs ) {
2279                                 backsql_srch_info       bsi2 = { 0 };
2280                                 Entry                   user_entry2 = { 0 };
2281
2282                                 /* retry with the full entry... */
2283                                 bsi2.bsi_e = &user_entry2;
2284                                 rc = backsql_init_search( &bsi2,
2285                                                 &e->e_nname,
2286                                                 LDAP_SCOPE_BASE, 
2287                                                 (time_t)(-1), NULL,
2288                                                 dbh, op, rs, NULL,
2289                                                 BACKSQL_ISF_GET_ENTRY );
2290                                 if ( rc == LDAP_SUCCESS ) {
2291                                         if ( is_entry_referral( &user_entry2 ) )
2292                                         {
2293                                                 refs = get_entry_referrals( op,
2294                                                                 &user_entry2 );
2295                                         } else {
2296                                                 rs->sr_err = LDAP_OTHER;
2297                                         }
2298                                         backsql_entry_clean( op, &user_entry2 );
2299                                 }
2300                                 if ( bsi2.bsi_attrs != NULL ) {
2301                                         op->o_tmpfree( bsi2.bsi_attrs,
2302                                                         op->o_tmpmemctx );
2303                                 }
2304                         }
2305
2306                         if ( refs ) {
2307                                 rs->sr_ref = referral_rewrite( refs,
2308                                                 &e->e_name,
2309                                                 &op->o_req_dn,
2310                                                 op->ors_scope );
2311                                 ber_bvarray_free( refs );
2312                         }
2313
2314                         if ( rs->sr_ref ) {
2315                                 rs->sr_err = LDAP_REFERRAL;
2316
2317                         } else {
2318                                 rs->sr_text = "bad referral object";
2319                         }
2320
2321                         rs->sr_entry = e;
2322                         rs->sr_matched = user_entry.e_name.bv_val;
2323                         send_search_reference( op, rs );
2324
2325                         ber_bvarray_free( rs->sr_ref );
2326                         rs->sr_ref = NULL;
2327                         rs->sr_matched = NULL;
2328                         rs->sr_entry = NULL;
2329
2330                         goto next_entry;
2331                 }
2332
2333                 /*
2334                  * We use this flag since we need to parse the filter
2335                  * anyway; we should have used the frontend API function
2336                  * filter_has_subordinates()
2337                  */
2338                 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2339                         rc = backsql_has_children( op, dbh, &e->e_nname );
2340
2341                         switch ( rc ) {
2342                         case LDAP_COMPARE_TRUE:
2343                         case LDAP_COMPARE_FALSE:
2344                                 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2345                                 if ( a_hasSubordinate != NULL ) {
2346                                         for ( ap = &user_entry.e_attrs; 
2347                                                         *ap; 
2348                                                         ap = &(*ap)->a_next );
2349
2350                                         *ap = a_hasSubordinate;
2351                                 }
2352                                 rc = 0;
2353                                 break;
2354
2355                         default:
2356                                 Debug(LDAP_DEBUG_TRACE, 
2357                                         "backsql_search(): "
2358                                         "has_children failed( %d)\n", 
2359                                         rc, 0, 0 );
2360                                 rc = 1;
2361                                 goto next_entry;
2362                         }
2363                 }
2364
2365                 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2366                         a_entryUUID = backsql_operational_entryUUID( bi, eid );
2367                         if ( a_entryUUID != NULL ) {
2368                                 if ( ap == NULL ) {
2369                                         ap = &user_entry.e_attrs;
2370                                 }
2371
2372                                 for ( ; *ap; ap = &(*ap)->a_next );
2373
2374                                 *ap = a_entryUUID;
2375                         }
2376                 }
2377
2378 #ifdef BACKSQL_SYNCPROV
2379                 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2380                         a_entryCSN = backsql_operational_entryCSN( op );
2381                         if ( a_entryCSN != NULL ) {
2382                                 if ( ap == NULL ) {
2383                                         ap = &user_entry.e_attrs;
2384                                 }
2385
2386                                 for ( ; *ap; ap = &(*ap)->a_next );
2387
2388                                 *ap = a_entryCSN;
2389                         }
2390                 }
2391 #endif /* BACKSQL_SYNCPROV */
2392
2393                 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2394                 {
2395                         /* If paged results are in effect, see if the page limit was exceeded */
2396                         if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2397                                 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
2398                                 {
2399                                         e = NULL;
2400                                         send_paged_response( op, rs, &lastid );
2401                                         goto done;
2402                                 }
2403                                 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
2404                         }
2405                         rs->sr_attrs = op->ors_attrs;
2406                         rs->sr_operational_attrs = NULL;
2407                         rs->sr_entry = e;
2408                         rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2409                         /* FIXME: need the whole entry (ITS#3480) */
2410                         rs->sr_err = send_search_entry( op, rs );
2411                         rs->sr_entry = NULL;
2412                         rs->sr_attrs = NULL;
2413                         rs->sr_operational_attrs = NULL;
2414
2415                         switch ( rs->sr_err ) {
2416                         case LDAP_UNAVAILABLE:
2417                                 /*
2418                                  * FIXME: send_search_entry failed;
2419                                  * better stop
2420                                  */
2421                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2422                                         "connection lost\n", 0, 0, 0 );
2423                                 goto end_of_search;
2424
2425                         case LDAP_SIZELIMIT_EXCEEDED:
2426                                 goto send_results;
2427                         }
2428                 }
2429
2430 next_entry:;
2431                 if ( e == &user_entry ) {
2432                         backsql_entry_clean( op, &user_entry );
2433                 }
2434
2435 next_entry2:;
2436         }
2437
2438 end_of_search:;
2439         if ( rs->sr_nentries > 0 ) {
2440                 rs->sr_ref = rs->sr_v2ref;
2441                 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2442                         : LDAP_REFERRAL;
2443
2444         } else {
2445                 rs->sr_err = bsi.bsi_status;
2446         }
2447
2448 send_results:;
2449         if ( rs->sr_err != SLAPD_ABANDON ) {
2450                 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2451                         send_paged_response( op, rs, NULL );
2452                 } else {
2453                         send_ldap_result( op, rs );
2454                 }
2455         }
2456
2457         /* cleanup in case of abandon */
2458         for ( ; eid != NULL; 
2459                         eid = backsql_free_entryID( op,
2460                                 eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
2461                 ;
2462
2463         backsql_entry_clean( op, &base_entry );
2464
2465         /* in case we got here accidentally */
2466         backsql_entry_clean( op, &user_entry );
2467
2468         if ( rs->sr_v2ref ) {
2469                 ber_bvarray_free( rs->sr_v2ref );
2470                 rs->sr_v2ref = NULL;
2471         }
2472
2473 #ifdef BACKSQL_SYNCPROV
2474         if ( op->o_sync ) {
2475                 Operation       op2 = *op;
2476                 SlapReply       rs2 = { 0 };
2477                 Entry           *e = entry_alloc();
2478                 slap_callback   cb = { 0 };
2479
2480                 op2.o_tag = LDAP_REQ_ADD;
2481                 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
2482                 op2.ora_e = e;
2483                 op2.o_callback = &cb;
2484
2485                 ber_dupbv( &e->e_name, op->o_bd->be_suffix );
2486                 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
2487
2488                 cb.sc_response = slap_null_cb;
2489
2490                 op2.o_bd->be_add( &op2, &rs2 );
2491
2492                 if ( op2.ora_e == e )
2493                         entry_free( e );
2494         }
2495 #endif /* BACKSQL_SYNCPROV */
2496
2497 done:;
2498         (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
2499
2500         if ( bsi.bsi_attrs != NULL ) {
2501                 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2502         }
2503
2504         if ( !BER_BVISNULL( &nbase )
2505                         && nbase.bv_val != op->o_req_ndn.bv_val )
2506         {
2507                 ch_free( nbase.bv_val );
2508         }
2509
2510         /* restore scope ... FIXME: this should be done before ANY
2511          * frontend call that uses op */
2512         if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2513                 op->ors_scope = LDAP_SCOPE_BASE;
2514         }
2515
2516         Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2517
2518         return rs->sr_err;
2519 }
2520
2521 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2522  */
2523 int
2524 backsql_entry_get(
2525                 Operation               *op,
2526                 struct berval           *ndn,
2527                 ObjectClass             *oc,
2528                 AttributeDescription    *at,
2529                 int                     rw,
2530                 Entry                   **ent )
2531 {
2532         backsql_srch_info       bsi = { 0 };
2533         SQLHDBC                 dbh = SQL_NULL_HDBC;
2534         int                     rc;
2535         SlapReply               rs = { 0 };
2536         AttributeName           anlist[ 2 ];
2537
2538         *ent = NULL;
2539
2540         rc = backsql_get_db_conn( op, &dbh );
2541         if ( !dbh ) {
2542                 return LDAP_OTHER;
2543         }
2544
2545         if ( at ) {
2546                 anlist[ 0 ].an_name = at->ad_cname;
2547                 anlist[ 0 ].an_desc = at;
2548                 BER_BVZERO( &anlist[ 1 ].an_name );
2549         }
2550
2551         bsi.bsi_e = entry_alloc();
2552         rc = backsql_init_search( &bsi,
2553                         ndn,
2554                         LDAP_SCOPE_BASE, 
2555                         (time_t)(-1), NULL,
2556                         dbh, op, &rs, at ? anlist : NULL,
2557                         BACKSQL_ISF_GET_ENTRY );
2558
2559         if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2560                 (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
2561         }
2562
2563         if ( rc == LDAP_SUCCESS ) {
2564
2565 #if 0 /* not supported at present */
2566                 /* find attribute values */
2567                 if ( is_entry_alias( bsi.bsi_e ) ) {
2568                         Debug( LDAP_DEBUG_ACL,
2569                                 "<= backsql_entry_get: entry is an alias\n",
2570                                 0, 0, 0 );
2571                         rc = LDAP_ALIAS_PROBLEM;
2572                         goto return_results;
2573                 }
2574 #endif
2575
2576                 if ( is_entry_referral( bsi.bsi_e ) ) {
2577                         Debug( LDAP_DEBUG_ACL,
2578                                 "<= backsql_entry_get: entry is a referral\n",
2579                                 0, 0, 0 );
2580                         rc = LDAP_REFERRAL;
2581                         goto return_results;
2582                 }
2583
2584                 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2585                         Debug( LDAP_DEBUG_ACL,
2586                                         "<= backsql_entry_get: "
2587                                         "failed to find objectClass\n",
2588                                         0, 0, 0 ); 
2589                         rc = LDAP_NO_SUCH_ATTRIBUTE;
2590                         goto return_results;
2591                 }
2592
2593                 *ent = bsi.bsi_e;
2594         }
2595
2596 return_results:;
2597         if ( bsi.bsi_attrs != NULL ) {
2598                 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2599         }
2600
2601         if ( rc != LDAP_SUCCESS ) {
2602                 if ( bsi.bsi_e ) {
2603                         entry_free( bsi.bsi_e );
2604                 }
2605         }
2606
2607         return rc;
2608 }
2609
2610 void
2611 backsql_entry_clean(
2612                 Operation       *op,
2613                 Entry           *e )
2614 {
2615         void *ctx;
2616
2617         ctx = ldap_pvt_thread_pool_context();
2618
2619         if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2620                 if ( !BER_BVISNULL( &e->e_name ) ) {
2621                         op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2622                         BER_BVZERO( &e->e_name );
2623                 }
2624
2625                 if ( !BER_BVISNULL( &e->e_nname ) ) {
2626                         op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2627                         BER_BVZERO( &e->e_nname );
2628                 }
2629         }
2630
2631         entry_clean( e );
2632 }
2633
2634 int
2635 backsql_entry_release(
2636                 Operation       *op,
2637                 Entry           *e,
2638                 int             rw )
2639 {
2640         backsql_entry_clean( op, e );
2641
2642         ch_free( e );
2643
2644         return 0;
2645 }
2646
2647
2648 /* This function is copied verbatim from back-bdb/search.c */
2649 static int
2650 parse_paged_cookie( Operation *op, SlapReply *rs )
2651 {
2652         LDAPControl     **c;
2653         int             rc = LDAP_SUCCESS;
2654         ber_tag_t       tag;
2655         ber_int_t       size;
2656         BerElement      *ber;
2657         struct berval   cookie = BER_BVNULL;
2658         PagedResultsState *ps = op->o_pagedresults_state;
2659
2660         /* this function must be invoked only if the pagedResults
2661          * control has been detected, parsed and partially checked
2662          * by the frontend */
2663         assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
2664
2665         /* look for the appropriate ctrl structure */
2666         for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
2667                 if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
2668                 {
2669                         break;
2670                 }
2671         }
2672
2673         if ( c[0] == NULL ) {
2674                 rs->sr_text = "missing pagedResults control";
2675                 return LDAP_PROTOCOL_ERROR;
2676         }
2677
2678         /* Tested by frontend */
2679         assert( c[0]->ldctl_value.bv_len > 0 );
2680
2681         /* Parse the control value
2682          *      realSearchControlValue ::= SEQUENCE {
2683          *              size    INTEGER (0..maxInt),
2684          *                              -- requested page size from client
2685          *                              -- result set size estimate from server
2686          *              cookie  OCTET STRING
2687          * }
2688          */
2689         ber = ber_init( &c[0]->ldctl_value );
2690         if ( ber == NULL ) {
2691                 rs->sr_text = "internal error";
2692                 return LDAP_OTHER;
2693         }
2694
2695         tag = ber_scanf( ber, "{im}", &size, &cookie );
2696
2697         /* Tested by frontend */
2698         assert( tag != LBER_ERROR );
2699         assert( size >= 0 );
2700
2701         /* cookie decoding/checks deferred to backend... */
2702         if ( cookie.bv_len ) {
2703                 PagedResultsCookie reqcookie;
2704                 if( cookie.bv_len != sizeof( reqcookie ) ) {
2705                         /* bad cookie */
2706                         rs->sr_text = "paged results cookie is invalid";
2707                         rc = LDAP_PROTOCOL_ERROR;
2708                         goto done;
2709                 }
2710
2711                 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
2712
2713                 if ( reqcookie > ps->ps_cookie ) {
2714                         /* bad cookie */
2715                         rs->sr_text = "paged results cookie is invalid";
2716                         rc = LDAP_PROTOCOL_ERROR;
2717                         goto done;
2718
2719                 } else if ( reqcookie < ps->ps_cookie ) {
2720                         rs->sr_text = "paged results cookie is invalid or old";
2721                         rc = LDAP_UNWILLING_TO_PERFORM;
2722                         goto done;
2723                 }
2724
2725         } else {
2726                 /* Initial request.  Initialize state. */
2727 #if 0
2728                 if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
2729                         /* There's another pagedResults control on the
2730                          * same connection; reject new pagedResults controls 
2731                          * (allowed by RFC2696) */
2732                         rs->sr_text = "paged results cookie unavailable; try later";
2733                         rc = LDAP_UNWILLING_TO_PERFORM;
2734                         goto done;
2735                 }
2736 #endif
2737                 ps->ps_cookie = 0;
2738                 ps->ps_count = 0;
2739         }
2740
2741 done:;
2742         (void)ber_free( ber, 1 );
2743
2744         return rc;
2745 }
2746
2747 /* This function is copied nearly verbatim from back-bdb/search.c */
2748 static void
2749 send_paged_response( 
2750         Operation       *op,
2751         SlapReply       *rs,
2752         unsigned long   *lastid )
2753 {
2754         LDAPControl     ctrl, *ctrls[2];
2755         BerElementBuffer berbuf;
2756         BerElement      *ber = (BerElement *)&berbuf;
2757         PagedResultsCookie respcookie;
2758         struct berval cookie;
2759
2760         Debug(LDAP_DEBUG_ARGS,
2761                 "send_paged_response: lastid=0x%08lx nentries=%d\n", 
2762                 lastid ? *lastid : 0, rs->sr_nentries, NULL );
2763
2764         BER_BVZERO( &ctrl.ldctl_value );
2765         ctrls[0] = &ctrl;
2766         ctrls[1] = NULL;
2767
2768         ber_init2( ber, NULL, LBER_USE_DER );
2769
2770         if ( lastid ) {
2771                 respcookie = ( PagedResultsCookie )(*lastid);
2772                 cookie.bv_len = sizeof( respcookie );
2773                 cookie.bv_val = (char *)&respcookie;
2774
2775         } else {
2776                 respcookie = ( PagedResultsCookie )0;
2777                 BER_BVSTR( &cookie, "" );
2778         }
2779
2780         op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
2781         op->o_conn->c_pagedresults_state.ps_count =
2782                 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
2783                 rs->sr_nentries;
2784
2785         /* return size of 0 -- no estimate */
2786         ber_printf( ber, "{iO}", 0, &cookie ); 
2787
2788         if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
2789                 goto done;
2790         }
2791
2792         ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2793         ctrls[0]->ldctl_iscritical = 0;
2794
2795         rs->sr_ctrls = ctrls;
2796         rs->sr_err = LDAP_SUCCESS;
2797         send_ldap_result( op, rs );
2798         rs->sr_ctrls = NULL;
2799
2800 done:
2801         (void) ber_free_buf( ber );
2802 }