]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/search.c
db0ccb580e3657587ddd644c036719efe5c0f43e
[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                         /* This should always be true... */
1516                         backsql_strfcat_x( &bsi->bsi_join_where,
1517                                         bsi->bsi_op->o_tmpmemctx,
1518                                         "b",
1519                                         &bi->sql_subtree_cond );
1520
1521                 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1522                         backsql_strfcat_x( &bsi->bsi_join_where,
1523                                         bsi->bsi_op->o_tmpmemctx,
1524                                         "bl",
1525                                         &bi->sql_upper_func,
1526                                         (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1527                                                 "(ldap_entries.dn) LIKE ?"  );
1528
1529                 } else {
1530                         backsql_strfcat_x( &bsi->bsi_join_where,
1531                                         bsi->bsi_op->o_tmpmemctx,
1532                                         "l",
1533                                         (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1534                                                 "ldap_entries.dn LIKE ?" );
1535                 }
1536
1537                 break;
1538
1539         default:
1540                 assert( 0 );
1541         }
1542
1543         /* If paged results are in effect, ignore low ldap_entries.id numbers */
1544         if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
1545                 unsigned long lowid = 0;
1546
1547                 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1548                 if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
1549                 {
1550                         lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
1551                 }
1552
1553                 if ( lowid ) {
1554                         char lowidstring[48];
1555                         int  lowidlen;
1556
1557                         lowidlen = snprintf( lowidstring, sizeof( lowidstring ),
1558                                 " AND ldap_entries.id>%lu", lowid );
1559                         backsql_strfcat_x( &bsi->bsi_join_where,
1560                                         bsi->bsi_op->o_tmpmemctx,
1561                                         "l",
1562                                         (ber_len_t)lowidlen,
1563                                         lowidstring );
1564                 }
1565         }
1566
1567         rc = backsql_process_filter( bsi, bsi->bsi_filter );
1568         if ( rc > 0 ) {
1569                 struct berbuf   bb = BB_NULL;
1570
1571                 backsql_strfcat_x( &bb,
1572                                 bsi->bsi_op->o_tmpmemctx,
1573                                 "bbblb",
1574                                 &bsi->bsi_sel.bb_val,
1575                                 &bsi->bsi_from.bb_val, 
1576                                 &bsi->bsi_join_where.bb_val,
1577                                 (ber_len_t)STRLENOF( " AND " ), " AND ",
1578                                 &bsi->bsi_flt_where.bb_val );
1579
1580                 *query = bb.bb_val;
1581
1582         } else if ( rc < 0 ) {
1583                 /* 
1584                  * Indicates that there's no possible way the filter matches
1585                  * anything.  No need to issue the query
1586                  */
1587                 free( query->bv_val );
1588                 BER_BVZERO( query );
1589         }
1590  
1591         bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1592         BER_BVZERO( &bsi->bsi_sel.bb_val );
1593         bsi->bsi_sel.bb_len = 0;
1594         bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1595         BER_BVZERO( &bsi->bsi_from.bb_val );
1596         bsi->bsi_from.bb_len = 0;
1597         bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1598         BER_BVZERO( &bsi->bsi_join_where.bb_val );
1599         bsi->bsi_join_where.bb_len = 0;
1600         bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1601         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1602         bsi->bsi_flt_where.bb_len = 0;
1603         
1604         Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1605                 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1606         
1607         return ( rc <= 0 ? 1 : 0 );
1608 }
1609
1610 static int
1611 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1612 {
1613         backsql_oc_map_rec      *oc = v_oc;
1614         backsql_srch_info       *bsi = v_bsi;
1615         Operation               *op = bsi->bsi_op;
1616         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1617         struct berval           query;
1618         SQLHSTMT                sth = SQL_NULL_HSTMT;
1619         RETCODE                 rc;
1620         int                     res;
1621         BACKSQL_ROW_NTS         row;
1622         int                     i;
1623         int                     j;
1624         int                     n_candidates = bsi->bsi_n_candidates;
1625
1626         /* 
1627          * + 1 because we need room for '%';
1628          * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1629          * this makes a subtree
1630          * search for a DN BACKSQL_MAX_DN_LEN long legal 
1631          * if it returns that DN only
1632          */
1633         char                    tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1634
1635         bsi->bsi_status = LDAP_SUCCESS;
1636  
1637         Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1638                         BACKSQL_OC_NAME( oc ), 0, 0 );
1639
1640         /* check for abandon */
1641         if ( op->o_abandon ) {
1642                 bsi->bsi_status = SLAPD_ABANDON;
1643                 return BACKSQL_AVL_STOP;
1644         }
1645
1646         /* If paged results have already completed this objectClass, skip it */
1647         if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1648                 if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
1649                 {
1650                         return BACKSQL_AVL_CONTINUE;
1651                 }
1652         }
1653
1654         if ( bsi->bsi_n_candidates == -1 ) {
1655                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1656                         "unchecked limit has been overcome\n", 0, 0, 0 );
1657                 /* should never get here */
1658                 assert( 0 );
1659                 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1660                 return BACKSQL_AVL_STOP;
1661         }
1662         
1663         bsi->bsi_oc = oc;
1664         res = backsql_srch_query( bsi, &query );
1665         if ( res ) {
1666                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1667                         "error while constructing query for objectclass \"%s\"\n",
1668                         oc->bom_oc->soc_cname.bv_val, 0, 0 );
1669                 /*
1670                  * FIXME: need to separate errors from legally
1671                  * impossible filters
1672                  */
1673                 switch ( bsi->bsi_status ) {
1674                 case LDAP_SUCCESS:
1675                 case LDAP_UNDEFINED_TYPE:
1676                 case LDAP_NO_SUCH_OBJECT:
1677                         /* we are conservative... */
1678                 default:
1679                         bsi->bsi_status = LDAP_SUCCESS;
1680                         /* try next */
1681                         return BACKSQL_AVL_CONTINUE;
1682
1683                 case LDAP_ADMINLIMIT_EXCEEDED:
1684                 case LDAP_OTHER:
1685                         /* don't try any more */
1686                         return BACKSQL_AVL_STOP;
1687                 }
1688         }
1689
1690         if ( BER_BVISNULL( &query ) ) {
1691                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1692                         "could not construct query for objectclass \"%s\"\n",
1693                         oc->bom_oc->soc_cname.bv_val, 0, 0 );
1694                 bsi->bsi_status = LDAP_SUCCESS;
1695                 return BACKSQL_AVL_CONTINUE;
1696         }
1697
1698         Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n", 
1699                         query.bv_val, 0, 0 );
1700
1701         rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1702         bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1703         BER_BVZERO( &query );
1704         if ( rc != SQL_SUCCESS ) {
1705                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1706                         "error preparing query\n", 0, 0, 0 );
1707                 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1708                 bsi->bsi_status = LDAP_OTHER;
1709                 return BACKSQL_AVL_CONTINUE;
1710         }
1711         
1712         Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1713
1714         rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1715                         &bsi->bsi_oc->bom_id );
1716         if ( rc != SQL_SUCCESS ) {
1717                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1718                         "error binding objectclass id parameter\n", 0, 0, 0 );
1719                 bsi->bsi_status = LDAP_OTHER;
1720                 return BACKSQL_AVL_CONTINUE;
1721         }
1722
1723         switch ( bsi->bsi_scope ) {
1724         case LDAP_SCOPE_BASE:
1725         case BACKSQL_SCOPE_BASE_LIKE:
1726                 /*
1727                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1728                  * however this should be handled earlier
1729                  */
1730                 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1731                         bsi->bsi_status = LDAP_OTHER;
1732                         return BACKSQL_AVL_CONTINUE;
1733                 }
1734
1735                 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1736                                 bsi->bsi_base_ndn->bv_len + 1 );
1737
1738                 /* uppercase DN only if the stored DN can be uppercased
1739                  * for comparison */
1740                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1741                         ldap_pvt_str2upper( tmp_base_ndn );
1742                 }
1743
1744                 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1745                                 tmp_base_ndn, 0, 0 );
1746
1747                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1748                                 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1749                 if ( rc != SQL_SUCCESS ) {
1750                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1751                                 "error binding base_ndn parameter\n", 0, 0, 0 );
1752                         backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 
1753                                         sth, rc );
1754                         bsi->bsi_status = LDAP_OTHER;
1755                         return BACKSQL_AVL_CONTINUE;
1756                 }
1757                 break;
1758
1759         case LDAP_SCOPE_SUBORDINATE:
1760         case LDAP_SCOPE_SUBTREE:
1761         {
1762                 /* if short-cutting the search base,
1763                  * don't bind any parameter */
1764                 if ( bsi->bsi_use_subtree_shortcut ) {
1765                         break;
1766                 }
1767                 
1768                 /*
1769                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1770                  * however this should be handled earlier
1771                  */
1772                 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1773                         bsi->bsi_status = LDAP_OTHER;
1774                         return BACKSQL_AVL_CONTINUE;
1775                 }
1776
1777                 /* 
1778                  * Sets the parameters for the SQL built earlier
1779                  * NOTE that all the databases could actually use 
1780                  * the TimesTen version, which would be cleaner 
1781                  * and would also eliminate the need for the
1782                  * subtree_cond line in the configuration file.  
1783                  * For now, I'm leaving it the way it is, 
1784                  * so non-TimesTen databases use the original code.
1785                  * But at some point this should get cleaned up.
1786                  *
1787                  * If "dn" is being used, do a suffix search.
1788                  * If "dn_ru" is being used, do a prefix search.
1789                  */
1790                 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1791                         tmp_base_ndn[ 0 ] = '\0';
1792
1793                         for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1794                                         j >= 0; i++, j--) {
1795                                 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1796                         }
1797
1798                         if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1799                                 tmp_base_ndn[ i++ ] = ',';
1800                         }
1801
1802                         tmp_base_ndn[ i ] = '%';
1803                         tmp_base_ndn[ i + 1 ] = '\0';
1804
1805                 } else {
1806                         i = 0;
1807
1808                         tmp_base_ndn[ i++ ] = '%';
1809
1810                         if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1811                                 tmp_base_ndn[ i++ ] = ',';
1812                         }
1813
1814                         AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1815                                 bsi->bsi_base_ndn->bv_len + 1 );
1816                 }
1817
1818                 /* uppercase DN only if the stored DN can be uppercased
1819                  * for comparison */
1820                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1821                         ldap_pvt_str2upper( tmp_base_ndn );
1822                 }
1823
1824                 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1825                         Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1826                                 tmp_base_ndn, 0, 0 );
1827                 } else {
1828                         Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1829                                 tmp_base_ndn, 0, 0 );
1830                 }
1831
1832                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1833                                 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1834                 if ( rc != SQL_SUCCESS ) {
1835                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1836                                 "error binding base_ndn parameter (2)\n",
1837                                 0, 0, 0 );
1838                         backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 
1839                                         sth, rc );
1840                         bsi->bsi_status = LDAP_OTHER;
1841                         return BACKSQL_AVL_CONTINUE;
1842                 }
1843                 break;
1844         }
1845
1846         case LDAP_SCOPE_ONELEVEL:
1847                 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1848
1849 #ifdef BACKSQL_ARBITRARY_KEY
1850                 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1851                                 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1852 #else /* ! BACKSQL_ARBITRARY_KEY */
1853                 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1854                                 bsi->bsi_base_id.eid_id, 0, 0 );
1855 #endif /* ! BACKSQL_ARBITRARY_KEY */
1856                 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1857                                 &bsi->bsi_base_id.eid_id );
1858                 if ( rc != SQL_SUCCESS ) {
1859                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1860                                 "error binding base id parameter\n", 0, 0, 0 );
1861                         bsi->bsi_status = LDAP_OTHER;
1862                         return BACKSQL_AVL_CONTINUE;
1863                 }
1864                 break;
1865         }
1866         
1867         rc = SQLExecute( sth );
1868         if ( !BACKSQL_SUCCESS( rc ) ) {
1869                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1870                         "error executing query\n", 0, 0, 0 );
1871                 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1872                 SQLFreeStmt( sth, SQL_DROP );
1873                 bsi->bsi_status = LDAP_OTHER;
1874                 return BACKSQL_AVL_CONTINUE;
1875         }
1876
1877         backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1878         rc = SQLFetch( sth );
1879         for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1880                 struct berval           dn, pdn, ndn;
1881                 backsql_entryID         *c_id = NULL;
1882                 int                     ret;
1883
1884                 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1885
1886                 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1887                         continue;
1888                 }
1889
1890                 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1891                 if ( dn.bv_val != row.cols[ 3 ] ) {
1892                         free( dn.bv_val );
1893                 }
1894
1895                 if ( ret != LDAP_SUCCESS ) {
1896                         continue;
1897                 }
1898
1899                 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1900                         goto cleanup;
1901                 }
1902
1903                 c_id = (backsql_entryID *)op->o_tmpcalloc( 1, 
1904                                 sizeof( backsql_entryID ), op->o_tmpmemctx );
1905 #ifdef BACKSQL_ARBITRARY_KEY
1906                 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1907                                 op->o_tmpmemctx );
1908                 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1909                                 op->o_tmpmemctx );
1910 #else /* ! BACKSQL_ARBITRARY_KEY */
1911                 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1912                         goto cleanup;
1913                 }
1914                 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1915                         goto cleanup;
1916                 }
1917 #endif /* ! BACKSQL_ARBITRARY_KEY */
1918                 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1919
1920                 c_id->eid_dn = pdn;
1921                 c_id->eid_ndn = ndn;
1922
1923                 /* append at end of list ... */
1924                 c_id->eid_next = NULL;
1925                 *bsi->bsi_id_listtail = c_id;
1926                 bsi->bsi_id_listtail = &c_id->eid_next;
1927
1928 #ifdef BACKSQL_ARBITRARY_KEY
1929                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1930                         "added entry id=%s, keyval=%s dn=\"%s\"\n",
1931                         c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1932                         row.cols[ 3 ] );
1933 #else /* ! BACKSQL_ARBITRARY_KEY */
1934                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1935                         "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1936                         c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1937 #endif /* ! BACKSQL_ARBITRARY_KEY */
1938
1939                 /* count candidates, for unchecked limit */
1940                 bsi->bsi_n_candidates--;
1941                 if ( bsi->bsi_n_candidates == -1 ) {
1942                         break;
1943                 }
1944                 continue;
1945
1946 cleanup:;
1947                 if ( !BER_BVISNULL( &pdn ) ) {
1948                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1949                 }
1950                 if ( !BER_BVISNULL( &ndn ) ) {
1951                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1952                 }
1953                 if ( c_id != NULL ) {
1954                         ch_free( c_id );
1955                 }
1956         }
1957         backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1958         SQLFreeStmt( sth, SQL_DROP );
1959
1960         Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1961                         n_candidates - bsi->bsi_n_candidates, 0, 0 );
1962
1963         return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1964 }
1965
1966 int
1967 backsql_search( Operation *op, SlapReply *rs )
1968 {
1969         backsql_info            *bi = (backsql_info *)op->o_bd->be_private;
1970         SQLHDBC                 dbh = SQL_NULL_HDBC;
1971         int                     sres;
1972         Entry                   user_entry = { 0 },
1973                                 base_entry = { 0 };
1974         int                     manageDSAit = get_manageDSAit( op );
1975         time_t                  stoptime = 0;
1976         backsql_srch_info       bsi = { 0 };
1977         backsql_entryID         *eid = NULL;
1978         struct berval           nbase = BER_BVNULL;
1979         unsigned long           lastid = 0;
1980
1981         Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1982                 "base=\"%s\", filter=\"%s\", scope=%d,", 
1983                 op->o_req_ndn.bv_val,
1984                 op->ors_filterstr.bv_val,
1985                 op->ors_scope );
1986         Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1987                 "attributes to load: %s\n",
1988                 op->ors_deref,
1989                 op->ors_attrsonly,
1990                 op->ors_attrs == NULL ? "all" : "custom list" );
1991
1992         if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1993                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1994                         "search base length (%ld) exceeds max length (%d)\n", 
1995                         op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1996                 /*
1997                  * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1998                  * since it is impossible that such a long DN exists
1999                  * in the backend
2000                  */
2001                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2002                 send_ldap_result( op, rs );
2003                 return 1;
2004         }
2005
2006         sres = backsql_get_db_conn( op, &dbh );
2007         if ( sres != LDAP_SUCCESS ) {
2008                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2009                         "could not get connection handle - exiting\n", 
2010                         0, 0, 0 );
2011                 rs->sr_err = sres;
2012                 rs->sr_text = sres == LDAP_OTHER ?  "SQL-backend error" : NULL;
2013                 send_ldap_result( op, rs );
2014                 return 1;
2015         }
2016
2017         /* compute it anyway; root does not use it */
2018         stoptime = op->o_time + op->ors_tlimit;
2019
2020         /* init search */
2021         bsi.bsi_e = &base_entry;
2022         rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2023                         op->ors_scope,
2024                         stoptime, op->ors_filter,
2025                         dbh, op, rs, op->ors_attrs,
2026                         ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
2027         switch ( rs->sr_err ) {
2028         case LDAP_SUCCESS:
2029                 break;
2030
2031         case LDAP_REFERRAL:
2032                 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
2033                                 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
2034                 {
2035                         rs->sr_err = LDAP_SUCCESS;
2036                         rs->sr_text = NULL;
2037                         rs->sr_matched = NULL;
2038                         if ( rs->sr_ref ) {
2039                                 ber_bvarray_free( rs->sr_ref );
2040                                 rs->sr_ref = NULL;
2041                         }
2042                         break;
2043                 }
2044
2045                 /* an entry was created; free it */
2046                 entry_clean( bsi.bsi_e );
2047
2048                 /* fall thru */
2049
2050         default:
2051                 if ( !BER_BVISNULL( &base_entry.e_nname )
2052                                 && !access_allowed( op, &base_entry,
2053                                         slap_schema.si_ad_entry, NULL,
2054                                         ACL_DISCLOSE, NULL ) )
2055                 {
2056                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
2057                         if ( rs->sr_ref ) {
2058                                 ber_bvarray_free( rs->sr_ref );
2059                                 rs->sr_ref = NULL;
2060                         }
2061                         rs->sr_matched = NULL;
2062                         rs->sr_text = NULL;
2063                 }
2064
2065                 send_ldap_result( op, rs );
2066
2067                 if ( rs->sr_ref ) {
2068                         ber_bvarray_free( rs->sr_ref );
2069                         rs->sr_ref = NULL;
2070                 }
2071
2072                 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2073                         entry_clean( &base_entry );
2074                 }
2075
2076                 goto done;
2077         }
2078         /* NOTE: __NEW__ "search" access is required
2079          * on searchBase object */
2080         {
2081                 slap_mask_t     mask;
2082                 
2083                 if ( get_assert( op ) &&
2084                                 ( test_filter( op, &base_entry, get_assertion( op ) )
2085                                   != LDAP_COMPARE_TRUE ) )
2086                 {
2087                         rs->sr_err = LDAP_ASSERTION_FAILED;
2088                         
2089                 }
2090                 if ( ! access_allowed_mask( op, &base_entry,
2091                                         slap_schema.si_ad_entry,
2092                                         NULL, ACL_SEARCH, NULL, &mask ) )
2093                 {
2094                         if ( rs->sr_err == LDAP_SUCCESS ) {
2095                                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2096                         }
2097                 }
2098
2099                 if ( rs->sr_err != LDAP_SUCCESS ) {
2100                         if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2101                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2102                                 rs->sr_text = NULL;
2103                         }
2104                         send_ldap_result( op, rs );
2105                         goto done;
2106                 }
2107         }
2108
2109         bsi.bsi_e = NULL;
2110
2111         bsi.bsi_n_candidates =
2112                 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 : 
2113                 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2114                 ( op->ors_limit->lms_s_unchecked ) ) );
2115
2116         /* If paged results are in effect, check the paging cookie */
2117         if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
2118                 rs->sr_err = parse_paged_cookie( op, rs );
2119                 if ( rs->sr_err != LDAP_SUCCESS ) {
2120                         send_ldap_result( op, rs );
2121                         goto done;
2122                 }
2123         }
2124
2125         switch ( bsi.bsi_scope ) {
2126         case LDAP_SCOPE_BASE:
2127         case BACKSQL_SCOPE_BASE_LIKE:
2128                 /*
2129                  * probably already found...
2130                  */
2131                 bsi.bsi_id_list = &bsi.bsi_base_id;
2132                 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2133                 break;
2134
2135         case LDAP_SCOPE_SUBTREE:
2136                 /*
2137                  * if baseObject is defined, and if it is the root 
2138                  * of the search, add it to the candidate list
2139                  */
2140                 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2141                 {
2142                         bsi.bsi_id_list = &bsi.bsi_base_id;
2143                         bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2144                 }
2145
2146                 /* FALLTHRU */
2147         default:
2148
2149                 /*
2150                  * for each objectclass we try to construct query which gets IDs
2151                  * of entries matching LDAP query filter and scope (or at least 
2152                  * candidates), and get the IDs. Do this in ID order for paging.
2153                  */
2154                 avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
2155                                 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2156
2157                 /* check for abandon */
2158                 if ( op->o_abandon ) {
2159                         eid = bsi.bsi_id_list;
2160                         rs->sr_err = SLAPD_ABANDON;
2161                         goto send_results;
2162                 }
2163         }
2164
2165         if ( op->ors_limit != NULL      /* isroot == FALSE */
2166                         && op->ors_limit->lms_s_unchecked != -1
2167                         && bsi.bsi_n_candidates == -1 )
2168         {
2169                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2170                 send_ldap_result( op, rs );
2171                 goto done;
2172         }
2173
2174         /*
2175          * now we load candidate entries (only those attributes 
2176          * mentioned in attrs and filter), test it against full filter 
2177          * and then send to client; don't free entry_id if baseObject...
2178          */
2179         for ( eid = bsi.bsi_id_list;
2180                 eid != NULL; 
2181                 eid = backsql_free_entryID( 
2182                         eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2183         {
2184                 int             rc;
2185                 Attribute       *a_hasSubordinate = NULL,
2186                                 *a_entryUUID = NULL,
2187                                 *a_entryCSN = NULL,
2188                                 **ap = NULL;
2189                 Entry           *e = NULL;
2190
2191                 /* check for abandon */
2192                 if ( op->o_abandon ) {
2193                         rs->sr_err = SLAPD_ABANDON;
2194                         goto send_results;
2195                 }
2196
2197                 /* check time limit */
2198                 if ( op->ors_tlimit != SLAP_NO_LIMIT
2199                                 && slap_get_time() > stoptime )
2200                 {
2201                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2202                         rs->sr_ctrls = NULL;
2203                         rs->sr_ref = rs->sr_v2ref;
2204                         goto send_results;
2205                 }
2206
2207 #ifdef BACKSQL_ARBITRARY_KEY
2208                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2209                         "for entry id=%s, oc_id=%ld, keyval=%s\n",
2210                         eid->eid_id.bv_val, eid->eid_oc_id,
2211                         eid->eid_keyval.bv_val );
2212 #else /* ! BACKSQL_ARBITRARY_KEY */
2213                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2214                         "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2215                         eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
2216 #endif /* ! BACKSQL_ARBITRARY_KEY */
2217
2218                 /* check scope */
2219                 switch ( op->ors_scope ) {
2220                 case LDAP_SCOPE_BASE:
2221                 case BACKSQL_SCOPE_BASE_LIKE:
2222                         if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2223                                 goto next_entry2;
2224                         }
2225                         break;
2226
2227                 case LDAP_SCOPE_ONE:
2228                 {
2229                         struct berval   rdn = eid->eid_ndn;
2230
2231                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2232                         if ( !dnIsOneLevelRDN( &rdn ) ) {
2233                                 goto next_entry2;
2234                         }
2235                         /* fall thru */
2236                 }
2237
2238                 case LDAP_SCOPE_SUBORDINATE:
2239                         /* discard the baseObject entry */
2240                         if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2241                                 goto next_entry2;
2242                         }
2243                         /* FALLTHRU */
2244                 case LDAP_SCOPE_SUBTREE:
2245                         /* FIXME: this should never fail... */
2246                         if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2247                                 goto next_entry2;
2248                         }
2249                         break;
2250                 }
2251
2252                 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2253                         /* don't recollect baseObject... */
2254                         e = bi->sql_baseObject;
2255
2256                 } else if ( eid == &bsi.bsi_base_id ) {
2257                         /* don't recollect searchBase object... */
2258                         e = &base_entry;
2259
2260                 } else {
2261                         bsi.bsi_e = &user_entry;
2262                         rc = backsql_id2entry( &bsi, eid );
2263                         if ( rc != LDAP_SUCCESS ) {
2264                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2265                                         "error %d in backsql_id2entry() "
2266                                         "- skipping\n", rc, 0, 0 );
2267                                 continue;
2268                         }
2269                         e = &user_entry;
2270                 }
2271
2272                 if ( !manageDSAit &&
2273                                 op->ors_scope != LDAP_SCOPE_BASE &&
2274                                 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2275                                 is_entry_referral( e ) )
2276                 {
2277                         BerVarray refs;
2278
2279                         refs = get_entry_referrals( op, e );
2280                         if ( !refs ) {
2281                                 backsql_srch_info       bsi2 = { 0 };
2282                                 Entry                   user_entry2 = { 0 };
2283
2284                                 /* retry with the full entry... */
2285                                 bsi2.bsi_e = &user_entry2;
2286                                 rc = backsql_init_search( &bsi2,
2287                                                 &e->e_nname,
2288                                                 LDAP_SCOPE_BASE, 
2289                                                 (time_t)(-1), NULL,
2290                                                 dbh, op, rs, NULL,
2291                                                 BACKSQL_ISF_GET_ENTRY );
2292                                 if ( rc == LDAP_SUCCESS ) {
2293                                         if ( is_entry_referral( &user_entry2 ) )
2294                                         {
2295                                                 refs = get_entry_referrals( op,
2296                                                                 &user_entry2 );
2297                                         } else {
2298                                                 rs->sr_err = LDAP_OTHER;
2299                                         }
2300                                         backsql_entry_clean( op, &user_entry2 );
2301                                 }
2302                                 if ( bsi2.bsi_attrs != NULL ) {
2303                                         op->o_tmpfree( bsi2.bsi_attrs,
2304                                                         op->o_tmpmemctx );
2305                                 }
2306                         }
2307
2308                         if ( refs ) {
2309                                 rs->sr_ref = referral_rewrite( refs,
2310                                                 &e->e_name,
2311                                                 &op->o_req_dn,
2312                                                 op->ors_scope );
2313                                 ber_bvarray_free( refs );
2314                         }
2315
2316                         if ( rs->sr_ref ) {
2317                                 rs->sr_err = LDAP_REFERRAL;
2318
2319                         } else {
2320                                 rs->sr_text = "bad referral object";
2321                         }
2322
2323                         rs->sr_entry = e;
2324                         rs->sr_matched = user_entry.e_name.bv_val;
2325                         send_search_reference( op, rs );
2326
2327                         ber_bvarray_free( rs->sr_ref );
2328                         rs->sr_ref = NULL;
2329                         rs->sr_matched = NULL;
2330                         rs->sr_entry = NULL;
2331
2332                         goto next_entry;
2333                 }
2334
2335                 /*
2336                  * We use this flag since we need to parse the filter
2337                  * anyway; we should have used the frontend API function
2338                  * filter_has_subordinates()
2339                  */
2340                 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2341                         rc = backsql_has_children( op, dbh, &e->e_nname );
2342
2343                         switch ( rc ) {
2344                         case LDAP_COMPARE_TRUE:
2345                         case LDAP_COMPARE_FALSE:
2346                                 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2347                                 if ( a_hasSubordinate != NULL ) {
2348                                         for ( ap = &user_entry.e_attrs; 
2349                                                         *ap; 
2350                                                         ap = &(*ap)->a_next );
2351
2352                                         *ap = a_hasSubordinate;
2353                                 }
2354                                 rc = 0;
2355                                 break;
2356
2357                         default:
2358                                 Debug(LDAP_DEBUG_TRACE, 
2359                                         "backsql_search(): "
2360                                         "has_children failed( %d)\n", 
2361                                         rc, 0, 0 );
2362                                 rc = 1;
2363                                 goto next_entry;
2364                         }
2365                 }
2366
2367                 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2368                         a_entryUUID = backsql_operational_entryUUID( bi, eid );
2369                         if ( a_entryUUID != NULL ) {
2370                                 if ( ap == NULL ) {
2371                                         ap = &user_entry.e_attrs;
2372                                 }
2373
2374                                 for ( ; *ap; ap = &(*ap)->a_next );
2375
2376                                 *ap = a_entryUUID;
2377                         }
2378                 }
2379
2380 #ifdef BACKSQL_SYNCPROV
2381                 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2382                         a_entryCSN = backsql_operational_entryCSN( op );
2383                         if ( a_entryCSN != NULL ) {
2384                                 if ( ap == NULL ) {
2385                                         ap = &user_entry.e_attrs;
2386                                 }
2387
2388                                 for ( ; *ap; ap = &(*ap)->a_next );
2389
2390                                 *ap = a_entryCSN;
2391                         }
2392                 }
2393 #endif /* BACKSQL_SYNCPROV */
2394
2395                 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2396                 {
2397                         /* If paged results are in effect, see if the page limit was exceeded */
2398                         if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2399                                 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
2400                                 {
2401                                         e = NULL;
2402                                         send_paged_response( op, rs, &lastid );
2403                                         goto done;
2404                                 }
2405                                 lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
2406                         }
2407                         rs->sr_attrs = op->ors_attrs;
2408                         rs->sr_operational_attrs = NULL;
2409                         rs->sr_entry = e;
2410                         e->e_private = (void *)eid;
2411                         rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2412                         /* FIXME: need the whole entry (ITS#3480) */
2413                         rs->sr_err = send_search_entry( op, rs );
2414                         e->e_private = NULL;
2415                         rs->sr_entry = NULL;
2416                         rs->sr_attrs = NULL;
2417                         rs->sr_operational_attrs = NULL;
2418
2419                         switch ( rs->sr_err ) {
2420                         case LDAP_UNAVAILABLE:
2421                                 /*
2422                                  * FIXME: send_search_entry failed;
2423                                  * better stop
2424                                  */
2425                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2426                                         "connection lost\n", 0, 0, 0 );
2427                                 goto end_of_search;
2428
2429                         case LDAP_SIZELIMIT_EXCEEDED:
2430                                 goto send_results;
2431                         }
2432                 }
2433
2434 next_entry:;
2435                 if ( e == &user_entry ) {
2436                         backsql_entry_clean( op, &user_entry );
2437                 }
2438
2439 next_entry2:;
2440         }
2441
2442 end_of_search:;
2443         if ( rs->sr_nentries > 0 ) {
2444                 rs->sr_ref = rs->sr_v2ref;
2445                 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2446                         : LDAP_REFERRAL;
2447
2448         } else {
2449                 rs->sr_err = bsi.bsi_status;
2450         }
2451
2452 send_results:;
2453         if ( rs->sr_err != SLAPD_ABANDON ) {
2454                 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
2455                         send_paged_response( op, rs, NULL );
2456                 } else {
2457                         send_ldap_result( op, rs );
2458                 }
2459         }
2460
2461         /* cleanup in case of abandon */
2462         for ( ; eid != NULL; 
2463                 eid = backsql_free_entryID(
2464                         eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
2465                 ;
2466
2467         backsql_entry_clean( op, &base_entry );
2468
2469         /* in case we got here accidentally */
2470         backsql_entry_clean( op, &user_entry );
2471
2472         if ( rs->sr_v2ref ) {
2473                 ber_bvarray_free( rs->sr_v2ref );
2474                 rs->sr_v2ref = NULL;
2475         }
2476
2477 #ifdef BACKSQL_SYNCPROV
2478         if ( op->o_sync ) {
2479                 Operation       op2 = *op;
2480                 SlapReply       rs2 = { 0 };
2481                 Entry           *e = entry_alloc();
2482                 slap_callback   cb = { 0 };
2483
2484                 op2.o_tag = LDAP_REQ_ADD;
2485                 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
2486                 op2.ora_e = e;
2487                 op2.o_callback = &cb;
2488
2489                 ber_dupbv( &e->e_name, op->o_bd->be_suffix );
2490                 ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
2491
2492                 cb.sc_response = slap_null_cb;
2493
2494                 op2.o_bd->be_add( &op2, &rs2 );
2495
2496                 if ( op2.ora_e == e )
2497                         entry_free( e );
2498         }
2499 #endif /* BACKSQL_SYNCPROV */
2500
2501 done:;
2502         (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2503
2504         if ( bsi.bsi_attrs != NULL ) {
2505                 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2506         }
2507
2508         if ( !BER_BVISNULL( &nbase )
2509                         && nbase.bv_val != op->o_req_ndn.bv_val )
2510         {
2511                 ch_free( nbase.bv_val );
2512         }
2513
2514         /* restore scope ... FIXME: this should be done before ANY
2515          * frontend call that uses op */
2516         if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2517                 op->ors_scope = LDAP_SCOPE_BASE;
2518         }
2519
2520         Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2521
2522         return rs->sr_err;
2523 }
2524
2525 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2526  */
2527 int
2528 backsql_entry_get(
2529                 Operation               *op,
2530                 struct berval           *ndn,
2531                 ObjectClass             *oc,
2532                 AttributeDescription    *at,
2533                 int                     rw,
2534                 Entry                   **ent )
2535 {
2536         backsql_srch_info       bsi = { 0 };
2537         SQLHDBC                 dbh = SQL_NULL_HDBC;
2538         int                     rc;
2539         SlapReply               rs = { 0 };
2540         AttributeName           anlist[ 2 ];
2541
2542         *ent = NULL;
2543
2544         rc = backsql_get_db_conn( op, &dbh );
2545         if ( rc != LDAP_SUCCESS ) {
2546                 return rc;
2547         }
2548
2549         if ( at ) {
2550                 anlist[ 0 ].an_name = at->ad_cname;
2551                 anlist[ 0 ].an_desc = at;
2552                 BER_BVZERO( &anlist[ 1 ].an_name );
2553         }
2554
2555         bsi.bsi_e = entry_alloc();
2556         rc = backsql_init_search( &bsi,
2557                         ndn,
2558                         LDAP_SCOPE_BASE, 
2559                         (time_t)(-1), NULL,
2560                         dbh, op, &rs, at ? anlist : NULL,
2561                         BACKSQL_ISF_GET_ENTRY );
2562
2563         if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2564                 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
2565         }
2566
2567         if ( rc == LDAP_SUCCESS ) {
2568
2569 #if 0 /* not supported at present */
2570                 /* find attribute values */
2571                 if ( is_entry_alias( bsi.bsi_e ) ) {
2572                         Debug( LDAP_DEBUG_ACL,
2573                                 "<= backsql_entry_get: entry is an alias\n",
2574                                 0, 0, 0 );
2575                         rc = LDAP_ALIAS_PROBLEM;
2576                         goto return_results;
2577                 }
2578 #endif
2579
2580                 if ( is_entry_referral( bsi.bsi_e ) ) {
2581                         Debug( LDAP_DEBUG_ACL,
2582                                 "<= backsql_entry_get: entry is a referral\n",
2583                                 0, 0, 0 );
2584                         rc = LDAP_REFERRAL;
2585                         goto return_results;
2586                 }
2587
2588                 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2589                         Debug( LDAP_DEBUG_ACL,
2590                                         "<= backsql_entry_get: "
2591                                         "failed to find objectClass\n",
2592                                         0, 0, 0 ); 
2593                         rc = LDAP_NO_SUCH_ATTRIBUTE;
2594                         goto return_results;
2595                 }
2596
2597                 *ent = bsi.bsi_e;
2598         }
2599
2600 return_results:;
2601         if ( bsi.bsi_attrs != NULL ) {
2602                 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2603         }
2604
2605         if ( rc != LDAP_SUCCESS ) {
2606                 if ( bsi.bsi_e ) {
2607                         entry_free( bsi.bsi_e );
2608                 }
2609         }
2610
2611         return rc;
2612 }
2613
2614 void
2615 backsql_entry_clean(
2616                 Operation       *op,
2617                 Entry           *e )
2618 {
2619         void *ctx;
2620
2621         ctx = ldap_pvt_thread_pool_context();
2622
2623         if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2624                 if ( !BER_BVISNULL( &e->e_name ) ) {
2625                         op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2626                         BER_BVZERO( &e->e_name );
2627                 }
2628
2629                 if ( !BER_BVISNULL( &e->e_nname ) ) {
2630                         op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2631                         BER_BVZERO( &e->e_nname );
2632                 }
2633         }
2634
2635         entry_clean( e );
2636 }
2637
2638 int
2639 backsql_entry_release(
2640                 Operation       *op,
2641                 Entry           *e,
2642                 int             rw )
2643 {
2644         backsql_entry_clean( op, e );
2645
2646         ch_free( e );
2647
2648         return 0;
2649 }
2650
2651
2652 /* This function is copied verbatim from back-bdb/search.c */
2653 static int
2654 parse_paged_cookie( Operation *op, SlapReply *rs )
2655 {
2656         LDAPControl     **c;
2657         int             rc = LDAP_SUCCESS;
2658         ber_tag_t       tag;
2659         ber_int_t       size;
2660         BerElement      *ber;
2661         struct berval   cookie = BER_BVNULL;
2662         PagedResultsState *ps = op->o_pagedresults_state;
2663
2664         /* this function must be invoked only if the pagedResults
2665          * control has been detected, parsed and partially checked
2666          * by the frontend */
2667         assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
2668
2669         /* look for the appropriate ctrl structure */
2670         for ( c = op->o_ctrls; c[0] != NULL; c++ ) {
2671                 if ( strcmp( c[0]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 )
2672                 {
2673                         break;
2674                 }
2675         }
2676
2677         if ( c[0] == NULL ) {
2678                 rs->sr_text = "missing pagedResults control";
2679                 return LDAP_PROTOCOL_ERROR;
2680         }
2681
2682         /* Tested by frontend */
2683         assert( c[0]->ldctl_value.bv_len > 0 );
2684
2685         /* Parse the control value
2686          *      realSearchControlValue ::= SEQUENCE {
2687          *              size    INTEGER (0..maxInt),
2688          *                              -- requested page size from client
2689          *                              -- result set size estimate from server
2690          *              cookie  OCTET STRING
2691          * }
2692          */
2693         ber = ber_init( &c[0]->ldctl_value );
2694         if ( ber == NULL ) {
2695                 rs->sr_text = "internal error";
2696                 return LDAP_OTHER;
2697         }
2698
2699         tag = ber_scanf( ber, "{im}", &size, &cookie );
2700
2701         /* Tested by frontend */
2702         assert( tag != LBER_ERROR );
2703         assert( size >= 0 );
2704
2705         /* cookie decoding/checks deferred to backend... */
2706         if ( cookie.bv_len ) {
2707                 PagedResultsCookie reqcookie;
2708                 if( cookie.bv_len != sizeof( reqcookie ) ) {
2709                         /* bad cookie */
2710                         rs->sr_text = "paged results cookie is invalid";
2711                         rc = LDAP_PROTOCOL_ERROR;
2712                         goto done;
2713                 }
2714
2715                 AC_MEMCPY( &reqcookie, cookie.bv_val, sizeof( reqcookie ));
2716
2717                 if ( reqcookie > ps->ps_cookie ) {
2718                         /* bad cookie */
2719                         rs->sr_text = "paged results cookie is invalid";
2720                         rc = LDAP_PROTOCOL_ERROR;
2721                         goto done;
2722
2723                 } else if ( reqcookie < ps->ps_cookie ) {
2724                         rs->sr_text = "paged results cookie is invalid or old";
2725                         rc = LDAP_UNWILLING_TO_PERFORM;
2726                         goto done;
2727                 }
2728
2729         } else {
2730                 /* Initial request.  Initialize state. */
2731 #if 0
2732                 if ( op->o_conn->c_pagedresults_state.ps_cookie != 0 ) {
2733                         /* There's another pagedResults control on the
2734                          * same connection; reject new pagedResults controls 
2735                          * (allowed by RFC2696) */
2736                         rs->sr_text = "paged results cookie unavailable; try later";
2737                         rc = LDAP_UNWILLING_TO_PERFORM;
2738                         goto done;
2739                 }
2740 #endif
2741                 ps->ps_cookie = 0;
2742                 ps->ps_count = 0;
2743         }
2744
2745 done:;
2746         (void)ber_free( ber, 1 );
2747
2748         return rc;
2749 }
2750
2751 /* This function is copied nearly verbatim from back-bdb/search.c */
2752 static void
2753 send_paged_response( 
2754         Operation       *op,
2755         SlapReply       *rs,
2756         unsigned long   *lastid )
2757 {
2758         LDAPControl     ctrl, *ctrls[2];
2759         BerElementBuffer berbuf;
2760         BerElement      *ber = (BerElement *)&berbuf;
2761         PagedResultsCookie respcookie;
2762         struct berval cookie;
2763
2764         Debug(LDAP_DEBUG_ARGS,
2765                 "send_paged_response: lastid=0x%08lx nentries=%d\n", 
2766                 lastid ? *lastid : 0, rs->sr_nentries, NULL );
2767
2768         BER_BVZERO( &ctrl.ldctl_value );
2769         ctrls[0] = &ctrl;
2770         ctrls[1] = NULL;
2771
2772         ber_init2( ber, NULL, LBER_USE_DER );
2773
2774         if ( lastid ) {
2775                 respcookie = ( PagedResultsCookie )(*lastid);
2776                 cookie.bv_len = sizeof( respcookie );
2777                 cookie.bv_val = (char *)&respcookie;
2778
2779         } else {
2780                 respcookie = ( PagedResultsCookie )0;
2781                 BER_BVSTR( &cookie, "" );
2782         }
2783
2784         op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
2785         op->o_conn->c_pagedresults_state.ps_count =
2786                 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
2787                 rs->sr_nentries;
2788
2789         /* return size of 0 -- no estimate */
2790         ber_printf( ber, "{iO}", 0, &cookie ); 
2791
2792         if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
2793                 goto done;
2794         }
2795
2796         ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
2797         ctrls[0]->ldctl_iscritical = 0;
2798
2799         rs->sr_ctrls = ctrls;
2800         rs->sr_err = LDAP_SUCCESS;
2801         send_ldap_result( op, rs );
2802         rs->sr_ctrls = NULL;
2803
2804 done:
2805         (void) ber_free_buf( ber );
2806 }